Matrizes na prática: o que realmente importa além da tabuada
Eu comecei a trabalhar com matrizes fazendo contas de transposição e determinante em papel, tipo 2012. Hoje eu resolvo sistemas lineares com arrays de três dimensões rodando em GPU, mas o problema de fundo é o mesmo: a maioria das pessoas aprende as propriedades da matriz como se fosse um conjunto de regras isoladas para passar em prova, e aí quando chega no código real, não consegue prever o que vai acontecer. Vou explicar do jeito que eu vejo funcionando. Começando pelo método, não pela definição.
Quando você vai multiplicar duas matrizes A e B, a primeira coisa que deve verificar não é o resultado, é a compatibilidade dimensional. Se A for m×n e B for p×q, precisa que n == p. Sim, óbvio. Mas o erro que eu vejo todo dia é alguém fazer A.dot(B) num numpy e esperar que de certa forma funcione porque os shapes são parecidos visualmente. Não funciona. A biblioteca lança uma exceção e pronto. O custo disso é quase zero em tempo de computação, mas em tempo de debugging pode ser meia manhã perdida se o array estiver vindo de um pipeline com múltiplas transformações antes.
O que você precisa saber sobre propriedades da matriz
As propriedades formais são essas aqui, mas a versão útil é diferente. Transposta de uma transposta volta pro original. Distributividade funciona tanto pela esquerda quanto pela direita. A propriedade mais traiçoeira é a da transposta de um produto: (AB)^T = B^T A^T. O sentido inverte. Muita gente esquece isso e aplica como se fosse (AB)^T = A^T B^T, o que só é válido se A e B forem matrizes simétricas e comutarem, condição que raramente se mantém em dados reais. O determinante também tem uma propriedade que ninguém usa direito: det(AB) = det(A)det(B). Isso significa que você pode calcular o determinante de um produto grande multiplicando os determinantes individuais. Em teoria, lindo. Na prática, para matrizes acima de 5×5 com valores flutuantes, o determinante perde precisão numérica rapidamente. Eu cheguei a ter um bug onde um determinante deveria ser zero porque a matriz era singular, mas o float64 devolveu algo como 2.3e-15. A matriz estava numericamente mal-condicionada, não era exatamente singular. A solução foi usar SVD em vez de depender do determinante. O código ficou mais lento em cerca de 30%, mas resolveu o problema de estabilidade que aparecia só em certos conjuntos de dados.
A inversa de uma matriz só existe se o determinante for diferente de zero. Isso é conhecimento básico. O que não é tão básico é que calcular a inversa explicitamente é quase sempre uma má ideia. Se você precisa resolver Ax = b, usar inv(A).dot(b) é mais lento e menos preciso do que usar um solver direto como numpy.linalg.solve ou, preferencialmente, um solver iterativo como lgmres se a matriz for esparsa. Eu vi engenheiros calcularem a inversa completa de uma matriz 1000×1000 só para aplicar em um vetor. O tempo de computação triplicou e a precisão caiu porque o número de condição da matriz amplificou os erros de arredondamento. Sobre simetria: uma matriz simétrica tem autovalores reais e pode ser diagonalizada por uma matriz ortogonal. Isso é fundamental em PCA, em métodos de elementos finitos, em qualquer coisa que envolva decomposição espectral. Se a matriz não for simétrica, os autovalores podem ser complexos, e aí a coisa fica mais complicada. Uma armadilha comum é assumir simetria porque os dados parecem simétricos, mas na construção da matriz você cometeu um erro e ela não é. Sempre verifique com um teste de simetria explícito antes de aplicar algoritmos que exigem simetria.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Oposto aditivo e elemento neutro são triviais, mas vale lembrar que a adição de matrizes é componente a componente e apenas para matrizes de mesmas dimensões. Nada surpreendente, mas em pipelines automatizados onde os shapes vêm de fontes diferentes, é fácil esquecer de validar isso no início do processamento. Propriedade associativa da multiplicação vale, mas associatividade não significa que a ordem de avaliação é irrelevante em termos computacionais. (AB)C pode ter custo diferente de A(BC) dependendo das dimensões intermediárias. Se A é 10×1000, B é 1000×1000 e C é 1000×10, calcular (AB)C gera uma matriz intermediária 10×1000 multiplicada por 1000×10, enquanto A(BC) gera uma intermediária 1000×10 multiplicada por 10×1000. O segundo caminho é absurdamente mais barato. Isso é o problema da ordenação de multiplicação de matrizes, e existem algoritmos dinâmicos para resolver isso automaticamente. Bibliotecas como TensorFlow já fazem isso internamente, mas em código puro NumPy você precisa cuidar disso manualmente.
Rank também é propriedade importante. Rank(A) <= min(m, n). Rank(AB)
= min(rank(A), rank(B)). Se você está trabalhando com dados de menor dimensionalidade e não sabe por quê, calcular o rank numérico (usando o número de valores singulares significativos, não o rank teórico) costuma revelar o problema. Matrizes com rank deficiente causam instabilidade em regressões lineares e em métodos de mínimos quadrados. A correção usual é regularização tipo Ridge, que basicamente adiciona um termo lambda*I à matriz de Gram, tornando-a full rank. Para matrizes esparsas, a propriedade mais relevante é que a estrutura de esparsidade pode ser preservada em operações selecionadas, mas transposição e multiplicação frequentemente introduzem preenchimento (fill-in). Isso aumenta a memória e o tempo de computação. Use formatos como CSR ou CSC ao invés de matriz densa quando a sparseza for alta, senão você gasta memória à toa e perde velocidade.
Um detalhe prático que poucas pessoas mencionam: a propriedade (A^n)^{-1} = A^{-n} só vale se A for invertível. Se A for singular, A^n também é singular para qualquer n >= 1, e a inversa não existe. Isso parece óbvio, mas aparece em problemas de modelos markovianos onde as matrizes de transição podem perder invertibilidade se algum estado se torna absorbing e a representação numérica não lida com isso adequadamente. Aqui vai um exemplo concreto que eu uso como referência. Suponha que você tenha uma matriz de covariância de dados multivariados. Ela é simétrica e semidefinida positiva por construção. As propriedades relevantes aqui são: autovalores não negativos, traço igual à soma das variâncias, determinante igual ao produto dos autovalores. Se um autovalor for zero ou muito próximo de zero, a matriz é singular ou quase singular, o que indica colinearidade entre variáveis. O procedimento padrão é fazer decomposição espectral, zerar autovalores abaixo de um threshold numérico, e reconstruir a matriz. Isso é basicamente o que o regularization faz de forma implícita, mas a decomposição espectral dá controle explícito sobre o quão longe você está de uma matriz singula.
Se você quer praticar isso no Python, o numpy já cobre tudo. Para matrizes grandes e esparsas, o scipy.sparse é mais adequado. Para álgebra linear numérica robusta, o scipy.linalg oferece rotinas mais seguras que o numpy.linalg em casos de fronteira. Em resumo, as propriedades teóricas são um mapa. A implementação é o terreno. O mapa mostra estradas que não existem mais quando o número de condição da sua matriz é maior que 1e12.