Chapter 9: Fundamentals of Linear Transformations
Chapter 9: Fundamentals of Linear Transformations
- Understand the definition and properties of linear transformations
- Master matrix representation of linear transformations
- Understand the geometric meaning of linear transformations
- Master the kernel and image of linear transformations
- Understand the correspondence between linear transformations and matrices
Definition of Linear Transformations
Function Perspective
A linear transformation is a special type of function that maps vectors from one vector space to another (or the same) vector space.
Let be a mapping from vector space to vector space . If for all and scalar , it satisfies:
- Additivity:
- Homogeneity:
Then is called a linear transformation (or linear mapping).
Equivalent Condition
A linear transformation can be expressed as a single condition:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def verify_linearity():
"""Verify linearity of transformations"""
# Define a linear transformation matrix (rotation + scaling)
A = np.array([[2, -1],
[1, 2]])
# Test vectors
v1 = np.array([1, 2])
v2 = np.array([3, 1])
c1, c2 = 2, -1
# Linear combination
linear_combination = c1 * v1 + c2 * v2
# Method 1: First do linear combination, then transform
T_combination = A @ linear_combination
# Method 2: First transform separately, then do linear combination
combination_T = c1 * (A @ v1) + c2 * (A @ v2)
print("Verifying linear transformation property:")
print(f"v1 = {v1}")
print(f"v2 = {v2}")
print(f"c1 = {c1}, c2 = {c2}")
print()
print(f"c1*v1 + c2*v2 = {linear_combination}")
print(f"T(c1*v1 + c2*v2) = {T_combination}")
print(f"c1*T(v1) + c2*T(v2) = {combination_T}")
print()
print(f"Linearity verified: {np.allclose(T_combination, combination_T)}")
return A, v1, v2
# Execute verification
A, v1, v2 = verify_linearity()
Common Linear Transformations
Basic Linear Transformations in 2D Plane
def basic_2d_transformations():
"""Demonstrate basic linear transformations in 2D plane"""
# Original vectors
original_vectors = np.array([[1, 0, 2, 1],
[0, 1, 1, 2]])
# Define various transformation matrices
transformations = {
'Identity': np.array([[1, 0], [0, 1]]),
'Horizontal Reflection': np.array([[1, 0], [0, -1]]),
'Vertical Reflection': np.array([[-1, 0], [0, 1]]),
'Rotation 90°': np.array([[0, -1], [1, 0]]),
'Rotation 45°': np.array([[np.cos(np.pi/4), -np.sin(np.pi/4)],
[np.sin(np.pi/4), np.cos(np.pi/4)]]),
'Scaling': np.array([[2, 0], [0, 0.5]]),
'Shear': np.array([[1, 0.5], [0, 1]]),
'Projection to x-axis': np.array([[1, 0], [0, 0]])
}
fig, axes = plt.subplots(2, 4, figsize=(16, 8))
axes = axes.flatten()
for i, (name, matrix) in enumerate(transformations.items()):
ax = axes[i]
# Transformed vectors
transformed = matrix @ original_vectors
# Draw original vectors
for j in range(original_vectors.shape[1]):
ax.arrow(0, 0, original_vectors[0, j], original_vectors[1, j],
head_width=0.1, head_length=0.1, fc='blue', ec='blue',
alpha=0.5, linestyle='--')
# Draw transformed vectors
for j in range(transformed.shape[1]):
ax.arrow(0, 0, transformed[0, j], transformed[1, j],
head_width=0.1, head_length=0.1, fc='red', ec='red')
ax.set_xlim(-3, 3)
ax.set_ylim(-3, 3)
ax.grid(True, alpha=0.3)
ax.set_aspect('equal')
ax.set_title(name)
# Display transformation matrix
ax.text(0.02, 0.98, f'$\\begin{{bmatrix}} {matrix[0,0]:.2f} & {matrix[0,1]:.2f} \\\\ {matrix[1,0]:.2f} & {matrix[1,1]:.2f} \\end{{bmatrix}}$',
transform=ax.transAxes, fontsize=8, verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
plt.tight_layout()
plt.show()
basic_2d_transformations()
Linear Transformations in 3D Space
def visualize_3d_transformation():
"""Visualize 3D linear transformations"""
# Create cube vertices
cube_vertices = np.array([
[0, 1, 1, 0, 0, 1, 1, 0], # x coordinates
[0, 0, 1, 1, 0, 0, 1, 1], # y coordinates
[0, 0, 0, 0, 1, 1, 1, 1] # z coordinates
])
# Define a 3D transformation matrix (rotation + scaling)
theta = np.pi/6 # 30 degrees
transform_matrix = np.array([
[2*np.cos(theta), -np.sin(theta), 0],
[np.sin(theta), np.cos(theta), 0.5],
[0, 0, 1.5]
])
# Apply transformation
transformed_vertices = transform_matrix @ cube_vertices
# Visualization
fig = plt.figure(figsize=(15, 6))
# Original cube
ax1 = fig.add_subplot(121, projection='3d')
ax1.scatter(cube_vertices[0], cube_vertices[1], cube_vertices[2],
c='blue', s=100, alpha=0.7)
# Draw cube edges
edges = [
[0, 1], [1, 2], [2, 3], [3, 0], # Bottom face
[4, 5], [5, 6], [6, 7], [7, 4], # Top face
[0, 4], [1, 5], [2, 6], [3, 7] # Vertical edges
]
for edge in edges:
points = cube_vertices[:, edge]
ax1.plot3D(points[0], points[1], points[2], 'b-', alpha=0.6)
ax1.set_title('Original Cube')
ax1.set_xlabel('X')
ax1.set_ylabel('Y')
ax1.set_zlabel('Z')
# Transformed cube
ax2 = fig.add_subplot(122, projection='3d')
ax2.scatter(transformed_vertices[0], transformed_vertices[1], transformed_vertices[2],
c='red', s=100, alpha=0.7)
for edge in edges:
points = transformed_vertices[:, edge]
ax2.plot3D(points[0], points[1], points[2], 'r-', alpha=0.6)
ax2.set_title('Transformed Shape')
ax2.set_xlabel('X')
ax2.set_ylabel('Y')
ax2.set_zlabel('Z')
plt.tight_layout()
plt.show()
print("Transformation matrix:")
print(transform_matrix)
print(f"\nDeterminant: {np.linalg.det(transform_matrix):.3f}")
print("(Determinant represents the volume scaling factor)")
visualize_3d_transformation()
Matrix Representation of Linear Transformations
Matrix Representation in Standard Basis
Given a linear transformation , its matrix representation is formed by the results of the transformation acting on standard basis vectors:
where is the -th standard basis vector.
def matrix_representation_demo():
"""Demonstrate matrix representation of linear transformations"""
# Define a linear transformation: rotation by θ angle
theta = np.pi/3 # 60 degrees
def rotation_transform(vector, angle):
"""Rotation transformation function"""
cos_theta = np.cos(angle)
sin_theta = np.sin(angle)
rotation_matrix = np.array([[cos_theta, -sin_theta],
[sin_theta, cos_theta]])
return rotation_matrix @ vector
# Standard basis vectors
e1 = np.array([1, 0])
e2 = np.array([0, 1])
# Calculate transformed basis vectors
T_e1 = rotation_transform(e1, theta)
T_e2 = rotation_transform(e2, theta)
# Construct transformation matrix
A = np.column_stack([T_e1, T_e2])
print("Matrix representation of rotation transformation:")
print("Standard basis vectors:")
print(f"e1 = {e1}")
print(f"e2 = {e2}")
print()
print("Transformed basis vectors:")
print(f"T(e1) = {T_e1}")
print(f"T(e2) = {T_e2}")
print()
print("Transformation matrix:")
print(A)
# Verification: transform an arbitrary vector
test_vector = np.array([3, 2])
direct_transform = rotation_transform(test_vector, theta)
matrix_transform = A @ test_vector
print(f"\nVerifying vector {test_vector}:")
print(f"Direct transformation result: {direct_transform}")
print(f"Matrix transformation result: {matrix_transform}")
print(f"Results match: {np.allclose(direct_transform, matrix_transform)}")
# Visualization
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# Original basis vectors and test vector
ax1.arrow(0, 0, e1[0], e1[1], head_width=0.1, head_length=0.1,
fc='blue', ec='blue', linewidth=2, label='e1')
ax1.arrow(0, 0, e2[0], e2[1], head_width=0.1, head_length=0.1,
fc='green', ec='green', linewidth=2, label='e2')
ax1.arrow(0, 0, test_vector[0], test_vector[1], head_width=0.1, head_length=0.1,
fc='red', ec='red', linewidth=2, label='test vector')
ax1.set_xlim(-0.5, 3.5)
ax1.set_ylim(-0.5, 2.5)
ax1.grid(True, alpha=0.3)
ax1.set_aspect('equal')
ax1.legend()
ax1.set_title('Original Vectors')
# Transformed vectors
ax2.arrow(0, 0, T_e1[0], T_e1[1], head_width=0.1, head_length=0.1,
fc='blue', ec='blue', linewidth=2, label='T(e1)')
ax2.arrow(0, 0, T_e2[0], T_e2[1], head_width=0.1, head_length=0.1,
fc='green', ec='green', linewidth=2, label='T(e2)')
ax2.arrow(0, 0, matrix_transform[0], matrix_transform[1], head_width=0.1, head_length=0.1,
fc='red', ec='red', linewidth=2, label='T(test vector)')
ax2.set_xlim(-2, 2)
ax2.set_ylim(-1, 3)
ax2.grid(True, alpha=0.3)
ax2.set_aspect('equal')
ax2.legend()
ax2.set_title(f'After Rotation {theta*180/np.pi:.0f}°')
plt.tight_layout()
plt.show()
matrix_representation_demo()
Kernel and Image
Definition of Kernel
The kernel (or null space) of a linear transformation is defined as:
Definition of Image
The image (or range) of a linear transformation is defined as:
def kernel_image_analysis():
"""Analyze kernel and image of linear transformations"""
# Define a transformation matrix (projection transformation)
A = np.array([[1, 2, 1],
[2, 4, 2],
[1, 2, 1]], dtype=float)
print("Transformation matrix A:")
print(A)
print()
# Calculate kernel (null space)
U, s, Vt = np.linalg.svd(A)
null_space = Vt[s < 1e-10] # Right singular vectors corresponding to near-zero singular values
print("Basis of kernel (null space):")
if null_space.size > 0:
for i, vec in enumerate(null_space):
print(f"Kernel vector {i+1}: {vec}")
# Verify
result = A @ vec
print(f"A × kernel vector = {result} (should be close to 0)")
else:
print("Kernel contains only the zero vector")
print()
# Calculate image (column space)
rank = np.linalg.matrix_rank(A)
print(f"Rank of matrix: {rank}")
print("Dimension of image space (column space) equals the rank of matrix")
# Find basis of column space
Q, R, P = scipy.linalg.qr(A, pivoting=True)
column_basis = A[:, P[:rank]]
print("\nBasis of image space:")
for i in range(rank):
print(f"Basis vector {i+1}: {column_basis[:, i]}")
print()
# Verify rank-nullity theorem
n = A.shape[1] # Dimension of domain
dim_kernel = null_space.shape[0] if null_space.size > 0 else 0
dim_image = rank
print("Rank-nullity theorem verification:")
print(f"Dimension of domain: {n}")
print(f"Dimension of kernel: {dim_kernel}")
print(f"Dimension of image: {dim_image}")
print(f"dim(V) = dim(ker(T)) + dim(Im(T)): {n} = {dim_kernel} + {dim_image}")
kernel_image_analysis()
Composition of Linear Transformations
Matrix Representation of Composite Transformations
If and are two linear transformations, then the matrix representation of the composite transformation is:
def transformation_composition():
"""Demonstrate composition of linear transformations"""
# Define two transformations
# T1: Rotation by 45 degrees
theta1 = np.pi/4
T1 = np.array([[np.cos(theta1), -np.sin(theta1)],
[np.sin(theta1), np.cos(theta1)]])
# T2: Scale by 2 in x direction
T2 = np.array([[2, 0],
[0, 1]])
# Composite transformation T2 ∘ T1
T_composite = T2 @ T1
print("Transformation T1 (rotation 45°):")
print(T1)
print("\nTransformation T2 (scale x by 2):")
print(T2)
print("\nComposite transformation T2 ∘ T1:")
print(T_composite)
# Test vectors
test_vectors = np.array([[1, 0, 2, 1],
[0, 1, 1, 2]])
# Apply transformations step by step
step1 = T1 @ test_vectors # Apply T1 first
step2 = T2 @ step1 # Then apply T2
# Apply composite transformation directly
direct = T_composite @ test_vectors
print(f"\nVerifying composite transformation:")
print(f"Step-by-step result:\n{step2}")
print(f"Direct result:\n{direct}")
print(f"Results match: {np.allclose(step2, direct)}")
# Visualization
fig, axes = plt.subplots(1, 4, figsize=(16, 4))
stages = [
(test_vectors, "Original Vectors", 'blue'),
(step1, "After Rotation 45°", 'green'),
(step2, "After Scaling", 'red'),
(direct, "Direct Composite", 'purple')
]
for i, (vectors, title, color) in enumerate(stages):
ax = axes[i]
for j in range(vectors.shape[1]):
ax.arrow(0, 0, vectors[0, j], vectors[1, j],
head_width=0.1, head_length=0.1, fc=color, ec=color)
ax.set_xlim(-3, 3)
ax.set_ylim(-3, 3)
ax.grid(True, alpha=0.3)
ax.set_aspect('equal')
ax.set_title(title)
plt.tight_layout()
plt.show()
transformation_composition()
Invertible Linear Transformations
Conditions for Invertibility
A linear transformation is invertible if and only if:
- is bijective (one-to-one correspondence)
def invertible_transformations():
"""Analyze invertible linear transformations"""
# Invertible transformation example
A_invertible = np.array([[2, 1],
[1, 1]], dtype=float)
# Non-invertible transformation example (singular matrix)
A_singular = np.array([[2, 4],
[1, 2]], dtype=float)
transformations = {
"Invertible transformation": A_invertible,
"Non-invertible transformation": A_singular
}
for name, A in transformations.items():
print(f"\n{name}:")
print(f"Matrix:\n{A}")
det_A = np.linalg.det(A)
print(f"Determinant: {det_A:.4f}")
if abs(det_A) > 1e-10:
print("Matrix is invertible")
A_inv = np.linalg.inv(A)
print(f"Inverse matrix:\n{A_inv}")
# Verify AA^(-1) = I
identity_check = A @ A_inv
print(f"AA^(-1) =\n{identity_check}")
print(f"Is identity matrix: {np.allclose(identity_check, np.eye(2))}")
else:
print("Matrix is non-invertible (singular)")
# Analyze null space
U, s, Vt = np.linalg.svd(A)
null_space = Vt[s < 1e-10]
print(f"Dimension of null space: {null_space.shape[0]}")
if null_space.shape[0] > 0:
print(f"Basis of null space: {null_space[0]}")
# Visualize unit circle transformation
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
# Create unit circle
theta = np.linspace(0, 2*np.pi, 100)
unit_circle = np.array([np.cos(theta), np.sin(theta)])
# Original unit circle
axes[0].plot(unit_circle[0], unit_circle[1], 'b-', linewidth=2)
axes[0].set_xlim(-3, 3)
axes[0].set_ylim(-3, 3)
axes[0].grid(True, alpha=0.3)
axes[0].set_aspect('equal')
axes[0].set_title('Original Unit Circle')
# Invertible transformation
transformed_invertible = A_invertible @ unit_circle
axes[1].plot(transformed_invertible[0], transformed_invertible[1], 'g-', linewidth=2)
axes[1].set_xlim(-3, 3)
axes[1].set_ylim(-3, 3)
axes[1].grid(True, alpha=0.3)
axes[1].set_aspect('equal')
axes[1].set_title(f'Invertible Transformation\n(det = {det_A:.2f})')
# Non-invertible transformation
transformed_singular = A_singular @ unit_circle
axes[2].plot(transformed_singular[0], transformed_singular[1], 'r-', linewidth=2)
axes[2].set_xlim(-3, 3)
axes[2].set_ylim(-3, 3)
axes[2].grid(True, alpha=0.3)
axes[2].set_aspect('equal')
axes[2].set_title(f'Non-invertible Transformation\n(det = 0, collapsed to line)')
plt.tight_layout()
plt.show()
invertible_transformations()
Linear Transformations in Different Bases
Coordinate Transformation
When changing basis, the matrix representation of a linear transformation also changes. If is the transformation matrix from basis to the standard basis, then:
def change_of_basis_demo():
"""Demonstrate the effect of change of basis on matrix representation of linear transformations"""
# Define linear transformation (in standard basis)
T_standard = np.array([[3, 1],
[1, 2]], dtype=float)
# Define new basis
v1 = np.array([1, 1]) # First vector of new basis
v2 = np.array([1, -1]) # Second vector of new basis
P = np.column_stack([v1, v2]) # Transformation matrix
print("Transformation matrix in standard basis:")
print(T_standard)
print()
print("New basis vectors:")
print(f"v1 = {v1}")
print(f"v2 = {v2}")
print(f"Transformation matrix P = {P}")
print()
# Calculate transformation matrix in new basis
P_inv = np.linalg.inv(P)
T_new_basis = P_inv @ T_standard @ P
print("Transformation matrix in new basis:")
print(f"T_new = P^(-1) × T × P =")
print(T_new_basis)
print()
# Verification: apply transformation to the same vector
test_vector_standard = np.array([2, 3]) # Coordinates in standard basis
# Transformation in standard basis
result_standard = T_standard @ test_vector_standard
# Convert to coordinates in new basis
test_vector_new_basis = P_inv @ test_vector_standard
result_new_basis = T_new_basis @ test_vector_new_basis
# Convert back to standard basis
result_converted_back = P @ result_new_basis
print("Verifying transformation results:")
print(f"Original vector (standard basis): {test_vector_standard}")
print(f"Original vector (new basis): {test_vector_new_basis}")
print(f"Result in standard basis: {result_standard}")
print(f"Result in new basis converted back to standard: {result_converted_back}")
print(f"Results match: {np.allclose(result_standard, result_converted_back)}")
# Visualization
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# Visualization in standard basis
ax1.arrow(0, 0, v1[0], v1[1], head_width=0.1, head_length=0.1,
fc='red', ec='red', linewidth=2, label='New basis v1')
ax1.arrow(0, 0, v2[0], v2[1], head_width=0.1, head_length=0.1,
fc='blue', ec='blue', linewidth=2, label='New basis v2')
ax1.arrow(0, 0, test_vector_standard[0], test_vector_standard[1],
head_width=0.1, head_length=0.1, fc='green', ec='green',
linewidth=2, label='Test vector')
ax1.arrow(0, 0, result_standard[0], result_standard[1],
head_width=0.1, head_length=0.1, fc='purple', ec='purple',
linewidth=2, label='Transformation result')
ax1.set_xlim(-1, 8)
ax1.set_ylim(-2, 6)
ax1.grid(True, alpha=0.3)
ax1.legend()
ax1.set_title('Transformation in Standard Basis')
# Coordinate system in new basis
ax2.arrow(0, 0, 1, 0, head_width=0.1, head_length=0.1,
fc='red', ec='red', linewidth=2, label='New basis axis 1')
ax2.arrow(0, 0, 0, 1, head_width=0.1, head_length=0.1,
fc='blue', ec='blue', linewidth=2, label='New basis axis 2')
ax2.arrow(0, 0, test_vector_new_basis[0], test_vector_new_basis[1],
head_width=0.1, head_length=0.1, fc='green', ec='green',
linewidth=2, label='Test vector (new basis coords)')
ax2.arrow(0, 0, result_new_basis[0], result_new_basis[1],
head_width=0.1, head_length=0.1, fc='purple', ec='purple',
linewidth=2, label='Result (new basis coords)')
ax2.set_xlim(-2, 3)
ax2.set_ylim(-2, 3)
ax2.grid(True, alpha=0.3)
ax2.legend()
ax2.set_title('Transformation in New Basis')
plt.tight_layout()
plt.show()
change_of_basis_demo()
Classification of Linear Transformations
Classification of Geometric Transformations
Summary Table of Transformation Properties
| Transformation Type | Matrix Characteristics | Geometric Properties | Determinant |
|---|---|---|---|
| Rotation | Orthogonal matrix, det=1 | Preserves distance and angle | 1 |
| Reflection | Orthogonal matrix, det=-1 | Preserves distance, changes orientation | -1 |
| Scaling | Diagonal matrix | Changes size, preserves direction | Product of scale factors |
| Shear | Lower/upper triangular | Preserves area, changes shape | 1 |
| Projection | Singular matrix | Dimension reduction, det=0 | 0 |
Chapter Summary
Core Concepts
def chapter_summary():
"""Summary of core concepts in this chapter"""
concepts = {
"Linear transformation definition": "T(au + bv) = aT(u) + bT(v)",
"Matrix representation": "A = [T(e1) T(e2) ... T(en)]",
"Kernel": "ker(T) = {v : T(v) = 0}",
"Image": "Im(T) = {T(v) : v ∈ V}",
"Rank-nullity theorem": "dim(V) = dim(ker(T)) + dim(Im(T))",
"Invertibility": "T invertible ⟺ det(A) ≠ 0 ⟺ ker(T) = {0}"
}
print("Core concepts of linear transformations:")
print("=" * 50)
for concept, definition in concepts.items():
print(f"{concept}: {definition}")
print("-" * 30)
# Practical application examples
print("\nPractical application domains:")
applications = [
"Computer graphics: 3D rotation, scaling, projection",
"Image processing: filtering, transformation, effects",
"Machine learning: principal component analysis, dimensionality reduction",
"Physics: coordinate transformations, symmetry",
"Engineering: vibration analysis, signal processing"
]
for i, app in enumerate(applications, 1):
print(f"{i}. {app}")
chapter_summary()
Linear transformations are one of the core concepts in linear algebra, perfectly combining abstract matrix operations with geometric intuition. Understanding linear transformations not only helps master subsequent topics such as eigenvalues and diagonalization, but also provides powerful mathematical tools for practical applications. Through this chapter, we have established an important way of thinking about linear algebra from a functional perspective.