| | | | |

1.1 Matrizes Esparsas

Ajude a manter o site livre, gratuito e sem propagandas. Colabore!

Em revisão

Uma matriz é dita ser esparsa quando ela tem apenas poucos elementos não nulos. A ideia é que os elementos não nulos não precisam ser guardados na memória do computador, gerando um grande benefício na redução da demanda de armazenamento de dados. O desafio está no desenvolvimento de estruturas de dados para a alocação eficiente de tais matrizes, i.e. que sejam suficientemente adequadas para os métodos numéricos conhecidos.

Refer to caption
Refer to caption
Figura 1.1: Em cima: exemplo de uma matriz esparsa estruturada. Em baixo: exemplo de uma matriz esparsa não-estruturada.

Matrizes esparsas podem ser classificadas como estruturadas ou não-estruturadas. Uma matriz estruturada é aquela em que as entradas não-nulas formam um padrão regular. Por exemplo, estão dispostas em poucas diagonais ou formam blocos (submatrizes densas) ao longo de sua diagonal principal. No caso de não haver um padrão regular das entradas não-nulas, a matriz esparsa é dita ser não-estruturada. Consulte a Figura 1.1 para exemplos.

A esparsidade de uma matriz é a porcentagem de elementos nulos que ela tem, i.e. para uma matriz quadrada n×n tem-se que a esparsidade é

e:=nnulosn2×100%. (1.1)

Por exemplo, a matriz identidade de tamanho n=100 tem esparsidade

e=10021001002×100%=99%. (1.2)

1.1.1 Sistemas Tridiagonais

Em revisão

Um sistema tridiagonal tem a seguinte forma matricial

[a1,1a1,20a2,1a2,2a2,3a3,2a3,3a3,4an1,n0an,n1an,n][x1x2x3xn1xn]=[b1b2b3bn1bn]. (1.3)

Ou seja, é um sistema cuja a matriz dos coeficientes é tridiagonal.

Uma matriz tridiagonal é uma matriz esparsa estruturada. Mais especificamente, é um caso particular de uma matriz banda, em que os elementos não nulos estão dispostos apenas em algumas de suas diagonais. Para armazenarmos tal matriz precisamos alocar apenas os seguintes três vetores

d1=(0,a1,2,,an1,n), (1.4)
d0=(a1,1,a2,2,,an,n), (1.5)
d1=(a2,1,,an,n1,0). (1.6)

Ou seja, precisamos armazenar 3n pontos flutuantes em vez de n2, como seria o caso se a matriz dos coeficientes fosse densa. Com isso, podemos alocar a matriz do sistema da seguinte forma

A~=[*a1,2an1,na1,1a2,2an,na2,1an,n1*]. (1.7)

Ou seja, A~=[a~i,j]i,j=13,n, sendo

a~1+ij,j=ai,j. (1.8)
Exemplo 1.1.1.

Seja o seguinte sistema linear

2x1x2=0 (1.9)
xi16xi+4xi+1=sen(iπ2(n1)) (1.10)
xn1+xn=1 (1.11)

O seguinte código Python faz a alocação de seu vetor dos termos constantes b e de sua matriz de coeficientes no formato compacto de A~.

Código 1: diagSis.py
1import numpy as np
2n = 100000
3
4# alocação
5# vetor dos termos constantes
6b = np.empty(n)
7b[0] = 0.
8for i in range(1,n-1):
9  b[i] = np.sin(i*np.pi/(2*(n-1)))
10b[n-1] = 1.
11print(b)
12print(f"b size: {b.size*b.itemsize/1024} Kbytes")
13
14# matriz compacta
15tA = np.zeros((3,n))
16
17# indexação
18def ind(i,j):
19return 1+i-j,j
20
21tA[ind(0,0)] = 2.
22tA[ind(0,1)] = -1.
23for i in range(1,n-1):
24  tA[ind(i,i-1)] = 1.
25  tA[ind(i,i)] = -3.
26  tA[ind(i,i+1)] = 4.
27tA[ind(n-1,n-2)] = 1.
28tA[ind(n-1,n-1)] = 1.
29print(tA)
30print(f"tA size: {tA.size*tA.itemsize/1024**2:1.1f} Mbytes")

