Etiqueta: algoritmo Python

  • 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 ]

  • 3.7 Método de Gauss-Seidel con Python

    [ Gauss-Seidel ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ] [Gráfica]
    ..


    Referencia: Chapra 11.2 p310, Burden 7.3 p337, Rodríguez 5.2 p162

    El método de Gauss-Seidel, reescribe las expresiones del sistema de ecuaciones, despejando la incógnita de la diagonal en cada ecuación. Usa el vector inicial X0, actualizando el vector X por cada nuevo valor calculado del vector. La diferencia principal con el método de Jacobi es la actualización de X en cada cálculo, por lo que las iteraciones llegan más rápido al punto cuando el método converge.

    Gauss-Seidel iteraciones animado

    Las iteraciones pueden ser o no, convergentes.

    La expresión para encontrar nuevos valores usando la matriz de coeficientes A, el vector de constantes B y como incógnitas X es la misma que Jacobi:

    x_i = \bigg(b_i - \sum_{j=0, j\neq i}^n a_{i,j}x_j\bigg) \frac{1}{a_{ii}}

    La convergencia del método iterativo se puede revisar usando el número de condición de la matriz de coeficientes A. Un resultado cercano a 1, implica que el sistema será convergente.

    cond(A) = ||A|| ||A-1||

    np.linalg.cond(A)

    [ Gauss-Seidel ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ] [Gráfica]
    ..


    2. Ejercicio

    Referencia: 1Eva_IIT2011_T2 Sistema de Ecuaciones, diagonal dominante

    Considere el siguiente sistema de ecuaciones AX=B dado por:

    \begin{cases} -2x+5y+9z=1\\7x+y+z=6\\-3x+7y-z=-26\end{cases}

    Luego del pivoteo parcial por filas, el sistema de ecuaciones a usar para el método de Gauss-Seidel es:

    \begin{cases} 7x+y+z=6\\-3x+7y-z=-26\\-2x+5y+9z=1\end{cases}

    Desarrolle el ejercicio usando el método de Gauss-Seidel, tomando como vector inicial X0=[0,0,0] y tolerancia de 0.0001

    Compare los resultados con el método de Jacobi.

    [ Gauss-Seidel ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ] [Gráfica]
    ..


    3. Desarrollo Analítico

    Para el método de Gauss-Seidel, se usan las ecuaciones reordenas con el pivoteo parcial por filas, para que en lo posible sea diagonal dominante que mejora la convergencia. El resultado del algoritmo a partir de las ecuaciones presentadas al inicio es:

    Pivoteo parcial por filas AB:
    [[  7.   1.   1.   6.]
     [ -3.   7.  -1. -26.]
     [ -2.   5.   9.   1.]]
    A:
    [[ 7.  1.  1.]
     [-3.  7. -1.]
     [-2.  5.  9.]]
    B:
    [  6. -26.   1.]
    
    \begin{cases} 7x+y+z=6\\-3x+7y-z=-26\\-2x+5y+9z=1\end{cases}

    se reescriben despejando la incógnita de la diagonal en cada ecuación.

    x=\frac{6-y-z}{7} y= \frac{-26+3x+z}{7} z=\frac{1+2x-5y}{9}

    El vector inicial X0=[0,0,0] representa un punto de partida, valores estimados o semilla para buscar la solución a las variables X0=[x0,y0,z0].

    Como las ecuaciones son el comportamiento del 'sistema', se usan con  X0 para obtener un nuevo y mejor valor estimado para la solución.

    itera = 0

    X_0=[x_0,y_0,z_0] X_1 = X_0=[0,0,0] x_1=\frac{6-(0)-(0)}{7} =\frac{6}{7}=0.8571

    Se actualiza el vector X1,

    X_1 = [0.8571,0,0] y_1= \frac{-26+3(0.8571)+(0)}{7} =-3.3469

    Se actualiza el vector X1,

    X_1 = [0.8571,-3.3469,0] z_1=\frac{1+2(0.8571)-5(-3.3469)}{9} =\frac{1}{9}=2.161 X_1=[0.8571, -3.3469, 2.161]

    Para revisar si se está más cerca o no de la solución se calculan los desplazamientos o diferencias entre los valores anteriores y los nuevos:

    \text{diferencias}=X_1-X_0 =[0.8571-0, -3.3469-0, 2.161-0]
     [0.8571, -3.3469, 2.161]
    -[0.    ,  0.    , 0.    ]
    __________________________
     [0.8571, -3.3469, 2.161]
    \text{diferencias}=[0.8571, -3.3469, 2.161]

    Para estimar el valor del error, se presenta un valor simplificado o escalar de las diferencias (norma del error). Por simplicidad se usa el valor de magnitud máxima. Se usa como variable 'errado', para evitar el conflicto de nombre de variable error usada como palabra reservada en Python:

    \text{errado}= max \Big|\text{diferencias}\Big| \text{errado}= max \Big|[0.8571, -3.3469, 2.161]\Big| = 3.3469

    Al comparar el valor errado con la tolerancia, se observa que el error aún es mayor que lo tolerado. Por lo que se sigue con la siguiente iteración.

    itera = 1

    X_1=[x_1,y_1,z_1] X_2 = X_1=[0.8571, -3.3469, 2.161] x_2=\frac{6-(-3.3469)-(2.161)}{7} = 1.0266

    Se actualiza el vector X2,

    X_2 = [1.0266, -3.3469, 2.161] y_2= \frac{-26+3(1.0266)+(2.161)}{7} =-2.9656

    Se actualiza el vector X2,

    X_2 = [1.0266, -2.9656, 2.161] z_2=\frac{1+2(1.0266)-5(-2.9656)}{9} =1.9868 X_2 = [1.0266, -2.9656, 1.9868] \text{diferencias}=|X_2-X_1| =|[1.0266-0.8571, -2.9656-(-3.3469), 1.9868-2.161]|
     [1.0266, -2.9656, 1.9868]
    -[0.8571, -3.3469, 2.161 ] 
    __________________________
     [0.1694,  0.3813, 0.1742]
    \text{diferencias}= |[0.1694, 0.3813, 0.1742]| \text{errado}=max |X_2-X_1|= 0.3813

    itera = 2

    X_3 = X_2=[1.0266, -2.9656, 1.9868] x_3=\frac{6-(-2.9656)-(1.9868)}{7} = 0.997 X_3 =[0.997, -2.9656, 1.9868] y_3= \frac{-26+3(0.997)+(1.9868)}{7} = -3.0032 X_3 =[0.997, -3.0032, 1.9868] z_3=\frac{1+2(0.997)-5(-3.0032)}{9} = 2.0011 X_3 =[0.997, -3.0032, 2.0011] \text{diferencias}=|X_3-X_2| =|[0.997-1.0266, -3.0032-(-2.9656), 2.0011-1.9868]|
     [ 0.997 , -3.0032, 2.0011]
    -[ 1.0266, -2.9656, 1.9868] 
    __________________________
     [-0.0296,  0.0376, 0.0143]
    \text{diferencias}=|[-0.0296, 0.0376, 0.0143]| \text{errado}=max |X_2-X_1|= 0.0376

    Método Gauss-Seidel X itera ErroresEn cada iteración el valor de error disminuye, por lo que se estima que el método converge.

    Al usar el algoritmo, de los resultados se observa que se detiene luego de 6 iteraciones, los errores son menores a la tolerancia:

    Iteraciones Gauss-Seidel
    itera,[X]
       errado,[diferencia]
    0 [0. 0. 0.] 0.0002
    1 [ 0.8571 -3.3469  2.161 ]
      3.3469387755102042 [0.8571 3.3469 2.161 ]
    2 [ 1.0266 -2.9656  1.9868]
      0.381322597066037 [0.1694 0.3813 0.1742]
    3 [ 0.997  -3.0032  2.0011]
      0.03756644188210334 [0.0296 0.0376 0.0143]
    4 [ 1.0003 -2.9997  1.9999]
      0.00346691102194141 [0.0033 0.0035 0.0012]
    5 [ 1. -3.  2.]
      0.0003256615309844557 [3.2566e-04 3.0918e-04 9.9398e-05]
    6 [ 1. -3.  2.]
      2.996898191776065e-05 [2.9969e-05 2.7044e-05 8.3644e-06]
    Metodo de Gauss-Seidel
    numero de condición: 1.9508402675105447
    X:  [ 1. -3.  2.]
    errado: 2.996898191776065e-05
    iteraciones: 6

    [ Gauss-Seidel ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ] [Gráfica]
    ..


    4. Algoritmo en Python

    El algoritmo para Gauss-Seidel es semejante al realizado para Jacobi, se debe incorporar las instrucciones para actualizar los valores de X[i] en cada uso de ecuación o fila.

    Para el algoritmo se usa el sistema de ecuaciones en su forma matricial A.X=B.
    Se asume que se ha aplicado el pivoteo parcial por filas y matriz aumentada.

    i=0 # una ecuacion
    suma = B[i] # numerador
    for j in range(0,m,1): # un coeficiente
        if (i!=j): # excepto diagonal de A
            suma = suma-A[i,j]*X[j]
                    
    nuevo = suma/A[i,i]
    diferencia[i] = abs(nuevo-X[i])
    X[i] = nuevo
    errado = np.max(diferencia)

    La actualización para el algoritmo es:

    # Método de Gauss-Seidel
    import numpy as np
    
    # INGRESO
    # NOTA: Para AB, se ha usado pivoteo parcial por filas
    A = [[ 7,  1,  1],
         [-3,  7, -1],
         [-2,  5,  9]]
    B = [6, -26, 1]
    
    X0 = [0,0,0]
    tolera = 0.0001
    iteramax = 100
    
    # PROCEDIMIENTO
    # Matrices como arreglo, numeros reales
    A = np.array(A,dtype=float)
    B = np.array(B,dtype=float)
    X0 = np.array(X0,dtype=float)
    tamano = np.shape(A) # tamaño A
    n = tamano[0]
    m = tamano[1]
    
    # valores iniciales
    diferencia = np.ones(n, dtype=float)
    errado = 2*tolera # np.max(diferencia)
    
    itera = 0 
    X = np.copy(X0)
    while errado>tolera and itera<=iteramax:
        
        for i in range(0,n,1): # una ecuacion
            suma = B[i] # numerador
            for j in range(0,m,1): # un coeficiente
                if (i!=j): # excepto diagonal de A
                    suma = suma-A[i,j]*X[j]
                    
            nuevo = suma/A[i,i]
            diferencia[i] = abs(nuevo-X[i])
            X[i] = nuevo
        errado = np.max(diferencia)
        
        print(itera, X)
        print('  ',errado,diferencia)
        itera = itera + 1
    
    if (itera>iteramax): # No converge
        X = np.nan
        print('No converge,iteramax superado')
    
    # numero de condicion
    ncond = np.linalg.cond(A)
    
    # SALIDA
    print('Método de Gauss-Seidel')
    print('numero de condición:', ncond)
    print('X: ',X)
    print('errado:',errado)
    print('iteraciones:', itera)
    

    [ Gauss-Seidel ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ] [Gráfica]
    ..


    5. Algoritmo en Python como función

    Las instrucciones del algoritmo, empaquetadas como función, añaden algunas facilidades para el desarrollo del ejercicio. Antes de usar el método de Gauss-Seidel, se aplica el pivoteo parcial por filas para mejorar de ser posible la convergencia.

    En la función gauss_seidel() se añaden las instrucciones para mostrar los resultados parciales en cada iteración. Se añade una tabla para revisión de valores al final del algoritmo, o para realizar la gráfica de errores por iteración.

    Si el sistema es de 3x3 se puede añadir una gráfica de los resultados por iteración, mostrando la trayectoria descrita por los resultados parciales en 3D.

    # Algoritmo Gauss-Seidel
    # solución de matrices
    # métodos iterativos
    # Referencia: Chapra 11.2, p.310,
    #      Rodriguez 5.2 p.162
    import numpy as np
    
    def gauss_seidel(A,B,X0,tolera, iteramax=100, vertabla=False, precision=4):
        ''' Método de Gauss Seidel, tolerancia, vector inicial X0
            para mostrar iteraciones: vertabla=True
        '''
        # Matrices como arreglo, numeros reales
        A = np.array(A, dtype=float)
        B = np.array(B, dtype=float)
        X0 = np.array(X0, dtype=float)
        tamano = np.shape(A)
        n = tamano[0]
        m = tamano[1]
    
        # valores iniciales
        diferencia = 2*tolera*np.ones(n, dtype=float)
        errado = 2*tolera # np.max(diferencia)
       
        tabla = [np.copy(X0)] # tabla de iteraciones
        tabla = np.concatenate((tabla,[[np.nan]]),
                                axis=1) # añade errado
    
        if vertabla==True:
            print('Iteraciones Gauss-Seidel')
            print('itera,[X]')
            print('   errado,[diferencia]')
            print(0,X0)
            print(' ',np.nan)
            np.set_printoptions(precision)
    
        itera = 0 # Gauss-Sediel
        X = np.copy(X0)
        while (errado>tolera and itera<iteramax):
            
            for i in range(0,n,1): # una ecuacion
                suma = B[i]
                for j in range(0,m,1):
                    if (i!=j):
                        suma = suma-A[i,j]*X[j]
                nuevo = suma/A[i,i]
                diferencia[i] = np.abs(nuevo-X[i])
                X[i] = nuevo
            errado = np.max(diferencia)
    
            Xfila= np.concatenate((X,[errado]),axis=0)
            tabla = np.concatenate((tabla,[Xfila]),axis = 0)
            itera = itera + 1
            if vertabla==True:
                print(itera, X)
                print(' ', errado,diferencia)
            
        # No converge
        if (itera>iteramax):
            X = np.nan
            print('No converge,iteramax superado')
        return(X,tabla)
    
    def pivoteafila(A,B,vertabla=False):
        '''
        Pivotea 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,'y', dondemax+i)
        if vertabla==True:
            if pivoteado==0:
                print('  Pivoteo por filas NO requerido')
            else:
                print('AB')
                print(AB)
        return(AB)
    
    # PROGRAMA Búsqueda de solucion  --------
    # INGRESO
    A = [[ 7,  1,  1],
         [-3,  7, -1],
         [-2,  5,  9]]
    B = [6, -26, 1]
    
    X0 = [0,0,0]
    tolera = 0.0001
    iteramax = 100
    verdecimal = 4
    
    # PROCEDIMIENTO
    AB = pivoteafila(A,B,vertabla=True)
    n,m = np.shape(AB)
    
    A = AB[:,:n] # separa en A y B
    B = AB[:,n]
    
    [X, tabla] = gauss_seidel(A,B,X0, tolera,
                               vertabla=True,
                               precision=verdecimal)
    n_itera = len(tabla)-1
    errado = tabla[-1,-1]
    
    # numero de condicion
    ncond = np.linalg.cond(A)
    
    # SALIDA
    print('Metodo de Gauss-Seidel')
    print('numero de condición:', ncond)
    print('X: ',X)
    print('errado:',errado)
    print('iteraciones:', n_itera)
    

    [ Gauss-Seidel ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ] [Gráfica]
    ..


    4.1 Gráficas de iteraciones

    Se muestra la gráfica de errores vs iteraciones.

    # GRAFICA de iteraciones
    errados = tabla[:,n]
    
    import matplotlib.pyplot as plt
    # grafica de error por iteracion
    fig2D = plt.figure()
    graf = fig2D.add_subplot(111)
    graf.plot(errados,color='purple')
    graf.set_xlabel('itera')
    graf.set_ylabel('|error|')
    graf.set_title('Método de Gauss-Seidel: error[itera]')
    graf.grid()
    
    plt.show()
    

    [ Gauss-Seidel ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ] [Gráfica]


    4.2 Gráfica de iteraciones para sistema de 3×3

    Si el sistema es de 3×3, se muestra una gráfica de la trayectoria de resultados parciales, semejante a la presentada para la descripción del concepto. Permite observar si la espiral es convergente o no.

    # grafica de iteraciones x,y,z
    # solo para 3x3
    xp = tabla[:,0]
    yp = tabla[:,1]
    zp = tabla[:,2]
    
    fig3D = plt.figure()
    graf3D = fig3D.add_subplot(111,projection='3d')
    
    graf3D.plot(xp,yp,zp,color='purple')
    graf3D.plot(xp[0],yp[0],zp[0],'o',label='X[0]')
    graf3D.plot(xp[n_itera],yp[n_itera],zp[n_itera],
                'o',label='X['+str(n_itera)+']')
    
    graf3D.set_xlabel('x')
    graf3D.set_ylabel('y')
    graf3D.set_zlabel('z')
    graf3D.set_title('Método de Gauss-Seidel: iteraciones X')
    
    # graf3D.view_init(45,45)
    plt.legend()
    plt.show()

    [ Gauss-Seidel ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Función ] [Gráfica]

  • 3.6 Método de Jacobi con Python

    [ Método de Jacobi ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Gráfica ]

    ..


    1. El método iterativo de Jacobi

    Referencia: Burden 7.3 p334, Rodríguez 5.1 p154, Chapra  11.2.1p312

    Los métodos iterativos para sistemas de ecuaciones, son semejantes al método de punto fijo para búsqueda de raíces, requieren un punto inicial para la búsqueda "de la raíz o solución" que satisface el sistema. Se plantea considerar aproximaciones o iteraciones de forma sucesiva desde un punto de partida o inicial X0, hacia una solución del sistema de ecuaciones.

    método de jacobi 3D animado

    Las iteraciones pueden ser o no, convergentes.

    Para describir el método iterativo de Jacobi, se usa como ejemplo: un sistema de 3 incógnitas y 3 ecuaciones, diagonalmente dominante, planteado en su forma matricial.

    \begin{cases} a_{0,0} x_0+a_{0,1}x_1+a_{0,2} x_2 = b_{0} \\ a_{1,0} x_0+a_{1,1}x_1+a_{1,2} x_2 = b_{1} \\ a_{2,0} x_0+a_{2,1}x_1+a_{2,2} x_2 = b_{2} \end{cases}

    La expresión para encontrar nuevos valores usando la matriz de coeficientes A, el vector de constantes B y como incógnitas X es:

    x_i = \bigg(b_i - \sum_{j=0, j\neq i}^n a_{i,j}x_j\bigg) \frac{1}{a_{ii}}

    El método de Jacobi, reescribe las expresiones despejando la incógnita de la diagonal en cada ecuación. El patrón que se observa en las ecuaciones despejadas, se describe en la fórmula para xi de la fila i, que se usará en el algoritmo en Python.

    x_0 = \frac{b_{0} -a_{0,1}x_1 -a_{0,2} x_2 }{a_{0,0}} x_1 = \frac{b_{1} -a_{1,0} x_0 -a_{1,2} x_2}{a_{1,1}} x_2 = \frac{b_{2} -a_{2,0} x_0 - a_{2,1} x_1}{a_{2,2}}

    La convergencia del método iterativo se puede revisar usando el número de condición de la matriz de coeficientes A. Un resultado cercano a 1, implica que el sistema será convergente.

    cond(A) = ||A|| ||A-1||

    np.linalg.cond(A)

    Usando de forma experimental los ejercicios de las evaluaciones de la sección matriciales iterativos,  el número de condición no debería superar el valor de 7 para que el sistema sea convergente. Puede revisar que si es mayor, el método tiende a no ser convergente.  Aunque los textos del libro no lo indican directamente,

    [ Método de Jacobi ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Gráfica ]

    ..


    2. Ejercicio

    Referencia: 1Eva_IIT2011_T2 Sistema de Ecuaciones, diagonal dominante

    Considere el siguiente sistema de ecuaciones A.X=B dado por:

    \begin{cases} -2x+5y+9z=1\\7x+y+z=6\\-3x+7y-z=-26\end{cases}

    Luego del pivoteo parcial por filas, el sistema de ecuaciones a usar para el método de Jacobi es:

    \begin{cases} 7x+y+z=6\\-3x+7y-z=-26\\-2x+5y+9z=1\end{cases}

    Desarrolle el ejercicio usando el método de Jacobi, tomando como vector inicial X0=[0,0,0] y tolerancia de 0.0001.

    [ Método de Jacobi ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Gráfica ]

    ..


    3. Desarrollo Analítico

    Para el método de Jacobi, se usan las ecuaciones reordenas con el pivoteo parcial por filas, para que en lo posible sea diagonal dominante que mejora la convergencia. El resultado del algoritmo a partir de las ecuaciones presentadas al inicio es:

    Pivoteo parcial por filas AB:
    [[  7.   1.   1.   6.]
     [ -3.   7.  -1. -26.]
     [ -2.   5.   9.   1.]]
    A:
    [[ 7.  1.  1.]
     [-3.  7. -1.]
     [-2.  5.  9.]]
    B:
    [  6. -26.   1.]
    
    \begin{cases} 7x+y+z=6\\-3x+7y-z=-26\\-2x+5y+9z=1\end{cases}

    Se despejan la incógnita de la diagonal en cada ecuación.

    x=\frac{6-y-z}{7} y= \frac{-26+3x+z}{7} z=\frac{1+2x-5y}{9}

    El vector inicial X0=[0,0,0] representa un punto de partida, valores estimados o semilla para buscar la solución a las variables x0,y0,z0.

    Como las ecuaciones son el comportamiento del 'sistema', se usan con  X0 para obtener un nuevo y mejor valor estimado para la solución.

    itera = 0

    X_0=[x_0,y_0,z_0] X_0=[0,0,0] x_1=\frac{6-(0)-(0)}{7} =\frac{6}{7}=0.8571 y_1= \frac{-26+3(0)+(0)}{7} =\frac{-26}{7}=-3.7142 z_1=\frac{1+2(0)-5(0)}{9} =\frac{1}{9}=0.1111 X_1=[0.8571, -3.7142, 0.1111]

    Para revisar si se está más cerca de la solución, se calculan las diferencias, tramos o desplazamientos entre los valores nuevos y anteriores:

    \text{diferencias}=X_1-X_0 =[0.8571-0, -3.7142-0, 0.1111-0]
     [0.8571, -3.7142, 0.1111]
    -[0.    ,  0.    , 0.    ]
    __________________________
     [0.8571, -3.7142, 0.1111]
    \text{diferencias}=[0.8571, -3.7142, 0.1111]

    Para estimar el valor del error, se presenta un valor simplificado o escalar de las diferencias (norma del error). Por simplicidad se usa el valor de magnitud máxima de los errores. Se usa como variable 'errado', para evitar el conflicto de nombre de variable error usada como palabra reservada en Python:

    \text{errado}= max \Big|\text{diferencias}\Big| \text{errado}= max \Big|[0.8571, -3.7142, 0.1111]\Big| = 3.7142

    Al comparar el valor errado con la tolerancia, se observa que el error aún es mayor que lo tolerado. Por lo que se continúa con otra iteración.

    itera = 1

    X_1=[x_1,y_1,z_1] X_1=[0.8571, -3.7142, 0.1111] x_2=\frac{6-(-3.7142)-(0.1111)}{7} = 1.3718 y_2= \frac{-26+3(0.8571)+(0.1111)}{7} =-3.3310 z_2=\frac{1+2(0.8571)-5(-3.7142)}{9} =2.3650 X_2=[1.3718, -3.3310, 2.3650] \text{diferencias}=|X_2-X_1| =|[0.8571-1.3718, -3.3310-(-3.7142), 2.3650-0.1111]|
     [1.3718, -3.3310, 2.3650]
    -[0.8571, -3.7142, 0.1111] 
    __________________________
    [0.51474, -0.38322, 2.25397]
    \text{diferencias}= |[0.51474, -0.38322, 2.25397]| \text{errado}=max |X_2-X_1|= 2.2539

    itera = 2

    X_2=[1.3718, -3.3310, 2.3650] x_3=\frac{6-(-3.3310)-(2.3650)}{7} = 0.9951 y_3= \frac{-26+3(1.3718)+(2.3650)}{7} = -2.7884 z_3=\frac{1+2(1.3718)-5(-3.3310)}{9} = 2.2665 X_3=[0.99514, -2.7884, 2.2665] \text{diferencias}=|X_3-X_2| =|[0.99514-1.3718, -2.7884-(-3.3310), 2.2665-2.3650]|
     [ 0.9951, -2.7884, 2.2665]
    -[ 1.3718, -3.3310, 2.3650] 
    __________________________
     [-0.3767,  0.5426, 0.0985]
    \text{diferencias}=|[-0.3767, 0.5426, 0.0985]| \text{errado}=max |X_2-X_1|= 0.5425

    método de Jacobi X itera ErroresEn cada iteración el valor de error disminuye, por lo que se estima que el método converge.

    Al usar el algoritmo, de los resultados se observa que se detiene luego de 14 iteraciones, los errores son menores a la tolerancia:

    Iteraciones Jacobi
    itera,[X]
         ,errado,|diferencia|
    0 [0. 0. 0.]
      nan
    1 [ 0.85714 -3.71429  0.11111]
      3.7142857142857144 [0.85714 3.71429 0.11111]
    2 [ 1.37188 -3.33107  2.36508]
      2.2539682539682544 [0.51474 0.38322 2.25397]
    3 [ 0.99514 -2.78847  2.26657]
      0.5425979915775843 [0.37674 0.5426  0.09851]
    4 [ 0.9317 -2.964   1.8814]
      0.3851635892452223 [0.06344 0.17553 0.38516]
    ...
      0.0003598675094789 [2.30116e-04 3.59868e-04 6.47473e-05]
    13 [ 1.00004 -3.00002  2.00007]
      0.0002510632800638 [4.21600e-05 1.07871e-04 2.51063e-04]
    14 [ 0.99999 -2.99997  2.00002]
      5.393476706316e-05 [5.12763e-05 5.39348e-05 5.05593e-05]
    Metodo de Jacobi
    numero de condición: 1.9508402675105447
    X:  [ 0.99999 -2.99997  2.00002]
    errado: 5.3934767063168465e-05
    iteraciones: 14

    [ Método de Jacobi ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Gráfica ]
    ..


    4. Algoritmo en Python

    Para el algoritmo se usa el sistema de ecuaciones en su forma matricial A.X=B. Se asume que se ha aplicado el pivoteo parcial por filas y matriz aumentada (luego se añade como una función). Se asegura que A, X y B sean arreglos de números reales.

    Las instrucciones inician seleccionando la primera ecuación, primera fila, i=0. El numerador de la expresión se acumula en suma, que inicia con B[i]. Luego se restan todas las combinaciones de A[i,j]*X[j], menos en la diagonal. El nuevo valor de X[i], o Xnuevo, es suma dividido para el coeficiente de la diagonal A[i,i]

    X_i = \bigg(B_i - \sum_{j=0, j\neq i}^n A_{i,j}X_j\bigg) \frac{1}{A_{ii}}
    i=0 # una ecuacion
    suma = B[i] # numerador
    for j in range(0,m,1): # un coeficiente
        if (i!=j): # excepto diagonal de A
            suma = suma-A[i,j]*X[j]
    Xnuevo[i] = suma/A[i,i]
    

    Se repite el proceso para las siguientes ecuaciones, cambiando el valor de 'i'

    En cada iteración se determinan las diferencias |X[i+1]-X[i]| como los errores de iteración para cada variable.

    diferencia = abs(Xnuevo-X)
    errado = np.max(diferencia)

    Como las diferencias es un vector y la tolerancia solo un número (escalar), se simplifica usando la norma np.max(diferencia).
    De ser necesario, se repite la iteración, actualizando el vector X.

    # Método de Jacobi
    import numpy as np
    
    # INGRESO
    # NOTA: Para AB, se ha usado pivoteo parcial por filas
    A = [[ 7,  1,  1],
         [-3,  7, -1],
         [-2,  5,  9]]
    B = [6, -26, 1]
    
    X0 = [0,0,0]
    tolera = 0.0001
    iteramax = 100
    
    # PROCEDIMIENTO
    # Matrices como arreglo, numeros reales
    A = np.array(A,dtype=float)
    B = np.array(B,dtype=float)
    X0 = np.array(X0,dtype=float)
    tamano = np.shape(A) # tamaño A
    n = tamano[0]
    m = tamano[1]
    
    # valores iniciales
    diferencia = np.ones(n, dtype=float)
    errado = 2*tolera # np.max(diferencia)
    
    itera = 0 
    X = np.copy(X0)
    Xnuevo = np.copy(X0)
    while errado>tolera and itera<=iteramax:
        
        for i in range(0,n,1): # una ecuacion
            suma = B[i] # numerador
            for j in range(0,m,1): # un coeficiente
                if (i!=j): # excepto diagonal de A
                    suma = suma-A[i,j]*X[j]
            Xnuevo[i] = suma/A[i,i]
        
        diferencia = abs(Xnuevo-X)
        errado = np.max(diferencia)
        
        print(itera, X)
        print('  ',errado,diferencia)
        
        X = np.copy(Xnuevo) # actualiza X
        itera = itera + 1
    
    if (itera>iteramax): # No converge
        X = np.nan
        print('No converge,iteramax superado')
    
    # numero de condicion
    ncond = np.linalg.cond(A)
    
    # SALIDA
    print('Metodo de Jacobi')
    print('numero de condición:', ncond)
    print('X: ',X)
    print('errado:',errado)
    print('iteraciones:', itera)
    

    [ Método de Jacobi ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Gráfica ]
    ..


    5. Algoritmo en Python como función

    Las instrucciones del algoritmo, empaquetadas como función, añaden algunas facilidades para el desarrollo del ejercicio. Antes de usar el método de Jacobi, se aplica el pivoteo parcial por filas para mejorar de ser posible la convergencia.

    En la función jacobi() se añaden las instrucciones para mostrar los resultados parciales en cada iteración. Se añade una tabla para revisión de valores al final del algoritmo, o para realizar la gráfica de errores por iteración.

    Si el sistema es de 3x3 se puede añadir una gráfica de los resultados por iteración, mostrando la trayectoria descrita por los resultados parciales en 3D.

    # 1Eva_IIT2011_T2 Sistema de Ecuaciones, diagonal dominante
    # Metodos Numericos
    import numpy as np
    
    def jacobi(A,B,X0, tolera, iteramax=100,
               vertabla=False, precision=4):
        ''' Método de Jacobi, tolerancia, vector inicial X0
            para mostrar iteraciones y tabla: vertabla=True
        '''
        # Matrices como arreglo, numeros reales
        A = np.array(A,dtype=float)
        B = np.array(B,dtype=float)
        X0 = np.array(X0,dtype=float)
        tamano = np.shape(A) # tamaño A
        n = tamano[0]
        m = tamano[1]
        
        # valores iniciales
        diferencia = 2*tolera*np.ones(n, dtype=float)
        errado = 2*tolera     # np.max(diferencia)
        tabla = [np.copy(X0)] # tabla de iteraciones
        tabla = np.concatenate((tabla,[[np.nan]]),
                                axis=1) # errado
    
        if vertabla==True:
            print('Iteraciones Jacobi')
            print('itera,[X]')
            print('     ,errado,|diferencia|')
            print(0,X0)
            print(' ',np.nan)
            np.set_printoptions(precision)
    
        itera = 0 # Jacobi
        X = np.copy(X0)
        Xnuevo = np.copy(X0)
        while errado>tolera and itera<=iteramax:
            
            for i in range(0,n,1): # una ecuacion
                suma = B[i]
                for j in range(0,m,1): # un coeficiente
                    if (i!=j): # excepto diagonal de A
                        suma = suma-A[i,j]*X[j]
                Xnuevo[i] = suma/A[i,i]
            diferencia = abs(Xnuevo-X)
            errado = np.max(diferencia)
            X = np.copy(Xnuevo)
    
            Xfila = np.concatenate((Xnuevo,[errado]),axis=0)
            tabla = np.concatenate((tabla,[Xfila]),axis=0)
    
            itera = itera + 1
    
            if vertabla==True:
                print(itera, Xnuevo)
                print(' ',errado,diferencia)
    
        # No converge
        if (itera>iteramax):
            X = np.nan
            print('No converge,iteramax superado')
    
        if vertabla==True:
            X = [X,tabla]
        return(X)
    
    def pivoteafila(A,B,vertabla=False):
        '''
        Pivotea 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,'y', dondemax+i)
        if vertabla==True:
            if pivoteado==0:
                print('  Pivoteo por filas NO requerido')
            else:
                print('AB')
                print(AB)
        return(AB)
    
    # PROGRAMA Búsqueda de solucion  --------
    # INGRESO
    A = [[ 7,  1,  1],
         [-3,  7, -1],
         [-2,  5,  9]]
    B = [6, -26, 1]
    
    X0 = [0,0,0]
    tolera = 0.0001
    iteramax = 100
    verdecimal = 5
    
    # PROCEDIMIENTO
    AB = pivoteafila(A,B,vertabla=True)
    n,m = np.shape(AB)
    A = AB[:,:n] # separa en A y B
    B = AB[:,n]
    
    [X, tabla] = jacobi(A,B,X0,tolera,iteramax,
                        vertabla=True,
                        precision=verdecimal)
    n_itera = len(tabla)-1
    errado = tabla[-1,-1]
    
    # numero de condicion
    ncond = np.linalg.cond(A)
    
    # SALIDA
    print('Metodo de Jacobi')
    print('numero de condición:', ncond)
    print('X: ',X)
    print('errado:',errado)
    print('iteraciones:', n_itera)
    

    [ Método de Jacobi ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Gráfica ]
    ..


    5.1 Gráficas de iteraciones

    Se muestra la gráfica de errores vs iteraciones.

    # GRAFICA de iteraciones
    errados = tabla[:,n]
    
    import matplotlib.pyplot as plt
    # grafica de error por iteracion
    fig2D = plt.figure()
    graf = fig2D.add_subplot(111)
    graf.plot(errados)
    graf.set_xlabel('itera')
    graf.set_ylabel('|error|')
    graf.set_title('Método de Jacobi: error[itera]')
    graf.grid()
    
    plt.show()
    

    [ Método de Jacobi ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Gráfica ]

    5.2 Gráfica de iteraciones para sistema de 3x3

    Si el sistema es de 3x3, se muestra una gráfica de la trayectoria de resultados parciales, semejante a la presentada para la descripción del concepto. Permite observar si la espiral es convergente o no.

    # Grafica de iteraciones x,y,z
    # solo para 3x3
    xp = tabla[:,0]
    yp = tabla[:,1]
    zp = tabla[:,2]
    
    fig3D = plt.figure()
    graf3D = fig3D.add_subplot(111,projection='3d')
    
    graf3D.plot(xp,yp,zp)
    graf3D.plot(xp[0],yp[0],zp[0],'o',label='X[0]')
    graf3D.plot(xp[n_itera],yp[n_itera],zp[n_itera],
                'o',label='X['+str(n_itera)+']')
    
    graf3D.set_xlabel('x')
    graf3D.set_ylabel('y')
    graf3D.set_zlabel('z')
    graf3D.set_title('Método de Jacobi: iteraciones X')
    
    # graf3D.view_init(45,45)
    plt.legend()
    
    plt.show()
    

    [ Método de Jacobi ] [ Ejercicio ] [ Analítico ] [ Algoritmo ] [ Gráfica ]

  • 3.5.2 Número de Condición

    Referencia: Chapra 10.3.2 p300/pdf324, Burden 9Ed 7.5 p470, Rodríguez 4.4.3 p133

    El número de condición de una matriz A, es una forma de medir la sensibilidad del resultado del sistema de ecuaciones ante pequeños cambios en los valores de la entrada X.  El número de condición de una matriz se usa para cuantificar su nivel de mal condicionamiento.

    Sea A.X=B un sistema de ecuaciones lineales, entonces:

    cond(A) = ||A|| ||A-1||

    es el número de condición de la matriz A.

    Los pequeños errores, por ejemplo por truncamiento, pueden amplificarse y afectar la precisión de la solución. Si el número de condición es cercano a 1 o 'bajo', implica que la matriz está bien condicionada, errores pequeños en los valores tienen poco impacto en la solución.

    Instrucción en Python

     np.linalg.cond(A)

    Ejemplo: Si la matriz A es la identidad, el número de condición es 1.

    A = [[1,0,0],
         [0,1,0],
         [0,0,1]]
    >>> np.linalg.cond(A)
    1.0

    Otro ejemplo: 1Eva_IT2010_T3_MN Precio artículos

    A = [[2,2,4,1],
         [2,2,5,2],
         [4,1,1,2],
         [2,5,2,1]]
    np.linalg.cond(A)
    18.46408777611575

    realizando el pivoteo parcial por filas a la matriz A, no afecta el número de condición.

    A = [[4,1,1,2],
         [2,5,2,1],
         [2,2,5,2],
         [2,2,4,1]]
    np.linalg.cond(A)
    18.464087776115733

    Se observará que si el número de condición está alejado de 1, es 'alto', al aplicar los métodos iterativos de Jacobi o Gauss-Seidel, resultan NO convergentes. Lo que puede ser un factor para seleccionar el método a usar para encontrar la solución al sistema de ecuaciones.


    Tarea

    Usando como base los procedimientos desarrollados en Python, elabore un algoritmo para encontrar el número de condición de una matriz.

    "el error relativo de la norma de la solución calculada puede ser tan grande como el error relativo de la norma de los coeficientes de [A], multiplicada por el número de condición."

    Por ejemplo,

    • si los coeficientes de [A] se encuentran a t dígitos de precisión
      (esto es, los errores de redondeo son del orden de 10–t) y
    • Cond [A] = 10c,
    • la solución [X] puede ser válida sólo para t – c dígitos
      (errores de redondeo ~ 10c–t).

    verifique el resultado obtenido con el algoritmo, comparando con usar la instrucción

     np.linalg.cond(A)
  • 3.5.1 Normas de Vector o Matriz con Python

    Referencia: Chapra 10.3.1 p298 pdf322, Burden 7.1 p320, Rodríguez 4.4.1 p132, MATG1049 Algebra Lineal - Norma, distancias y ángulos

    Es una manera de expresar la magnitud de sus componentes:

    Sea X un vector de n componentes:

    ||X|| = \sum_{i=1}^{n}|X_i| ||X|| = max|X_i| , i=1,2, ...,n ||X|| = \left( \sum_{i=1}^{n}X_i^2 \right) ^{1/2}

    Sea una matriz A de nxn componentes:

    ||A|| = max(\sum_{j=1}^{n}|a_{i,j}|, i = 1,2,...,n) ||A|| = max(\sum_{i=1}^{n}|a_{i,j}|, j = 1,2,...,n) ||A|| = \left( \sum_{i=1}^{n}\sum_{j=1}^{n} a_{i,j}^2 \right) ^{1/2}

    Ejercicios

    Ejercicio 1. Usando los conceptos de normas mostradas, para el siguiente vector:

     x= [5, -3, 2] 
    

    a) calcule las normas mostradas (en papel),
    b) Realice los respectivos algoritmos en python,
    c) Determine los tiempos de ejecución de cada algoritmo. ¿Cúál es el más rápido?

    Ejercicio 2. Usando los conceptos de normas mostradas, para la siguiente matriz:

    A = [[5, -3, 2],
         [4,  8,-4],
         [2,  6, 1]] 
    

    Repita los literales del ejercicio anterior.

    Nota: para convertir una lista X en arreglo use: np.array(X)


    Normas con Python y Numpy

    Algunas normas vectoriales y matriciales con Python. Cálculo del número de condición.

    Se presenta un ejemplo usando la matriz A y el vector B en un programa de prueba.

    A = [[3,-0.1,-0.2],
         [0.1,7,-0.3],
         [0.3,-0.2,10]]
    B = [7.85,-19.3,71.4]
    

    Instrucciones en Python:

    # Normas vectoriales y matriciales
    # Referencia: Chapra 10.3, p299, pdf323
    import numpy as np
    
    def norma_p(X,p):
        Xp = (np.abs(X))**p
        suma = np.sum(Xp)
        norma = suma**(1/p)
        return(norma)
    
    def norma_euclidiana(X):
        norma = norma_p(X,2)
        return(norma)
    
    def norma_filasum(X):
        sfila = np.sum(np.abs(X),axis=1)
        norma = np.max(sfila)
        return(norma)
    
    def norma_frobenius(X):
        tamano = np.shape(X)
        n = tamano[0]
        m = tamano[1]
        norma = 0
        for i in range(0,n,1):
            for j in range(0,m,1):
                norma =  norma + np.abs(X[i,j])**2
        norma = np.sqrt(norma)
        return(norma)
    
    def num_condicion(X):
        M = np.copy(X)
        Mi = np.linalg.inv(M)
        nM = norma_filasum(M)
        nMi= norma_filasum(Mi)
        ncondicion = nM*nMi
        return(ncondicion)
    
    # Programa de prueba #######
    # INGRESO
    A = [[3,-0.1,-0.2],
         [0.1,7,-0.3],
         [0.3,-0.2,10]]
    B = [7.85,-19.3,71.4]
    p = 2
    
    # PROCEDIMIENTO
    # Matrices como arreglo, numeros reales
    A = np.array(A,dtype=float)
    B = np.array(B,dtype=float)
    
    normap    = norma_p(B, p)
    normaeucl = norma_euclidiana(B)
    normafilasuma   = norma_filasum(A)
    numerocondicion = num_condicion(A)
    
    # SALIDA
    print('vector:',B)
    print('norma p: ',2)
    print(normap)
    
    print('norma euclididana: ')
    print(normaeucl)
    
    print('******')
    print('matriz: ')
    print(A)
    print('norma suma fila: ',normafilasuma)
    
    print('n mero de condici n:')
    print(numerocondicion)
    
    

    cuyos resultados del ejercicio serán:

    vector: [  7.85 -19.3   71.4 ]
    norma p:  2
    74.3779033046778
    norma euclididana: 
    74.3779033046778
    ******
    matriz: 
    [[ 3.  -0.1 -0.2]
     [ 0.1  7.  -0.3]
     [ 0.3 -0.2 10. ]]
    norma suma fila:  10.5
    número de condición:
    3.6144243248254124
    >>> 
    

    compare sus resultados con las funciones numpy:

    np.linalg.norm(A)
    np.linalg.cond(A)
    

    Referencias: [1] http://www.numpy.org/devdocs/reference/generated/numpy.linalg.norm.html

    [2] http://www.numpy.org/devdocs/reference/generated/numpy.linalg.cond.html