Decomposição LU e Pivoteamento Parcial
Objetivos de Aprendizagem
- Ler a decomposição LU como eliminação gaussiana com os multiplicadores registrados e dizer quando existe.
- Calcular à mão com pivoteamento parcial, resolver por substituição direta e retroativa e reutilizar os fatores para um novo lado direito.
- Contar o número exato de operações de ponto flutuante da fatoração e de cada solução.
- Mostrar em float64 como a eliminação sem pivoteamento destrói a resposta numa matriz bem condicionada, medindo erro direto, erro reverso e crescimento.
- Separar o pivô do pivoteamento parcial do pivô do simplex e apontar os casos em que o pivoteamento parcial é desnecessário ou insuficiente.
Contexto e Motivação
gaussian-elimination resolveu um sistema por operações de linha, com troca só quando um pivô necessário era zero. Duas coisas mudam num computador. Primeiro, aquela lição jogou os multiplicadores fora, então um segundo lado direito repetiria todo o trabalho, embora a matriz seja a mesma. Guardar os multiplicadores resolve isso: o trabalho sobre é feito uma vez e cada novo sai barato. Segundo, em ponto flutuante a regra de troca não basta. Um pivô minúsculo, mas não nulo, passa pela regra e arruína o resultado, mesmo quando o problema em si é bem condicionado, no sentido de conditioning-and-numerical-stability.
Teoria Central
A eliminação registra a própria inversa
O passo subtrai vezes a linha de cada linha . Em forma matricial isso é com , em que guarda os multiplicadores abaixo da posição . Depois de passos, é triangular superior. Cada inversa só troca um sinal, , e na ordem em que aparecem os produtos não interagem:
Assim, é triangular inferior unitária, com os multiplicadores no próprio lugar. A eliminação não precisa de uma segunda matriz: os multiplicadores sobrescrevem os zeros que criam, e esse é o formato que scipy.linalg.lu_factor devolve.
Existência. Uma matriz quadrada tem com triangular inferior unitária e triangular superior não singular se e somente se toda submatriz principal líder de é não singular. Não basta ser não singular: é não singular e não tem essa fatoração, pois contradiria .
Por que um pivô pequeno causa dano
Os erros de arredondamento de uma fatoração escalam com as entradas de e , não com as de . Um pivô pequeno gera um multiplicador grande, que amplia uma linha, e somar isso a uma linha menor faz o arredondamento apagar a menor. Duas medidas acompanham o efeito: o fator de crescimento e o erro reverso , que diz quanto os dados precisam mudar para ser solução exata. O erro direto relativo é no máximo algo como o número de condição vezes o erro reverso, então um erro direto grande numa matriz bem condicionada acusa o algoritmo.
Pivoteamento parcial e PA = LU
No passo , procure na coluna , da diagonal para baixo, o maior módulo, troque essa linha para a posição (linhas inteiras, multiplicadores guardados inclusive) e só então elimine. Todo multiplicador passa a ter . "Parcial" significa que só uma coluna é examinada; o pivoteamento completo examina todo o bloco restante.
Teorema. Para toda matriz quadrada existe uma matriz de permutação com , em que é triangular inferior unitária com e é triangular superior. é não singular se e somente se todo .
Para resolver , resolva para a frente e para trás. Vale também para trocas.
Este pivô não é o pivô do simplex
the-simplex-method-tableau-and-pivoting também diz "pivô". A palavra é compartilhada porque nos dois casos se divide por uma entrada para limpar uma coluna; as funções são diferentes.
| Pivoteamento parcial em LU | Pivô do simplex | |
|---|---|---|
| Finalidade | limitar o erro de arredondamento | ir para um vértice adjacente |
| Entrada escolhida por | maior módulo na coluna | regra de entrada na linha objetivo, depois o teste da razão |
| Operação | troca linhas, elimina só abaixo | Gauss-Jordan: limpa a coluna inteira, linha objetivo inclusive |
| Efeito na resposta | nenhum: o mesmo , por um caminho com menos arredondamento | muda a solução atual, de propósito |
| Em aritmética exata | necessário só para pivôs nulos | é o próprio algoritmo |
A regra do simplex olha viabilidade e objetivo, não o tamanho da entrada, então seu pivô pode ser pequeno. A regra do pivoteamento parcial olha só o tamanho.
Contando o trabalho
Conte um flop para cada , , , . O passo faz divisões e pares de multiplicação e subtração, então
A substituição direta com diagonal unitária custa . A retroativa custa , com divisões incluídas. Cada solução custa, portanto, flops. A busca do pivô acrescenta comparações e as trocas, e nada disso é flop. Com lados direitos, o total é .
Exemplos Resolvidos
Exemplo 1: o mesmo sistema, agora fatorado
Use o sistema de gaussian-elimination:
As operações de linha daquela lição usaram multiplicadores e , e a terceira linha não precisou de nada na coluna 2. Guardá-los dá, sem trocas,
Com pivoteamento parcial, a maior entrada da coluna 1 é o , então as linhas 1 e 2 trocam de lugar. Os multiplicadores são , o que deixa as linhas e . A coluna 2 dá empate, então não há troca, o multiplicador é e
Agora , a substituição direta dá e a retroativa dá , a resposta daquela lição. Um novo não exige eliminação: , , , por 15 flops. Além disso, . scipy.linalg.lu devolve os mesmos e , com seu definido por , então o dela é a transposta deste. numpy.linalg.solve dá as mesmas duas soluções.
Exemplo 2: entradas comuns, um pivô feito de ruído
Nada aqui é pequeno, e , então o problema é bem condicionado. Em decimal, o primeiro passo torna a entrada exatamente , e viria uma troca. Mas e não são frações binárias. Em float64, 0.3/3 dá 0.09999999999999999, e a entrada vira . Isso é ruído de arredondamento, não é zero, e a regra de troca de gaussian-elimination nunca dispara. O multiplicador seguinte é , e , em que o desapareceu. Os resultados:
textmetodo x erro max erro reverso crescimento sem pivoteamento (1.3333, 0, 1.0000) 1.0 0.12 2.0e+16 pivoteamento parcial (1, 1, 1) 2.2e-16 0 1 numpy.linalg.solve (1, 1, 1)
O resíduo da resposta sem pivoteamento é : as duas primeiras equações valem e a terceira se perdeu. O pivoteamento parcial usa a linha com como segundo pivô, e a entrada de ruído só produz um multiplicador de .
Exemplo 3: o tamanho do dano e uma armadilha de escala
Para e , a solução exata fica perto de e para todos estes :
textepsilon erro sem pivoteamento erro com pivoteamento parcial 1e-08 5.0e-09 2.2e-16 1e-12 2.2e-05 0 1e-20 1.0 (x = (0, 1)) 0
O pivoteamento parcial compara tamanhos crus, então depende da escala. Multiplique a primeira equação do caso por : com . A solução é a mesma, mas a primeira coluna agora empata, não há troca, e tanto um LU escrito à mão quanto numpy.linalg.solve devolvem . Dividir cada linha pela sua maior entrada antes recupera . O pivoteamento parcial supõe linhas de escala comparável.
O dano também aparece sem nenhuma entrada plantada. Tome rng = numpy.random.default_rng(42), A = rng.uniform(-1, 1, (1000, 1000)), x = rng.uniform(-1, 1, 1000), b = A @ x, para os quais . Com erro direto relativo na norma infinito:
texterro direto erro reverso crescimento max |L| sem pivoteamento 4.5e-10 7.5e-13 2.9e+04 1.3e+03 parcial 1.3e-12 1.3e-15 4.7e+01 1
Aqui o pivoteamento salva mais de dois dígitos, e a implementação concorda com numpy.linalg.solve em .
Exemplo 4: as contagens, executadas
Um laço instrumentado que incrementa um contador a cada operação aritmética, em matrizes aleatórias:
textn sub mul div fator formula direta retro sol. 2n^2-n 2 1 1 1 3 3 2 4 6 6 3 5 5 3 13 13 6 9 15 15 10 285 285 45 615 615 90 100 190 190 100 328350 328350 4950 661650 661650 9900 10000 19900 19900 200 2646700 2646700 19900 5313300 5313300 39800 40000 79800 79800
Para a fatoração custa flops e uma solução , então uma fatoração equivale a cerca de 667 soluções. Para 100 lados direitos, fatorar uma vez e resolver 100 vezes custa flops, contra de 100 eliminações separadas, um fator de 87.
Isso também resolve o "é só inverter ". Calcular as colunas de acrescenta à fatoração, cerca de nesta contagem (menos se os zeros dos vetores unitários forem aproveitados), e depois custa , exatamente o que custam as duas soluções triangulares. Não é mais rápido por lado direito, e na matriz aleatória do Exemplo 3 o erro reverso foi contra de numpy.linalg.solve, 38 vezes maior; as sementes de 0 a 3 deram fatores de 22 a 52.
Exemplo 5: onde o pivoteamento parcial não basta, e onde nem é preciso
Não basta. O teorema limita os multiplicadores, não o crescimento, e pode ser atingido. Tome uns na diagonal, abaixo dela e uns na última coluna. Toda entrada abaixo da diagonal empata com o pivô, então não há troca (empates mantêm a linha atual), a última coluna dobra a cada passo e, para :
textkappa_2 = 26.8 crescimento = 5.8e+17 (2^59) numpy.linalg.solve: erro direto 0.89, erro reverso 0.045 fatoracao QR: erro direto 3.0e-15
Logo, o pivoteamento parcial não é estável no sentido reverso no pior caso. Ele é o padrão porque matrizes assim são raras na prática: nas 20 matrizes numpy.random.default_rng(s).uniform(-1, 1, (500, 500)), com , o crescimento ficou entre 25 e 41.
singular. A matriz de linhas , , é singular e não tem solução. O arredondamento deixa em vez de , então numpy.linalg.solve não levanta nada e devolve entradas da ordem de . scipy.linalg.solve devolve o mesmo com um aviso de mau condicionamento. numpy.linalg.solve levanta LinAlgError em , em que o pivô é exatamente . Um pivô não nulo não prova que a matriz é não singular.
Nem é preciso. Se cada entrada da diagonal domina estritamente sua coluna, ela continua a maior e o pivoteamento parcial nunca troca. Tome rng = numpy.random.default_rng(42), R = rng.uniform(-1, 1, (500, 500)), x = rng.uniform(-1, 1, 500), b = A @ x. Com igual a , mas com cada entrada da diagonal trocada por mais a soma dos valores absolutos das outras entradas da sua coluna, houve 0 trocas, crescimento e erro reverso sem pivoteamento. A simétrica definida positiva , com , deu crescimento e erro reverso sem pivoteamento, e essas matrizes usam Cholesky, com cerca de metade dos flops. iterative-methods-jacobi-and-gauss-seidel trata do caso esparso, em que a fatoração tem outro problema.
Equívocos Comuns e Armadilhas
- "Pivoteamento é para pivôs nulos." Em ponto flutuante o pivô perigoso é o pequeno, e no Exemplo 2 um zero verdadeiro chegou disfarçado de .
- "Resposta ruim significa matriz ruim." O Exemplo 2 tem . O dano veio do algoritmo.
- "O pivoteamento parcial torna o LU sempre estável." O Exemplo 5 perdeu todos os dígitos em com . Ele é confiável na prática, não no pior caso.
- "O pivô do simplex é o mesmo pivô." Um escolhe um vértice e muda a solução. O outro só reduz o arredondamento e não mexe na solução.
- "Resíduo pequeno significa solução precisa." Significa erro reverso pequeno. O erro direto é isso vezes até o número de condição.
- "Inverta e multiplique." Custa mais de início, o mesmo por lado direito, e o erro reverso foi 38 vezes maior na matriz do Exemplo 4.
- "
scipy.linalg.ludá ." Dá . Confira a convenção antes de usar seu .
Resumo
A decomposição LU é eliminação com memória: os multiplicadores formam uma triangular inferior unitária, o resultado é , e com não singular existe exatamente quando todas as submatrizes principais líderes são não singulares. O pivoteamento parcial toma a maior entrada de cada coluna, o que dá para toda matriz quadrada, com . A fatoração custa exatamente flops e cada solução , então reutilizar os fatores é o principal ganho prático. Sem pivoteamento, um pivô de ruído de destruiu um sistema com , e um sistema aleatório com perdeu mais de dois dígitos. O pivô aqui reduz arredondamento e deixa a solução intacta, ao contrário do pivô do simplex. Ele depende da escala das linhas, pode ser derrotado por um crescimento de e é desnecessário em matrizes diagonalmente dominantes e definidas positivas.
Documentation Links
- Driscoll and Braun: Fundamentals of Numerical Computation, 2.4 LU factorization
- Driscoll and Braun: Fundamentals of Numerical Computation, 2.5 Efficiency of matrix computations
- Driscoll and Braun: Fundamentals of Numerical Computation, 2.6 Row pivoting
- Driscoll and Braun: Fundamentals of Numerical Computation, 2.9 Exploiting matrix structure
- Goldberg (1991): What Every Computer Scientist Should Know About Floating-Point Arithmetic
- SciPy documentation: scipy.linalg.lu
- SciPy documentation: scipy.linalg.lu_factor
- NumPy documentation: numpy.linalg.solve