WHY "more stable"? It's not magic — both call LAPACK. The practical differences:
scipy.linalg is always compiled against an optimized, complete LAPACK/BLAS; numpy may fall
back to slower/less-complete routines depending on build.
scipy lets you pick the stable algorithm explicitly (e.g. solve via LU with partial pivoting,
or via Cholesky if SPD) instead of a one-size-fits-all path.
Never compute inv(A) @ b — that's the real instability. Use solve/factorizations.
import numpy as npfrom scipy.linalg import lu, lu_factor, lu_solve, solveA = np.array([[2.,1.],[6.,5.]]); b = np.array([3.,17.])P, L, U = lu(A) # SciPy convention: P @ A = L @ U (so A = P.T @ L @ U)x = solve(A, b) # the right way to solve -> x = [-0.5, 4.]lu_piv = lu_factor(A) # reuse factorization for many b'sx2 = lu_solve(lu_piv, b)
from scipy.linalg import qr, lstsqQ, R = qr(A, mode='economic') # economic: Q is m×n, R is n×nx, res, rank, sv = lstsq(A, b) # robust least squares (uses SVD/QR internally)
from scipy.linalg import schur, eigT, Z = schur(A) # A = Z @ T @ Z.conj().Tw, vl, vr = eig(A, left=True) # scipy gives left+right eigenvectors# generalized eigenproblem A x = λ B x (numpy can't do this!):w_gen = eig(A, b=np.eye(len(A)))
Recall Feynman: explain to a 12-year-old
Imagine a tangled machine that twists arrows around. It's hard to understand the tangle directly.
So we break the machine into simple pieces: one piece only stretches (triangular), one piece
only spins without stretching (orthogonal/unitary). Once it's in simple pieces, answering questions
like "which arrow comes out as itself?" (eigenvalues) or "undo the machine to get back the original
arrow" (solve Ax=b) becomes easy. scipy.linalg is the box of tools that does these clean breaks
carefully so tiny rounding mistakes don't blow up.
Why use scipy.linalg over numpy.linalg?
Full LAPACK interface (lu, qr, schur, cho_factor, generalized eig), always linked to optimized LAPACK/BLAS, and lets you pick the stable algorithm explicitly.
What is the LU factorization with pivoting?
PA=LU — P permutation (row swaps for stability), L unit-lower-triangular (elimination multipliers), U upper triangular.
How do you solve Ax=b once you have PA=LU?
Forward-solve Ly=Pb, then back-solve Ux=y.
Why never compute inv(A)@b?
~3× more flops and amplifies rounding error; use solve()/factorizations instead.
What convention does scipy.linalg.lu return?
P, L, U with P @ A = L @ U (equivalently A = P.T @ L @ U), NOT A = P @ L @ U.
Define QR factorization.
A=QR with Q orthogonal (QTQ=I) and R upper triangular.
Why does QR solve least squares stably?
Orthogonal Q preserves length, so min reduces to Rx=QTb; avoids normal equations which square the condition number.
What sits on the diagonal of T in the Schur form A=ZTZH?
The eigenvalues of A.
Why prefer Schur over diagonalization numerically?
Schur always exists and uses unitary Z (condition number 1), even for defective/non-diagonalizable matrices.
Which scipy feature does numpy.linalg lack for eigenproblems?
The generalized eigenproblem Ax=λBx via eig(A, b=B).
Relationship between κ(A) and the normal equations?
Normal equations have condition number κ(A)2, making them less accurate than QR.
Dekho, matrix ek "machine" hai jo vectors ko transform karti hai. Seedha-seedha is machine ke
saath kaam karna mushkil hota hai, isliye hum use simple pieces me todte hain — yahi
"factorization" kehlata hai. scipy.linalg me yeh saare tools available hain, aur numpy.linalg
se zyada complete hai: isme lu, qr, schur, generalized eigenvalues sab milte hain.
Teen main factorizations yaad rakho. LU (PA=LU) = Gaussian elimination ko record karna,
jise hum Ax=b solve karne ke liye use karte hain — pehle Ly=Pb (forward), phir Ux=y (back).
QR (A=QR) me Q orthogonal hota hai (length preserve karta hai), isliye least-squares problem
∥Ax−b∥ minimize karna easy ho jaata hai bina normal equations (ATA) use kiye — kyunki normal
equations condition number ko square kar dete hain, accuracy gir jaati hai. Schur (A=ZTZH)
me eigenvalues T ke diagonal pe baith jaate hain, aur yeh hamesha exist karta hai even jab matrix
diagonalize nahi hoti — kyunki Z unitary hai (perfectly conditioned).
Sabse important practical baat: kabhi inv(A)@b mat likho. Yeh slow bhi hai aur rounding error
ko badhata hai. Hamesha solve(A,b) use karo. Note: scipy.linalg.lu(A) ka convention hai
P@A=L@U (yaani A=PT@L@U), galti se A=P@L@U mat samajhna. Mnemonic:
"LU Solves, QR Squares, Schur Spectra." Bas yeh ek line yaad rahi toh poora chapter ka 80% cover ho gaya.