Algoritmo de Thomas (TDMA)

Em revisão

O algoritmo de Thomas11endnote: 1Llewellyn Hilleth Thomas, 1903 - 1992, físico e matemático aplicado britânico. Fonte: Wikipedia. ou TDMA (do inglês, Tridiagonal Matrix Algorithm) é uma forma otimizada do método de eliminação gaussiana22endnote: 2Johann Carl Friedrich Gauss, 1777 - 1855, matemático alemão. Fonte: Wikipédia: Carl Friedrich Gauss. aplicada a sistemas tridiagonais. Enquanto este requer O(n3) operações, esse demanda apenas O(n).

Eliminando os termos abaixo da diagonal em (1.3), obtemos o sistema equivalente

[a1,1a1,20|b1a~2,2a2,3|b~2a~3,3|an1,n|b~n10a~n,n|b~n] (1.12)

Este é obtido pela seguinte iteração

wai+1,iai,i (1.13)
ai,iai,iwai1,i (1.14)
bibiwbi1 (1.15)

onde, o ~ foi esquecido de propósito, indicando a reutilização da matriz A~ e do vetor b. A solução do sistema é, então, obtida de baixo para cima, i.e.

xnbnan,n (1.16)
xibiai,i+1xi+1ai,i, (1.17)

com i=n1,n2,,1.

Código 2: tdma.py
1def tdma(ta, b):
2  a = ta.copy()
3  x = b.copy()
4  # eliminação
5  for i in range(1,n):
6    w = a[2,i-1]/a[1,i-1]
7    a[1,i] -= w * a[0,i]
8    x[i] -= w * x[i-1]
9  # resolve
10  x[n-1] = x[n-1]/a[1,n-1]
11  for i in range(n-2,-1,-1):
12    x[i] = (x[i] - a[0,i+1]*x[i+1])/a[1,i]
13  return x

1.1.2 Matrizes Banda

Em revisão

Uma matriz banda é aquela em que os elementos não nulos estão dispostos em apenas algumas de suas diagonais. Consulte a Figura 1.2.

Refer to caption
Figura 1.2: Exemplo de uma matriz banda.
Exemplo 1.1.2.

Consideramos o seguinte problema de Poisson33endnote: 3Siméon Denis Poisson, 1781 - 1840, matemático francês. Fonte: Wikipédia:Siméon Denis Poisson.

Δu=f(x,y),(x,y)(0,π)×(0,π), (1.18)
u(0,y)=0,y[0,π], (1.19)
u(π,y)=0,y[0,π], (1.20)
u(x,0)=0,x[0,π], (1.21)
u(x,π)=0,x[0,π], (1.22)

onde, Δ:=(2x2,2y2) é o operador laplaciano44endnote: 4Pierre-Simon Laplace, 1749 - 1827, matemático francês. Fonte: Wikipédia: Pierre-Simon Laplace.. Para fixarmos as ideias, vamos assumir

f(x,y)=sen(x)sen(y) (1.23)

Vamos empregar o método de diferenças finitas para computar uma aproximação para a sua solução. Começamos assumindo uma malha uniforme de n2 nodos

xi=(i1)h (1.24)
yj=(j1)h (1.25)

com tamanho de malha h=π/(n1), i=1,2,,n e j=1,2,,n. Empregando a fórmula de diferenças central, encontramos o seguinte problema discreto associado

ui,1=u1,j=0 (1.26)
1h2ui1,j1h2ui,j1+4h2ui,j
1h2ui+1,j1h2ui,j+1=f(xi,yj) (1.27)
ui,n=un,j=0 (1.28)

Este é um sistema linear n2×n2. Tomando em conta as condições de contorno, ele pode ser reduzido a um sistema (n2)2×(n2)2

