Preconditioners in SciPy - Mini Project: Build & Apply
Start learning this pattern below
Jump into concepts and practice - no test required
A using scipy.sparse.diags with diagonals [4, -1, -1] and offsets [0, -1, 1] for size 5. Create a vector b as a NumPy array with values [1, 2, 3, 4, 5].Use diags to create a tridiagonal matrix with main diagonal 4 and -1 on the diagonals just above and below.
M_inv as the inverse of the diagonal of A using scipy.sparse.diags. Extract the diagonal of A with A.diagonal() and invert it element-wise.Get the diagonal of A with A.diagonal(). Then create M_inv with the inverse values on the diagonal.
scipy.sparse.linalg.cg to solve Ax = b with the preconditioner M_inv. Pass M_inv as the M argument. Store the solution vector in x.Call cg with A, b, and M=M_inv. Capture the solution in x.
x using print(x).Use print(x) to display the solution vector.
Practice
What is the main purpose of a preconditioner in scipy when solving linear systems?
Solution
Step 1: Understand the role of preconditioners
Preconditioners are used to improve the efficiency of iterative methods by transforming the system into an easier one to solve.Step 2: Identify the effect on convergence
They help iterative solvers like Conjugate Gradient converge faster by approximating the inverse of the matrix.Final Answer:
To speed up the convergence of iterative solvers -> Option AQuick Check:
Preconditioner purpose = speed up convergence [OK]
- Thinking preconditioners change the solution
- Believing preconditioners increase matrix size
- Confusing preconditioners with matrix transformations that alter shape
Which of the following is the correct way to create a simple Jacobi preconditioner using scipy.sparse.linalg.LinearOperator?
import numpy as np
from scipy.sparse.linalg import LinearOperator
A = np.array([[4, 1], [1, 3]])
M = LinearOperator(shape=A.shape, matvec=lambda x: ...)
Solution
Step 1: Recall Jacobi preconditioner definition
Jacobi preconditioner uses the inverse of the diagonal elements of matrix A.Step 2: Implement matvec for Jacobi
Applying the preconditioner means dividing each element of x by the corresponding diagonal element of A.Final Answer:
matvec=lambda x: x / np.diag(A) -> Option AQuick Check:
Jacobi preconditioner = divide by diagonal [OK]
- Using matrix multiplication instead of division
- Trying to solve full system instead of diagonal scaling
- Multiplying by diagonal instead of dividing
Given the following code, what will be the output of print(M.matvec(b))?
import numpy as np
from scipy.sparse.linalg import LinearOperator
A = np.array([[2, 0], [0, 5]])
b = np.array([4, 10])
M = LinearOperator(shape=A.shape, matvec=lambda x: x / np.diag(A))
print(M.matvec(b))
Solution
Step 1: Calculate diagonal of A
Diagonal elements are [2, 5].Step 2: Apply matvec function
Divide each element of b by corresponding diagonal: [4/2, 10/5] = [2.0, 2.0].Final Answer:
[2.0, 2.0] -> Option CQuick Check:
Vector divided by diagonal = [2.0, 2.0] [OK]
- Multiplying instead of dividing
- Confusing vector and matrix multiplication
- Using wrong diagonal values
Identify the error in the following code that attempts to create a Jacobi preconditioner:
import numpy as np
from scipy.sparse.linalg import LinearOperator
A = np.array([[3, 1], [1, 4]])
M = LinearOperator(shape=A.shape, matvec=lambda x: np.diag(A) * x)
print(M.matvec(np.array([1, 2])))
Solution
Step 1: Understand Jacobi preconditioner operation
Jacobi preconditioner divides vector elements by diagonal elements of A.Step 2: Check given matvec function
Code multiplies vector by diagonal instead of dividing, which is incorrect.Final Answer:
Multiplying by diagonal instead of dividing -> Option BQuick Check:
Jacobi requires division, not multiplication [OK]
- Confusing multiplication with division
- Ignoring element-wise operations
- Not verifying mathematical definition
You want to speed up solving a large sparse system Ax = b using Conjugate Gradient in scipy. Which approach best uses a preconditioner?
from scipy.sparse.linalg import cg, LinearOperator
import numpy as np
# A is large sparse matrix
# b is known vector
# Option 1: Use identity preconditioner
M1 = LinearOperator(A.shape, matvec=lambda x: x)
# Option 2: Use Jacobi preconditioner
diag = A.diagonal()
M2 = LinearOperator(A.shape, matvec=lambda x: x / diag)
# Option 3: Use incomplete Cholesky (not shown)
x, info = cg(A, b, M=M2)
Why is Option 2 preferred over Option 1?
Solution
Step 1: Understand identity preconditioner effect
Identity preconditioner does nothing; it returns the vector unchanged, so no speedup.Step 2: Understand Jacobi preconditioner effect
Jacobi approximates the inverse of the diagonal, improving convergence speed of iterative solver.Final Answer:
Because Jacobi preconditioner approximates inverse and speeds convergence -> Option DQuick Check:
Jacobi preconditioner = faster convergence [OK]
- Thinking identity preconditioner changes solution
- Believing Jacobi increases matrix size
- Confusing LinearOperator requirements
