Método dos gradientes conjugados
obs.: Para vizualizar as fórmulas matemáticas, abra o arquivo .ipynb
.
Introdução
O método dos gradientes conjugados (daqui para frente abreviado como CG, da sigla em inglês) é o método iterativo mais popular para resolver sistemas grandes de equações lineares. Esse método é particularmente efetivo para resolver sistemas
Apesar de CG ser usado para resolver sistemas de equações lineares, um melhor entendimento sobre o método vem ao considerarmos um problema de otimização quadrática $$ min \quad f(x) = \frac12(x,x)_A - (b,x) + c $$\tag{1}
onde
Se
Ideia geral do método
A ideia básica desse método (assim como a de outros métodos) é
- Começando de um
$x_0$ ; - Escolha uma direção d_i;
- Escolha quanto se quer "andar" na direção
$d_i$ , determinando o tamanho de passo$\alpha$ ; - Faça
$x_i := x_{i-1} + \alpha d_i$ .
Assim, podemos definir os vetores
- Resíduo:
$r_i := b - Ax_i$ ; - Erro:
$e_i := x_i - x$ (note que$r_i = Ae_i$ )
Note que
O método de máxima descida, no qual CG se baseia, faz
O método de máxima descida no geral leva mais de uma iteração para encontrar a solução. Na verdade, máxima descida converge em apenas uma iteração apenas se o chute inicial
Gradientes conjugados
A ideia do método de gradientes conjugados é buscar pela solução iterativamente por
Conjugação de Gram-Schmidt
Em particular, fazemos as direções de busca
Para obter essas direções, podemos prosseguir pelo processo de Gram-Schmidt, mas tomando o produto interno como sendo o A-produto interno
Tome um conjunto de
Em CG, faz-se
Disso tiramos a propriedade de que, como
Mais especificamente, obtemos que $$ (r_i, d_j)A = \begin{cases} \frac1{\alpha_i} (r_i,r_i), & i = j \ -\frac1{\alpha{i-1}} (r_i,r_i), & i = j + 1\ 0, & \text{caso contrário} \end{cases} $$
o que simplifica grandemente a expressão de
Precondicionador
Dada uma matriz
Para lidar com esse problema, uma alternativa é a pre-multiplicação do sistema por uma matriz
O uso de precondicionadores apresenta, contudo, alguns problemas de ordem prática e teórica. Um problema é que, mesmo
Qual precondicionador usar é um problema de ordem prática, existindo algumas opções comuns, a depender do problema em mãos. Uma opção simples, mas de resultados mediocres, é o precondicionador de Jacobi, em que
Mas vale dizer que é, em geral, entendido que CG é sempre feito com o uso de algum precondicionador, ao menos para problemas de grandes, o que motiva a não-omissão desse tema neste documento introdutório.
Algoritmo
O método de gradientes conjugados pode então ser resumido no seguinte algoritmo (em que foram feitas algumas manipulações algébricas para comportar o condicionador
Dados
Apesar de, a princípio, o método convergir em
Por esses motivos são necessários os parâmetros
Implementação
Para fins de exemplo, veja como esse algoritmo pode ser implementado em Julia
using LinearAlgebra # para podermos usar I, a identidade, por conveniência e sem perda de eficiência computacional
function CG(A, b, x0, imax = size(A)[1], Minv = I, ϵ = 1e-5)
i = 0
x = x0
r = b - A*x
d = Minv*r
δn = r'd
δ0 = δn
while i < imax && δn > ϵ^2*δ0
q = A*d
α = δn/(d'q)
x = x + α*d
if (i + 1) ÷ 50 == 0 # por questão de estabilidade numérica, recalculamos r a cada 50 iterações
r = b - A*x
else
r = r - α*q
end
s = Minv*r
δv = δn # δ velho
δn = r's # δ novo
β = δn/δv
d = s + β*d
i += 1
end
return x
end
CG (generic function with 4 methods)
Teste simples
A = [3 2; 2 6]
b = [2; -8]
x0 = [14; -20]
x = CG(A, b, x0)
2-element Array{Float64,1}:
2.0
-2.000000000000001
Vemos que, ao menos para esse problema pequeno (para sanity check), o método funciona bem, apesar de haver a adição de um valor espúrio da ordem de
A\b
2-element Array{Float64,1}:
2.0
-2.0
CG não linear (NCG)
CG pode ser usado para encontrar o mínimo de quaisquer funções contínuas
- a fórmula para o resíduo muda;
- é mais difícil calcular
$\alpha$ - é preciso fazer, por exemplo, uma busca linear; - existem diferentes escolhas possíveis para
$\beta$ .
Não entraremos em muitos detalhes aqui, mas vale fazer uma comparação com o popular BFGS. BFGS é um método quase-Newton bem conhecido, cuja principal característica que afeta sua performance (em particular, em armazenamento) é a utilização de uma aproximação da Hessiana de
Se a memória não for um problema, no entanto, BFGS, exceto para casos específicos, tende a se tornar uma melhor opção (na média, uma iteração de BFGS equivale a
A variante L-BFGS (lê-se "limited memory BFGS") do BFGS é uma aproximação do mesmo que apresenta menor consumo de memória e (assintoticamente) menor tempo computacional por iteração, apesar de convergência mais lenta.
Vale lembrar que essas são considerações gerais, no entanto, sendo que cada problema prático pode exigir uma análise mais detalhada, apontando qual método seria mais efetivo.
Exemplo de Aplicação: Resolvendo o problema de Poisson em elementos finitos
O problema de Poisson é o de encontrar
Onde
Apesar de simples, esse problema é muito importante, por exemplo, para aplicações em física e engenharia.
Para prosseguir pelo método de elementos finitos, precisamos reescrever o problema em sua forma variacional. Para tanto, primeiro multiplica-se a equação por uma função
então, aplicando integração por partes e insistindo que
ou, de forma mais compacta (em que se colocam os problemas para serem resolvidos por métodos elementos finitos no geral)
onde a forma bilinear
A solução
Para resolver o problema, introduzimos uma discretização e buscaremos uma aproximação poligonal
Dada uma base
do que segue que $$ \sum_1^N U_j\int_{\Omega} \phi_j'\phi_k' dx = \int_{\Omega} f \phi_k dx \text{ para } k = 1,\ldots, N $$
Assim, o problema se torn encontrar
onde
Tipicamente, a matriz
Experimentos computacionais
A seguir são feitos experimentos computacionais quanto à adequação de CG para a resolução de sistemas esparsos, com ou sem condicionador.
Para efeitos dos experimentos, foi utilizada uma matriz de Wathen. Uma matriz de Wathen(Nx,Ny) é uma matriz de elementos finitos aleatória N por N (fazendo
using BenchmarkTools, MatrixDepot, IterativeSolvers, LinearAlgebra, SparseArrays
# Matriz de Wathen de dimensões 30401 x 30401
A = matrixdepot("wathen", 100)
include group.jl for user defined matrix generators
verify download of index files...
reading database
adding metadata...
adding svd data...
writing database
used remote sites are sparse.tamu.edu with MAT index and math.nist.gov with HTML index
30401×30401 SparseMatrixCSC{Float64,Int64} with 471601 stored entries:
[1 , 1] = 6.2388
[2 , 1] = -6.2388
[3 , 1] = 2.0796
[202 , 1] = -6.2388
[203 , 1] = -8.31839
[303 , 1] = 2.0796
[304 , 1] = -8.31839
[305 , 1] = 3.1194
[1 , 2] = -6.2388
[2 , 2] = 33.2736
[3 , 2] = -6.2388
[202 , 2] = 20.796
⋮
[30199, 30400] = 21.3736
[30200, 30400] = 21.3736
[30399, 30400] = -6.41209
[30400, 30400] = 34.1978
[30401, 30400] = -6.41209
[30097, 30401] = 3.20604
[30098, 30401] = -8.54945
[30099, 30401] = 2.13736
[30199, 30401] = -8.54945
[30200, 30401] = -6.41209
[30399, 30401] = 2.13736
[30400, 30401] = -6.41209
[30401, 30401] = 6.41209
using UnicodePlots
spy(A)
�[1m Sparsity Pattern�[22m
�[90m ┌──────────────────────────────────────────┐�[39m
�[90m1�[39m�[90m │�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m �[31m> 0�[39m
�[90m │�[39m�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m �[34m< 0�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[0m⠀�[0m⠀�[90m│�[39m
�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[35m⡀�[39m�[0m⠀�[90m│�[39m
�[90m30401�[39m�[90m │�[39m�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[0m⠀�[35m⠈�[39m�[35m⠻�[39m�[35m⣦�[39m�[90m│�[39m
�[90m └──────────────────────────────────────────┘�[39m
�[90m 1�[39m�[90m �[39m�[90m 30401�[39m
�[0m nz = 471601
# Nível de esparsidade
count(!iszero, A) / length(A)
0.0005102687577359558
b = ones(size(A, 1))
# Resolve Ax=b by CG
xcg = cg(A, b);
@benchmark cg($A, $b)
BenchmarkTools.Trial:
memory estimate: 951.36 KiB
allocs estimate: 16
--------------
minimum time: 280.710 ms (0.00% GC)
median time: 430.303 ms (0.00% GC)
mean time: 438.680 ms (0.00% GC)
maximum time: 765.398 ms (0.00% GC)
--------------
samples: 12
evals/sample: 1
Usando precondicionador de Cholesky
using Preconditioners
@time p = CholeskyPreconditioner(A, 2)
4.076178 seconds (2.43 M allocations: 151.649 MiB, 1.26% gc time)
CholeskyPreconditioner{Float64,SparseMatrixCSC{Float64,Int64}}([7.7740167657825285 0.0 … 0.0 0.0; 0.0 11.520622371156024 … 0.0 0.0; … ; 0.0 0.0 … 3.048358439214821 0.0; 0.0 0.0 … 0.0 5.7811090761367625], 2)
Resolve Ax=b com precondicionador
xpcg = cg(A, b, Pl=p)
# same answer?
norm(xcg - xpcg)
5.306315159734449e-7
CG foi, neste exemplo > vezes 10 mais lento do que CG
O que é curioso, porque parece que em outros computadores o resultado é o inverso (CG com precondicionador (PCG) é > 10 vezes mais rápido).
@benchmark cg($A, $b, Pl=$p)
BenchmarkTools.Trial:
memory estimate: 951.36 KiB
allocs estimate: 16
--------------
minimum time: 4.958 s (0.00% GC)
median time: 5.235 s (0.00% GC)
mean time: 5.235 s (0.00% GC)
maximum time: 5.513 s (0.00% GC)
--------------
samples: 2
evals/sample: 1
Referências
Para escrever este documento, foram usadas, primariamente, as seguintes referências:
- An Introduction to the Conjugate Gradient Method Without the Agonizing Pain, de Jonathan Richard Shewchuk, primariamente para o desenvolvimento teórico disponível aqui
- Notas de aula de Hua-Zhou, primariamente para a implementação computacional disponível aqui
- Documentação da função Wathen para a sua utilização disponível aqui (foi utilizada a documentação da função mas não a função indicada, que foi feita para MatLab)
- Galerkin Approximations and Finite Element Methods, de Ricardo G. Durán, para alguns detalhes quanto a métodos de elementos finitos disponível aqui
- Solving PDEs in Python - The FEniCS Tutorial Volume 1, de Hans Peter Langtangen e Anders Logg, para outros detalhes quanto a métodos de elementos finitos para o problema de Poisson, disponível aqui