Aw=b (1.29)

usando a enumeração das incógnitas

(i,j)k=i1+(j2)(n2), (1.30)

i.e.

ui,j=wk=i1+(j2)(n2) (1.31)

para i,j=2,,n2. Consulte a Figura 1.3 para uma representação da enumeração em relação a malha.

Refer to caption
Figura 1.3: Representação da enumeração das incógnitas referente ao problema discutido no Exemplo 1.1.2.

Afim de obtermos uma matriz diagonal dominante, vamos ordenar as equações do sistema discreto como segue

  • j=2, i=2:

    4wkwk+1wk+n2=h2fi,j (1.32)
  • j=2, i=3,,n2:

    wk1+4wkwk+1wk+n2=h2fi,j (1.33)
  • j=2, i=n1:

    wk1+4wkwk+n2=h2fi,j (1.34)
  • j=3,,n2, i=2:

    wk(n2)+4wkwk+1wk+n2=h2fi,j (1.35)
  • j=3,,n2, i=3,,n2:

    wk1wk(n2)+4wkwk+1wk+n2=h2fi,j (1.36)
  • j=3,,n2, i=n1:

    wk1wk(n2)+4wkwk+n2=h2fi,j (1.37)
  • j=n1, i=2:

    wk(n2)+4wkwk+1=h2fi,j (1.38)
  • j=n1, i=3,,n2:

    wk1wk(n2)+4wkwk+1=h2fi,j (1.39)
  • j=n1, i=n1:

    wk1wk(n2)+4wk=h2fi,j (1.40)

Com isso, temos um sistema com matriz com 5 bandas, consulte a Figura 1.4.

Refer to caption
Figura 1.4: Representação da matriz do sistema discreto construído no Exemplo 1.1.2.

1.1.3 Esquemas de Armazenamento

Em revisão

A ideia é armazenar apenas os elementos não-nulos de uma matriz esparsa, de forma a economizar a demanda de armazenamento computacional. Cuidados devem ser tomados para que a estrutura de armazenamento utilizada seja adequada para a computação das operações matriciais mais comuns.

Formato COO

Em revisão

O formato COO (do inglês, COOrdinate format) é o esquema de armazenamento simples de matrizes esparsas. As estrutura de dados consiste em três arranjos:

  1. 1.

    um arranjo contendo as entradas não-nulas da matriz;

  2. 2.

    um arranjo contendo seus índices de linha;

  3. 3.

    um arranjo contendo seus índices de coluna.

O método scipy.sparse.coo_array permite a alocação de matrizes no formato COO.

Exemplo 1.1.3.

O seguinte código armazena a matriz

A=[2.0.1.0.0.3.2.1.012.0.0001.] (1.41)

no formato COO.

1import numpy as np
2from scipy.sparse import coo_array
3
4data = np.array([2.,1.,3.,2.,-1.,-1.,-2.,1.])
5row = np.array([0,0,1,1,1,2,2,3])
6col = np.array([0,2,1,2,3,1,2,3])
7Acoo = coo_array((data, (row, col)), shape=(4,4))
8print("Acoo = \n", Acoo)
9print("A = \n", Acoo.toarray())

Vantagens do formato COO

  • Permite a entrada de dados duplicados (simplicidade).

  • Conversão rápida para os formatos CSR e CSC55endnote: 5CSR e CSC são formatos de matrizes esparsas mais eficientes para a computação matricial..

Desavantagens do formato COO

  • complexidade em operações aritméticas.

  • complexidade na extração de submatrizes.

Observação 1.1.1.

(Entradas Duplicadas.) O formato COO permite a entrada duplicada de elementos da matriz. Na conversão para outros formatos (por exemplo, CSR ou CSC), as entradas duplicadas são somadas.

Formato CSR

Em revisão

