Compiled from the original manuscript of Chapter 6 exercises provided by Professor Ping-Zen Ong: 44 top-level exercises in all, in the same order as in the manuscript. Every exercise comes with a hint and a complete solution that can be expanded; computational exercises are followed by a NumPy verification, while pure proof exercises have no accompanying program.
This part uses 0-based indexing. Inconsistent notation and ambiguities in the statements of the original manuscript are noted in the corresponding exercises, and computed results follow the original data.
import numpy as np
np.set_printoptions(precision=4, suppress=True, linewidth=100)
def print_header(title):
print("=" * 60)
print(f" {title}")
print("=" * 60)
def print_step(step, desc):
print(f"\n▶ Step {step}: {desc}")
print("-" * 40)
def compare_print(label, actual, expected_desc):
print(f"\n[{label}]")
print(f" Computed value: {actual}")
print(f" Expected value: {expected_desc}")
def check(label, actual, expected):
compare_print(label, actual, expected)
assert np.allclose(actual, expected), label
print(" check passed")
def rref(A, tol=1e-10):
"""Gauss–Jordan elimination with partial pivoting; only for the small numerical examples of this chapter."""
R = np.array(A, dtype=float, copy=True)
row = 0
for col in range(R.shape[1]):
if row == R.shape[0]:
break
pivot = row + np.argmax(np.abs(R[row:, col]))
if abs(R[pivot, col]) <= tol:
continue
R[[row, pivot]] = R[[pivot, row]]
R[row] /= R[row, col]
for i in range(R.shape[0]):
if i != row:
R[i] -= R[i, col] * R[row]
row += 1
R[np.abs(R) < tol] = 0
return R
def lu_no_pivot(A):
"""Only for the square matrices in this book whose first n-1 pivots are nonzero."""
U = np.array(A, dtype=float, copy=True)
n = len(U)
L = np.eye(n)
for j in range(n - 1):
assert abs(U[j, j]) > 1e-10, "zero pivot: this function does not exchange rows"
for i in range(j + 1, n):
L[i, j] = U[i, j] / U[j, j]
U[i] -= L[i, j] * U[j]
return L, U
Row Operations, Reduced Forms, and Rank¶
# ============================================================
print_header("Exercise 6-1 | Row-Reduced Form")
# ============================================================
# --- Step 1 ---
print_step(1, "Build the matrix and perform Gauss–Jordan elimination")
A = np.array([[1,-2,3,1],[2,-4,8,6],[-1,2,-2,1]])
check("rref(A)", rref(A), [[1,-2,0,-5],[0,0,1,2],[0,0,0,0]])
# --- Step 2 ---
print_step(2, "Check the rank")
check("rank(A)", np.linalg.matrix_rank(A), 2)============================================================
Exercise 6-1 | Row-Reduced Form
============================================================
▶ Step 1: Build the matrix and perform Gauss–Jordan elimination
----------------------------------------
[rref(A)]
Computed value: [[ 1. -2. 0. -5.]
[ 0. 0. 1. 2.]
[ 0. 0. 0. 0.]]
Expected value: [[1, -2, 0, -5], [0, 0, 1, 2], [0, 0, 0, 0]]
check passed
▶ Step 2: Check the rank
----------------------------------------
[rank(A)]
Computed value: 2
Expected value: 2
check passed
# ============================================================
print_header("Exercise 6-2 | Different Ways of Eliminating")
# ============================================================
# --- Step 1 ---
print_step(1, "Compare three readings of the operations")
expected = [[1,0,-1],[0,1,2]]
for B in ([[2,4,6],[0,-1,-2]], [[2,4,6],[0,2,4]], [[0,2,4],[1,3,5]]):
check("rref", rref(B), expected)============================================================
Exercise 6-2 | Different Ways of Eliminating
============================================================
▶ Step 1: Compare three readings of the operations
----------------------------------------
[rref]
Computed value: [[ 1. 0. -1.]
[ 0. 1. 2.]]
Expected value: [[1, 0, -1], [0, 1, 2]]
check passed
[rref]
Computed value: [[ 1. 0. -1.]
[ 0. 1. 2.]]
Expected value: [[1, 0, -1], [0, 1, 2]]
check passed
[rref]
Computed value: [[ 1. 0. -1.]
[ 0. 1. 2.]]
Expected value: [[1, 0, -1], [0, 1, 2]]
check passed
# ============================================================
print_header("Exercise 6-3 | Recovering the Matrix")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the rref of the recovered matrix")
A = np.array([[1,3,2,0],[2,6,5,-1],[1,3,3,-1]])
check("rref(A)", rref(A), [[1,3,0,2],[0,0,1,-1],[0,0,0,0]])
check("column relation", A[:,3], 2*A[:,0]-A[:,2])
check("rank of the two basis columns", np.linalg.matrix_rank(A[:,[0,2]]), 2)============================================================
Exercise 6-3 | Recovering the Matrix
============================================================
▶ Step 1: Check the rref of the recovered matrix
----------------------------------------
[rref(A)]
Computed value: [[ 1. 3. 0. 2.]
[ 0. 0. 1. -1.]
[ 0. 0. 0. 0.]]
Expected value: [[1, 3, 0, 2], [0, 0, 1, -1], [0, 0, 0, 0]]
check passed
[column relation]
Computed value: [ 0 -1 -1]
Expected value: [ 0 -1 -1]
check passed
[rank of the two basis columns]
Computed value: 2
Expected value: 2
check passed
# ============================================================
print_header("Exercise 6-4 | Undoing Row Operations")
# ============================================================
# --- Step 1 ---
print_step(1, "Redo the three row operations after recovering M")
E0 = np.array([[1,0,0],[-2,1,0],[0,0,1]])
E1 = np.eye(3)[[0,2,1]]
E2 = np.diag([1,1,1/3])
M = np.array([[3,1,1],[6,2,5],[0,2,1]])
check("E2 E1 E0 M", E2@E1@E0@M, [[3,1,1],[0,2,1],[0,0,1]])============================================================
Exercise 6-4 | Undoing Row Operations
============================================================
▶ Step 1: Redo the three row operations after recovering M
----------------------------------------
[E2 E1 E0 M]
Computed value: [[3. 1. 1.]
[0. 2. 1.]
[0. 0. 1.]]
Expected value: [[3, 1, 1], [0, 2, 1], [0, 0, 1]]
check passed
# ============================================================
print_header("Exercise 6-5 | A Parameter and the Rank")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the degenerate value and generic values")
for lam in [-3,-2,0,4]:
A = [[1,-2,1],[-3,6,lam]]
check(f"λ={lam}: rank", np.linalg.matrix_rank(A), 1 if lam == -3 else 2)============================================================
Exercise 6-5 | A Parameter and the Rank
============================================================
▶ Step 1: Check the degenerate value and generic values
----------------------------------------
[λ=-3: rank]
Computed value: 1
Expected value: 1
check passed
[λ=-2: rank]
Computed value: 2
Expected value: 2
check passed
[λ=0: rank]
Computed value: 2
Expected value: 2
check passed
[λ=4: rank]
Computed value: 2
Expected value: 2
check passed
# ============================================================
print_header("Exercise 6-9 | Reduced Form with a Parameter")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the two degenerate values and generic values separately")
for lam in [1,-2,0,2,-3]:
A = np.array([[1,1,lam],[1,lam,1],[lam,1,1]])
expected = ([[1,1,1],[0,0,0],[0,0,0]] if lam == 1 else
[[1,0,-1],[0,1,-1],[0,0,0]] if lam == -2 else np.eye(3))
check(f"λ={lam}: rref", rref(A), expected)============================================================
Exercise 6-9 | Reduced Form with a Parameter
============================================================
▶ Step 1: Check the two degenerate values and generic values separately
----------------------------------------
[λ=1: rref]
Computed value: [[1. 1. 1.]
[0. 0. 0.]
[0. 0. 0.]]
Expected value: [[1, 1, 1], [0, 0, 0], [0, 0, 0]]
check passed
[λ=-2: rref]
Computed value: [[ 1. 0. -1.]
[ 0. 1. -1.]
[ 0. 0. 0.]]
Expected value: [[1, 0, -1], [0, 1, -1], [0, 0, 0]]
check passed
[λ=0: rref]
Computed value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
Expected value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
check passed
[λ=2: rref]
Computed value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
Expected value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
check passed
[λ=-3: rref]
Computed value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
Expected value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
check passed
Homogeneous Systems of Linear Equations¶
# ============================================================
print_header("Exercise 6-11 | Solving Homogeneous Systems")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the three rrefs and the kernel vectors")
matrices=[[[1,2,-1],[3,6,-2],[-2,-4,7]], [[1,-1,2],[2,1,-1],[-1,2,1],[1,4,3]], [[0,0,2],[1,2,2],[3,6,5]]]
for i,A in enumerate(matrices):
expected=np.vstack([np.eye(3),np.zeros((1,3))]) if i==1 else [[1,2,0],[0,0,1],[0,0,0]]
check(f"system {i+1}: rref", rref(A), expected)
if i != 1:check("kernel vector", np.array(A)@np.array([-2,1,0]), np.zeros(3))============================================================
Exercise 6-11 | Solving Homogeneous Systems
============================================================
▶ Step 1: Check the three rrefs and the kernel vectors
----------------------------------------
[system 1: rref]
Computed value: [[1. 2. 0.]
[0. 0. 1.]
[0. 0. 0.]]
Expected value: [[1, 2, 0], [0, 0, 1], [0, 0, 0]]
check passed
[kernel vector]
Computed value: [0 0 0]
Expected value: [0. 0. 0.]
check passed
[system 2: rref]
Computed value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]
[0. 0. 0.]]
Expected value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]
[0. 0. 0.]]
check passed
[system 3: rref]
Computed value: [[1. 2. 0.]
[0. 0. 1.]
[0. 0. 0.]]
Expected value: [[1, 2, 0], [0, 0, 1], [0, 0, 0]]
check passed
[kernel vector]
Computed value: [0 0 0]
Expected value: [0. 0. 0.]
check passed
# ============================================================
print_header("Exercise 6-12 | Kernel in Four Unknowns")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the rref")
A=np.array([[1,2,-1,3],[2,4,-1,8],[-1,-2,2,-1]])
check("rref(A)", rref(A), [[1,2,0,5],[0,0,1,2],[0,0,0,0]])
# --- Step 2 ---
print_step(2, "Check the basis of the kernel")
N=np.array([[-2,-5],[1,0],[0,-2],[0,1]])
check("AN", A@N, np.zeros((3,2)))
check("rank(N)", np.linalg.matrix_rank(N), 2)============================================================
Exercise 6-12 | Kernel in Four Unknowns
============================================================
▶ Step 1: Check the rref
----------------------------------------
[rref(A)]
Computed value: [[1. 2. 0. 5.]
[0. 0. 1. 2.]
[0. 0. 0. 0.]]
Expected value: [[1, 2, 0, 5], [0, 0, 1, 2], [0, 0, 0, 0]]
check passed
▶ Step 2: Check the basis of the kernel
----------------------------------------
[AN]
Computed value: [[0 0]
[0 0]
[0 0]]
Expected value: [[0. 0.]
[0. 0.]
[0. 0.]]
check passed
[rank(N)]
Computed value: 2
Expected value: 2
check passed
# ============================================================
print_header("Exercise 6-13 | Only the Zero Solution")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the rank and the reduced form")
B=np.array([[1,1,2],[1,2,5],[2,3,8]])
check("rref(B)", rref(B), np.eye(3))
check("rank(B)", np.linalg.matrix_rank(B), 3)============================================================
Exercise 6-13 | Only the Zero Solution
============================================================
▶ Step 1: Check the rank and the reduced form
----------------------------------------
[rref(B)]
Computed value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
Expected value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
check passed
[rank(B)]
Computed value: 3
Expected value: 3
check passed
# ============================================================
print_header("Exercise 6-14 | Homogeneous Solutions with a Parameter")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the kernels and their dimensions in the degenerate cases")
for lam,N in [(1,np.array([[-1,-1],[1,0],[0,1]])),(-2,np.ones((3,1)))]:
A=np.array([[1,1,lam],[1,lam,1],[lam,1,1]])
check("AN", A@N, np.zeros((3,N.shape[1])))
check("nullity(A)", 3-np.linalg.matrix_rank(A), N.shape[1])
check("only the zero solution when λ=0", np.linalg.matrix_rank([[1,1,0],[1,0,1],[0,1,1]]), 3)============================================================
Exercise 6-14 | Homogeneous Solutions with a Parameter
============================================================
▶ Step 1: Check the kernels and their dimensions in the degenerate cases
----------------------------------------
[AN]
Computed value: [[0 0]
[0 0]
[0 0]]
Expected value: [[0. 0.]
[0. 0.]
[0. 0.]]
check passed
[nullity(A)]
Computed value: 2
Expected value: 2
check passed
[AN]
Computed value: [[0.]
[0.]
[0.]]
Expected value: [[0.]
[0.]
[0.]]
check passed
[nullity(A)]
Computed value: 1
Expected value: 1
check passed
[only the zero solution when λ=0]
Computed value: 3
Expected value: 3
check passed
# ============================================================
print_header("Exercise 6-15 | Solving from the Row Space")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the kernel of the two equivalent equations")
B=np.array([[1,-1,2],[2,1,1]])
check("B(-1,1,1)^T", B@np.array([-1,1,1]), np.zeros(2))
check("rank(B)", np.linalg.matrix_rank(B), 2)============================================================
Exercise 6-15 | Solving from the Row Space
============================================================
▶ Step 1: Check the kernel of the two equivalent equations
----------------------------------------
[B(-1,1,1)^T]
Computed value: [0 0]
Expected value: [0. 0.]
check passed
[rank(B)]
Computed value: 2
Expected value: 2
check passed
Nonhomogeneous Systems¶
# ============================================================
print_header("Exercise 6-17 | Solving Nonhomogeneous Systems")
# ============================================================
# --- Step 1 ---
print_step(1, "Check that both the new z and the 7z reading from the homogeneous version have no solution")
for A,b in [(np.array([[1,2,-1],[3,6,-2],[-2,-4,1]]),[1,0,0]),
(np.array([[1,2,-1],[3,6,-2],[-2,-4,7]]),[1,0,0])]:
check("rank(A)", np.linalg.matrix_rank(A), 2)
check("rank([A | b])", np.linalg.matrix_rank(np.column_stack([A,b])), 3)
# --- Step 2 ---
print_step(2, "Verify the unique solution and the affine family of solutions")
A=np.array([[1,-1,2],[2,1,-1],[-1,2,1],[1,4,3]])
check("system 2 substituted", A@np.array([1,-1,1]), [4,0,-2,0])
A=np.array([[0,0,2],[1,2,2],[3,6,5]])
check("system 3 particular solution", A@np.array([-1,0,1]), [2,1,2])
check("system 3 homogeneous direction", A@np.array([-2,1,0]), [0,0,0])============================================================
Exercise 6-17 | Solving Nonhomogeneous Systems
============================================================
▶ Step 1: Check that both the new z and the 7z reading from the homogeneous version have no solution
----------------------------------------
[rank(A)]
Computed value: 2
Expected value: 2
check passed
[rank([A | b])]
Computed value: 3
Expected value: 3
check passed
[rank(A)]
Computed value: 2
Expected value: 2
check passed
[rank([A | b])]
Computed value: 3
Expected value: 3
check passed
▶ Step 2: Verify the unique solution and the affine family of solutions
----------------------------------------
[system 2 substituted]
Computed value: [ 4 0 -2 0]
Expected value: [4, 0, -2, 0]
check passed
[system 3 particular solution]
Computed value: [2 1 2]
Expected value: [2, 1, 2]
check passed
[system 3 homogeneous direction]
Computed value: [0 0 0]
Expected value: [0, 0, 0]
check passed
# ============================================================
print_header("Exercise 6-18 | Structure of the General Solution of a Nonhomogeneous System")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the rref of the augmented matrix and consistency")
A = np.array([[1, 2, -1, 3], [2, 4, -1, 8], [-1, -2, 2, -1]])
b = np.array([2, 9, 3])
augmented = np.column_stack([A, b])
check("rref of the augmented matrix", rref(augmented),
[[1, 2, 0, 5, 7], [0, 0, 1, 2, 5], [0, 0, 0, 0, 0]])
check("rank(A)", np.linalg.matrix_rank(A), 2)
check("rank([A | b])", np.linalg.matrix_rank(augmented), 2)
# --- Step 2 ---
print_step(2, "Check the particular solution and the homogeneous solution space")
xp = np.array([7, 0, 5, 0])
N = np.array([[-2, -5], [1, 0], [0, -2], [0, 1]])
check("A x_p", A @ xp, b)
check("AN", A @ N, np.zeros((3, 2)))
check("number of kernel basis vectors vs. rank", np.linalg.matrix_rank(N), A.shape[1] - np.linalg.matrix_rank(A))
# --- Step 3 ---
print_step(3, "Substitute the affine general solution x = x_p + x_h")
for s, t in [(0, 0), (1, -2), (-3, 4)]:
x = xp + N @ np.array([s, t])
check(f"s={s}, t={t}: general solution", x, [7 - 2*s - 5*t, s, 5 - 2*t, t])
check("A x", A @ x, b)============================================================
Exercise 6-18 | Structure of the General Solution of a Nonhomogeneous System
============================================================
▶ Step 1: Check the rref of the augmented matrix and consistency
----------------------------------------
[rref of the augmented matrix]
Computed value: [[1. 2. 0. 5. 7.]
[0. 0. 1. 2. 5.]
[0. 0. 0. 0. 0.]]
Expected value: [[1, 2, 0, 5, 7], [0, 0, 1, 2, 5], [0, 0, 0, 0, 0]]
check passed
[rank(A)]
Computed value: 2
Expected value: 2
check passed
[rank([A | b])]
Computed value: 2
Expected value: 2
check passed
▶ Step 2: Check the particular solution and the homogeneous solution space
----------------------------------------
[A x_p]
Computed value: [2 9 3]
Expected value: [2 9 3]
check passed
[AN]
Computed value: [[0 0]
[0 0]
[0 0]]
Expected value: [[0. 0.]
[0. 0.]
[0. 0.]]
check passed
[number of kernel basis vectors vs. rank]
Computed value: 2
Expected value: 2
check passed
▶ Step 3: Substitute the affine general solution x = x_p + x_h
----------------------------------------
[s=0, t=0: general solution]
Computed value: [7 0 5 0]
Expected value: [7, 0, 5, 0]
check passed
[A x]
Computed value: [2 9 3]
Expected value: [2 9 3]
check passed
[s=1, t=-2: general solution]
Computed value: [15 1 9 -2]
Expected value: [15, 1, 9, -2]
check passed
[A x]
Computed value: [2 9 3]
Expected value: [2 9 3]
check passed
[s=-3, t=4: general solution]
Computed value: [-7 -3 -3 4]
Expected value: [-7, -3, -3, 4]
check passed
[A x]
Computed value: [2 9 3]
Expected value: [2 9 3]
check passed
# ============================================================
print_header("Exercise 6-19 | The Augmented-Rank Criterion")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the rrefs in the consistent and inconsistent cases")
A=np.array([[1,1,2],[1,2,5],[2,3,7]])
for alpha in [7,0,8]:
expected=([[1,0,-1,-1],[0,1,3,3],[0,0,0,0]] if alpha==7 else [[1,0,-1,0],[0,1,3,0],[0,0,0,1]])
check(f"α={alpha}", rref(np.column_stack([A,[2,5,alpha]])), expected)
check("particular solution", A@np.array([-1,3,0]), [2,5,7])
check("homogeneous direction", A@np.array([1,-3,1]), [0,0,0])============================================================
Exercise 6-19 | The Augmented-Rank Criterion
============================================================
▶ Step 1: Check the rrefs in the consistent and inconsistent cases
----------------------------------------
[α=7]
Computed value: [[ 1. 0. -1. -1.]
[ 0. 1. 3. 3.]
[ 0. 0. 0. 0.]]
Expected value: [[1, 0, -1, -1], [0, 1, 3, 3], [0, 0, 0, 0]]
check passed
[α=0]
Computed value: [[ 1. 0. -1. 0.]
[ 0. 1. 3. 0.]
[ 0. 0. 0. 1.]]
Expected value: [[1, 0, -1, 0], [0, 1, 3, 0], [0, 0, 0, 1]]
check passed
[α=8]
Computed value: [[ 1. 0. -1. 0.]
[ 0. 1. 3. 0.]
[ 0. 0. 0. 1.]]
Expected value: [[1, 0, -1, 0], [0, 1, 3, 0], [0, 0, 0, 1]]
check passed
[particular solution]
Computed value: [2 5 7]
Expected value: [2, 5, 7]
check passed
[homogeneous direction]
Computed value: [0 0 0]
Expected value: [0, 0, 0]
check passed
# ============================================================
print_header("Exercise 6-20 | Nonhomogeneous System with a Parameter")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the unique solution for generic parameters")
for lam in [0,2,-3]:
A=np.array([[1,1,lam],[1,lam,1],[lam,1,1]])
check(f"λ={lam}: solution", np.linalg.solve(A,np.ones(3)), np.ones(3)/(lam+2))
# --- Step 2 ---
print_step(2, "Check the two special values")
A=np.array([[1,1,-2],[1,-2,1],[-2,1,1]])
check("λ=-2: rank of the augmented matrix", np.linalg.matrix_rank(np.column_stack([A,np.ones(3)])), 3)
A=np.ones((3,3)); N=np.array([[-1,-1],[1,0],[0,1]])
check("λ=1: particular solution", A@np.array([1,0,0]), np.ones(3))
check("λ=1: homogeneous directions", A@N, np.zeros((3,2)))============================================================
Exercise 6-20 | Nonhomogeneous System with a Parameter
============================================================
▶ Step 1: Check the unique solution for generic parameters
----------------------------------------
[λ=0: solution]
Computed value: [0.5 0.5 0.5]
Expected value: [0.5 0.5 0.5]
check passed
[λ=2: solution]
Computed value: [0.25 0.25 0.25]
Expected value: [0.25 0.25 0.25]
check passed
[λ=-3: solution]
Computed value: [-1. -1. -1.]
Expected value: [-1. -1. -1.]
check passed
▶ Step 2: Check the two special values
----------------------------------------
[λ=-2: rank of the augmented matrix]
Computed value: 3
Expected value: 3
check passed
[λ=1: particular solution]
Computed value: [1. 1. 1.]
Expected value: [1. 1. 1.]
check passed
[λ=1: homogeneous directions]
Computed value: [[0. 0.]
[0. 0.]
[0. 0.]]
Expected value: [[0. 0.]
[0. 0.]
[0. 0.]]
check passed
Solution Sets and Affine Translations¶
# ============================================================
print_header("Exercise 6-22 | True or False: Translations")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the wrong and the correct direction in statement 2")
B=np.array([[1,1,1],[1,-1,-2]])
check("residual of the given direction", B@np.array([1,-2,1]), [0,1])
check("residual of the correct direction", B@np.array([1,-3,2]), [0,0])
# --- Step 2 ---
print_step(2, "Check the remaining statements about translations")
check("statement 1: translation constant", np.array([1,-1])@np.array([2,0]), 2)
check("statement 3: translation constant", np.sum([1,0,-1]), 0)
check("statement 4: rank of the directions", np.linalg.matrix_rank(np.array([[8,-4],[-2,1],[-6,3]])), 1)
check("constant of the correct plane", np.sum([-3,0,1]), -2)============================================================
Exercise 6-22 | True or False: Translations
============================================================
▶ Step 1: Check the wrong and the correct direction in statement 2
----------------------------------------
[residual of the given direction]
Computed value: [0 1]
Expected value: [0, 1]
check passed
[residual of the correct direction]
Computed value: [0 0]
Expected value: [0, 0]
check passed
▶ Step 2: Check the remaining statements about translations
----------------------------------------
[statement 1: translation constant]
Computed value: 2
Expected value: 2
check passed
[statement 3: translation constant]
Computed value: 0
Expected value: 0
check passed
[statement 4: rank of the directions]
Computed value: 1
Expected value: 1
check passed
[constant of the correct plane]
Computed value: -2
Expected value: -2
check passed
LU and Permutation Matrices¶
# ============================================================
print_header("Exercise 6-25 | LU Decompositions of 2×2 Matrices")
# ============================================================
# --- Step 1 ---
print_step(1, "Check every matrix for which a decomposition exists")
cases = [(np.array([[2,1],[5,3]]), [2.5]),
(np.array([[2,0],[10,5]]), [5]),
(np.array([[0,1],[0,-1]]), [-1,0,2]),
(np.array([[1,0],[1,0]]), [1]),
(np.array([[0,0],[0,1]]), [-2,0,3])]
for A, values in cases:
for ell in values:
L = np.array([[1,0],[ell,1]])
U = np.array([[A[0,0],A[0,1]],[0,A[1,1]-ell*A[0,1]]])
check(f"ℓ={ell}: LU", L@U, A)============================================================
Exercise 6-25 | LU Decompositions of 2×2 Matrices
============================================================
▶ Step 1: Check every matrix for which a decomposition exists
----------------------------------------
[ℓ=2.5: LU]
Computed value: [[2. 1.]
[5. 3.]]
Expected value: [[2 1]
[5 3]]
check passed
[ℓ=5: LU]
Computed value: [[ 2 0]
[10 5]]
Expected value: [[ 2 0]
[10 5]]
check passed
[ℓ=-1: LU]
Computed value: [[ 0 1]
[ 0 -1]]
Expected value: [[ 0 1]
[ 0 -1]]
check passed
[ℓ=0: LU]
Computed value: [[ 0 1]
[ 0 -1]]
Expected value: [[ 0 1]
[ 0 -1]]
check passed
[ℓ=2: LU]
Computed value: [[ 0 1]
[ 0 -1]]
Expected value: [[ 0 1]
[ 0 -1]]
check passed
[ℓ=1: LU]
Computed value: [[1 0]
[1 0]]
Expected value: [[1 0]
[1 0]]
check passed
[ℓ=-2: LU]
Computed value: [[0 0]
[0 1]]
Expected value: [[0 0]
[0 1]]
check passed
[ℓ=0: LU]
Computed value: [[0 0]
[0 1]]
Expected value: [[0 0]
[0 1]]
check passed
[ℓ=3: LU]
Computed value: [[0 0]
[0 1]]
Expected value: [[0 0]
[0 1]]
check passed
# ============================================================
print_header("Exercise 6-29 | Elimination for a 3×3 Matrix")
# ============================================================
# --- Step 1 ---
print_step(1, "Compute and compare the LU factors")
A = np.array([[2,1,1],[4,5,2],[2,10,7]])
L,U = lu_no_pivot(A)
check("L", L, [[1,0,0],[2,1,0],[1,3,1]])
check("U", U, [[2,1,1],[0,3,0],[0,0,6]])
check("LU", L@U, A)============================================================
Exercise 6-29 | Elimination for a 3×3 Matrix
============================================================
▶ Step 1: Compute and compare the LU factors
----------------------------------------
[L]
Computed value: [[1. 0. 0.]
[2. 1. 0.]
[1. 3. 1.]]
Expected value: [[1, 0, 0], [2, 1, 0], [1, 3, 1]]
check passed
[U]
Computed value: [[2. 1. 1.]
[0. 3. 0.]
[0. 0. 6.]]
Expected value: [[2, 1, 1], [0, 3, 0], [0, 0, 6]]
check passed
[LU]
Computed value: [[ 2. 1. 1.]
[ 4. 5. 2.]
[ 2. 10. 7.]]
Expected value: [[ 2 1 1]
[ 4 5 2]
[ 2 10 7]]
check passed
# ============================================================
print_header("Exercise 6-30 | Decomposing a Rectangular Matrix")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the LU decomposition and the rank")
C = np.array([[1,2,0,1],[2,4,1,3],[3,6,1,4]])
L = np.array([[1,0,0],[2,1,0],[3,1,1]])
U = np.array([[1,2,0,1],[0,0,1,1],[0,0,0,0]])
check("LU", L@U, C)
check("rank(C)", np.linalg.matrix_rank(C), 2)============================================================
Exercise 6-30 | Decomposing a Rectangular Matrix
============================================================
▶ Step 1: Check the LU decomposition and the rank
----------------------------------------
[LU]
Computed value: [[1 2 0 1]
[2 4 1 3]
[3 6 1 4]]
Expected value: [[1 2 0 1]
[2 4 1 3]
[3 6 1 4]]
check passed
[rank(C)]
Computed value: 2
Expected value: 2
check passed
# ============================================================
print_header("Exercise 6-31 | Permutation Matrices")
# ============================================================
# --- Step 1 ---
print_step(1, "Read off the three permutations")
perms = [[0,1,2],[1,2,0],[1,3,0,2]]
for sigma in perms:
P = np.eye(len(sigma))[sigma]
check("column index of the 1 in each row", np.argmax(P,axis=1), sigma)
# --- Step 2 ---
print_step(2, "Check the order of composition with concrete noncommuting permutations")
sigma = np.array([1,3,0,2]); tau = np.array([1,0,2,3])
P = np.eye(4)[sigma]; Q = np.eye(4)[tau]
check("Pσ Pτ", P@Q, np.eye(4)[tau[sigma]])
check("Pσ^T Pσ", P.T@P, np.eye(4))============================================================
Exercise 6-31 | Permutation Matrices
============================================================
▶ Step 1: Read off the three permutations
----------------------------------------
[column index of the 1 in each row]
Computed value: [0 1 2]
Expected value: [0, 1, 2]
check passed
[column index of the 1 in each row]
Computed value: [1 2 0]
Expected value: [1, 2, 0]
check passed
[column index of the 1 in each row]
Computed value: [1 3 0 2]
Expected value: [1, 3, 0, 2]
check passed
▶ Step 2: Check the order of composition with concrete noncommuting permutations
----------------------------------------
[Pσ Pτ]
Computed value: [[1. 0. 0. 0.]
[0. 0. 0. 1.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]]
Expected value: [[1. 0. 0. 0.]
[0. 0. 0. 1.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]]
check passed
[Pσ^T Pσ]
Computed value: [[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
Expected value: [[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
check passed
# ============================================================
print_header("Exercise 6-32 | Decomposition with Row Exchanges")
# ============================================================
# --- Step 1 ---
print_step(1, "Check PA=LU and A=P^T LU separately")
cases = [([[0,1,2],[1,2,3],[2,3,4]], [1,0,2], [[1,0,0],[0,1,0],[2,-1,1]], [[1,2,3],[0,1,2],[0,0,0]]),
([[1,2,3],[2,4,0],[0,2,1]], [0,2,1], [[1,0,0],[0,1,0],[2,0,1]], [[1,2,3],[0,2,1],[0,0,-6]]),
([[0,1,2],[0,0,1],[1,3,5]], [2,0,1], np.eye(3), [[1,3,5],[0,1,2],[0,0,1]])]
for A, order, L, U in cases:
A,L,U = map(np.array,(A,L,U)); P=np.eye(3)[order]
check("PA", P@A, L@U)
check("P^T LU", P.T@L@U, A)============================================================
Exercise 6-32 | Decomposition with Row Exchanges
============================================================
▶ Step 1: Check PA=LU and A=P^T LU separately
----------------------------------------
[PA]
Computed value: [[1. 2. 3.]
[0. 1. 2.]
[2. 3. 4.]]
Expected value: [[1 2 3]
[0 1 2]
[2 3 4]]
check passed
[P^T LU]
Computed value: [[0. 1. 2.]
[1. 2. 3.]
[2. 3. 4.]]
Expected value: [[0 1 2]
[1 2 3]
[2 3 4]]
check passed
[PA]
Computed value: [[1. 2. 3.]
[0. 2. 1.]
[2. 4. 0.]]
Expected value: [[1 2 3]
[0 2 1]
[2 4 0]]
check passed
[P^T LU]
Computed value: [[1. 2. 3.]
[2. 4. 0.]
[0. 2. 1.]]
Expected value: [[1 2 3]
[2 4 0]
[0 2 1]]
check passed
[PA]
Computed value: [[1. 3. 5.]
[0. 1. 2.]
[0. 0. 1.]]
Expected value: [[1. 3. 5.]
[0. 1. 2.]
[0. 0. 1.]]
check passed
[P^T LU]
Computed value: [[0. 1. 2.]
[0. 0. 1.]
[1. 3. 5.]]
Expected value: [[0 1 2]
[0 0 1]
[1 3 5]]
check passed
# ============================================================
print_header("Exercise 6-35 | Solving via the Decomposition")
# ============================================================
# --- Step 1 ---
print_step(1, "Forward substitution, then back substitution")
P=np.eye(3)[[1,2,0]]; L=np.array([[1,0,0],[2,1,0],[-1,3,1]])
U=np.array([[2,1,-1],[0,3,2],[0,0,4]]); b=np.array([1,2,5])
y=np.linalg.solve(L,P@b); x=np.linalg.solve(U,y)
check("P b", P@b, [2,5,1]); check("y", y, [2,1,0]); check("x", x, [5/6,1/3,0])
# --- Step 2 ---
print_step(2, "Rebuild the coefficient matrix and substitute back")
A=P.T@L@U
check("A x", A@x, b)============================================================
Exercise 6-35 | Solving via the Decomposition
============================================================
▶ Step 1: Forward substitution, then back substitution
----------------------------------------
[P b]
Computed value: [2. 5. 1.]
Expected value: [2, 5, 1]
check passed
[y]
Computed value: [ 2. 1. -0.]
Expected value: [2, 1, 0]
check passed
[x]
Computed value: [ 0.8333 0.3333 -0. ]
Expected value: [0.8333333333333334, 0.3333333333333333, 0]
check passed
▶ Step 2: Rebuild the coefficient matrix and substitute back
----------------------------------------
[A x]
Computed value: [1. 2. 5.]
Expected value: [1 2 5]
check passed
LDU and Symmetric Decompositions¶
# ============================================================
print_header("Exercise 6-36 | LDU Decompositions of 2×2 Matrices")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the unique decompositions and the family with free parameters")
cases = [([[2,1],[5,3]],(2,.5,2.5,.5)), ([[2,0],[0,7]],(2,7,0,0)),
([[2,8],[1,4]],(2,0,.5,4)), ([[1,0],[0,0]],(1,0,0,0))]
for A,(p,q,ell,u) in cases:
check("LDU", np.array([[1,0],[ell,1]])@np.diag([p,q])@np.array([[1,u],[0,1]]), A)
for ell,u in [(-2,3),(0,0),(4,-1)]:
check("LDU with free parameters", np.array([[1,0],[ell,1]])@np.diag([0,1])@np.array([[1,u],[0,1]]), [[0,0],[0,1]])============================================================
Exercise 6-36 | LDU Decompositions of 2×2 Matrices
============================================================
▶ Step 1: Check the unique decompositions and the family with free parameters
----------------------------------------
[LDU]
Computed value: [[2. 1.]
[5. 3.]]
Expected value: [[2, 1], [5, 3]]
check passed
[LDU]
Computed value: [[2 0]
[0 7]]
Expected value: [[2, 0], [0, 7]]
check passed
[LDU]
Computed value: [[2. 8.]
[1. 4.]]
Expected value: [[2, 8], [1, 4]]
check passed
[LDU]
Computed value: [[1 0]
[0 0]]
Expected value: [[1, 0], [0, 0]]
check passed
[LDU with free parameters]
Computed value: [[0 0]
[0 1]]
Expected value: [[0, 0], [0, 1]]
check passed
[LDU with free parameters]
Computed value: [[0 0]
[0 1]]
Expected value: [[0, 0], [0, 1]]
check passed
[LDU with free parameters]
Computed value: [[0 0]
[0 1]]
Expected value: [[0, 0], [0, 1]]
check passed
# ============================================================
print_header("Exercise 6-38 | LDU Decomposition with a Parameter")
# ============================================================
# --- Step 1 ---
print_step(1, "Check generic values and the singular value λ=0")
for lam in [0,2,-3]:
t=lam**2-1
A=np.array([[1,1,1],[1,lam**2,0],[1,0,0]])
L=np.array([[1,0,0],[1,1,0],[1,-1/t,1]])
D=np.diag([1,t,-lam**2/t]); U=np.array([[1,1,1],[0,1,-1/t],[0,0,1]])
check(f"λ={lam}: LDU", L@D@U, A)
# --- Step 2 ---
print_step(2, "Check that elimination works for λ=±1 after a row exchange")
for lam in [-1,1]:
A=np.array([[1,1,1],[1,lam**2,0],[1,0,0]])
P=np.eye(3)[[0,2,1]]; L,U=lu_no_pivot(P@A)
check("PA=LU", P@A, L@U)============================================================
Exercise 6-38 | LDU Decomposition with a Parameter
============================================================
▶ Step 1: Check generic values and the singular value λ=0
----------------------------------------
[λ=0: LDU]
Computed value: [[1. 1. 1.]
[1. 0. 0.]
[1. 0. 0.]]
Expected value: [[1 1 1]
[1 0 0]
[1 0 0]]
check passed
[λ=2: LDU]
Computed value: [[1. 1. 1.]
[1. 4. 0.]
[1. 0. 0.]]
Expected value: [[1 1 1]
[1 4 0]
[1 0 0]]
check passed
[λ=-3: LDU]
Computed value: [[1. 1. 1.]
[1. 9. 0.]
[1. 0. 0.]]
Expected value: [[1 1 1]
[1 9 0]
[1 0 0]]
check passed
▶ Step 2: Check that elimination works for λ=±1 after a row exchange
----------------------------------------
[PA=LU]
Computed value: [[1. 1. 1.]
[1. 0. 0.]
[1. 1. 0.]]
Expected value: [[1. 1. 1.]
[1. 0. 0.]
[1. 1. 0.]]
check passed
[PA=LU]
Computed value: [[1. 1. 1.]
[1. 0. 0.]
[1. 1. 0.]]
Expected value: [[1. 1. 1.]
[1. 0. 0.]
[1. 1. 0.]]
check passed
# ============================================================
print_header("Exercise 6-39 | Symmetric Decomposition")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the LU and LDL^T decompositions")
A=np.array([[1,2,1],[2,7,5],[1,5,10]])
L,V=lu_no_pivot(A); D=np.diag([1,3,6])
check("L", L, [[1,0,0],[2,1,0],[1,1,1]])
check("V", V, [[1,2,1],[0,3,3],[0,0,6]])
check("LDL^T", L@D@L.T, A)============================================================
Exercise 6-39 | Symmetric Decomposition
============================================================
▶ Step 1: Check the LU and LDL^T decompositions
----------------------------------------
[L]
Computed value: [[1. 0. 0.]
[2. 1. 0.]
[1. 1. 1.]]
Expected value: [[1, 0, 0], [2, 1, 0], [1, 1, 1]]
check passed
[V]
Computed value: [[1. 2. 1.]
[0. 3. 3.]
[0. 0. 6.]]
Expected value: [[1, 2, 1], [0, 3, 3], [0, 0, 6]]
check passed
[LDL^T]
Computed value: [[ 1. 2. 1.]
[ 2. 7. 5.]
[ 1. 5. 10.]]
Expected value: [[ 1 2 1]
[ 2 7 5]
[ 1 5 10]]
check passed
# ============================================================
print_header("Exercise 6-40 | A Degenerate Symmetric Form")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the decomposition for different t")
for t in [-2,0,3]:
L=np.array([[1,0],[t,1]])
check(f"t={t}: LDL^T", L@np.diag([0,3])@L.T, [[0,0],[0,3]])============================================================
Exercise 6-40 | A Degenerate Symmetric Form
============================================================
▶ Step 1: Check the decomposition for different t
----------------------------------------
[t=-2: LDL^T]
Computed value: [[0 0]
[0 3]]
Expected value: [[0, 0], [0, 3]]
check passed
[t=0: LDL^T]
Computed value: [[0 0]
[0 3]]
Expected value: [[0, 0], [0, 3]]
check passed
[t=3: LDL^T]
Computed value: [[0 0]
[0 3]]
Expected value: [[0, 0], [0, 3]]
check passed
Augmented Matrices and Inversion¶
# ============================================================
print_header("Exercise 6-43 | Gauss–Jordan Inversion")
# ============================================================
# --- Step 1 ---
print_step(1, "Form the augmented matrix and eliminate downward")
A = np.array([[1, 0, 0, 1], [0, 1, -1, 2], [2, -1, 2, 0], [1, 0, 2, 2]])
G = np.column_stack([A, np.eye(4)])
print("[A | I_4] =\n", G)
G[2] -= 2 * G[0]
G[2] += G[1]
G[3] -= G[0]
G[3] -= 2 * G[2]
check("augmented matrix after downward elimination", G,
[[1, 0, 0, 1, 1, 0, 0, 0], [0, 1, -1, 2, 0, 1, 0, 0],
[0, 0, 1, 0, -2, 1, 1, 0], [0, 0, 0, 1, 3, -2, -2, 1]])
check("det(A)", np.linalg.det(A), 1)
check("rank(A)", np.linalg.matrix_rank(A), 4)
# --- Step 2 ---
print_step(2, "Eliminate upward and read off the inverse matrix")
G[0] -= G[3]
G[1] -= 2 * G[3]
G[1] += G[2]
B = G[:, 4:]
expected_inverse = np.array([[-2, 2, 2, -1], [-8, 6, 5, -2],
[-2, 1, 1, 0], [3, -2, -2, 1]])
check("left half", G[:, :4], np.eye(4))
check("A^{-1}", B, expected_inverse)
# --- Step 3 ---
print_step(3, "Check by multiplying on both sides")
check("AB", A @ B, np.eye(4))
check("BA", B @ A, np.eye(4))============================================================
Exercise 6-43 | Gauss–Jordan Inversion
============================================================
▶ Step 1: Form the augmented matrix and eliminate downward
----------------------------------------
[A | I_4] =
[[ 1. 0. 0. 1. 1. 0. 0. 0.]
[ 0. 1. -1. 2. 0. 1. 0. 0.]
[ 2. -1. 2. 0. 0. 0. 1. 0.]
[ 1. 0. 2. 2. 0. 0. 0. 1.]]
[augmented matrix after downward elimination]
Computed value: [[ 1. 0. 0. 1. 1. 0. 0. 0.]
[ 0. 1. -1. 2. 0. 1. 0. 0.]
[ 0. 0. 1. 0. -2. 1. 1. 0.]
[ 0. 0. 0. 1. 3. -2. -2. 1.]]
Expected value: [[1, 0, 0, 1, 1, 0, 0, 0], [0, 1, -1, 2, 0, 1, 0, 0], [0, 0, 1, 0, -2, 1, 1, 0], [0, 0, 0, 1, 3, -2, -2, 1]]
check passed
[det(A)]
Computed value: 0.9999999999999998
Expected value: 1
check passed
[rank(A)]
Computed value: 4
Expected value: 4
check passed
▶ Step 2: Eliminate upward and read off the inverse matrix
----------------------------------------
[left half]
Computed value: [[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
Expected value: [[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
check passed
[A^{-1}]
Computed value: [[-2. 2. 2. -1.]
[-8. 6. 5. -2.]
[-2. 1. 1. 0.]
[ 3. -2. -2. 1.]]
Expected value: [[-2 2 2 -1]
[-8 6 5 -2]
[-2 1 1 0]
[ 3 -2 -2 1]]
check passed
▶ Step 3: Check by multiplying on both sides
----------------------------------------
[AB]
Computed value: [[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
Expected value: [[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
check passed
[BA]
Computed value: [[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
Expected value: [[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
check passed
# ============================================================
print_header("Exercise 6-44 | Inversion with a Parameter")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the degenerate parameter")
check("rank for a=2", np.linalg.matrix_rank([[1,1,0],[1,2,1],[0,2,2]]), 2)
# --- Step 2 ---
print_step(2, "Check the inverse matrix on both sides")
B=np.array([[1,1,0],[1,2,1],[0,2,3]])
C=np.array([[4,-3,1],[-3,3,-1],[2,-2,1]])
check("B C", B@C, np.eye(3)); check("C B", C@B, np.eye(3))============================================================
Exercise 6-44 | Inversion with a Parameter
============================================================
▶ Step 1: Check the degenerate parameter
----------------------------------------
[rank for a=2]
Computed value: 2
Expected value: 2
check passed
▶ Step 2: Check the inverse matrix on both sides
----------------------------------------
[B C]
Computed value: [[1 0 0]
[0 1 0]
[0 0 1]]
Expected value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
check passed
[C B]
Computed value: [[1 0 0]
[0 1 0]
[0 0 1]]
Expected value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
check passed
✓ All exercises completed.