Aplicaciones

Los métodos numéricos validados tienen diversas aplicaciones, en teoría se podría escribir código en términos de artimética de intervalos para la mayoría, sino todos, de los actuales códigos numéricos. Se han creado librerías y paquetes en la mayoría de los lenguajes más importantes, los cuales están disponibles para su uso junto con una documentación detallada (en algunos casos).

Se han utilizado métodos numéricos validados para resolver y mejorar la solución a muchos problemas matemáticos, físicos y computacionales. Entre ellos:

  • Verificación de la existencia del atractor de Lorenz
  • Verificación de la existencia del caos
  • Optimización global
  • Estrategias precisas para las fronteras KAM y su implementación
  • Encontrar eigenfrecuencias de turbinas
  • Soluciones a problemas complicados con sistemas dinámicos

En física se han utilizado para encontrar órbitas periódicas en sistemas dinámicos, integración y evolución de ecuaciones diferenciales ordinarias en sistemas hamiltonianos, entre muchos otros.

Hay un problema común que se encuentra en muchas aplicaciones físicas, y es encontrar raíces de funciones en un intervalo dado. Como hemos demostrado, estos métodos validados permiten encontrar estas raíces rigurosamente, garantizando que no "perderemos" ningún cero en un intervalo dado, y que nos diga también rigurosamente si hay un único cero en una región dada, lamentablemente aún no se logra un algoritmo que pueda asegurar la unicidad de los ceros para cualquier función.

A continuación les mostraré un código que he desarrollado para encontrar raíces a funciones de una variable (actualmente la librería solo trabaja para funciones de una variable) utilizando métodos numéricos validados.

Función roots_interval

Breve digresión matemática

La función roots_interval utiliza el método de Newton de intervalos que vimos anteriormente. Hay que probar la existencia y unicidad de las raíces en el método de Newton de intervalos, así como su estabilidad para que pueda considerarse un método numérico valido y serio. Esto fue hecho por los que plantearon este algoritmo originalmente y algunos otros matemáticos, pueden encontrarse en el libro de Ramon Moore y algunos artículos de Warwick Tucker.

En resumen, se utilizan teoremas de punto fijo para probar la existencia y unicidad. En caso del algoritmo de intervalos de Newton, la existencia está dada por el teorema de punto fijo de Brouwer que nos dice si un conjunto $B$ es homeomorfo a la bola cerrada unitaria en $\mathbb{R}^n$, entonces dado un mapeo continuo $f: B \rightarrow B$ entonces $\exists x \in B$ tal que $f(x) = x$. Esto aplica a la última parte del método de Newton para intervalos, en el operado de Newton $\mathcal{N}$ para verificar la existencia de la raíz. La unicidad viene de que $f'\ne 0$ utilizando los teoremas de Schauder y Banach.

Antes de mostrarle la función y los resultados que obtuvieron, primero veamos algunos ejemplos de funciones polinomiales importantes, que pueden obtenerse utilizando las librerías Polynomials.jl y Jacobi.jl.


In [ ]:
using ValidatedNumerics, Polynomials, Jacobi, PyPlot, PyCall
@pyimport matplotlib.patches as patches

Librería Polynomials.jl

Esta librería permite evaluar los polinomios en algún valor dado, diferenciar polimonios, integrarlos, usar las operaciones básicas con ellos, y también encontrar raíces. Ahora encuentra raíces utilizando métodos normales, es decir sin aritmética de intervalos.


In [ ]:
# Construir polinomio 1 + 2x
Poly([1,2])

In [ ]:
# Construir polinomio 1 + 2x + 10x^2 + 22x^3 + 4x^4
Poly([1,2,10,22,4])

In [ ]:
# Puedo escoger la variable
Poly([1,2,10,22,4], :y)

In [ ]:
# Construir polinomio con sus raíces
# Lo siguiente representa (x-1)*(x-2)*(x-3)

poly([1,2,3])

Librería Jacobi.jl

Esta librería permite hacer operaciones y trabajar con los polinomios de orden $n$ de Jacobi, Legendre y los polinomios de Chebyshev de primero y segundo tipo; así como evaluarlos y obtener sus raíces (de nuevo con métodos usuales). Además utilizando la librería Polynomials.jl permite obtener expresiones para todos estos polinomios.


In [ ]:
poly_legendre(4)

In [ ]:
poly_chebyshev(3)

In [ ]:
poly_legendre(20)

In [ ]:
poly_chebyshev(24)

Graficando los polinomios de Legendre

Utilizando esta librería podemos graficar de una manera muy simple cualquiera de los polinomios que define. Hagamos un ejemplo, y grafiquemos los polimonios de legendre desde el primer al sexto orden.


In [ ]:
x = linspace(-1.0,1.0,401)
y1 = legendre(x,1)
y2 = legendre(x,2)
y3 = legendre(x,3)
y4 = legendre(x,4)
y5 = legendre(x,5)
y6 = legendre(x,6)
plot(x,y1,x,y2,x,y3,x,y4,x,y5,x,y6)
axhline(y=0.0, color="black")