O formato CSR (do inglês, Compressed Sparse Row) é uma variação do COO que busca diminuir a alocação de dados repetidos. Assim como o COO, o formato conta com três arranjos d, c, p:

  • d é o arranjo contendo os elementos não-nulos da matriz, ordenados por linhas (i.e., da esquerda para direita, de cima para baixo);

  • c é o arranjo contendo o índice das colunas das entradas não-nulas da matriz (como no formato COO);

  • p é um arranjo cujos elementos são a posição no arranjo c em que cada linha da matriz começa a ser representada. O número de elementos de i-ésima linha da matriz dado por pj+1pj.

O método scipy.sparse.csr_array permite a alocação de matrizes no formato CSR.

Exemplo 1.1.4.

No Exemplo 1.1.3, alocamos a matriz

A=[2.0.1.0.0.3.2.1.012.0.0001.] (1.42)

no formato COO. Aqui, vamos converter a alocação para o formato CSR e, então, verificar seus atributos.

1from scipy.sparse import csr_array
2Acsr = Acoo.tocsr()
3print(f'd = {Acsr.data}')
4print(f'c = {Acsr.indices}')
5print(f'p = {Acsr.indptr}')
d = [ 2.  1.  3.  2. -1. -1. -2.  1.]
c = [0 2 1 2 3 1 2 3]
p = [0 2 5 7 8]

Por exemplo, o elemento p[i=2] = 5 aponta para o c[k=5] = 1, o que fornece A[i=2,j=1] = d[k] = -1.. Verifique!

Vantagens do formato CSR

  • operações aritméticas eficientes;

  • fatiamento por linhas eficiente;

  • multiplicação matriz vetor eficiente.

Desvantagens do formato CSR

  • fatiamento por colunas não eficiente;

  • custo elevado de realocamento com alteração da esparsidade da matriz.

Formato CSC

Em revisão

O formato CSC (do inglês, Compressed Sparse Column) é uma variação análoga do CSR, mas para armazenamento por colunas. O formato conta com três arranjos d, l, p:

  • d é o arranjo contendo os elementos não-nulos da matriz, ordenados por colunas (i.e., de cima para baixo, da esquerda para direita);

  • l é o arranjo contendo o índice das linhas das entradas não-nulas da matriz;

  • p é um arranjo cujos elementos são a posição no arranjo l em que cada coluna da matriz começa a ser representada. O número de elementos de j-ésima coluna da matriz dado por pj+1pj.

O método scipy.sparse.csc_array permite a alocação de matrizes no formato CSC.

Exemplo 1.1.5.

No Exemplo 1.1.3, alocamos a matriz

A=[2.0.1.0.0.3.2.1.012.0.0001.] (1.43)

no formato COO. Aqui, vamos converter a alocação para o formato CSC e, então, verificar seus atributos.

1  from scipy.sparse import csc_array
2  Acsc = Acoo.tocsc()
3  l = Acsc.indices
4  p = Acsc.indptr
5  print(f'd = {Acsc.date}')
6  print(f'l = {Acsc.indices}')
7  print(f'p = {Acsc.indptr}')
  d = [2.  3. -1.  1.  2. -2. -1.  1.]
  l = [0 1 2 0 1 2 1 3]
  p = [0 1 3 6 8]

Assim sendo, o elemento p[j=2] = 3 aponta para o l[k=3] = 0, o que informa que A[i=0,j=2]=d[k]=1.. Verifique!

Vantagens do formato CSC

  • fatiamento por colunas eficiente;

  • operações aritméticas eficientes;

  • multiplicação matriz vetor eficiente66endnote: 6CSR é mais eficiente em muitos casos..

Desvantagens do formato CSC

  • fatiamento por linhas não eficiente;

  • custo elevado de realocamento com alteração da esparsidade da matriz.

Observação 1.1.2.

Além dos formatos COO, CSR e CSC, exitem ainda vários outros que podem empregados e que são mais eficientes em determinadas aplicações. Recomendamos a leitura de [9, Seção 3.4] e da documentação do scipy.sparse.

Exercícios

