Categoría: Unidades

Unidades de estudio

  • 4.4 Trazadores lineales (Splines) grado1 con Python

    [ Trazador Lineal ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    ..


    1. Trazadores lineales (Splines) grado1

    Referencia: Chapra 18.6.1 p525
    spline 01

    El concepto de trazador se originó en la técnica de dibujo que usa una cinta delgada y flexible (spline) para dibujar curvas suaves a través de un conjunto de puntos.

    La unión más simple entre dos puntos es una línea recta. El método crea un polinomio para cada par de puntos consecutivos en el intervalo, por lo que el resultado será una tabla de polinomios.

    spline 02

    Los trazadores de primer grado para un grupo de datos ordenados pueden definirse como un conjunto de funciones lineales.

    f_0(x) = f(x_0) + m_0(x-x_0), x_0\leq x\leq x_1 f_1(x) = f(x_1) + m_1(x-x_1), x_1\leq x\leq x_2

    ...

    f_n(x) = f(x_{n-1}) + m_{n-1}(x-x_{n-1}), x_{n-1}\leq x\leq x_n

    donde

    m_i = \frac{f(x_{i+1}) - f(x_i)}{(x_{i+1}-x_i)}

    Observe que la expresión de f(x) para un tramo entre dos puntos es el polinomio de grado 1 realizado con diferencia finita avanzadas  o las diferencias divididas.

    Las ecuaciones se pueden usar para evaluar la función en cualquier punto entre x0 y xn. Al localizar primero el intervalo dentro del cual está el punto, puede seleccionar el polinomio que corresponde a ese tramo.

    [ Trazador Lineal ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    ..


    2. Ejercicio

    Datos de los puntos como ejemplo para el algoritmo

    xi = [0.1 , 0.2, 0.3, 0.4]
    fi = [1.45, 1.8, 1.7, 2.0]

    [ Trazador Lineal ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]

    ..


    3. Desarrollo analítico

    El método con trazadores lineales, permite plantear los bloques necesarios para manejar varios polinomios, uno por cada tramo entre dos puntos dentro del intervalo del problema

    0.1 ≤ x≤ 0.2

    m_0= \frac{1.8 - 1.45}{0.2-0.1} = 3.5 f_0(x) = 1.45 + 3.5(x-0.1)

    0.2 ≤ x ≤ 0.3

    m_1= \frac{1.7 - 1.8}{0.3-0.2} = -1 f_1(x) = 1.8 + -1(x-0.2)

    0.3 ≤ x ≤ 0.4

    m_2= \frac{2 - 1.7}{0.4-0.3} = 3 f_3(x) = 1.7 + 3(x-0.3)

    El método permitirá disponer de un punto de partida para trazadores de mayor grado, por ejemplo los cúbicos.

    spline 02

    [ Trazador Lineal ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    ..


    4. Algoritmo en Python

    Los polinomios de cada tramo se almacenan en una tabla, cada uno puede ser utilizado individualmente en su respectivo tramo, por ejemplo para realizar la gráfica de línea entre tramos.

    # Trazador (spline) lineal, grado 1
    import numpy as np
    import sympy as sym
    
    # INGRESO , Datos de prueba
    xi = [0.1 , 0.2, 0.3, 0.4]
    fi = [1.45, 1.8, 1.7, 2.0]
    
    # PROCEDIMIENTO
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    
    # trazador lineal, grado 1
    x = sym.Symbol('x')
    px_tabla = [] # por cada tramo
    
    i = 0 # tramo inicial px = f0 +d1f*(x-x0)
    while i<(n-1):
        # con 1ra diferencia finita avanzada
        d1f =(fi[i+1]-fi[i])/(xi[i+1]-xi[i])
        pxtramo = fi[i] + d1f*(x-xi[i])
        px_tabla.append(pxtramo)
        i = i + 1
    
    # SALIDA
    print('Polinomios por tramos: ')
    for i in range(0,n-1,1):
        print('x = [',xi[i],',',xi[i+1],']')
        print('',px_tabla[i])
    

    Se obtiene como resultado:

    Polinomios por tramos: 
    x = [ 0.1 , 0.2 ]
     3.5*x + 1.1
    x = [ 0.2 , 0.3 ]
     2.0 - 1.0*x
    x = [ 0.3 , 0.4 ]
     3.0*x + 0.8
    >>> 
    

    [ Trazador Lineal ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    ..


    5. Gráfica con Python

    Para añadir la gráfica se añaden las instrucciones para:

    • evaluar el polinomio en cada tramo
    • concatenar los resultados de todos los tramos en los vectores xtraza, ytraza.
    • poner en la gráfica los puntos del problema y las líneas que genera cada polinomio
    # GRAFICA --------------
    import matplotlib.pyplot as plt
    
    muestras = 5 # entre cada par de puntos
    # Puntos para graficar cada tramo
    xtraza = np.array([],dtype=float)
    ytraza = np.array([],dtype=float)
    i = 0
    while i<(n-1): # cada tramo
        xtramo = np.linspace(xi[i],xi[i+1],muestras)
        # evalua polinomio del tramo
        pxt = sym.lambdify('x',px_tabla[i])
        ytramo = pxt(xtramo)
        # vectores de trazador en x,y
        xtraza = np.concatenate((xtraza,xtramo))
        ytraza = np.concatenate((ytraza,ytramo))
        i = i + 1
    
    # Graficar
    plt.plot(xi,fi,'o', label='puntos')
    plt.plot(xtraza,ytraza, label='trazador')
    plt.title('Trazadores lineales (splines)')
    plt.xlabel('xi')
    plt.ylabel('px(xi)')
    plt.legend()
    plt.show()

    [ Trazador Lineal ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]

  • 4.3 Interpolación polinómica de Lagrange con Python

    [ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]
    ..


    1. Interpolación polinómica de Lagrange

    Referencia: Chapra 18.2 p516, Burden 3.1 p80, Rodríguez 6.2 p195

    El polinomio de interpolación de Lagrange reformula el polinomio de interpolación de Newton evitando el cálculo de la tabla de diferencias divididas. El método de Lagrange tolera las diferencias entre distancias x de los puntos de muestra.

    El polinomio de Lagrange se construye a partir de las fórmulas:

    f_{n} (x) = \sum_{i=0}^{n} L_{i} (x) f(x_{i}) L_{i} (x) = \prod_{j=0, j \neq i}^{n} \frac{x-x_j}{x_i - x_j}

    Donde una vez que se han seleccionado los puntos a usar que generan la misma cantidad de términos que puntos.

    interpola Lagrange 01

    [ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]
    ..


    2. Ejercicio

    Dados los 4 puntos en la tabla se requiere un polinomio de grado 3.

    p(x) = a_0 x^3 + a_1 x^2 + a_2 x^1 + a_3
    xi 0 0.2 0.3 0.4
    fi 1 1.6 1.7 2.0

    [ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]

    ..


    3. Desarrollo analítico

    Se calculan cuatro términos de Lagrange según las fórmulas,

    término 1

    L_{0} (x) = \frac{(x-0.2)(x-0.3)(x-0.4)}{(0-0.2)(0-0.3)(0-0.4)}

    término 2

    L_{1} (x) = \frac{(x-0)(x-0.3)(x-0.4)}{(0.2-0)(0.2-0.3)(0.2-0.4)}

    término 3

    L_{2} (x) = \frac{(x-0)(x-0.2)(x-0.4)}{(0.3-0)(0.3-0.2)(0.3-0.4)}

    término 4

    L_{3} (x) = \frac{(x-0)(x-0.2)(x-0.3)}{(0.4-0)(0.4-0.2)(0.4-0.3)}

    se construye el polinomio usando la fórmula para fn(x) para cada valor fi,

    p_3(x) = 1 L_{0} (x) + 1.6 L_{1} (x) + 1.7 L_{2} (x) + 2 L_{3} (x) p_3(x) = 1 \frac{(x-0.2)(x-0.3)(x-0.4)}{(0-0.2)(0-0.3)(0-0.4)} + + 1.6 \frac{(x-0)(x-0.3)(x-0.4)}{(0.2-0)(0.2-0.3)(0.2-0.4)} + 1.7 \frac{(x-0)(x-0.2)(x-0.4)}{(0.3-0)(0.3-0.2)(0.3-0.4)} + 2 \frac{(x-0)(x-0.2)(x-0.3)}{(0.4-0)(0.4-0.2)(0.4-0.3)}

    La simplificación de la expresión del polinomio se puede dejar como tarea o realizarla con Sympy con la instrucción sym.expand().

    p_3(x) = 41.6666 x^3 - 27.5 x^2 + 6.8333 x + 1

    Una forma de verificar que el polinomio es correcto es usar un punto original xi[i] y comprobar que la evaluación tiene como resultado fi[i].

    interpola lagrange 01

    [ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]
    ..


    3. Algoritmo en Python

    En el algoritmo en Python, para construir las expresiones de cada término de Lagrange se usa la forma simbólica con Sympy, con los datos:

    xi = [0, 0.2, 0.3, 0.4]
    fi = [1, 1.6, 1.7, 2.0]
    

    En cada término Li(x) se usan todos los elementos i , excepto el mismo elemento i, en el numerador y denominador de la expresión.

    En polinomio se agrupan todos los términos multiplicados por su respectivo valor fi[i].

        valores de fi:  [1.  1.6 1.7 2. ]
    divisores en L[i]:  [-0.024  0.004 -0.003  0.008]
    
    Polinomio de Lagrange, expresiones
    250.0*x*(x - 0.3)*(x - 0.2) +
    400.0*x*(x - 0.4)*(x - 0.3) +
    -566.666666666667*x*(x - 0.4)*(x - 0.2) +
    -41.6666666666667*(x - 0.4)*(x - 0.3)*(x - 0.2)
    
    Polinomio de Lagrange: 
    41.6666666666667*x**3 - 27.5*x**2 + 6.83333333333334*x + 1.0
    
                      3         2                           
    41.6666666666667⋅x  - 27.5⋅x  + 6.83333333333334⋅x + 1.0
    

    Las expresiones del polinomio contiene los binomios en su forma básica, para resolver y simplificar las ecuaciones se usa polinomio.expand().

    Para realizar la gráfica del polinomio es conveniente convertirlo a la forma lambda con Numpy, de esta forma se evalúa en una sola linea todos los puntos para el intervalo [a,b] en x.

    # Interpolacion de Lagrange
    # divisoresL solo para mostrar valores denominador
    import numpy as np
    import sympy as sym
    
    # INGRESO , Datos de prueba
    xi = [0, 0.2, 0.3, 0.4]
    fi = [1, 1.6, 1.7, 2.0]
    
    # PROCEDIMIENTO
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    
    # Polinomio de Lagrange
    x = sym.Symbol('x')
    polinomio = 0*x   # sym.S.Zero en Sympy
    divisorL = np.zeros(n, dtype = float)
    for i in range(0,n,1):
        
        # Termino de Lagrange
        numerador = 1
        denominador = 1
        for j  in range(0,n,1):
            if (j!=i):
                numerador = numerador*(x-xi[j])
                denominador = denominador*(xi[i]-xi[j])
        terminoLi = numerador/denominador
    
        polinomio = polinomio + terminoLi*fi[i]
        divisorL[i] = denominador
    
    polisimple = polinomio.expand() # simplifica los (x-xi)
    px = sym.lambdify(x,polisimple) # evaluacién numérica
    
    # SALIDA
    print('    valores de fi: ',fi)
    print('divisores en L[i]: ',divisorL)
    print()
    print('Polinomio de Lagrange, expresiones')
    #print(polinomio)
    terminos = sym.Add.make_args(polinomio)
    n_term = len(terminos)
    for i in range(0,n_term,1):
        if i<(n_term-1):
            print(terminos[i],'+')
        else:
            print(terminos[i])
    print()
    print('Polinomio de Lagrange: ')
    print(polisimple)
    print()
    sym.pprint(polisimple)
    

    [ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]
    ..


    4. Gráfica del polinomio

    Para la gráfica se añaden las siguientes instrucciones

    # Gráfica --------------
    import matplotlib.pyplot as plt
    muestras = 21   # resolución gráfica
    
    a = np.min(xi)  # intervalo [a,b]
    b = np.max(xi)
    xk = np.linspace(a,b,muestras)
    yk = px(xk)
    
    plt.plot(xi,fi,'o', label = '[xi,fi]')
    plt.plot(xk,yk, label = 'p(x)')
    plt.legend()
    plt.xlabel('xi')
    plt.ylabel('fi')
    plt.title('Interpolación Lagrange')
    plt.show()
    

    [ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]
    ..


    5.Función en librería Scipy.interpolate.lagrange

    >>> import scipy as sp
    >>> xi = [0,0.2,0.3,0.4]
    >>> fi = [1,1.6,1.7,2.0]
    >>> sp.interpolate.lagrange(xi,fi)
    poly1d([ 41.66666667, -27.5       ,   6.83333333,   1.        ])
    >>> 
    

    [ Interpola Lagrange ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ función ]

     

  • 4.2.2 Interpolación por Diferencias divididas de Newton (Δx) con Python

    [ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]
    ..


    1. Diferencias divididas de Newton

    Referencia: Chapra 18.1.3 p508, Rodríguez 6.7 p223, Burden 9Ed 3.3 p124.

    El método se usa en el caso que los puntos en el "eje x" se encuentran espaciados de forma arbitraria y provienen de una función desconocida pero supuestamente diferenciable.

    Diferencias Divididas de Newton interpolación polinómica

    La n-ésima diferencia dividida finita es:

    f[x_{n}, x_{n-1}, \text{...}, x_{1}, x_{0}] = \frac{f[x_{n}, x_{n-1}, \text{...}, x_{1}]- f[x_{n-1}, x_{n-2}, \text{...}, x_{0}]}{x_{n}-x_{0}}

    Para lo cual se debe interpretar la tabla de diferencias divididas, también como una aproximación a una derivada:

    i xi f[xi] Primero Segundo Tercero
    0 x0 f[x0] f[x1,x0] f[x2,x1,x0] f[x3,x2,x1,x0]
    1 x1 f[x1] f[x2,x1] f[x3,x2,x1]
    2 x2 f[x2] f[x3,x2]
    3 x2 f[x3]

    En la fórmula del polinomio, las diferencias divididas sirven para evaluar los coeficientes de cada término adicional para aumentar el grado. Semejante al proceso realizado para  "diferencias finitas divididas":

    f_n(x) = f(x_0)+(x-x_0)f[x_1,x_0] + + (x-x_0)(x-x_1)f[x_2,x_1,x_0] + \text{...}+ + (x-x_0)(x-x_1)\text{...}(x-x_{n-1})f[x_n,x_{n-1},\text{...},x_0]

    Este polinomio de interpolación se conoce como de Newton en diferencias divididas.

    [ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]
    ..


    2. Ejercicio

    Referencia: 1Eva_IIT2008_T3_MN Ganancia en inversión inversion Ganancia 01

    Se dispone de los datos (x, f(x)), en donde x es un valor de inversión y f(x) es un valor de ganancia, ambos en miles de dólares:

    inversión ganancia
    3.2 5.12
    3.8 6.42
    4.2 7.25
    4.5 6.85

    Considere que los valores invertidos en materia prima para producción, dependen de la demanda de del producto en el mercado, motivo por el que los valores de inversión no guardan distancias equidistantes entre si.

    [ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]

    ..


    3. Desarrollo analítico

    Se toman los datos de la tabla como arreglos para el algoritmo

    xi = [3.2 , 3.8 , 4.2 , 4.5 ]
    fi = [5.12, 6.42, 7.25, 6.85]

    Con los datos se llena la tabla de diferencias divididas, donde por simplicidad, se escriben las operaciones en cada casilla. La última columna o cuarta diferencia dividida es cero por no disponer de datos para hacer el cálculo.

    i xi f[xi] Primero Segundo Tercero
    0 3.2 5.12 \frac{6.42-5.12}{3.8-3.2} =2.1667 \frac{2.075-2.1667}{4.2-3.2} =-0.0917 \frac{-4.869-(-0.0917)}{4.5-3.2} =-3.6749
    1 3.8 6.42 \frac{7.25-6.42}{4.2-3.8} =2.075 \frac{-1.3333-2.075}{4.5-3.8} =-4.869
    2 4.2 7.25 \frac{6.85-7.25}{4.5-4.2} =-1.3333
    3 4.5 6.85

    Las diferencias divididas de la primera fila son los valores usados para la expresión del polinomio de interpolación:

    ddividida = tabla[0,3:] 
              = [ 2.1667, -0.0917, -3.6749, 0. ]

    La expresión del polinomio inicia con el primer valor de la función f(x0).

    p_3(x) = f_0 = 5.12

    se calcula el primer término usando el factor de la primera diferencia dividida y se añade a la expresión del polinomio.

    término = (x-x_0)f[x1,x0] = (x-3.2)2.1667 p_3(x) = 5.12 + 2.1667(x-3.2)

    con el siguiente valor de ddividida[] se procede de manera semejante:

    término = (x-x_0)(x-x_1)f[x1,x0] = = (x-3.2)(x-3.8)(-0.0917) p_3(x) = 5.12 + 2.1667(x-3.2)+ + (-0.0917)(x-3.2)(x-3.8)

    Realizando el cálculo del tercer y último termino con diferencias divididas,  el polinomio obtenido es:

    p_3(x) = 5.12 + 2.1667(x-3.2) + + (-0.0917)(x-3.2)(x-3.8) + + (-3.6749)(x - 3.2)(x - 3.8)(x - 4.2)

    que simplificando al multiplicar entre los términos (x-xi):

    p_3(x) = 184.7569 - 149.9208 x + 41.0673 x^2 - 3.6749 x^3

    El polinomio en el intervalo de xi, en la gráfica muestra que pasa por todos los puntos.

    Diferencias Divididas 02

    [ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]
    ..


    4. Algoritmo en Python

    El algoritmo para  interpolación de Diferencias Divididas o Newton, considera reutilizar el procedimiento de cálculo de diferencias finitas incorporando la parte del denominador.

    La creación de la expresión del polinomio también es semejante a la usada para diferencias finitas avanzadas.

    Se incorpora la parte gráfica para observar los resultados en el intervalo xi, con el número de muestras = 101 para tener una buena resolución de la línea del polinomio.

    # Diferencias Divididas de Newton -Interpolación
    # Tarea: Verificar tamaño de vectores,
    import numpy as np
    import sympy as sym
    
    # INGRESO
    xi = [3.2 , 3.8 , 4.2 , 4.5 ]
    fi = [5.12, 6.42, 7.25, 6.85]
    
    # PROCEDIMIENTO
    casicero = 1e-15  # redondear a cero
    # Matrices como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    
    # Tabla de Diferencias Divididas
    tabla_etiq = ['i   ','xi  ','fi  ']
    ki = np.arange(0,n,1) # filas
    tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
    tabla = np.transpose(tabla)
    dfinita = np.zeros(shape=(n,n),dtype=float)
    tabla = np.concatenate((tabla,dfinita), axis=1)
    n,m = np.shape(tabla)
    # Calcula tabla de Diferencias Divididas
    diagonal = n-1 # derecha a izquierda
    j = 3  # inicia en columna 3
    while (j < m): # columna
        tabla_etiq.append('F['+str(j-2)+']') # título columna
        i = 0 # cada fila de columna
        paso = j-2 # inicia en 1
        while (i < diagonal): # antes de diagonal
            tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]
    
            denominador = (xi[i+paso]-xi[i])
            tabla[i,j] = tabla[i,j]/denominador
    
            if abs(tabla[i,j])<casicero: # casicero revisa
                tabla[i,j]=0
            i = i + 1
        diagonal = diagonal - 1
        j = j + 1
        
    dDividida = tabla[0,3:] # diferencias Divididas
    
    # polinomio con diferencias Divididas de Newton
    x = sym.Symbol('x')
    polinomio = fi[0] +0*x # en Sympy
    for i in range(1,n,1):
        factor = dDividida[i-1]
        termino = 1
        for j in range(0,i,1):
            termino = termino*(x-xi[j])
        polinomio = polinomio + termino*factor
    
    polisimple = polinomio.expand() # simplifica los (x-xi)
    px = sym.lambdify(x,polisimple) # evaluación numérica
    
    # SALIDA
    np.set_printoptions(precision = 4)
    print('Tabla Diferencia Dividida-Newton')
    print([tabla_etiq])
    print(tabla)
    print('dDividida: ')
    print(dDividida)
    print('polinomio: ')
    print(polinomio)
    print('polinomio simplificado: ' )
    print(polisimple)
    

    El resultado del algoritmo se muestra a continuación:

    Tabla Diferencia Dividida-Newton
    [['i   ', 'xi  ', 'fi  ', 'F[1]', 'F[2]', 'F[3]', 'F[4]']]
    [[ 0.      3.2     5.12    2.1667 -0.0917 -3.6749  0.    ]
     [ 1.      3.8     6.42    2.075  -4.869   0.      0.    ]
     [ 2.      4.2     7.25   -1.3333  0.      0.      0.    ]
     [ 3.      4.5     6.85    0.      0.      0.      0.    ]]
    dDividida: 
    [ 2.1667 -0.0917 -3.6749  0.    ]
    polinomio: 
    2.16666666666667*x - 3.67490842490842*(x-4.2)*(x-3.8)*(x-3.2)
     - 0.0916666666666694*(x - 3.8)*(x - 3.2) - 1.81333333333334
    polinomio simplificado: 
    -3.67490842490842*x**3 + 41.0673076923077*x**2 
    - 149.920860805861*x + 184.756923076923
    >>> 
    

    [ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]
    ..


    5. Gráfica de polinomio de interpolación

    # GRAFICA  --------------
    import matplotlib.pyplot as plt
    
    titulo = 'Interpolación: Diferencias Divididas - Newton'
    muestras = 21  # resolución gráfica
    
    a = np.min(xi) # intervalo [a,b]
    b = np.max(xi)
    xk = np.linspace(a,b,muestras)
    yk = px(xk)
    
    plt.plot(xi,fi,'o', label='[xi,fi]')
    plt.plot(xk,yk, label='p(x)')
    
    try: # existen mensajes de error
        msj_existe = len(msj)
    except NameError:
        msj = []
    if len(msj)>0: # tramos con error
        untipo = msj[0][0]
        donde = msj[0][1] # indice error
        plt.plot(xi[donde:donde+2],fi[donde:donde+2],
                 'ro',label=untipo)
        plt.plot(xi[donde:donde+2],fi[donde:donde+2],
                 'r--')
    
    plt.xlabel('xi')
    plt.ylabel('fi')
    plt.legend()
    plt.title(titulo)
    plt.show()
    

    [ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]
    ..


    6. Algoritmo como función

    Se reordena el algoritmo para usar funciones, la gráfica es la misma que para el algoritmo sin funciones anterior

    El bloque de salida es semejante al resultado anterior.

    # Diferencias Divididas de Newton - Interpolación
    # funciones diferencias_tabla
    # Tarea: Verificar tamaño de vectores
    import numpy as np
    import math
    import sympy as sym
    
    def diferencias_tabla(xi,fi,tipo='finitas', vertabla=False,
                    casicero = 1e-15, precision=4):
        '''Genera la tabla de diferencias finitas o divididas
          tipo = 'finitas, tipo = 'divididas'
        resultado en: tabla, titulo
        Tarea: verificar tamaño de vectores
        '''
        # revisa tipo de tabla
        tipolist = ['finitas','divididas']
        if not(tipo in tipolist):
            print('error de tipo, seleccione:',tipolist)
            return()
        
        prefijo = ['d','f']
        if tipo=='divididas':
            prefijo = ['F[',']']
        
        # Matrices como arreglo, numeros reales
        xi = np.array(xi,dtype=float)
        fi = np.array(fi,dtype=float)
        n = len(xi)
    
        # Tabla de Diferencias Finitas/Divididas
        tabla_etiq = ['i','xi','fi']
        ki = np.arange(0,n,1) # filas
        tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
        tabla = np.transpose(tabla)
        dfinita = np.zeros(shape=(n,n),dtype=float)
        tabla = np.concatenate((tabla,dfinita), axis=1)
        n,m = np.shape(tabla)
        # Calular tabla de Diferencias Finitas/Divididas
        diagonal = n-1 # derecha a izquierda
        j = 3  # inicia en columna 3
        while (j < m): # columna
            tabla_etiq.append(prefijo[0]+str(j-2)+prefijo[1])
            # paso = j-2 # inicia en 1
            i = 0 # fila
            while (i < diagonal): # antes de diagonal
                tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]
    
                if tipo=='divididas':
                    # denominador = xi[i+paso]-xi[i]
                    denominador = xi[i+j-2]-xi[i]
                    tabla[i,j] = tabla[i,j]/denominador
                    
                if abs(tabla[i,j])<casicero: # casicero revisa
                        tabla[i,j]=0
                i = i + 1
            diagonal = diagonal - 1
            j = j + 1
                
        if vertabla==True:
            np.set_printoptions(precision)
            print('Tabla de Diferencias',tipo)
            print([tabla_etiq])
            print(tabla)
        return([tabla, tabla_etiq])
    
    # PROGRAMA ---------------------
    
    # INGRESO
    xi = [3.2, 3.8, 4.2, 4.5]
    fi = [5.12, 6.42, 7.25, 6.85]
    
    tipo_tabla = 'divididas'
    
    # PROCEDIMIENTO
    casicero = 1e-12
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    
    tabla,tabla_etiq = diferencias_tabla(xi,fi,
                            tipo=tipo_tabla,
                            vertabla=True,casicero = casicero)
    
    dfinita = tabla[0,3:] # diferencias divididas
    h = xi[1] - xi[0]     # suponer tramos equidistantes
    
    # polinomio con diferencias Finitas Avanzadas/ divididas
    x = sym.Symbol('x')
    polinomio = fi[0] +0*x # sym.S.Zero en Sympy
    for i in range(1,n,1):
        factor = dfinita[i-1] # diferencias divididas
        if tipo_tabla=='finitas': # diferencias Finitas Avanzadas
            denominador = math.factorial(i)*(h**i)
            factor = factor/denominador
        termino = 1
        for j in range(0,i,1):
            termino = termino*(x-xi[j])
        polinomio = polinomio + termino*factor
    
    polisimple = polinomio.expand() # simplifica los (x-xi)
    px = sym.lambdify(x,polisimple) # evaluacion numerica
    
    # SALIDA
    print('d'+tipo_tabla+': ')
    print(dfinita)
    print('Tramo h:',h)
    print('polinomio: ')
    #print(polinomio)
    terminos = sym.Add.make_args(polinomio)
    n_term = len(terminos)
    for i in range(0,n_term,1):
        if i<(n_term-1):
            print(terminos[i],'+')
        else:
            print(terminos[i])
    print()
    print('polinomio simplificado: ' )
    print(polisimple)
    

    La gráfica se realiza con el bloque de la sección anterior


    [ Dif_Divididas_Newton] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]

  • 4.2.1 Interpolación por Diferencias finitas avanzadas (h) con Python

    Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]
    ..


    1. Interpolación por Diferencias finitas avanzadas

    Referencia: Rodríguez 6.6.4 p221, Burden 9Ed p129

    Se usa en interpolación cuando los puntos en el "eje x" se encuentran igualmente espaciados, la diferencia entre puntos consecutivos xi es una constante denominada h.

    h = xi+1 - xi

    diferencias finitas Avanzadas GIF animado
    Una relación entre derivadas y diferencias finitas se establece mediante: f^{(n)}(z) = \frac{\Delta ^{n} f_{0}}{h^{n}} para algún z en el intervalo [x0,xn].

    \frac{\Delta ^{n} f_{0}}{h^{n}} es una aproximación para la n-ésima derivada f(n)

    El polinomio de interpolación se puede construir por medio de diferencias finitas avanzadas con las siguiente fórmula:

    p_n (x) = f_0 + \frac{\Delta f_0}{h} (x - x_0) + + \frac{\Delta^2 f_0}{2!h^2} (x - x_0)(x - x_1) + + \frac{\Delta^3 f_0}{3!h^3} (x - x_0)(x - x_1)(x - x_2) + \text{...} + \frac{\Delta^n f_0}{n!h^n} (x - x_0)(x - x_1) \text{...} (x - x_{n-1})

    Observe que en la medida que se toman más puntos de muestra, el grado del polinomio puede ser mayor.

    polinomio de diferencias finitas avanzadas

    Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]
    ..


    2. Ejercicio

    Se toman los datos del ejercicio de diferencias finitas , observando que se requiere que el tamaño de paso h  sea constante entre los puntos consecutivos xi.

    xi = [0.1, 0.2, 0.3, 0.4]
    fi = [1.45, 1.6, 1.7, 2.0]
    xi 0.1 0.2 0.3 0.4
    fi 1.45 1.6 1.7 2.0

    Para éste ejercicio se comprueba que h = 0.1

    Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]
    ..


    3. Desarrollo Analítico

    Se inicia el cálculo de la distancia entre puntos xi, comprobando que debe ser constante h, además que los valores xi deben encontrarse ordenados de forma ascendente:

    h = xi[1] - xi[0] = 0.2-0.1 = 0.1
    h = xi[2] - xi[1] = 0.3-0.2 = 0.1
    h = xi[3] - xi[2] = 0.4-0.3 = 0.1

    Se usan los resultados previos del ejercicio con diferencias finitas:

    Tabla Diferencia Finita: 
    [['i', 'xi', 'fi', 'd1f', 'd2f', 'd3f', 'd4f']]
    [[ 0.    0.1   1.45  0.15 -0.05  0.25  0.  ]
     [ 1.    0.2   1.6   0.1   0.2   0.    0.  ]
     [ 2.    0.3   1.7   0.3   0.    0.    0.  ]
     [ 3.    0.4   2.    0.    0.    0.    0.  ]]
    

    Como los tamaños de paso en xi son constantes, h=0.1, es posible usar el método. Para la construcción del polinomio, se usan los valores de diferencias finitas de la primera fila desde la columna 3 en adelante.

    dfinita = tabla[0,3:] = [0.15, -0.05, 0.25, 0.]

    Se empieza con el valor del primer punto de la función f(0.1), j=0.

    p3(x) = f0 = 1.45

    para añadirle el término j=1, más el factor por calcular:

    factor = \frac{\Delta^1 f_0}{1!h^1} = \frac{0.15}{1!(0.1)} = 1.5

    completando el término como:

    término = factor(x-x_0) = 1.5(x-0.1) p_3(x) = 1.45 + 1.5(x-0.1)

    Para el segundo término j=2, se repite el proceso:

    factor = \frac{\Delta^2 f_0}{2!h^2} = \frac{-0.05}{2!(0.1)^2} = -2.5 término = factor(x-x_0) = -2.5(x-0.1)(x-0.2) p_3(x)= 1.45 + 1.5(x-0.1) +(-2.5)(x-0.1)(x-0.2)

    Finalmente, añadiendo el término j=3 cuyo cálculo es semejante a los anteriores, se deja como tarea.

    El resultado del método es:

    p_3(x)= 1.45 + 1.5(x-0.1) -2.5(x-0.1)(x-0.2) + + 41.667(x - 0.3)(x - 0.2)(x - 0.1)

    Se puede seguir simplificando la respuesta, por ejemplo usando solo el término de grado con 1.5(x-0.1) se tiene que:

    p_3(x) = 1.3 + 1.5x -2.5(x-0.1)(x-0.2)+ + 41.667(x - 0.3)(x - 0.2)(x - 0.1)

    Seguir simplificando la expresión en papel, se deja como tarea.

    La gráfica del polinomio obtenido comprueba que pasa por cada uno de los puntos dados para el ejercicio.

    interpolación polinómica

    Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]
    ..


    4. Algoritmo en Python

    El polinomio se construye usando el ejercicio de diferencias finitas.

    Para construir la expresión del polinomio añadiendo los términos de la fórmula, se define la variable simbólica x con Sympy.

    Para simplificar el polinomio resultante con las expresiones de multiplicación, se utiliza la instrucción sym.expand().

    En caso de requerir evaluar la fórmula con un vector de datos se la convierte a la forma lambda para evaluación numérica.

    Se añaden las instrucciones para realizar la gráfica en el intervalo [a,b] dado por los valores xi, definiendo el número de muestras = 21 que son suficientes para que la gráfica se observe sin distorsión.

    Se añade un bloque para la revisión que los puntos xi sean equidistantes y se encuentren en orden.

    # Diferencias finitas Avanzadas - Interpolación
    # Tarea: Verificar tamaño de vectores
    import numpy as np
    import math
    import sympy as sym
    
    # INGRESO
    xi = [0.1, 0.2, 0.3, 0.4]
    fi = [1.45, 1.6, 1.7, 2.0]
    
    # PROCEDIMIENTO
    casicero = 1e-15
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    
    msj = [] # mensajes de error
    
    # xi revisar orden ascendente
    tramos  = np.diff(xi,1)  # diferencias en xi
    donde = -1  # suponer ordenados
    i = 0
    while i<(n-1) and donde<0:
        if tramos[i]<0:
            donde = i+1
            msj.append(['sin orden en',donde])
        i = i+1
    
    # xi tramos desiguales o no equidistantes
    if (n-1)>=2: # len(tramos)>=2
        dtramos = np.diff(tramos,1) # cero o menores a casicero
        errado  = np.max(np.abs(dtramos)) # |error| mayor
        donde = -1 # suponer equidistantes
        if errado>=casicero:  # no equidistantes
            donde = np.argmax(np.abs(dtramos))+1
            msj.append(['no equidistantes',donde])
    
    
    # Tabla de Diferencias Finitas
    tabla_etiq = ['i','xi','fi']
    ki = np.arange(0,n,1) # filas
    tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
    tabla = np.transpose(tabla)
    dfinita = np.zeros(shape=(n,n),dtype=float)
    tabla = np.concatenate((tabla,dfinita), axis=1)
    n,m = np.shape(tabla)
    # Calular tabla de Diferencias Finitas
    diagonal = n-1 # derecha a izquierda
    j = 3  # inicia en columna 3
    while (j < m): # columna
        tabla_etiq.append('d'+str(j-2)+'f')
        i = 0 # fila
        while (i < diagonal): # antes de diagonal
            tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]
    
            if abs(tabla[i,j])<casicero: # casicero revisa
                    tabla[i,j]=0
            i = i + 1
        diagonal = diagonal - 1
        j = j + 1
    
    dfinita = tabla[0,3:] # diferencias finitas
    h = xi[1] - xi[0]     # suponer tramos equidistantes
    
    # polinomio con diferencias Finitas Avanzadas
    x = sym.Symbol('x')
    polinomio = fi[0] +0*x # en Sympy
    for i in range(1,n,1):
        denominador = math.factorial(i)*(h**i)
        factor = dfinita[i-1]/denominador
        termino = 1
        for j in range(0,i,1):
            termino = termino*(x-xi[j])
        polinomio = polinomio + termino*factor
    
    polisimple = polinomio.expand() # simplifica los (x-xi)
    px = sym.lambdify(x,polisimple) # evaluación numérica
    
    # SALIDA
    if len(msj)>0: # mensajes de error
        print('Revisar tramos, d1x:',tramos)
        for unmsj in msj:
            print('Tramos',unmsj[0],
                  'desde i:',unmsj[1])
    else:
        print('Tramo h:',h)
    print('Tabla Diferencia Finita')
    print([tabla_etiq])
    print(tabla)
    print('dfinita: ')
    print(dfinita)
    print('polinomio: ')
    print(polinomio)
    print('polinomio simplificado: ' )
    print(polisimple)
    

    teniendo como resultado:

    Tramo h: 0.1
    Tabla Diferencia Finita
    [['i', 'xi', 'fi', 'd1f', 'd2f', 'd3f', 'd4f']]
    [[ 0.    0.1   1.45  0.15 -0.05  0.25  0.  ]
     [ 1.    0.2   1.6   0.1   0.2   0.    0.  ]
     [ 2.    0.3   1.7   0.3   0.    0.    0.  ]
     [ 3.    0.4   2.    0.    0.    0.    0.  ]]
    dfinita: 
    [ 0.15 -0.05  0.25  0.  ]
    polinomio: 
    1.5*x + 41.6666666666667*(x - 0.3)*(x - 0.2)*(x - 0.1) - 2.50000000000001*(x - 0.2)*(x - 0.1) + 1.3
    polinomio simplificado: 
    41.6666666666667*x**3 - 27.5*x**2 + 6.83333333333335*x + 0.999999999999999
    

    el polinomio de puede evaluar como px(valor) una vez que se convierte a la forma lambda para usar con Numpy:

    >>> px(0.1)
    1.4500000000000004
    >>> px(0.2)
    1.6000000000000025
    >>> px0.3)
    1.7000000000000042
    >>> 
    

    Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]
    ..


    5. Gráfica de polinomio de interpolación

    Las instrucciones son semejantes a las presentadas en polinomio de interpolación. Se añade instrucciones para el caso en que los puntos no sean equidistantes en el eje de las x.

    Como el bloque de gráfica se usa en otros métodos de la unidad, se incorpora la verificación de lista de errores 'msj' usando el bloque 'try-except'.

    # GRAFICA --------------
    import matplotlib.pyplot as plt
    
    titulo = 'Interpolación: Diferencias Finitas Avanzadas'
    muestras = 21  # resolución gráfica
    
    a = np.min(xi) # intervalo [a,b]
    b = np.max(xi)
    xk = np.linspace(a,b,muestras)
    yk = px(xk)
    
    plt.plot(xi,fi,'o', label='[xi,fi]')
    plt.plot(xk,yk, label='p(x)')
    
    try: # existen mensajes de error
        msj_existe = len(msj)
    except NameError:
        msj = []
        
    if len(msj)>0: # tramos con error
        untipo = msj[0][0]
        donde = msj[0][1] # indice error
        plt.plot(xi[donde:donde+2],fi[donde:donde+2],
                 'ro',label=untipo)
        plt.plot(xi[donde:donde+2],fi[donde:donde+2],
                 'r--')
        
    plt.xlabel('xi')
    plt.ylabel('fi')
    plt.legend()
    plt.title(titulo)
    plt.show()

    diferencias finitas avanzadas 01

    Tarea: se recomienda realizar las gráficas comparativas entre métodos, debe mostrar la diferencia con los métodos que requieren el tamaño de paso equidistante h, y los que no lo requieren. Permite detectar errores de selección de método para interpolación.

    Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]
    ..


    6. Algoritmo como función

    Se reordena el algoritmo para usar funciones, la gráfica es la misma que para el algoritmo sin funciones anterior

    El bloque de salida es semejante al resultado anterior. Las funciones pueden ser reutilizadas de ser necesarias en el siguiente método de interpolación.

    Tabla de Diferencias finitas
    [['i', 'xi', 'fi', 'd1f', 'd2f', 'd3f', 'd4f']]
    [[ 0.    0.1   1.45  0.15 -0.05  0.25  0.  ]
     [ 1.    0.2   1.6   0.1   0.2   0.    0.  ]
     [ 2.    0.3   1.7   0.3   0.    0.    0.  ]
     [ 3.    0.4   2.    0.    0.    0.    0.  ]]
    xi_ascendente: True
    tramos xi Equidistantes: True
    Tramo h: 0.1
    polinomio: 
    1.30000000000000 +
    1.5*x +
    -2.50000000000001*(x - 0.2)*(x - 0.1) +
    41.6666666666667*(x - 0.3)*(x - 0.2)*(x - 0.1)
    
    polinomio simplificado: 
    41.6666666666667*x**3 - 27.5*x**2 + 6.83333333333335*x + 0.999999999999999
    

    Instrucciones en Python

    # Diferencias finitas Avanzadas - Interpolación
    # funciones dif_finitas
    # Tarea: Verificar tamaño de vectores
    import numpy as np
    import math
    import sympy as sym
    
    def revisa_orden(xi,orden='up'):
        ''' Revisa orden en vector xi.
          orden='up': orden ascendente
          orden='down' : orden descendente
        resultado:
          True:  xi ordenado,
          False: xi desordenado y dónde.
        '''
        # Vectores como arreglo, numeros reales
        xi = np.array(xi,dtype=float)
        n = len(xi)
        
        msj = [] # mensajes de error
        # xi revisar orden ascendente o descendente
        tramos  = np.diff(xi,1)  # diferencias en xi
        ordenado = True  # suponer ordenados
        k = 1 # 1: ascendente
        if orden=='down':
            k = -1 # -1: descendente
        donde = -1
        i = 0
        while i<(n-1) and donde<0:
            if k*tramos[i]<0:
                ordenado = False
                donde = i+1
                msj.append(['sin orden',donde])
            i = i+1
        return([ordenado,msj])
    
    def tramos_equidistantes(xi, casicero = 1e-15):
        ''' Revisa tamaños de paso h en vector xi.
        True:  h son equidistantes,
        False: h tiene tamaño de paso diferentes y dónde.
        '''
        # Vectores como arreglo, numeros reales
        xi = np.array(xi,dtype=float)
        n = len(xi)
        msj = [] # mensajes de error
        
        # revisa tamaños de paso desiguales o no equidistantes
        h_iguales = True
        if (n-1)>=2: # al menos dos tramos
            dtramos = np.diff(xi,2) # cero o menores a casicero
            errado  = np.max(np.abs(dtramos)) # |error| mayor
            if errado>=casicero:  # no equidistantes
                h_iguales=False
                donde = np.argmax(np.abs(dtramos))+1
                msj.append(['no equidistantes',donde])
    
        return([h_iguales,msj])
    
    def diferencias_tabla(xi,fi,tipo='finitas', vertabla=False,
                    casicero = 1e-15, precision=4):
        '''Genera la tabla de diferencias finitas o divididas
          tipo = 'finitas, tipo = 'divididas'
        resultado en: tabla, titulo
        Tarea: verificar tamaño de vectores
        '''
        # revisa tipo de tabla
        tipolist = ['finitas','divididas']
        if not(tipo in tipolist):
            print('error de tipo, seleccione:',tipolist)
            return()
        
        prefijo = ['d','f']
        if tipo=='divididas':
            prefijo = ['F[',']']
        
        # Matrices como arreglo, numeros reales
        xi = np.array(xi,dtype=float)
        fi = np.array(fi,dtype=float)
        n = len(xi)
    
        # Tabla de Diferencias Finitas/Divididas
        tabla_etiq = ['i','xi','fi']
        ki = np.arange(0,n,1) # filas
        tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
        tabla = np.transpose(tabla)
        dfinita = np.zeros(shape=(n,n),dtype=float)
        tabla = np.concatenate((tabla,dfinita), axis=1)
        n,m = np.shape(tabla)
        # Calular tabla de Diferencias Finitas/Divididas
        diagonal = n-1 # derecha a izquierda
        j = 3  # inicia en columna 3
        while (j < m): # columna
            tabla_etiq.append(prefijo[0]+str(j-2)+prefijo[1])
            # paso = j-2 # inicia en 1
            i = 0 # fila
            while (i < diagonal): # antes de diagonal
                tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]
    
                if tipo=='divididas':
                    # denominador = xi[i+paso]-xi[i]
                    denominador = xi[i+j-2]-xi[i]
                    tabla[i,j] = tabla[i,j]/denominador
                    
                if abs(tabla[i,j])<casicero: # casicero revisa
                        tabla[i,j]=0
                i = i + 1
            diagonal = diagonal - 1
            j = j + 1
                
        if vertabla==True:
            np.set_printoptions(precision)
            print('Tabla de Diferencias',tipo)
            print([tabla_etiq])
            print(tabla)
        return([tabla, tabla_etiq])
    
    # PROGRAMA ---------------------
    
    # INGRESO
    xi = [0.1, 0.2, 0.3, 0.4]
    fi = [1.45, 1.6, 1.7, 2.0]
    
    tipo_tabla = 'finitas'
    
    # PROCEDIMIENTO
    casicero = 1e-12
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    
    xi_ascendente,msj = revisa_orden(xi)
    h_iguales,msj_h = tramos_equidistantes(xi, casicero = casicero)
    if len(msj_h)>0:
        msj.extend(msj_h)   
    tabla,titulo = diferencias_tabla(xi,fi,tipo=tipo_tabla,
                                     vertabla=True, casicero = casicero)
    
    dfinita = tabla[0,3:] # diferencias finitas
    h = xi[1] - xi[0]     # suponer tramos equidistantes
    
    # polinomio con diferencias Finitas Avanzadas/ divididas
    x = sym.Symbol('x')
    polinomio = fi[0] +0*x # sym.S.Zero en Sympy
    for i in range(1,n,1):
        factor = dfinita[i-1] # diferencias divididas
        if tipo_tabla=='finitas': # diferencias Finitas Avanzadas
            denominador = math.factorial(i)*(h**i)
            factor = factor/denominador
        termino = 1
        for j in range(0,i,1):
            termino = termino*(x-xi[j])
        polinomio = polinomio + termino*factor
    
    polisimple = polinomio.expand() # simplifica los (x-xi)
    px = sym.lambdify(x,polisimple) # evaluacion numerica
    
    # SALIDA
    print('xi_ascendente:',xi_ascendente)
    print('tramos xi Equidistantes:',h_iguales)
    if len(msj)>0: # mensajes de error
        print('Revisar tramos, d1x:',np.diff(xi,1))
        for unmsj in msj:
            print('Tramos',unmsj[0],
                  'desde i:',unmsj[1])
    else:
        print('Tramo h:',h)
    
    print('d'+tipo_tabla+': ')
    print(dfinita)
    print('polinomio: ')
    #print(polinomio)
    terminos = sym.Add.make_args(polinomio)
    n_term = len(terminos)
    for i in range(0,n_term,1):
        if i<(n_term-1):
            print(terminos[i],'+')
        else:
            print(terminos[i])
    print()
    print('polinomio simplificado: ' )
    print(polisimple)
    

    La gráfica se genera con el mismo bloque de instrucciones de la sección gráfica.

    Dif_Finitas avanzadas ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ]
    [ función ]

  • 4.2 Diferencias finitas, tabla con Python

    [ Dif_Finitas ] [ ejercicio ] [ analítico ] [ algoritmo ] [ gráfica ] [ función ]
    ..


    1. Diferencias finitas

    Referencia: Rodríguez 6.5 p211, Chapra 18.1.3 p509

    Establece relaciones simples entre los puntos dados que describen la función f(x). Las tabla de diferencias finitas es un elemento base para varios métodos de interpolación, por lo que se trata como un tema inicial.

    diferencias finitas 01 animado
    [ Dif_Finitas ] [ ejercicio [ algoritmo ] [ gráfica ] [ función ]
    ..


    2. Ejercicio

    Los datos provienen del ejercicio para el polinomio de interpolación con Vandermonde. Se modifica el primer par ordenado a [0.1, 1.45].

    xi = [0.1, 0.2, 0.3, 0.4] 
    fi = [1.45, 1.6, 1.7, 2.0]
    xi 0.1 0.2 0.3 0.4
    fi 1.45 1.6 1.7 2.0

    [ Dif_Finitas ] [ ejercicio ] [ analítico ] [ algoritmo ] [ gráfica ] [ función ]
    ..


    3. Desarrollo analítico

    La tabla de diferencias finitas se construye tomando los datos y sus índices como parte de las primeras columnas.

    i xi fi Δ1fi Δ2fi Δ3fi Δ4fi
    0 0.1 1.45 0.15 -0.05 0.25 0.
    1 0.2 1.6 0.1 0.2 0. 0.
    2 0.3 1.7 0.3 0. 0. 0.
    3 0.4 2.0 0. 0. 0. 0.

    Cada casilla de diferencia finita Δjfi se obtiene restando los dos valores consecutivos de la columna anterior. Por ejemplo, para la primera columna:

    \Delta ^1 f_0 = 1.6-1.45 = 0.15 \Delta ^1 f_1 = 1.7-1.6 = 0.1 \Delta ^1 f_2 = 2.0-1.7 = 0.3

    y la siguiente Δ1f3 no es posible calcular.

    Siguiendo el mismo procedimiento se calculan los valores de la siguiente columna Δ2fi como las diferencias de la columna anterior.

    \Delta ^2 f_0 = 0.1-0.15 = -0.05 \Delta ^2 f_1 = 0.3-0.1 = 0.2

    finalmente Δ3fi

    \Delta ^3 f_0 = 0.2-(-0.05) = 0.25

    Si la función f(x) de donde provienen los datos es un polinomio de grado n, entonces la n-ésima diferencia finita será una constante, y las siguientes diferencias se anularán.

    [ Dif_Finitas ] [ ejercicio ] [ analítico ] [ algoritmo ] [ gráfica ] [ función ]
    ..


    4. Algoritmo en Python

    Para crear la tabla de diferencias finitas, las primeras columnas requieren concatenar los valores de los índices ixi y fi.

    xi = [0.10, 0.2, 0.3, 0.4]
    fi = [1.45, 1.6, 1.7, 2.0]

    Los índices i se crean en un vector ki, pues la variable i es usada como fila en matrices, por lo que evitamos confundirlas al usar la variable.

    A la matriz con las tres columnas descritas, se le añade a la derecha una matriz de nxn para calcular las diferencias.

    Se calculan las diferencias para cada columna, realizando la operación entre los valores de cada fila. Considere que no se realizan cálculos desde la diagonal hacia abajo en la tabla, los valores quedan como cero.

    Al final se muestra el título y el resultado de la tabla.

    # Diferencias finitas: [tabla_etiq,tabla]
    # Tarea: verificar tamaño de vectores
    import numpy as np
    
    # INGRESO, Datos de prueba
    xi = [0.10, 0.2, 0.3, 0.4]
    fi = [1.45, 1.6, 1.7, 2.0]
    
    # PROCEDIMIENTO
    casicero = 1e-15  # redondear a cero
    # Matrices como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    
    # Tabla de Diferencias Finitas
    tabla_etiq = ['i','xi','fi']
    ki = np.arange(0,n,1) # filas
    tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
    tabla = np.transpose(tabla)
    dfinita = np.zeros(shape=(n,n),dtype=float)
    tabla = np.concatenate((tabla,dfinita), axis=1)
    n,m = np.shape(tabla)
    # Calular tabla de Diferencias Finitas
    diagonal = n-1 # derecha a izquierda
    j = 3  # inicia en columna 3
    while (j < m): # columna
        tabla_etiq.append('d'+str(j-2)+'f') 
        i = 0 # fila
        while (i < diagonal): # antes de diagonal
            tabla[i,j] = tabla[i+1,j-1]-tabla[i,j-1]
            
            if abs(tabla[i,j])<casicero: # casicero revisa
                    tabla[i,j]=0
            i = i + 1
        diagonal = diagonal - 1
        j = j + 1
    
    # SALIDA
    print('Tabla Diferencia Finita: ')
    print([tabla_etiq])
    print(tabla)

    el resultado de aplicar el algoritmo es:

    Tabla Diferencia Finita: 
    [['i', 'xi', 'fi', 'd1f', 'd2f', 'd3f', 'd4f']]
    [[ 0.    0.1   1.45  0.15 -0.05  0.25  0.  ]
     [ 1.    0.2   1.6   0.1   0.2   0.    0.  ]
     [ 2.    0.3   1.7   0.3   0.    0.    0.  ]
     [ 3.    0.4   2.    0.    0.    0.    0.  ]]
    >>> 
    

    [ Dif_Finitas ] [ ejercicio ] [ analítico ] [ algoritmo ] [ gráfica ] [ función ]
    ..


    5. Gráfica de puntos con líneas horizontales

    Para tener una referencia visual sobre las primeras diferencias finitas, en una gráfica se trazan las líneas horizontales que pasan por cada punto. Para las segundas diferencias se debe incluir en la gráfica las primeras diferencias finitas vs xi repitiendo el proceso. Las líneas de distancia se añadieron con un editor de imágenes.

    diferencias finitas 01 graf

    Las instrucciones adicionales al algoritmo anterior para añadir la gráfica son:

    # Gráfica --------------
    import matplotlib.pyplot as plt
    
    titulo = 'Diferencia Finita'
    
    for i in range(0,n,1):
        plt.axhline(fi[i],ls='--', color='yellow')
        if i<(n-1):
            plt.annotate("", xytext=(xi[i+1], fi[i]),
                         xy=(xi[i+1], fi[i+1]),
                         arrowprops=dict(arrowstyle="<->"),
                         color='magenta')
            plt.annotate("Δ1f["+str(i)+"]",
                         xy=(xi[i+1], fi[i+1]),
                         xytext=(xi[i+1],(fi[i]+fi[i+1])/2))
                         
    plt.plot(xi,fi,'o', label = 'Puntos')
    plt.legend()
    plt.xlabel('xi')
    plt.ylabel('fi')
    plt.title(titulo)
    plt.show()
    

    [ Dif_Finitas ] [ ejercicio ] [ analítico ] [ algoritmo ] [ gráfica ] [ función ]
    ..


    6. Diferencias finitas como función en Numpy.diff

    np.diff(vector,orden)calcula la diferencia de elementos consecutivos a lo largo de un eje específico de una matriz. Incluye un parámetro de orden para la n-esima diferencia finita. Ejemplo:

    >>> import numpy as np
    >>> xi = [0.10, 0.2, 0.3, 0.4]
    >>> fi = [1.45, 1.6, 1.7, 2.0]
    >>> d1f = np.diff(fi,1)
    >>> d1f
    array([0.15, 0.1 , 0.3 ])
    >>> d2f = np.diff(fi,2)
    >>> d2f
    array([-0.05,  0.2 ])
    >>> d1x = np.diff(xi,1)
    >>> d1x
    array([0.1, 0.1, 0.1])
    >>> d2x = np.diff(xi,n=2)
    >>> d2x
    array([-2.77555756e-17,  5.55111512e-17])

    Δ2xi es un valor casicero que se debe incluir en el algoritmo para redondear a cero cuando sea por ejemplo 10-15. Asunto que ya fue incluido desde Método de Gauss en la unidad de sistemas de ecuaciones

    Referenciahttps://numpy.org/doc/stable/reference/generated/numpy.diff.html

    [ Dif_Finitas ] [ ejercicio ] [ analítico ] [ algoritmo ] [ gráfica ] [ función ]

  • 4.1 Polinomio de interpolación con Vandermonde en Python

    Interpolación: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ Función ]
    ..


    1. Ejercicio

    Referencia: Rodríguez 6.1 p191, Chapra 18.3 p520

    Unir n puntos en el plano usando un polinomios de interpolación se puede realizar con un polinomio de grado (n-1).

    interpolación polinómica, matriz de Vandermonde

    xi 0 0.2 0.3 0.4
    fi 1 1.6 1.7 2.0

    Por ejemplo, dados los 4 puntos en la tabla [xi,fi] se requiere generar un polinomio de grado 3 de la forma:

    p(x) = a_0 x^3 + a_1 x^2 + a_2 x^1 + a_3

    Interpolación: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ Función ]
    ..


    2. Desarrollo Analítico

    Es posible generar un sistema de ecuaciones para p(x) haciendo que pase por cada uno de los puntos o coordenadas.
    Por ejemplo si se toma el primer punto con xi=0 y fi=1 se establece la ecuación:

    p(0) = 1 a_0 (0)^3 + a_1 (0)^2 + a_2 (0)^1 + a_3 = 1 p(0.2) = 1.6 a_0 (0.2)^3 + a_1 (0.2)^2 + a_2 (0.2)^1 + a_3 = 1.6

    Note que ahora las incógnitas son los coeficientes ai. Luego se continúa con los otros puntos seleccionados hasta completar las ecuaciones necesarias para el grado del polinomio seleccionado.

    a_0 (0.0)^3 + a_1 (0.0)^2 + a_2 (0.0)^1 + a_3 = 1.0 a_0 (0.2)^3 + a_1 (0.2)^2 + a_2 (0.2)^1 + a_3 = 1.6 a_0 (0.3)^3 + a_1 (0.3)^2 + a_2 (0.3)^1 + a_3 = 1.7 a_0 (0.4)^3 + a_1 (0.4)^2 + a_2 (0.4)^1 + a_3 = 2.0

    El sistema obtenido se resuelve con alguno de los métodos conocidos para la Solución a sistemas de ecuaciones, que requieren escribir las ecuaciones en la forma de matriz A y vector B, desarrollar la matriz aumentada, pivotear por filas, etc.

    \begin{pmatrix} 0.0^3 & 0.0^2 & 0.0^1 & 1 \\ 0.2^3 & 0.2^2 & 0.2^1 & 1 \\ 0.3^3 & 0.3^2 & 0.3^1 & 1 \\ 0.4^3 & 0.4^2 & 0.4^1 & 1 \end{pmatrix} . \begin{pmatrix} a_0 \\ a_1 \\ a_2 \\ a_3 \end{pmatrix} = \begin{pmatrix} 1.0 \\ 1.6 \\ 1.7 \\ 2.0 \end{pmatrix}

    La matriz A también se conoce como Matriz Vandermonde D, que se construye observando que los coeficientes se elevan al exponente referenciado al índice columna pero de derecha a izquierda. La última columna tiene valores 1 por tener como coeficiente el valor de xi0.

    Para enfocarnos en la interpolación, en la solución se propone usar un algoritmo o función en Python, obteniendo el siguiente resultado:

    los coeficientes del polinomio: 
    [ 41.66666667 -27.5          6.83333333   1.        ]

    a partir del cual que se construye el polinomio con los valores obtenidos.

    Polinomio de interpolación p(x): 
    41.66666666667*x**3 - 27.5*x**2 + 6.83333333333*x + 1.0
    

    que re-escrito es,

    p(x) = 41.6666 x^3 - 27.5 x^2 + 6.8333 x + 1

    Se realiza  la gráfica de p(x) dentro del intervalo de los datos del ejercicio y se obtiene la línea contínua que pasa por cada uno de los puntos.

    interpola polinomio 02

    Interpolación: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ Función ]
    ..


    3. Algoritmo en Python

    Para desarrollar el ejercicio, se realiza un bloque para construir la matriz A y vector B, usando los vectores que representan los puntos de muestra de la función o experimento.

    xi = [0,0.2,0.3,0.4]
    fi = [1,1.6,1.7,2.0]

    Observe que en éste caso los datos se dan en forma de lista y se deben convertir hacia arreglos.

    La matriz A o Matriz Vandermonde D, se construye observando que los coeficientes del vector xi se elevan al exponente referenciado al índice columna pero de derecha a izquierda.

    Construir el polinomio consiste en resolver el sistema de ecuaciones indicado en la sección anterior. Para simplificar la solución del sistema, se usa Numpy, que entrega el vector solución que representan los coeficientes del polinomio de interpolación.

    Para construir la expresión del polinomio, se usa la forma simbólica con Sympy, de forma semejante a la usada para construir el polinomio de Taylor.
    Para mostrar el polinomio de una manera más fácil de interpretar se usa la instrucción sym.pprint(), usada al final del algoritmo.

    # El polinomio de interpolación y Vandermonde
    import numpy as np
    import sympy as sym
    
    # INGRESO
    xi = [0,0.2,0.3,0.4]
    fi = [1,1.6,1.7,2.0]
    
    # PROCEDIMIENTO
    # Matrices como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    fi = np.array(fi,dtype=float)
    n = len(xi)
    # Matriz Vandermonde D
    D = np.zeros(shape=(n,n),dtype=float)
    for i in range(0,n,1):
        for j in range(0,n,1):
            potencia = (n-1)-j # Derecha a izquierda
            D[i,j] = xi[i]**potencia
    
    # Resuelve sistema de ecuaciones A.X=B
    coeficiente = np.linalg.solve(D,fi)
    
    # Polinomio en forma simbólica
    x = sym.Symbol('x')
    polinomio = 0*x   # sym.S.Zero
    for i in range(0,n,1):
        potencia = (n-1)-i   # Derecha a izquierda
        termino = coeficiente[i]*(x**potencia)
        polinomio = polinomio + termino
    
    # polinomio para evaluación numérica
    px = sym.lambdify(x,polinomio)
    
    # SALIDA
    print('Matriz Vandermonde: ')
    print(D)
    print('los coeficientes del polinomio: ')
    print(coeficiente)
    print('Polinomio de interpolación: ')
    print(polinomio)
    print('\n formato pprint')
    sym.pprint(polinomio)
    

    con lo que se obtiene el siguiente resultado:

    Matriz Vandermonde: 
    [[0.    0.    0.    1.   ]
     [0.008 0.04  0.2   1.   ]
     [0.027 0.09  0.3   1.   ]
     [0.064 0.16  0.4   1.   ]]
    los coeficientes del polinomio: 
    [ 41.66666667 -27.5 6.83333333 1. ]
    Polinomio de interpolación: 
    41.66666666667*x**3 - 27.5*x**2 + 6.83333333333*x + 1.0
    
    formato pprint
                      3         2                           
    41.66666666667*x  - 27.5*x  + 6.83333333333*x + 1.0
    

    Interpolación: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ Función ]
    ..


    4. Gráfica del polinomio

    Para facilitar la evaluación numérica del polinomio, se convierte el polinomio a la forma Lambda px. La gráfica se realiza con un número de muestras suficientes para suavizar la curva dentro del intervalo [a,b], por ejemplo 21, con lo que se comprueba que la curva pase por todos los puntos de la tabla xi,fi dados en el ejercicio.

    Instrucciones adicionales al algoritmo para la gráfica:

    # GRAFICA
    import matplotlib.pyplot as plt
    
    muestras = 21  # muestras = tramos+1
    
    a = np.min(xi) # intervalo [a,b]
    b = np.max(xi)
    xk = np.linspace(a,b,muestras)
    yk = px(xk)
    
    # Usando evaluación simbólica
    ##yk = np.zeros(muestras,dtype=float)
    ##for k in range(0,muestras,1):
    ##    yin[k] = polinomio.subs(x,xk[k])
    
    plt.plot(xi,fi,'o', label='[xi,fi]')
    plt.plot(xk,yk, label='p(x)')
    plt.xlabel('xi')
    plt.ylabel('fi')
    plt.legend()
    plt.title(polinomio)
    plt.show()
    

    Interpolación: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ Función ]
    ..


    5. Vandermonde como función en Numpy.vander

    La matriz de Vandermonde se puede crear con Numpy usando la instrucción np.vander(xi,len(xi)).

    >>> import numpy as np
    >>> xi = [0. , 0.2, 0.3, 0.4]
    >>> fi = [1. , 1.6, 1.7, 2. ]
    >>> D = np.vander(xi,len(xi))
    >>> D
    array([[0.   , 0.   , 0.   , 1.   ],
           [0.008, 0.04 , 0.2  , 1.   ],
           [0.027, 0.09 , 0.3  , 1.   ],
           [0.064, 0.16 , 0.4  , 1.   ]])
    >>> coeficiente = np.linalg.solve(D,fi)
    >>> coeficiente
    array([ 41.66667, -27.5    ,   6.83333,   1.     ])

    Interpolación: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ gráfica ] [ Función ]

  • 3.9.2 Método de Gauss-Jordan para matriz Inversa con Python

    matriz inversa: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ]
    ..


    1. Ejercicio

    Referencia: Chapra 10.2 p292, Burden 6.3 p292, Rodríguez 4.2.5 Ejemplo 1 p118
    Obtener la inversa de una matriz usando el método de Gauss-Jordan, a partir de la matriz:

    \begin{pmatrix} 4 & 2 & 5 \\ 2 & 5 & 8 \\ 5 & 4 & 3 \end{pmatrix}
    A = [[4,2,5],
         [2,5,8],
         [5,4,3]]

    matriz inversa: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ]
    ..


    2. Desarrollo analítico

    Para el procedimiento, se crea la matriz aumentada de A con la identidad I.

    AI = A|I

    \begin{pmatrix} 4 & 2 & 5 & 1 & 0 & 0\\ 2 & 5 & 8 & 0 & 1 & 0 \\ 5 & 4 & 3 & 0 & 0 & 1 \end{pmatrix}
    [[  4.    2.    5.   1.    0.    0. ]
     [  2.    5.    8.   0.    1.    0. ]
     [  5.    4.    3.   0.    0.    1. ]]
    

    Con la matriz aumentada AI  se repiten los procedimientos aplicados en el método de Gauss-Jordan:

    • pivoteo parcial por filas
    • eliminación hacia adelante
    • eliminación hacia atrás

    De la matriz aumentada resultante, se obtiene la inversa A-1 en la mitad derecha de AI, lugar que originalmente correspondía a la identidad.

    el resultado buscado es:

    la matriz inversa es:
    [[ 0.2        -0.16470588  0.10588235]
     [-0.4         0.15294118  0.25882353]
     [ 0.2         0.07058824 -0.18823529]]
    
    \begin{pmatrix} 0.2 & -0.16470588 & 0.10588235 \\ -0.4 & 0.15294118 & 0.25882353 \\ 0.2 & 0.07058824 & -0.18823529 \end{pmatrix}

    Verifica resultado

    El resultado se verifica realizando la operación producto punto entre A y la inversa, que debe resultar la matriz identidad.

    A.A-1 = I

    El resultado de la operación es una matriz identidad. Observe que los valores del orden de 10-15 o menores se consideran como casi cero o cero.

     A.inversa = identidad
    [[ 1.00000000e+00 -1.38777878e-17 -1.38777878e-16]
     [ 2.22044605e-16  1.00000000e+00 -2.22044605e-16]
     [ 5.55111512e-17 -9.71445147e-17  1.00000000e+00]]
    

    matriz inversa: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ]
    ..


    3. Algoritmo en Python

    El algoritmo que describe el proceso en python:

    # Matriz Inversa con Gauss-Jordan
    # AI es la matriz aumentada A con Identidad
    import numpy as np
    
    # INGRESO
    A = [[4,2,5],
         [2,5,8],
         [5,4,3]]
    
    # PROCEDIMIENTO
    # B = matriz_identidad
    n,m = np.shape(A)
    identidad = np.identity(n)
    
    np.set_printoptions(precision=4) # 4 decimales en print
    casicero = 1e-15  # redondear a cero
    
    # Matrices como arreglo, numeros reales
    A = np.array(A,dtype=float)
    B = np.array(identidad,dtype=float)
    
    def pivoteafila(A,B,vertabla=False):
        '''
        Pivoteo parcial por filas, entrega matriz aumentada AB
        Si hay ceros en diagonal es matriz singular,
        Tarea: Revisar si diagonal tiene ceros
        '''
        A = np.array(A,dtype=float)
        B = np.array(B,dtype=float)
        # Matriz aumentada
        nB = len(np.shape(B))
        if nB == 1:
            B = np.transpose([B])
        AB  = np.concatenate((A,B),axis=1)
        
        if vertabla==True:
            print('Matriz aumentada')
            print(AB)
            print('Pivoteo parcial:')
        
        # Pivoteo por filas AB
        tamano = np.shape(AB)
        n = tamano[0]
        m = tamano[1]
        
        # Para cada fila en AB
        pivoteado = 0
        for i in range(0,n-1,1):
            # columna desde diagonal i en adelante
            columna = np.abs(AB[i:,i])
            dondemax = np.argmax(columna)
            
            # dondemax no es en diagonal
            if (dondemax != 0):
                # intercambia filas
                temporal = np.copy(AB[i,:])
                AB[i,:] = AB[dondemax+i,:]
                AB[dondemax+i,:] = temporal
    
                pivoteado = pivoteado + 1
                if vertabla==True:
                    print(' ',pivoteado,
                          'intercambiar filas: ',i,
                          'con', dondemax+i)
        if vertabla==True:
            if pivoteado==0:
                print('  Pivoteo por filas NO requerido')
            else:
                print(AB)
        return(AB)
    
    def gauss_eliminaAdelante(AB,vertabla=False,
                              lu=False,casicero = 1e-15):
        ''' Gauss elimina hacia adelante
        tarea: verificar términos cero
        '''
        tamano = np.shape(AB)
        n = tamano[0]
        m = tamano[1]
        if vertabla==True:
            print('Elimina hacia adelante:')
        for i in range(0,n,1):
            pivote = AB[i,i]
            adelante = i+1
            if vertabla==True:
                print(' fila i:',i,' pivote:', pivote)
            for k in range(adelante,n,1):
                if (np.abs(pivote)>=casicero):
                    factor = AB[k,i]/pivote
                    AB[k,:] = AB[k,:] - factor*AB[i,:]
                    for j in range(0,m,1): # casicero revisa
                        if abs(AB[k,j])<casicero:
                            AB[k,j]=0
                    if vertabla==True:
                        print('  fila k:',k,
                              ' factor:',factor)
                else:
                    print('  pivote:', pivote,'en fila:',i,
                          'genera division para cero')
            if vertabla==True:
                print(AB)
        respuesta = np.copy(AB)
        if lu==True: # matriz triangular A=L.U
            U = AB[:,:n-1]
            respuesta = [AB,L,U]
        return(respuesta)
    
    def gauss_eliminaAtras(AB, vertabla=False,
                           inversa=False,
                           casicero = 1e-14):
        ''' Gauss-Jordan elimina hacia atras
        Requiere la matriz triangular inferior
        Tarea: Verificar que sea triangular inferior
        '''
        tamano = np.shape(AB)
        n = tamano[0]
        m = tamano[1]
        
        ultfila = n-1
        ultcolumna = m-1
        if vertabla==True:
            print('Elimina hacia Atras:')
            
        for i in range(ultfila,0-1,-1):
            pivote = AB[i,i]
            atras = i-1  # arriba de la fila i
            if vertabla==True:
                print(' fila i:',i,' pivote:', pivote)
                
            for k in range(atras,0-1,-1):
                if np.abs(AB[k,i])>=casicero:
                    factor = AB[k,i]/pivote
                    AB[k,:] = AB[k,:] - factor*AB[i,:]
                    
                    # redondeo a cero
                    for j in range(0,m,1): 
                        if np.abs(AB[k,j])<=casicero:
                            AB[k,j]=0
                    if vertabla==True:
                        print('  fila k:',k,
                              ' factor:',factor)
                else:
                    print('  pivote:', pivote,'en fila:',i,
                          'genera division para cero')
            AB[i,:] = AB[i,:]/AB[i,i] # diagonal a unos
            
            if vertabla==True:
                print(AB)
        
        respuesta = np.copy(AB[:,ultcolumna])
        if inversa==True: # matriz inversa
            respuesta = np.copy(AB[:,n:])
        return(respuesta)
    
    AB = pivoteafila(A,identidad,vertabla=True)
    AB = gauss_eliminaAdelante(AB,vertabla=True)
    A_inversa = gauss_eliminaAtras(AB,inversa=True,
                                   vertabla=True)
    A_verifica = np.dot(A,A_inversa)
    # redondeo a cero
    for i in range(0,n,1):
        for j in range(0,m,1): 
            if np.abs(A_verifica[i,j])<=casicero:
                A_verifica[i,j]=0
    
    # SALIDA
    print('A_inversa: ')
    print(A_inversa)
    print('verifica A.A_inversa = Identidad:')
    print(A_verifica)
    

    el resultado buscado es:

    Matriz aumentada
    [[4. 2. 5. 1. 0. 0.]
     [2. 5. 8. 0. 1. 0.]
     [5. 4. 3. 0. 0. 1.]]
    Pivoteo parcial:
      1 intercambiar filas:  0 con 2
    [[5. 4. 3. 0. 0. 1.]
     [2. 5. 8. 0. 1. 0.]
     [4. 2. 5. 1. 0. 0.]]
    Elimina hacia adelante:
     fila i: 0  pivote: 5.0
      fila k: 1  factor: 0.4
      fila k: 2  factor: 0.8
    [[ 5.   4.   3.   0.   0.   1. ]
     [ 0.   3.4  6.8  0.   1.  -0.4]
     [ 0.  -1.2  2.6  1.   0.  -0.8]]
     fila i: 1  pivote: 3.4
      fila k: 2  factor: -0.3529411764705883
    [[ 5.      4.      3.      0.      0.      1.    ]
     [ 0.      3.4     6.8     0.      1.     -0.4   ]
     [ 0.      0.      5.      1.      0.3529 -0.9412]]
     fila i: 2  pivote: 5.0
    [[ 5.      4.      3.      0.      0.      1.    ]
     [ 0.      3.4     6.8     0.      1.     -0.4   ]
     [ 0.      0.      5.      1.      0.3529 -0.9412]]
    Elimina hacia Atras:
     fila i: 2  pivote: 5.0
      fila k: 1  factor: 1.3599999999999999
      fila k: 0  factor: 0.6
    [[ 5.      4.      0.     -0.6    -0.2118  1.5647]
     [ 0.      3.4     0.     -1.36    0.52    0.88  ]
     [ 0.      0.      1.      0.2     0.0706 -0.1882]]
     fila i: 1  pivote: 3.4
      fila k: 0  factor: 1.1764705882352942
    [[ 5.      0.      0.      1.     -0.8235  0.5294]
     [ 0.      1.      0.     -0.4     0.1529  0.2588]
     [ 0.      0.      1.      0.2     0.0706 -0.1882]]
     fila i: 0  pivote: 5.0
    [[ 1.      0.      0.      0.2    -0.1647  0.1059]
     [ 0.      1.      0.     -0.4     0.1529  0.2588]
     [ 0.      0.      1.      0.2     0.0706 -0.1882]]
    A_inversa: 
    [[ 0.2    -0.1647  0.1059]
     [-0.4     0.1529  0.2588]
     [ 0.2     0.0706 -0.1882]]
    verifica A.A_inversa = Identidad:
    [[1. 0. 0.]
     [0. 1. 0.]
     [0. 0. 1.]]
    

    Observe que el algoritmo se pude reducir si usan los procesos de Gauss-Jordan como funciones.

    Tarea: Realizar el algoritmo usando una función creada para Gauss-Jordan

    matriz inversa: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ]
    ..


    4. Función en Numpy

    La función en la librería Numpy es np.linalg.inv():

    >>> np.linalg.inv(A)
    array([[ 0.2       , -0.16470588,  0.10588235],
           [-0.4       ,  0.15294118,  0.25882353],
           [ 0.2       ,  0.07058824, -0.18823529]])
    >>> 
    

    matriz inversa: [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ]

  • 3.9.1 Método de Gauss - determinante de matriz con Python

    [ Determinante ] [ Ejercicio ] [ Algoritmo ] [ Función ]
    ..


    1. Determinante de matriz

    Referencia: Rodríguez 4.3.9 p129, Burden 6.4 p296, Chapra 9.1.2 p250.

    El determinante de una matriz cuadrada triangular superior también puede calcularse como el producto de los coeficientes de la diagonal principal, considerando el número de cambios de fila del pivoteo k.

    det(A) = (-1)^k \prod_{i=1}^n a_{i,i}

    Si observamos que en las secciones anteriores se tiene desarrollado los algoritmos  para obtener la matriz triangular superior en el método de Gauss, se usan como punto de partida para obtener los resultados del cálculo del determinante.

    [ Determinante ] [ Ejercicio ] [ Algoritmo ] [ Función ]
    ..


    2. Ejercicio

    Referencia: 1Eva_IIT2010_T2 Sistema ecuaciones, X0 = unos, Chapra Ejemplo 9.12 p277

    Calcular el determinante de la matriz A del sistema A.X=B dado por

    A= \begin{pmatrix} 0.4 & 1.1 & 3.1 \\ 4 & 0.15 & 0.25 \\2 & 5.6 & 3.1 \end{pmatrix} B= [7.5, 4.45, 0.1]

    El  algoritmo para ejercicio se convierte en una extensión de los algoritmos anteriores.

    A = [[0.4, 1.1 ,  3.1],
         [4.0, 0.15, 0.25],
         [2.0, 5.6 , 3.1]]
    B = [7.5, 4.45, 0.1]
    

    [ Determinante ] [ Ejercicio ] [ Algoritmo ] [ Función ]
    ..


    3. Algoritmo en Python

    El algoritmo parte de lo realizado en método de Gauss, indicando que la matriz a procesar es solamente A. Se mantienen los procedimientos de "pivoteo parcial por filas" y " eliminación hacia adelante"

    Para contar el número de cambios de filas, se añade un contador de  pivoteado dentro del condicional para intercambio de filas.

    Para el resultado del operador multiplicación, se usan todas las casillas de la diagonal al acumular las multiplicaciones.

    # Determinante de una matriz A
    # convirtiendo a diagonal superior 
    
    import numpy as np
    
    # INGRESO
    A = [[0.4, 1.1 ,  3.1],
         [4.0, 0.15, 0.25],
         [2.0, 5.6 , 3.1]]
    B = [7.5, 4.45, 0.1]
    
    # PROCEDIMIENTO
    # Matrices como arreglo, numeros reales
    A = np.array(A,dtype=float)
    B = np.array(B,dtype=float)
    
    # Matriz aumentada AB
    B_columna = np.transpose([B])
    AB  = np.concatenate((A,B_columna),axis=1)
    AB0 = np.copy(AB) # copia de AB
    
    # Pivoteo parcial por filas
    tamano = np.shape(AB)
    n = tamano[0]
    m = tamano[1]
    
    # Para cada fila en AB
    pivoteado = 0 # contador para cambio fila
    
    for i in range(0,n-1,1):
        # columna desde diagonal i en adelante
        columna = abs(AB[i:,i])
        dondemax = np.argmax(columna)
        
        if (dondemax !=0): # NO en diagonal
            # intercambia filas
            temporal = np.copy(AB[i,:])
            AB[i,:]  = AB[dondemax+i,:]
            AB[dondemax+i,:] = temporal
            
            pivoteado = pivoteado + 1 # cuenta cambio fila
            
    # Actualiza A y B pivoteado
    AB1 = np.copy(AB)
    
    # eliminación hacia adelante
    for i in range(0,n-1,1):
        pivote   = AB[i,i]
        adelante = i + 1
        for k in range(adelante,n,1):
            factor  = AB[k,i]/pivote
            AB[k,:] = AB[k,:] - AB[i,:]*factor
    
    # calcula determinante
    multiplica = 1
    for i in range(0,n,1):
        multiplica = multiplica*AB[i,i]
    determinante = ((-1)**pivoteado)*multiplica
    
    # SALIDA
    print('Pivoteo parcial por filas')
    print(AB1)
    print('Cambios de fila, pivoteado: ',pivoteado)
    print('eliminación hacia adelante')
    print(AB)
    print('determinante: ')
    print(determinante)
    

    Se aplica la operación de la fórmula planteada para el método, y se presenta el resultado:

    Pivoteo parcial por filas
    [[4.   0.15 0.25 4.45]
     [2.   5.6  3.1  0.1 ]
     [0.4  1.1  3.1  7.5 ]]
    Cambios de fila, pivoteado:  2
    eliminación hacia adelante
    [[ 4.          0.15        0.25        4.45      ]
     [ 0.          5.525       2.975      -2.125     ]
     [ 0.          0.          2.49076923  7.47230769]]
    determinante: 
    55.04599999999999
    

    [ Determinante ] [ Ejercicio ] [ Algoritmo ] [ Función ]
    ..


    4. Algoritmo como función en Numpy

    El resultado se puede verificar usando la función de Numpy:

    >>> np.linalg.det(A)
    55.04599999999999
    

    [ Determinante ] [ Ejercicio ] [ Algoritmo ] [ Función ]

  • 3.8 Matrices triangulares A=L.U con Python

    [ Matriz A=L.U ] [ Ejercicio ] [ Algoritmo LU ] [ Función LU ] ||
    [ Solución LY=B , UX=Y ]

    ..


    1. Matrices triangulares A=L.U

    Referencia: Chapra 10.1 p284. Burden 6.5 p298

    Una matriz A puede separarse en dos matrices triangulares que entre ellas tienen la propiedad:  A = L.U:

    • L de tipo triangular inferior
    • U de tipo triangular superior

    Matriz LU 01

    La matriz U se obtiene después de aplicar el proceso de eliminación hacia adelante del método de Gauss.

    La matriz L contiene los factores usados en el proceso de eliminación hacia adelante del método de Gauss, escritos sobre una matriz identidad en las posiciones donde se calcularon.

    [ Matriz A=L.U ] [ Ejercicio ] [ Algoritmo LU ] [ Función LU ] ||
    [ Solución LY=B , UX=Y ]
    ..


    2. Ejercicio

    Referencia: Chapra Ejemplo 10.1 p285

    Presente las matrices LU de la matriz A siguiente:

    A= \begin{pmatrix} 3 & -0.1 & -0.2 \\ 0.1 & 7 & -0.3 \\0.3 & -0.2 & 10 \end{pmatrix}
    A = [[ 3. , -0.1, -0.2],
         [ 0.1,  7. , -0.3],
         [ 0.3, -0.2, 10. ]]
    
    B = [7.85,-19.3,71.4]

    El resultado del "pivoteo por filas" y "eliminación hacia adelante" se tiene:

    Pivoteo parcial por filas
    [[  3.    -0.1   -0.2    7.85]
     [  0.1    7.    -0.3  -19.3 ]
     [  0.3   -0.2   10.    71.4 ]]
    

    de donde la última columna es el nuevo vector B

    eliminación hacia adelante
    Matriz U: 
    [[ 3.         -0.1        -0.2       ]
     [ 0.          7.00333333 -0.29333333]
     [ 0.          0.         10.01204188]]
    

    y guardando los factores del procedimiento de "eliminación hacia adelante " en una matriz L que empieza con la matriz identidad se obtiene:

    matriz L: 
    [[ 1.          0.          0.        ]
     [ 0.03333333  1.          0.        ]
     [ 0.1        -0.02712994  1.        ]]

    [ Matriz A=L.U ] [ Ejercicio ] [ Algoritmo LU ] [ Función LU ] ||
    [ Solución LY=B , UX=Y ]
    ..


    3. Algoritmo LU en Python

    Realizado a partir del algoritmo de la sección método de Gauss y modificando las partes necesarias para el algoritmo.

    Para éste algoritmo, se procede desde el bloque de pivoteo por filas", continuando con el algoritmo de "eliminación hacia adelante" del método de Gauss.  Procedimientos que dan como resultado la matriz U.

    La matriz L requiere iniciar con una matriz identidad,  y el procedimiento requiere que al ejecutar "eliminación hacia adelante" se almacene cada factor con el que se multiplica la fila para hacer cero. El factor se lo almacena en la matriz L, en la posición de dónde se determinó el factor.

    El resultado obtenido con el algoritmo es:

    Matriz aumentada
    [[  3.    -0.1   -0.2    7.85]
     [  0.1    7.    -0.3  -19.3 ]
     [  0.3   -0.2   10.    71.4 ]]
    Pivoteo parcial:
      Pivoteo por filas NO requerido
    matriz L: 
    [[ 1.      0.      0.    ]
     [ 0.0333  1.      0.    ]
     [ 0.1    -0.0271  1.    ]]
    Matriz U: 
    [[ 3.     -0.1    -0.2   ]
     [ 0.      7.0033 -0.2933]
     [ 0.      0.     10.012 ]]
    >>> 
    

    Donde se puede verificar que L.U = A usando la instrucción np.dot(L.U):

    >>> np.dot(L,U)
    array([[ 3. , -0.1, -0.2],
           [ 0.1,  7. , -0.3],
           [ 0.3, -0.2, 10. ]])
    >>> 
    

    Instrucciones en Python

    # Matrices L y U
    # Modificando el método de Gauss
    import numpy as np
    
    # PROGRAMA ------------
    # INGRESO
    A = [[ 3. , -0.1, -0.2],
         [ 0.1,  7. , -0.3],
         [ 0.3, -0.2, 10. ]]
    
    B = [7.85,-19.3,71.4]
    
    # PROCEDIMIENTO
    np.set_printoptions(precision=4) # 4 decimales en print
    casicero = 1e-15  # redondear a cero
    
    def pivoteafila(A,B,vertabla=False):
        '''
        Pivotea parcial por filas
        Si hay ceros en diagonal es matriz singular,
        Tarea: Revisar si diagonal tiene ceros
        '''
        A = np.array(A,dtype=float)
        B = np.array(B,dtype=float)
        # Matriz aumentada
        nB = len(np.shape(B))
        if nB == 1:
            B = np.transpose([B])
        AB  = np.concatenate((A,B),axis=1)
        
        if vertabla==True:
            print('Matriz aumentada')
            print(AB)
            print('Pivoteo parcial:')
        
        # Pivoteo por filas AB
        tamano = np.shape(AB)
        n = tamano[0]
        m = tamano[1]
        
        # Para cada fila en AB
        pivoteado = 0
        for i in range(0,n-1,1):
            # columna desde diagonal i en adelante
            columna = np.abs(AB[i:,i])
            dondemax = np.argmax(columna)
            
            # dondemax no es en diagonal
            if (dondemax != 0):
                # intercambia filas
                temporal = np.copy(AB[i,:])
                AB[i,:] = AB[dondemax+i,:]
                AB[dondemax+i,:] = temporal
    
                pivoteado = pivoteado + 1
                if vertabla==True:
                    print(' ',pivoteado,
                          'intercambiar filas: ',i,
                          'con', dondemax+i)
        if vertabla==True:
            if pivoteado==0:
                print('  Pivoteo por filas NO requerido')
            else:
                print(AB)
        return(AB)
    
    AB = pivoteafila(A,B,vertabla=True)
    
    tamano = np.shape(AB)
    n = tamano[0]
    m = tamano[1]
    
    AB0 = np.copy(AB)
    
    L = np.identity(n,dtype=float) # Inicializa L
    
    # eliminacion hacia adelante
    for i in range(0,n-1,1):
        pivote = AB[i,i]
        adelante = i+1
        for k in range(adelante,n,1):
            factor = AB[k,i]/pivote
            AB[k,:] = AB[k,:] - AB[i,:]*factor
            
            L[k,i] = factor # llena L
    
    U = np.copy(AB[:,:n])
    
    # SALIDA
    print('matriz L: ')
    print(L)
    print('Matriz U: ')
    print(U)
    

    Si se requiere una respuesta unificada en una variable, se puede convertir en una arreglo de matrices [L,U].
    Las matrices L y U se pueden leer como L=LU[0] y U=LU[1]

    LU = np.array([L,U])
    
    # SALIDA
    print('triangular inferior L')
    print(LU[0])
    print('triangular superior U')
    print(LU[1])
    

    [ Matriz A=L.U ] [ Ejercicio ] [ Algoritmo LU ] [ Función LU ] ||
    [ Solución LY=B , UX=Y ]

    ..


    4. Algoritmo como Función de Python

    El resultado a obtener es:

    Matriz aumentada
    [[  3.    -0.1   -0.2    7.85]
     [  0.1    7.    -0.3  -19.3 ]
     [  0.3   -0.2   10.    71.4 ]]
    Pivoteo parcial:
      Pivoteo por filas NO requerido
    Elimina hacia adelante:
     fila 0 pivote:  3.0
       factor:  0.03333333333333333  para fila:  1
       factor:  0.09999999999999999  para fila:  2
     fila 1 pivote:  7.003333333333333
       factor:  -0.027129938124702525  para fila:  2
     fila 2 pivote:  10.012041884816753
    [[  3.      -0.1     -0.2      7.85  ]
     [  0.       7.0033  -0.2933 -19.5617]
     [  0.       0.      10.012   70.0843]]
    matriz L: 
    [[ 1.      0.      0.    ]
     [ 0.0333  1.      0.    ]
     [ 0.1    -0.0271  1.    ]]
    Matriz U: 
    [[  3.      -0.1     -0.2      7.85  ]
     [  0.       7.0033  -0.2933 -19.5617]
     [  0.       0.      10.012   70.0843]]
    >>>
    

    Instrucciones en Python

    # Matrices L y U
    # Modificando el método de Gauss
    import numpy as np
    
    def pivoteafila(A,B,vertabla=False):
        '''
        Pivoteo parcial por filas, entrega matriz aumentada AB
        Si hay ceros en diagonal es matriz singular,
        Tarea: Revisar si diagonal tiene ceros
        '''
        A = np.array(A,dtype=float)
        B = np.array(B,dtype=float)
        # Matriz aumentada
        nB = len(np.shape(B))
        if nB == 1:
            B = np.transpose([B])
        AB  = np.concatenate((A,B),axis=1)
        
        if vertabla==True:
            print('Matriz aumentada')
            print(AB)
            print('Pivoteo parcial:')
        
        # Pivoteo por filas AB
        tamano = np.shape(AB)
        n = tamano[0]
        m = tamano[1]
        
        # Para cada fila en AB
        pivoteado = 0
        for i in range(0,n-1,1):
            # columna desde diagonal i en adelante
            columna = np.abs(AB[i:,i])
            dondemax = np.argmax(columna)
            
            # dondemax no es en diagonal
            if (dondemax != 0):
                # intercambia filas
                temporal = np.copy(AB[i,:])
                AB[i,:] = AB[dondemax+i,:]
                AB[dondemax+i,:] = temporal
    
                pivoteado = pivoteado + 1
                if vertabla==True:
                    print(' ',pivoteado,
                          'intercambiar filas: ',i,
                          'con', dondemax+i)
        if vertabla==True:
            if pivoteado==0:
                print('  Pivoteo por filas NO requerido')
            else:
                print(AB)
        return(AB)
    
    def gauss_eliminaAdelante(AB, vertabla=False,lu=False,casicero = 1e-15):
        ''' Gauss elimina hacia adelante, a partir de,
        matriz aumentada y pivoteada.
        Para respuesta en forma A=L.U usar lu=True entrega[AB,L,U]
        '''
        tamano = np.shape(AB)
        n = tamano[0]
        m = tamano[1]
        L = np.identity(n,dtype=float) # Inicializa L
        if vertabla==True:
            print('Elimina hacia adelante:')
        for i in range(0,n,1):
            pivote = AB[i,i]
            adelante = i+1
            if vertabla==True:
                print(' fila',i,'pivote: ', pivote)
            for k in range(adelante,n,1):
                if (np.abs(pivote)>=casicero):
                    factor = AB[k,i]/pivote
                    AB[k,:] = AB[k,:] - factor*AB[i,:]
                    for j in range(0,m,1): # casicero revisa
                        if abs(AB[k,j])<casicero:
                            AB[k,j]=0
    
                    L[k,i] = factor # llena L
                    
                    if vertabla==True:
                        print('   factor: ',factor,' para fila: ',k)
                else:
                    print('  pivote:', pivote,'en fila:',i,
                          'genera division para cero')
        respuesta = AB
        if vertabla==True:
            print(AB)
        if lu==True:
            U = AB[:,:n-1]
            respuesta = [AB,L,U]
        return(respuesta)
    
    # PROGRAMA ------------------------
    # INGRESO
    A = [[ 3. , -0.1, -0.2],
         [ 0.1,  7. , -0.3],
         [ 0.3, -0.2, 10. ]]
    
    B = [7.85,-19.3,71.4]
    
    # PROCEDIMIENTO
    np.set_printoptions(precision=4) # 4 decimales en print
    casicero = 1e-15  # redondear a cero
    
    AB = pivoteafila(A,B,vertabla=True)
    
    AB = gauss_eliminaAdelante(AB,vertabla=True, lu=True)
    L = AB[1]
    U = AB[0]
    
    # SALIDA
    print('matriz L: ')
    print(L)
    print('Matriz U: ')
    print(U)
    

    Función en Scipy

    Luego del resultado o definida la matriz a, la instrucción en la librería Scipy es:

    >>> import scipy as sp
    >>> [L,U] =sp.linalg.lu(A,permute_l=True)
    >>> L
    array([[ 1.        ,  0.        ,  0.        ],
           [ 0.03333333,  1.        ,  0.        ],
           [ 0.1       , -0.02712994,  1.        ]])
    >>> U
    array([[ 3.        , -0.1       , -0.2       ],
           [ 0.        ,  7.00333333, -0.29333333],
           [ 0.        ,  0.        , 10.01204188]])
    >>> 
    

    [ Matriz A=L.U ] [ Ejercicio ] [ Algoritmo LU ] [ Función LU ] ||
    [ Solución LY=B , UX=Y ]
    ..


    5. Solución con LY=B , UX=Y

    Referencia: Chapra 10.2 p287, pdf312

    Se plantea resolver el sistema de ecuaciones usando matrices triangulares

    A = \begin{pmatrix} 3 & -0.1 & -0.2 \\ 0.1 & 7 & -0.3 \\0.3 & -0.2 & 10 \end{pmatrix} B = [7.85,-19.3,71.4]

    Para encontrar la solución al sistema de ecuaciones, se plantea resolver:
    - sustitución hacia adelante de LY=B
    - sustitución hacia atrás para UX=Y

    Al algoritmo de la sección anterior se añade los procedimientos correspondientes con los que se obtiene la solución:

    Matriz aumentada
    [[  3.    -0.1   -0.2    7.85]
     [  0.1    7.    -0.3  -19.3 ]
     [  0.3   -0.2   10.    71.4 ]]
    Pivoteo parcial:
      Pivoteo por filas NO requerido
    matriz L: 
    [[ 1.          0.          0.        ]
     [ 0.03333333  1.          0.        ]
     [ 0.1        -0.02712994  1.        ]]
    Matriz U: 
    [[ 3.         -0.1        -0.2       ]
     [ 0.          7.00333333 -0.29333333]
     [ 0.          0.         10.01204188]]
    B1 :
    [[  7.85]
     [-19.3 ]
     [ 71.4 ]]
    Y Sustitución hacia adelante
    [[  7.85      ]
     [-19.56166667]
     [ 70.08429319]]
    X Sustitución hacia atras
    [ 3.  -2.5  7. ]
    >>>

    Instrucciones en Python

    # Solución usando Matrices L y U
    # continuación de ejercicio anterior
    import numpy as np
    
    # PROGRAMA ------------
    # INGRESO
    A = [[ 3. , -0.1, -0.2],
         [ 0.1,  7. , -0.3],
         [ 0.3, -0.2, 10. ]]
    
    B = [7.85,-19.3,71.4]
    
    # PROCEDIMIENTO
    np.set_printoptions(precision=4) # 4 decimales en print
    casicero = 1e-15  # redondear a cero
    
    def pivoteafila(A,B,vertabla=False):
        '''
        Pivoteo parcial por filas, entrega matriz aumentada AB
        Si hay ceros en diagonal es matriz singular,
        Tarea: Revisar si diagonal tiene ceros
        '''
        A = np.array(A,dtype=float)
        B = np.array(B,dtype=float)
        # Matriz aumentada
        nB = len(np.shape(B))
        if nB == 1:
            B = np.transpose([B])
        AB  = np.concatenate((A,B),axis=1)
        
        if vertabla==True:
            print('Matriz aumentada')
            print(AB)
            print('Pivoteo parcial:')
        
        # Pivoteo por filas AB
        tamano = np.shape(AB)
        n = tamano[0]
        m = tamano[1]
        
        # Para cada fila en AB
        pivoteado = 0
        for i in range(0,n-1,1):
            # columna desde diagonal i en adelante
            columna = np.abs(AB[i:,i])
            dondemax = np.argmax(columna)
            
            # dondemax no es en diagonal
            if (dondemax != 0):
                # intercambia filas
                temporal = np.copy(AB[i,:])
                AB[i,:] = AB[dondemax+i,:]
                AB[dondemax+i,:] = temporal
    
                pivoteado = pivoteado + 1
                if vertabla==True:
                    print(' ',pivoteado,
                          'intercambiar filas: ',i,
                          'con', dondemax+i)
        if vertabla==True:
            if pivoteado==0:
                print('  Pivoteo por filas NO requerido')
            else:
                print(AB)
        return(AB)
    
    AB = pivoteafila(A,B,vertabla=True)
    
    tamano = np.shape(AB)
    n = tamano[0]
    m = tamano[1]
    
    AB0 = np.copy(AB)
    B1 = np.copy(AB[:,m-1])
    
    # Matrices L y U
    L = np.identity(n,dtype=float) # Inicializa L
    # eliminacion hacia adelante
    for i in range(0,n-1,1):
        pivote = AB[i,i]
        adelante = i+1
        for k in range(adelante,n,1):
            factor = AB[k,i]/pivote
            AB[k,:] = AB[k,:] - AB[i,:]*factor
            
            L[k,i] = factor # llena L
    
    U = np.copy(AB[:,:n])
    
    # Resolver LY = B
    B1  = np.transpose([B1])
    AB =np.concatenate((L,B1),axis=1)
    
    # sustitución hacia adelante
    Y = np.zeros(n,dtype=float)
    Y[0] = AB[0,n]
    for i in range(1,n,1):
        suma = 0
        for j in range(0,i,1):
            suma = suma + AB[i,j]*Y[j]
        b = AB[i,n]
        Y[i] = (b-suma)/AB[i,i]
    
    Y = np.transpose([Y])
    
    # Resolver UX = Y
    AB =np.concatenate((U,Y),axis=1)
    
    # sustitución hacia atrás
    ultfila = n-1
    ultcolumna = m-1
    X = np.zeros(n,dtype=float)
    
    for i in range(ultfila,0-1,-1):
        suma = 0
        for j in range(i+1,ultcolumna,1):
            suma = suma + AB[i,j]*X[j]
        b = AB[i,ultcolumna]
        X[i] = (b-suma)/AB[i,i]
    
    # SALIDA
    print('matriz L: ')
    print(L)
    print('Matriz U: ')
    print(U)
    print('B1 :')
    print(B1)
    print('Y Sustitución hacia adelante')
    print(Y)
    print('X Sustitución hacia atras')
    print(X)
    

    [ Matriz A=L.U ] [ Ejercicio ] [ Algoritmo LU ] [ Función LU ] ||
    [ Solución LY=B , UX=Y ]