Compiled from the original manuscript of Chapter 7 exercises provided by Professor Ping-Zen Ong: 36 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, and permutations are written as lists of images. Inconsistent notation and ambiguities in the statements of the original manuscript are noted in the corresponding exercises.
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
from itertools import permutations
import matplotlib.pyplot as plt
# --- Color palette (Accent Mix) ---
C_BG = "#F8F8F8"
C_GRID = "#D6D6D6"
C_AXIS = "#000000"
C_V1 = "#57068C"
C_V2 = "#006385"
C_T1 = "#2AD2C9"
C_T2 = "#8900E1"
C_WARN = "#FF5D47"
C_AUX = "#AB82C5"
C_AREA = "rgba(87, 6, 140, 0.25)" # for Plotly only.
# Fonts: the English edition needs no CJK font, so nothing is downloaded when _lang == 'en';
# the Chinese editions use this same block to fetch Noto Sans TC/SC where no CJK font is installed
import os, urllib.request
import matplotlib.font_manager as fm
_lang = 'en'
_cjk = ['Microsoft JhengHei', 'PingFang TC', 'Noto Sans CJK TC', 'Noto Sans TC']
_have = {f.name for f in fm.fontManager.ttflist}
if _lang != 'en' and not _have & set(_cjk):
_font = os.path.join(os.path.expanduser('~'), '.cache', 'fonts', 'NotoSansTC.ttf')
try:
if not os.path.exists(_font):
os.makedirs(os.path.dirname(_font), exist_ok=True)
urllib.request.urlretrieve('https://github.com/google/fonts/raw/main/ofl/notosanstc/NotoSansTC%5Bwght%5D.ttf', _font + '.part')
os.replace(_font + '.part', _font)
fm.fontManager.addfont(_font)
_have.add('Noto Sans TC')
except OSError as err:
print('Could not download the CJK font; Chinese text in figures may not display:', err)
plt.rcParams['font.family'] = [f for f in _cjk if f in _have] + ['DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
def inversion_count(sigma):
return sum(sigma[i] > sigma[j] for i in range(len(sigma)) for j in range(i+1,len(sigma)))
def permutation_sign(sigma):
return (-1)**int(inversion_count(sigma))
def cofactor(A, i, j):
minor = np.delete(np.delete(A, i, axis=0), j, axis=1)
return (-1)**(i+j) * np.linalg.det(minor)
Permutations, Inversions, and the Classical Definition¶
# ============================================================
print_header("Exercise 7-1 | Inversions and Crossings")
# ============================================================
# --- Step 1 ---
print_step(1, "Compute the number of inversions and the inverse permutation")
sigma=np.array([3,0,4,1,2]); inverse=np.argsort(sigma)
check("number of inversions", inversion_count(sigma), 5)
check("sgn(σ)", permutation_sign(sigma), -1)
check("pairs of crossing segments", inversion_count(inverse), 5)
# --- Step 2 ---
print_step(2, "Draw the permutation diagram joining equal values in the two rows")
fig,ax=plt.subplots(figsize=(6,6),facecolor=C_BG)
ax.set_facecolor(C_BG)
ax.grid(True,color=C_GRID,linewidth=.8,alpha=.7)
colors=[C_V1,C_V2,C_T1,C_WARN,C_T2]
for value, bottom in enumerate(inverse):
ax.plot([value,bottom],[2,0],color=colors[value],linewidth=2.4)
for position in range(5):
ax.scatter([position,position],[2,0],s=75,color=C_BG,edgecolor=C_AXIS,zorder=3)
ax.text(position,2.18,str(position),ha='center',color=C_AXIS,fontsize=13)
ax.text(position,-.28,str(sigma[position]),ha='center',color=C_AXIS,fontsize=13)
ax.set_xlim(-.65,4.65); ax.set_ylim(-1.65,3.65)
ax.set_aspect('equal',adjustable='box'); ax.set_box_aspect(1)
ax.set_xticks([]); ax.set_yticks([])
ax.set_title('Line diagram of the permutation: 5 pairs of crossing segments, sign −1',color=C_AXIS)
for spine in ax.spines.values():spine.set_visible(False)
plt.tight_layout(); plt.show()============================================================
Exercise 7-1 | Inversions and Crossings
============================================================
▶ Step 1: Compute the number of inversions and the inverse permutation
----------------------------------------
[number of inversions]
Computed value: 5
Expected value: 5
check passed
[sgn(σ)]
Computed value: -1
Expected value: -1
check passed
[pairs of crossing segments]
Computed value: 5
Expected value: 5
check passed
▶ Step 2: Draw the permutation diagram joining equal values in the two rows
----------------------------------------

# ============================================================
print_header("Exercise 7-2 | Computing Signs")
# ============================================================
# --- Step 1 ---
print_step(1, "Compare the numbers of inversions and the signs one by one")
for sigma,count in [([4,1,2,3,0],7),([0,1,2,3,4],0),([1,0,2,4,3],2),([1,2,3,4,0],4)]:
check("number of inversions", inversion_count(sigma), count)
check("sign", permutation_sign(sigma), (-1)**count)============================================================
Exercise 7-2 | Computing Signs
============================================================
▶ Step 1: Compare the numbers of inversions and the signs one by one
----------------------------------------
[number of inversions]
Computed value: 7
Expected value: 7
check passed
[sign]
Computed value: -1
Expected value: -1
check passed
[number of inversions]
Computed value: 0
Expected value: 0
check passed
[sign]
Computed value: 1
Expected value: 1
check passed
[number of inversions]
Computed value: 2
Expected value: 2
check passed
[sign]
Computed value: 1
Expected value: 1
check passed
[number of inversions]
Computed value: 4
Expected value: 4
check passed
[sign]
Computed value: 1
Expected value: 1
check passed
# ============================================================
print_header("Exercise 7-3 | Signs of Compositions")
# ============================================================
# --- Step 1 ---
print_step(1, "Build the lists of images in the order of composition")
cases=[([1,4,3,2,0],[0,1,2,3,4],[1,4,3,2,0]),([2,3,4,0,1],[1,0,3,4,2],[3,2,0,1,4]),([1,4,3,2,0],[1,0,3,4,2],[4,1,2,0,3])]
for sigma,tau,expected in cases:
comp=np.array(sigma)[tau]
check("σ∘τ", comp, expected)
check("sign of the composition", permutation_sign(comp), permutation_sign(sigma)*permutation_sign(tau))============================================================
Exercise 7-3 | Signs of Compositions
============================================================
▶ Step 1: Build the lists of images in the order of composition
----------------------------------------
[σ∘τ]
Computed value: [1 4 3 2 0]
Expected value: [1, 4, 3, 2, 0]
check passed
[sign of the composition]
Computed value: -1
Expected value: -1
check passed
[σ∘τ]
Computed value: [3 2 0 1 4]
Expected value: [3, 2, 0, 1, 4]
check passed
[sign of the composition]
Computed value: -1
Expected value: -1
check passed
[σ∘τ]
Computed value: [4 1 2 0 3]
Expected value: [4, 1, 2, 0, 3]
check passed
[sign of the composition]
Computed value: 1
Expected value: 1
check passed
# ============================================================
print_header("Exercise 7-5 | A Sparse Expansion")
# ============================================================
# --- Step 1 ---
print_step(1, "List the nonzero terms of the classical definition")
A=np.array([[0,2,0,5],[1,0,0,3],[2,0,4,0],[0,5,0,6]])
terms=[]
for sigma in permutations(range(4)):
term=permutation_sign(sigma)*np.prod(A[np.arange(4),sigma])
if term:
print(f"σ={sigma}: {term}"); terms.append(term)
check("nonzero terms", sorted(terms), [-48,100])
check("determinant", np.linalg.det(A), 52)============================================================
Exercise 7-5 | A Sparse Expansion
============================================================
▶ Step 1: List the nonzero terms of the classical definition
----------------------------------------
σ=(1, 0, 2, 3): -48
σ=(3, 0, 2, 1): 100
[nonzero terms]
Computed value: [np.int64(-48), np.int64(100)]
Expected value: [-48, 100]
check passed
[determinant]
Computed value: 51.999999999999986
Expected value: 52
check passed
# ============================================================
print_header("Exercise 7-6 | The Reversal Matrix")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the identity and reversal matrices of each order")
for n in range(1,9):
check(f"det(I_{n})", np.linalg.det(np.eye(n)), 1)
check(f"reversal matrix n={n}", np.linalg.det(np.eye(n)[::-1]), (-1)**(n*(n-1)//2))============================================================
Exercise 7-6 | The Reversal Matrix
============================================================
▶ Step 1: Check the identity and reversal matrices of each order
----------------------------------------
[det(I_1)]
Computed value: 1.0
Expected value: 1
check passed
[reversal matrix n=1]
Computed value: 1.0
Expected value: 1
check passed
[det(I_2)]
Computed value: 1.0
Expected value: 1
check passed
[reversal matrix n=2]
Computed value: -1.0
Expected value: -1
check passed
[det(I_3)]
Computed value: 1.0
Expected value: 1
check passed
[reversal matrix n=3]
Computed value: -1.0
Expected value: -1
check passed
[det(I_4)]
Computed value: 1.0
Expected value: 1
check passed
[reversal matrix n=4]
Computed value: 1.0
Expected value: 1
check passed
[det(I_5)]
Computed value: 1.0
Expected value: 1
check passed
[reversal matrix n=5]
Computed value: 1.0
Expected value: 1
check passed
[det(I_6)]
Computed value: 1.0
Expected value: 1
check passed
[reversal matrix n=6]
Computed value: -1.0
Expected value: -1
check passed
[det(I_7)]
Computed value: 1.0
Expected value: 1
check passed
[reversal matrix n=7]
Computed value: -1.0
Expected value: -1
check passed
[det(I_8)]
Computed value: 1.0
Expected value: 1
check passed
[reversal matrix n=8]
Computed value: 1.0
Expected value: 1
check passed
Basic Properties and Computation by Elimination¶
# ============================================================
print_header("Exercise 7-10 | Computation by Elimination")
# ============================================================
# --- Step 1 ---
print_step(1, "Cross-check the diagonal after elimination against the NumPy determinant")
cases=[([[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3]],160),([[3,1,1,1],[2,3,4,1],[1,1,2,1],[2,-1,-1,1]],0),([[1,0,2,3],[0,1,1,2],[4,3,1,0],[2,1,0,1]],-20)]
for A,expected in cases:
L,U=lu_no_pivot(A)
check("LU reconstruction", L@U, A)
check("product of the diagonal of U", np.prod(np.diag(U)), expected)
check("det(A)", np.linalg.det(A), expected)============================================================
Exercise 7-10 | Computation by Elimination
============================================================
▶ Step 1: Cross-check the diagonal after elimination against the NumPy determinant
----------------------------------------
[LU reconstruction]
Computed value: [[1. 2. 3. 4.]
[2. 3. 4. 1.]
[3. 4. 1. 2.]
[4. 1. 2. 3.]]
Expected value: [[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
check passed
[product of the diagonal of U]
Computed value: 160.0
Expected value: 160
check passed
[det(A)]
Computed value: 160.00000000000009
Expected value: 160
check passed
[LU reconstruction]
Computed value: [[ 3. 1. 1. 1.]
[ 2. 3. 4. 1.]
[ 1. 1. 2. 1.]
[ 2. -1. -1. 1.]]
Expected value: [[3, 1, 1, 1], [2, 3, 4, 1], [1, 1, 2, 1], [2, -1, -1, 1]]
check passed
[product of the diagonal of U]
Computed value: -1.1102230246251563e-15
Expected value: 0
check passed
[det(A)]
Computed value: 0.0
Expected value: 0
check passed
[LU reconstruction]
Computed value: [[1. 0. 2. 3.]
[0. 1. 1. 2.]
[4. 3. 1. 0.]
[2. 1. 0. 1.]]
Expected value: [[1, 0, 2, 3], [0, 1, 1, 2], [4, 3, 1, 0], [2, 1, 0, 1]]
check passed
[product of the diagonal of U]
Computed value: -20.0
Expected value: -20
check passed
[det(A)]
Computed value: -20.000000000000007
Expected value: -20
check passed
# ============================================================
print_header("Exercise 7-14 | Recording the Operations")
# ============================================================
# --- Step 1 ---
print_step(1, "Form the elementary matrices step by step and eliminate")
A=np.array([[0,0,-1,4],[2,4,6,0],[1,2,1,1],[2,0,1,3]],dtype=float)
E0=np.eye(4)[[2,1,0,3]]; E1=np.eye(4); E1[1,0]=-2
E2=np.eye(4); E2[3,0]=-2
E3=np.eye(4)[[0,3,2,1]]; E4=np.eye(4); E4[3,2]=4
U=A.copy(); det_factors=[]
for E in [E0,E1,E2,E3,E4]:
U=E@U; det_factors.append(np.linalg.det(E))
check("U", U, [[1,2,1,1],[0,-4,-1,1],[0,0,-1,4],[0,0,0,14]])
# --- Step 2 ---
print_step(2, "Check the product of the determinants of the inverse operations")
check("det(U)", np.linalg.det(U), 56)
check("det(A)", np.linalg.det(U)/np.prod(det_factors), np.linalg.det(A))============================================================
Exercise 7-14 | Recording the Operations
============================================================
▶ Step 1: Form the elementary matrices step by step and eliminate
----------------------------------------
[U]
Computed value: [[ 1. 2. 1. 1.]
[ 0. -4. -1. 1.]
[ 0. 0. -1. 4.]
[ 0. 0. 0. 14.]]
Expected value: [[1, 2, 1, 1], [0, -4, -1, 1], [0, 0, -1, 4], [0, 0, 0, 14]]
check passed
▶ Step 2: Check the product of the determinants of the inverse operations
----------------------------------------
[det(U)]
Computed value: 55.99999999999997
Expected value: 56
check passed
[det(A)]
Computed value: 55.99999999999997
Expected value: 56.00000000000002
check passed
Block Determinants¶
# ============================================================
print_header("Exercise 7-17 | Block Computations")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the three block reductions")
M0=np.array([[2,3,0,0],[1,4,0,0],[5,-1,3,1],[2,6,1,2]])
M1=np.array([[3,1,1,0],[1,0,0,1],[1,1,2,1],[2,-1,-1,1]])
M2=np.array([[1,0,2,3],[0,1,1,2],[4,3,1,0],[2,1,0,1]])
check("block lower triangular", np.linalg.det(M0[:2,:2])*np.linalg.det(M0[2:,2:]), 25)
check("C-DA", M1[2:,:2]-M1[2:,2:]@M1[:2,:2], [[-6,-1],[4,0]])
check("D-CB", M2[2:,2:]-M2[2:,:2]@M2[:2,2:], [[-10,-18],[-5,-7]])
for M,expected in [(M0,25),(M1,4),(M2,-20)]:check("det(M)", np.linalg.det(M), expected)============================================================
Exercise 7-17 | Block Computations
============================================================
▶ Step 1: Check the three block reductions
----------------------------------------
[block lower triangular]
Computed value: 25.00000000000001
Expected value: 25
check passed
[C-DA]
Computed value: [[-6 -1]
[ 4 0]]
Expected value: [[-6, -1], [4, 0]]
check passed
[D-CB]
Computed value: [[-10 -18]
[ -5 -7]]
Expected value: [[-10, -18], [-5, -7]]
check passed
[det(M)]
Computed value: 25.000000000000007
Expected value: 25
check passed
[det(M)]
Computed value: 4.000000000000001
Expected value: 4
check passed
[det(M)]
Computed value: -20.000000000000007
Expected value: -20
check passed
# ============================================================
print_header("Exercise 7-18 | Computing with the Schur Complement")
# ============================================================
# --- Step 1 ---
print_step(1, "Compute the Schur complement with solve, avoiding an explicit inverse")
cases=[([[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3]],160,False),([[3,1,1,1],[2,3,4,1],[1,1,2,1],[2,-1,-1,1]],0,False),([[0,5,0,2],[0,3,1,0],[2,0,4,0],[0,5,0,6]],40,True)]
for values,expected,swap in cases:
M=np.array(values,dtype=float); N=M[[0,2,1,3]] if swap else M
A,B,C,D=N[:2,:2],N[:2,2:],N[2:,:2],N[2:,2:]
S=D-C@np.linalg.solve(A,B)
factor=-1 if swap else 1
check("block formula", factor*np.linalg.det(A)*np.linalg.det(S), expected)
check("direct determinant", np.linalg.det(M), expected)============================================================
Exercise 7-18 | Computing with the Schur Complement
============================================================
▶ Step 1: Compute the Schur complement with solve, avoiding an explicit inverse
----------------------------------------
[block formula]
Computed value: 159.99999999999994
Expected value: 160
check passed
[direct determinant]
Computed value: 160.00000000000009
Expected value: 160
check passed
[block formula]
Computed value: -3.9650822308041406e-16
Expected value: 0
check passed
[direct determinant]
Computed value: 0.0
Expected value: 0
check passed
[block formula]
Computed value: 39.99999999999999
Expected value: 40
check passed
[direct determinant]
Computed value: 39.99999999999998
Expected value: 40
check passed
Properties of Determinants and Expansions¶
# ============================================================
print_header("Exercise 7-19 | Computing with Properties")
# ============================================================
# --- Step 1 ---
print_step(1, "Test the formulas with concrete matrices of several sizes")
for n in [1,2,4]:
A=np.eye(n); A[0,0]=3
B=np.eye(n); B[0,0]=-2
check("det(2A)", np.linalg.det(2*A), 3*2**n)
check("det((A²)^T(B^T)²)", np.linalg.det((A@A).T@B.T@B.T), 36)
check("det(A^{-1})", np.linalg.det(np.linalg.inv(A)), 1/3)
check("det(A^{-1}BA)", np.linalg.det(np.linalg.solve(A,B@A)), -2)============================================================
Exercise 7-19 | Computing with Properties
============================================================
▶ Step 1: Test the formulas with concrete matrices of several sizes
----------------------------------------
[det(2A)]
Computed value: 6.0
Expected value: 6
check passed
[det((A²)^T(B^T)²)]
Computed value: 36.0
Expected value: 36
check passed
[det(A^{-1})]
Computed value: 0.3333333333333333
Expected value: 0.3333333333333333
check passed
[det(A^{-1}BA)]
Computed value: -2.0
Expected value: -2
check passed
[det(2A)]
Computed value: 12.0
Expected value: 12
check passed
[det((A²)^T(B^T)²)]
Computed value: 36.0
Expected value: 36
check passed
[det(A^{-1})]
Computed value: 0.3333333333333333
Expected value: 0.3333333333333333
check passed
[det(A^{-1}BA)]
Computed value: -2.0
Expected value: -2
check passed
[det(2A)]
Computed value: 48.00000000000001
Expected value: 48
check passed
[det((A²)^T(B^T)²)]
Computed value: 36.0
Expected value: 36
check passed
[det(A^{-1})]
Computed value: 0.3333333333333333
Expected value: 0.3333333333333333
check passed
[det(A^{-1}BA)]
Computed value: -2.0
Expected value: -2
check passed
# ============================================================
print_header("Exercise 7-20 | Properties of Column Operations")
# ============================================================
# --- Step 1 ---
print_step(1, "Take differences of adjacent columns from right to left")
B=np.array([[1,1,1,1],[1,2,2,2],[1,2,3,3],[1,2,3,4]],dtype=float)
M=B.copy()
for j in range(3,0,-1):M[:,j]-=M[:,j-1]
check("after column operations", M, np.tril(np.ones((4,4))))
# --- Step 2 ---
print_step(2, "Take differences of adjacent rows from bottom to top")
for i in range(3,0,-1):M[i]-=M[i-1]
check("after row operations", M, np.eye(4))
check("det(B)", np.linalg.det(B), 1)============================================================
Exercise 7-20 | Properties of Column Operations
============================================================
▶ Step 1: Take differences of adjacent columns from right to left
----------------------------------------
[after column operations]
Computed value: [[1. 0. 0. 0.]
[1. 1. 0. 0.]
[1. 1. 1. 0.]
[1. 1. 1. 1.]]
Expected value: [[1. 0. 0. 0.]
[1. 1. 0. 0.]
[1. 1. 1. 0.]
[1. 1. 1. 1.]]
check passed
▶ Step 2: Take differences of adjacent rows from bottom to top
----------------------------------------
[after row operations]
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
[det(B)]
Computed value: 1.0
Expected value: 1
check passed
# ============================================================
print_header("Exercise 7-21 | Cofactor Expansion")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the sparse cofactor expansions against direct computation")
cases=[([[0,0,0,4],[0,0,4,1],[0,4,1,2],[4,1,2,3]],0,256),([[3,1,0,1],[0,0,4,0],[1,1,0,1],[2,-1,0,1]],1,-16),([[1,0,2,3],[3,0,6,0],[4,0,1,0],[2,-4,12,0]],0,252)]
for values,row,expected in cases:
A=np.array(values)
result=sum(A[row,j]*cofactor(A,row,j) for j in range(4))
check("expansion along a row", result, expected)
check("direct determinant", np.linalg.det(A), expected)============================================================
Exercise 7-21 | Cofactor Expansion
============================================================
▶ Step 1: Check the sparse cofactor expansions against direct computation
----------------------------------------
[expansion along a row]
Computed value: 255.99999999999991
Expected value: 256
check passed
[direct determinant]
Computed value: 255.99999999999994
Expected value: 256
check passed
[expansion along a row]
Computed value: -16.000000000000007
Expected value: -16
check passed
[direct determinant]
Computed value: -16.000000000000007
Expected value: -16
check passed
[expansion along a row]
Computed value: 251.99999999999994
Expected value: 252
check passed
[direct determinant]
Computed value: 252.00000000000003
Expected value: 252
check passed
Recurrences and Polynomials¶
# ============================================================
print_header("Exercise 7-22 | A Tridiagonal Recurrence")
# ============================================================
# --- Step 1 ---
print_step(1, "Compare the matrix determinants with the general formula")
for n in range(1,11):
A=2*np.eye(n)+np.diag(np.ones(n-1),1)+np.diag(np.ones(n-1),-1)
check(f"A_{n}", np.linalg.det(A), n+1)============================================================
Exercise 7-22 | A Tridiagonal Recurrence
============================================================
▶ Step 1: Compare the matrix determinants with the general formula
----------------------------------------
[A_1]
Computed value: 2.0
Expected value: 2
check passed
[A_2]
Computed value: 2.9999999999999996
Expected value: 3
check passed
[A_3]
Computed value: 4.0
Expected value: 4
check passed
[A_4]
Computed value: 4.999999999999999
Expected value: 5
check passed
[A_5]
Computed value: 6.0
Expected value: 6
check passed
[A_6]
Computed value: 6.999999999999998
Expected value: 7
check passed
[A_7]
Computed value: 7.999999999999998
Expected value: 8
check passed
[A_8]
Computed value: 8.999999999999998
Expected value: 9
check passed
[A_9]
Computed value: 9.999999999999998
Expected value: 10
check passed
[A_10]
Computed value: 10.999999999999996
Expected value: 11
check passed
# ============================================================
print_header("Exercise 7-23 | A General Tridiagonal Formula")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the nonsymmetric tridiagonal matrices")
for n in range(1,11):
A=3*np.eye(n)+2*np.diag(np.ones(n-1),1)+np.diag(np.ones(n-1),-1)
check(f"D_{n}", np.linalg.det(A), 2**(n+1)-1)============================================================
Exercise 7-23 | A General Tridiagonal Formula
============================================================
▶ Step 1: Check the nonsymmetric tridiagonal matrices
----------------------------------------
[D_1]
Computed value: 3.0000000000000004
Expected value: 3
check passed
[D_2]
Computed value: 7.000000000000001
Expected value: 7
check passed
[D_3]
Computed value: 15.0
Expected value: 15
check passed
[D_4]
Computed value: 31.0
Expected value: 31
check passed
[D_5]
Computed value: 62.99999999999999
Expected value: 63
check passed
[D_6]
Computed value: 126.99999999999999
Expected value: 127
check passed
[D_7]
Computed value: 254.99999999999991
Expected value: 255
check passed
[D_8]
Computed value: 511.0
Expected value: 511
check passed
[D_9]
Computed value: 1022.9999999999995
Expected value: 1023
check passed
[D_10]
Computed value: 2046.9999999999995
Expected value: 2047
check passed
# ============================================================
print_header("Exercise 7-24 | An Arrowhead Matrix")
# ============================================================
# --- Step 1 ---
print_step(1, "Check small sizes, the degenerate size, and negative values")
for n in range(1,10):
A=2*np.eye(n)
A[-1,:-1]=1; A[:-1,-1]=1
check(f"K_{n}", np.linalg.det(A), 2.0**(n-2)*(5-n))============================================================
Exercise 7-24 | An Arrowhead Matrix
============================================================
▶ Step 1: Check small sizes, the degenerate size, and negative values
----------------------------------------
[K_1]
Computed value: 2.0
Expected value: 2.0
check passed
[K_2]
Computed value: 2.9999999999999996
Expected value: 3.0
check passed
[K_3]
Computed value: 4.0
Expected value: 4.0
check passed
[K_4]
Computed value: 3.999999999999999
Expected value: 4.0
check passed
[K_5]
Computed value: 0.0
Expected value: 0.0
check passed
[K_6]
Computed value: -15.999999999999998
Expected value: -16.0
check passed
[K_7]
Computed value: -63.99999999999998
Expected value: -64.0
check passed
[K_8]
Computed value: -192.0
Expected value: -192.0
check passed
[K_9]
Computed value: -511.99999999999994
Expected value: -512.0
check passed
Adjugates, Inverses, and LU¶
# ============================================================
print_header("Exercise 7-26 | Adjugate and Inverse")
# ============================================================
# --- Step 1 ---
print_step(1, "Compute the cofactors one by one")
A=np.array([[1,2,0],[0,1,3],[2,0,1]])
C=np.array([[cofactor(A,i,j) for j in range(3)] for i in range(3)])
check("cofactor matrix", C, [[1,6,-2],[-2,1,4],[6,-3,1]])
# --- Step 2 ---
print_step(2, "Transpose, divide by the determinant, and check on both sides")
B=C.T/13
check("det(A)", np.linalg.det(A), 13)
check("A A^{-1}", A@B, np.eye(3)); check("A^{-1} A", B@A, np.eye(3))============================================================
Exercise 7-26 | Adjugate and Inverse
============================================================
▶ Step 1: Compute the cofactors one by one
----------------------------------------
[cofactor matrix]
Computed value: [[ 1. 6. -2.]
[-2. 1. 4.]
[ 6. -3. 1.]]
Expected value: [[1, 6, -2], [-2, 1, 4], [6, -3, 1]]
check passed
▶ Step 2: Transpose, divide by the determinant, and check on both sides
----------------------------------------
[det(A)]
Computed value: 13.0
Expected value: 13
check passed
[A A^{-1}]
Computed value: [[ 1. 0. -0.]
[ 0. 1. -0.]
[ 0. 0. 1.]]
Expected value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
check passed
[A^{-1} A]
Computed value: [[ 1. 0. 0.]
[-0. 1. -0.]
[ 0. 0. 1.]]
Expected value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
check passed
Area, Volume, and the Gram Matrix¶
# ============================================================
print_header("Exercise 7-29 | Area and Order")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the composite matrices, the areas, and the side lengths")
A=np.diag([2,.5]); B=np.array([[1,-1],[1,1]])/np.sqrt(2)
for M in [A@B,B@A]:check("area scaling", abs(np.linalg.det(M)), 1)
check("the two columns of AB have equal length", np.linalg.norm((A@B)[:,0]), np.linalg.norm((A@B)[:,1]))
check("inner product of the columns of BA", (B@A)[:,0]@(B@A)[:,1], 0)
check("side lengths of the image rectangle under BA", 2*np.linalg.norm(B@A,axis=0), [4,1])
# --- Step 2 ---
print_step(2, "Check the vertices of the image under AB")
vertices=np.array([[-1,-1],[1,-1],[1,1],[-1,1]]).T
check("vertices under AB", A@B@vertices, [[0,2*np.sqrt(2),0,-2*np.sqrt(2)],[-1/np.sqrt(2),0,1/np.sqrt(2),0]])============================================================
Exercise 7-29 | Area and Order
============================================================
▶ Step 1: Check the composite matrices, the areas, and the side lengths
----------------------------------------
[area scaling]
Computed value: 0.9999999999999998
Expected value: 1
check passed
[area scaling]
Computed value: 0.9999999999999998
Expected value: 1
check passed
[the two columns of AB have equal length]
Computed value: 1.457737973711325
Expected value: 1.457737973711325
check passed
[inner product of the columns of BA]
Computed value: 0.0
Expected value: 0
check passed
[side lengths of the image rectangle under BA]
Computed value: [4. 1.]
Expected value: [4, 1]
check passed
▶ Step 2: Check the vertices of the image under AB
----------------------------------------
[vertices under AB]
Computed value: [[ 0. 2.8284 0. -2.8284]
[-0.7071 0. 0.7071 0. ]]
Expected value: [[0, np.float64(2.8284271247461903), 0, np.float64(-2.8284271247461903)], [np.float64(-0.7071067811865475), 0, np.float64(0.7071067811865475), 0]]
check passed
# ============================================================
print_header("Exercise 7-30 | Area in the Plane")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the orthogonal factorization and the sign conditions")
M=np.array([[2,1],[2,-3]])
F=np.array([[1,1],[1,-1]])/np.sqrt(2)
R=np.array([[2,-1],[0,2]])*np.sqrt(2)
check("F^T F", F.T@F, np.eye(2)); check("FR", F@R, M)
check("f0·u > 0", F[:,0]@M[:,0], 2*np.sqrt(2))
check("f1·v > 0", F[:,1]@M[:,1], 2*np.sqrt(2))
# --- Step 2 ---
print_step(2, "Compare base times height, the determinant, and the Gram determinant")
check("base times height", np.prod(np.diag(R)), 8)
check("|det(M)|", abs(np.linalg.det(M)), 8)
check("det(M^T M)", np.linalg.det(M.T@M), 64)============================================================
Exercise 7-30 | Area in the Plane
============================================================
▶ Step 1: Check the orthogonal factorization and the sign conditions
----------------------------------------
[F^T F]
Computed value: [[ 1. -0.]
[-0. 1.]]
Expected value: [[1. 0.]
[0. 1.]]
check passed
[FR]
Computed value: [[ 2. 1.]
[ 2. -3.]]
Expected value: [[ 2 1]
[ 2 -3]]
check passed
[f0·u > 0]
Computed value: 2.82842712474619
Expected value: 2.8284271247461903
check passed
[f1·v > 0]
Computed value: 2.82842712474619
Expected value: 2.8284271247461903
check passed
▶ Step 2: Compare base times height, the determinant, and the Gram determinant
----------------------------------------
[base times height]
Computed value: 8.000000000000002
Expected value: 8
check passed
[|det(M)|]
Computed value: 7.999999999999998
Expected value: 8
check passed
[det(M^T M)]
Computed value: 63.99999999999998
Expected value: 64
check passed
# ============================================================
print_header("Exercise 7-31 | Area in Space")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the rectangular orthogonal factorization")
M=np.array([[1,2],[2,0],[2,2]])
F=np.array([[1,2],[2,-2],[2,1]])/3
R=np.array([[3,2],[0,2]])
check("F^T F", F.T@F, np.eye(2)); check("FR", F@R, M)
check("f0·u > 0", F[:,0]@M[:,0], 3)
check("f1·v > 0", F[:,1]@M[:,1], 2)
# --- Step 2 ---
print_step(2, "Compare the Gram area with the cross-product area")
check("Gram", M.T@M, [[9,6],[6,8]])
check("Gram area", np.sqrt(np.linalg.det(M.T@M)), 6)
check("cross product", np.cross(M[:,0],M[:,1]), [4,2,-4])
check("length of the cross product", np.linalg.norm(np.cross(M[:,0],M[:,1])), 6)============================================================
Exercise 7-31 | Area in Space
============================================================
▶ Step 1: Check the rectangular orthogonal factorization
----------------------------------------
[F^T F]
Computed value: [[1. 0.]
[0. 1.]]
Expected value: [[1. 0.]
[0. 1.]]
check passed
[FR]
Computed value: [[1. 2.]
[2. 0.]
[2. 2.]]
Expected value: [[1 2]
[2 0]
[2 2]]
check passed
[f0·u > 0]
Computed value: 3.0
Expected value: 3
check passed
[f1·v > 0]
Computed value: 2.0
Expected value: 2
check passed
▶ Step 2: Compare the Gram area with the cross-product area
----------------------------------------
[Gram]
Computed value: [[9 6]
[6 8]]
Expected value: [[9, 6], [6, 8]]
check passed
[Gram area]
Computed value: 6.0
Expected value: 6
check passed
[cross product]
Computed value: [ 4 2 -4]
Expected value: [4, 2, -4]
check passed
[length of the cross product]
Computed value: 6.0
Expected value: 6
check passed
# ============================================================
print_header("Exercise 7-32 | Volume in Space")
# ============================================================
# --- Step 1 ---
print_step(1, "Check the three orthonormal directions and the factorization with positive diagonal")
M=np.array([[1,2,1],[2,0,3],[2,2,-2]])
F=np.array([[1,2,2],[2,-2,1],[2,1,-2]])/3
R=np.array([[3,2,1],[0,2,-2],[0,0,3]])
check("F^T F", F.T@F, np.eye(3)); check("FR", F@R, M)
check("three heights", np.diag(F.T@M), [3,2,3])
# --- Step 2 ---
print_step(2, "Check the leading principal minors and the volume")
G=M.T@M
for k,expected in [(1,9),(2,36),(3,324)]:check(f"det(G_{k})", np.linalg.det(G[:k,:k]), expected)
check("volume", abs(np.linalg.det(M)), 18)============================================================
Exercise 7-32 | Volume in Space
============================================================
▶ Step 1: Check the three orthonormal directions and the factorization with positive diagonal
----------------------------------------
[F^T F]
Computed value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
Expected value: [[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
check passed
[FR]
Computed value: [[ 1. 2. 1.]
[ 2. 0. 3.]
[ 2. 2. -2.]]
Expected value: [[ 1 2 1]
[ 2 0 3]
[ 2 2 -2]]
check passed
[three heights]
Computed value: [3. 2. 3.]
Expected value: [3, 2, 3]
check passed
▶ Step 2: Check the leading principal minors and the volume
----------------------------------------
[det(G_1)]
Computed value: 9.000000000000002
Expected value: 9
check passed
[det(G_2)]
Computed value: 36.0
Expected value: 36
check passed
[det(G_3)]
Computed value: 323.9999999999999
Expected value: 324
check passed
[volume]
Computed value: 17.999999999999996
Expected value: 18
check passed
# ============================================================
print_header("Exercise 7-34 | Volume in Four Dimensions")
# ============================================================
# --- Step 1 ---
print_step(1, "Build the Gram matrix")
M=np.array([[1,1,-1],[0,1,1],[1,1,1],[0,1,1]])
G=M.T@M
check("G", G, [[2,2,0],[2,4,2],[0,2,4]])
# --- Step 2 ---
print_step(2, "Check the determinant and the three-dimensional volume")
check("det(G)", np.linalg.det(G), 8)
check("volume", np.sqrt(np.linalg.det(G)), 2*np.sqrt(2))============================================================
Exercise 7-34 | Volume in Four Dimensions
============================================================
▶ Step 1: Build the Gram matrix
----------------------------------------
[G]
Computed value: [[2 2 0]
[2 4 2]
[0 2 4]]
Expected value: [[2, 2, 0], [2, 4, 2], [0, 2, 4]]
check passed
▶ Step 2: Check the determinant and the three-dimensional volume
----------------------------------------
[det(G)]
Computed value: 7.999999999999998
Expected value: 8
check passed
[volume]
Computed value: 2.82842712474619
Expected value: 2.8284271247461903
check passed
Generalized Cross Products¶
✓ All exercises completed.