"La función"

La función roots_interval que he creado recibe una función de una variable, un límite inferior y uno superior para definir el intervalo donde buscará los ceros. La función se le pasa como una función anónima (lambda function). Por adentro utiliza dos funciones de la librería ValidatedNumerics, uno es la ya vista newton que permite encontrar ceros para una función dada, y además utiliza la función find_roots_midpoint que ubica el punto medio del intervalo y da un valor concreto para el cero. Luego utilizando PyPlot grafica la función y sus ceros.

El cero que muestra la gráfica es el valor concreto del punto medio del invervalo donde se encuentra el cero, pero debe recordarse que se están utilizando métodos numéricos validados y por lo tanto nos interesa es el intervalo donde se encuentra el cero, ya que está garantizado que adentro del mismo está el cero real. La función también imprime el intervalo donde están los ceros.


In [ ]:
function roots_interval(fun,infBound,supBound; 
    steps=Int((supBound-infBound)*100),lineColor="b",zerosColor="rs")
    try
        x = linspace(infBound,supBound,steps)
        result = fun(x)
        ro = newton(fun,@interval(infBound,supBound))
        z = find_roots_midpoint(fun,infBound,supBound)[1]
        plot(x,result,lineColor)
        plot(z,zeros(z),zerosColor)
        axhline(y=0.0, color="black")
        ro
    catch err
        if isa(err,BoundsError)
            print("There are no zeros in the interval ","[",infBound,",",supBound,"]")
        elseif isa(err,UndefVarError)
            print("Function is not correctly written. Check you variable.")
        elseif isa(err,MethodError)
            print("Function is not correctly written. Check your methods.")
        end
    end
end

Resultados

Ejemplo simple, ceros del polinomio de Legendre de cuarto orden

$$ P_4(x) = \frac{1}{8}(35x^4 - 30x^2 + 3) $$

In [ ]:
# Obtención del polimonio con Jacobi.jl

poly_legendre(4)

In [ ]:
roots_interval(x -> 0.375 - 3.75x.^2 + 4.375x.^4,-1,1)

La función valida que la función que se le ingresa esté bien escrita, y también le dice al usuario si no existen ceros en el intervalo que se le está pasando.


In [ ]:
roots_interval(x -> cos(x) + x.^4,-1,1)

In [ ]:
plot(x,cos(x) + x.^4,-1,1)

Algunos casos más complicados


In [ ]:
roots_interval(x -> sin(2x) + cos(3x),-5,5)

In [ ]:
roots_interval(x -> x.^7 - 28x.^6 + 322x.^5
- 1960x.^4+6769x.^3-13132x.^2+13068x -5040,-5,10)

Función roots_interval_rectangle

Aunque hemos graficado el punto medio del intervalo, que nos da la raíz a una alta precisión, sería interesante poder graficar la función y los intervalos donde se encuentra la raíz, en vez de el punto medio de los mismos. Tomando en consideración esto, nace la función roots_interval_rectangle que permite hacer lo mismo que la función roots_interval pero grafica el intervalo donde se encuentra la raíz y no el punto intermedio donde está.


In [ ]:
function draw_rectangle(x, y, xwidth, yheight, color="green")
    rectangle = patches.Rectangle
    ax = gca()
    ax[:add_patch](rectangle((x, y), xwidth, yheight, facecolor=color, alpha=0.5))
end

In [ ]:
function roots_interval_rectangle(fun,infBound,supBound; 
    steps=Int((supBound-infBound)*100),lineColor="b",zerosColor="rs")
    try
        z = linspace(infBound,supBound,steps)
        result = fun(z)
        ro = newton(fun,@interval(infBound,supBound))
        roots = map(i -> (i.interval.lo,i.interval.hi),ro)
        for r in roots
            xwidth = fun(r[1])+0.07
            x = r[1]-xwidth/2
            y = - abs(fun(x))
            yheight = abs(y)*1.8
            draw_rectangle(x,y,xwidth,yheight)
        end
        plot(z,result,lineColor)
        axhline(y=0.0, color="black")
        ro
    catch err
        if isa(err,BoundsError)
            print("There are no zeros in the interval ","[",infBound,",",supBound,"]")
        elseif isa(err,UndefVarError)
            print("Function is not correctly written. Check you variable.")
        elseif isa(err,MethodError)
            print("Function is not correctly written. Check your methods.")
        end
    end
end

In [ ]:
roots_interval_rectangle(x -> 0.375 - 3.75x.^2 + 4.375x.^4,-1,1)

La parte más peligrosa de la charla

¿Alguna función que quieran probar para encontrar sus raíces?

$$ \Large{\text{roots_interval}(x \rightarrow ¿¿??,a,b)} $$

In [ ]:
roots_interval(x -> ?? ,a,b)