add homework11

This commit is contained in:
2025-12-06 17:52:21 -05:00
parent 33a3157bc8
commit 621fb1feb2

159
homework11/Homework11_Q5.py Normal file
View File

@@ -0,0 +1,159 @@
import numpy as np
import matplotlib.pyplot as plt
# --- 1. Define Function, Gradient, and Hessian ---
# Objective function: Rosenbrock function
def objective_function(x):
x1, x2 = x[0], x[1]
return 100 * (x2 - x1**2)**2 + (1 - x1)**2
# Gradient vector calculation
def gradient(x):
x1, x2 = x[0], x[1]
df_dx1 = -400 * x1 * (x2 - x1**2) - 2 * (1 - x1)
df_dx2 = 200 * (x2 - x1**2)
return np.array([df_dx1, df_dx2])
# Hessian matrix calculation
def hessian(x):
x1, x2 = x[0], x[1]
d2f_dx1dx1 = -400 * (x2 - 3 * x1**2) + 2
d2f_dx1dx2 = -400 * x1
d2f_dx2dx1 = -400 * x1
d2f_dx2dx2 = 200
return np.array([[d2f_dx1dx1, d2f_dx1dx2], [d2f_dx2dx1, d2f_dx2dx2]])
# --- 2. Backtracking Line Search ---
def backtracking_line_search(x, p, grad, alpha_bar=1.0, rho=0.5, c1=1e-3):
"""
Finds a step size alpha that satisfies the 1st Wolfe condition (Armijo condition).
Condition: f(x + alpha*p) <= f(x) + c1 * alpha * grad^T * p
"""
alpha = alpha_bar
f_val = objective_function(x)
grad_dot_p = np.dot(grad, p)
# Loop until condition is met
while objective_function(x + alpha * p) > f_val + c1 * alpha * grad_dot_p:
alpha *= rho
# Safety break to prevent infinite loops if alpha becomes too small
if alpha < 1e-16:
break
return alpha
# --- 3. Optimization Algorithms ---
def run_optimization(method, x0, tol_ratio=1e-12, max_iter=20000):
"""
Runs the specified optimization method.
Returns: list of alphas used, total iterations
"""
x = x0.copy()
grad_0_norm = np.linalg.norm(gradient(x0))
alphas = []
print(f"[{method}] Starting from {x0}...")
for i in range(max_iter):
grad = gradient(x)
grad_norm = np.linalg.norm(grad)
# Convergence Check: ||grad f(xk)|| < 10^-12 * ||grad f(x0)||
if grad_norm < tol_ratio * grad_0_norm:
print(f" -> Converged at iteration {i}. Final Loss: {objective_function(x):.6e}")
return alphas, i
# Determine Search Direction
if method == "Steepest_Descent":
direction = -grad
elif method == "Newton":
hess = hessian(x)
try:
# Solve H * d = -g for d
direction = np.linalg.solve(hess, -grad)
except np.linalg.LinAlgError:
# Fallback if Hessian is singular
direction = -grad
# Perform Backtracking Line Search
alpha = backtracking_line_search(x, direction, grad, alpha_bar=1.0, rho=0.5, c1=1e-3)
# Update Position
x = x + alpha * direction
alphas.append(alpha)
print(f" -> Did not converge within {max_iter} iterations.")
print(f" -> Final Gradient Norm: {grad_norm:.6e}")
return alphas, max_iter
# --- 4. Main Execution and Plotting ---
# Configuration
initial_points = [np.array([1.2, 1.2]), np.array([-1.2, 1.0])]
methods = ["Steepest_Descent", "Newton"]
# Dictionary to hold simulation results
results = {}
# Run simulations
for x0 in initial_points:
for method in methods:
# Construct a unique key for storage
key = f"{method}_x0_{x0}"
alphas, num_iters = run_optimization(method, x0)
results[key] = {'alphas': alphas, 'iters': num_iters, 'x0': x0}
# Plot configuration
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
plt.subplots_adjust(hspace=0.4, wspace=0.3)
# Mapping results to subplots
plot_mapping = {
("Steepest_Descent", str(initial_points[0])): (0, 0),
("Newton", str(initial_points[0])): (0, 1),
("Steepest_Descent", str(initial_points[1])): (1, 0),
("Newton", str(initial_points[1])): (1, 1),
}
for key, data in results.items():
method_name = key.split("_x0_")[0]
x0_str = str(data['x0'])
ax_idx = plot_mapping[(method_name, x0_str)]
ax = axes[ax_idx]
y_data = data['alphas']
x_data = range(len(y_data))
# --- PLOTTING LOGIC MODIFICATION ---
# For Steepest Descent, show only first {limit} steps
if method_name == "Steepest_Descent":
limit = 30
if len(y_data) > limit:
y_data = y_data[:limit]
x_data = range(limit)
title_suffix = f" (First {limit} Steps Shown)"
else:
title_suffix = ""
else:
title_suffix = ""
ax.plot(x_data, y_data, marker='o', linestyle='-', markersize=4, linewidth=1.5)
# Titles and Labels
ax.set_title(f"{method_name}\nStart: {data['x0']}\nTotal Iters: {data['iters']}{title_suffix}")
ax.set_xlabel("Iteration")
ax.set_ylabel("Step Size (Alpha)")
ax.grid(True, which='both', linestyle='--', alpha=0.7)
# Add annotation for total iterations
ax.text(0.95, 0.95, f"Total Iters: {data['iters']}",
transform=ax.transAxes,
verticalalignment='top', horizontalalignment='right',
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
# Save and Show
#plt.suptitle("Step Size vs Iteration (Rosenbrock Function)", fontsize=16)
plt.savefig("optimization_results.png")
#plt.show()