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 3×3 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')
    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 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)
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 ]