Em revisão

E. 1.1.1.

Considere o problema de Poisson dado no Exemplo 1.1.2.

  1. a)

    Armazene a matriz do problema discreto associado usando o formato COO.

  2. b)

    Converta a matriz armazenada para o formato CSR77endnote: 7Use o método coo_matrix.tocsr().. Então, compute a solução do problema discreto com o método spsolve88endnote: 8scipy.sparse.linalg.spsolve é uma implementação do Método LU otimizado para matrizes esparsas..

  3. c)

    Converta a matriz armazenada para o formato CSC99endnote: 9Use o método coo_matrix.tocsc().. Então, compute a solução do problema discreto com o método spsolve.

  4. d)

    Compare a eficiência da computação entre os itens b) e c) para tamanhos de malha h=101,102,103,104.

E. 1.1.2.

Considere o seguinte sistema linear

2x1x2=0 (1.44)
xi16xi+4xi+1=sen(iπ2(n1)) (1.45)
xn1+xn=1 (1.46)
  1. a)

    Compute sua solução usando o Algoritmo de Thomas para n=3.

  2. b)

    Compare a solução obtida no item anterior com a gerada pela função scipy.linalg.solve.

  3. c)

    Compare a solução com a obtida no item anterior com a gerada pela função scipy.linalg.solve_banded.

  4. d)

    Use o módulo Python datetime para comprar a demanda de tempo computacional de cada um dos métodos acima. Compute para n=10,100,1000,10000.

E. 1.1.3.

Considere que o problema de valor de contorno (PVC)

u′′=senπx,0<x<1, (1.47)
u(0)=0, (1.48)
u(1)=0 (1.49)

seja simulado com o Método das Diferenças Finitas1010endnote: 10Consulte mais em Notas de Aula: Matemática Numérica.. Vamos assumir uma discretização espacial uniforme com n nodos e tamanho de malha

h=1n1. (1.50)

Com isso, temos os nodos xi=(i1)h, i=1,2,,n. Nos nodos internos, aplicamos a fórmula de diferenças central

u′′(xi)ui12ui+ui+1h2, (1.51)

onde, uiu(xi). Com isso, a discretização da EDO fornece

1h2ui1+2h2ui1h2ui+1=senπxi (1.52)

para i=2,3,,n1. Das condições de contorno temos u1=un=0. Logo, o problema discreto lê-se: encontrar u=(u1,u2,,un)n tal que

u1=0 (1.53)
1h2ui1+2h2ui1h2ui+1=senπxi (1.54)
un=0 (1.55)
  1. a)

    Calcule a solução analítica do PVC.

  2. b)

    Use a função scipy.linalg.solve_banded para computar a solução do problema discreto associado para diferentes tamanhos de malha h=101,102,103,104. Compute o erro da solução discreta em relação à solução analítica.

  3. c)

    Compare a demanda de tempo computacional se a função scipy.linalg.solve for empregada na computação da solução discreta.

E. 1.1.4.

Consideremos o problema trabalho no Exemplo 1.1.2.

  1. a)

    Use a função scipy.linalg.solve para computar a solução do problema discreto associado para diferentes tamanhos de malha h=101,102,103,104. Compute o erro da solução discreta em relação à solução analítica. Compare as aproximações com a solução analítica

    u(x,y)=12sen(x)sen(y). (1.56)
  2. b)

    Compare a demanda de tempo e memória computacional se a função scipy.linalg.solve_banded for empregada na computação da solução discreta.

  3. c)

    Baseado no Algoritmo de Thomas, implemente o Método de Eliminação Gaussiana otimizado para a matriz banda deste problema. Compare com as abordagens dos itens a) e b).


Envie seu comentário

As informações preenchidas são enviadas por e-mail para o desenvolvedor do site e tratadas de forma privada. Consulte a Política de Uso de Dados para mais informações. Aproveito para agradecer a todas/os que de forma assídua ou esporádica contribuem enviando correções, sugestões e críticas!