Week3: Finished Exercises
This commit is contained in:
parent
667490a069
commit
0b5dc4864e
2 changed files with 264 additions and 0 deletions
151
week3/ex1.py
Executable file
151
week3/ex1.py
Executable file
|
@ -0,0 +1,151 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from matplotlib import pyplot
|
||||||
|
|
||||||
|
from solvers import *
|
||||||
|
|
||||||
|
def linear_equation_system_1():
|
||||||
|
A = [[ 10, -1, 2, 0 ],
|
||||||
|
[ -1, 11, -1, 3 ],
|
||||||
|
[ 2, -1, 10, -1 ],
|
||||||
|
[ 0, 3, -1, 8 ]]
|
||||||
|
|
||||||
|
b = [ 6, 25, -11, 15 ]
|
||||||
|
|
||||||
|
return A, b
|
||||||
|
|
||||||
|
def linear_equation_system_2():
|
||||||
|
A = [[ 0.2, 0.1, 1.0, 1.0, 0.0],
|
||||||
|
[ 0.1, 4.0, -1.0, 1.0, -1.0],
|
||||||
|
[ 1.0, -1.0, 60.0, 0.0, -2.0],
|
||||||
|
[ 1.0, 1.0, 0.0, 8.0, 4.0],
|
||||||
|
[ 0.0, -1.0, -2.0, 4.0, 700.0]]
|
||||||
|
|
||||||
|
b = [ 1, 2, 3, 4, 5 ]
|
||||||
|
|
||||||
|
return A, b
|
||||||
|
|
||||||
|
|
||||||
|
def get_solver_stats(A = None, b = None, epsilons = None, solvers = None):
|
||||||
|
import timeit
|
||||||
|
|
||||||
|
if A is None:
|
||||||
|
A, b = linear_equation_system_1()
|
||||||
|
|
||||||
|
if epsilons is None:
|
||||||
|
epsilons = np.array([ 0.1, 0.01, 0.001, 0.0001 ])
|
||||||
|
|
||||||
|
if solvers is None:
|
||||||
|
solvers = [ jacobi, steepest_descent, conjugate_gradient ]
|
||||||
|
|
||||||
|
x_exact = np.linalg.solve(A, b)
|
||||||
|
|
||||||
|
N = len(solvers)
|
||||||
|
M = len(epsilons)
|
||||||
|
|
||||||
|
steps = np.zeros((N,M), dtype=np.int)
|
||||||
|
diffs = np.zeros((N,M), dtype=np.float64)
|
||||||
|
times = np.zeros((N,M), dtype=np.float64)
|
||||||
|
|
||||||
|
for i, func in enumerate(solvers):
|
||||||
|
for j, eps in enumerate(epsilons):
|
||||||
|
start = timeit.timeit()
|
||||||
|
x, k = func(A, b, eps)
|
||||||
|
|
||||||
|
diffs[i][j] = diff(x_exact, x)
|
||||||
|
steps[i][j] = k
|
||||||
|
times[i][j] = start - timeit.timeit()
|
||||||
|
|
||||||
|
return steps, diffs, times
|
||||||
|
|
||||||
|
def plot_steps(epsilons, steps, names, fmt = None, show = True):
|
||||||
|
pyplot.subplots()
|
||||||
|
|
||||||
|
pyplot.title("Performance for different Iterative Methods")
|
||||||
|
pyplot.xlabel("Accuracy $\epsilon$")
|
||||||
|
pyplot.ylabel("Steps $N$")
|
||||||
|
pyplot.yscale('log')
|
||||||
|
pyplot.xscale('log')
|
||||||
|
|
||||||
|
if fmt is None:
|
||||||
|
fmt = '-d'
|
||||||
|
|
||||||
|
for i, name in enumerate(names):
|
||||||
|
if isinstance(fmt, list):
|
||||||
|
pyplot.plot(epsilons, steps[i], fmt[i], label=name)
|
||||||
|
else:
|
||||||
|
pyplot.plot(epsilons, steps[i], fmt, label=name)
|
||||||
|
|
||||||
|
pyplot.legend()
|
||||||
|
|
||||||
|
if show:
|
||||||
|
pyplot.show()
|
||||||
|
|
||||||
|
# Main functions
|
||||||
|
def main1():
|
||||||
|
solvers = [ jacobi, steepest_descent, conjugate_gradient ]
|
||||||
|
|
||||||
|
## System 1
|
||||||
|
print("Linear Equation System 1")
|
||||||
|
print(24*"-")
|
||||||
|
A, b = linear_equation_system_1()
|
||||||
|
epsilons = np.logspace(-1, -4, 4)
|
||||||
|
steps, diffs, times = get_solver_stats(A, b, epsilons, solvers)
|
||||||
|
for i, func in enumerate(solvers):
|
||||||
|
print("Name: ", func.__qualname__)
|
||||||
|
print("Eps: ", epsilons)
|
||||||
|
print("Diffs:", diffs[i])
|
||||||
|
print("Steps:", steps[i])
|
||||||
|
print("Times:", times[i])
|
||||||
|
print()
|
||||||
|
|
||||||
|
plot_steps(epsilons, steps, map(lambda x: x.__name__, solvers))
|
||||||
|
|
||||||
|
|
||||||
|
def main2():
|
||||||
|
solvers = [ jacobi, steepest_descent, conjugate_gradient ]
|
||||||
|
# System 2
|
||||||
|
print("Linear Equation System 2")
|
||||||
|
print("------------------------")
|
||||||
|
|
||||||
|
A, b = linear_equation_system_2()
|
||||||
|
epsilons = np.logspace(-1, -8, 8)
|
||||||
|
|
||||||
|
steps, diffs, times = get_solver_stats(A, b, epsilons, solvers)
|
||||||
|
|
||||||
|
print("Condition Number:", np.linalg.cond(A))
|
||||||
|
print("""
|
||||||
|
Both Steepest Descent and Conjugate Gradient are fast for small accuracies.
|
||||||
|
However, they take many more steps than the Jacobi method when we require larger accuracies.
|
||||||
|
This can be explained by a large Condition Number.
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
# Preconditioning of System 2
|
||||||
|
print("\nConditioned Linear Equation System 2")
|
||||||
|
print( "------------------------------------")
|
||||||
|
D = np.diag(np.array(A, dtype=np.float64))
|
||||||
|
C = np.diagflat(np.sqrt(np.reciprocal(D)))
|
||||||
|
|
||||||
|
A_tilde = np.dot(np.dot(C,A), C)
|
||||||
|
b_tilde = np.dot(C,b)
|
||||||
|
|
||||||
|
cond_steps, cond_diffs, cond_times = get_solver_stats(A_tilde, b_tilde, epsilons, solvers)
|
||||||
|
|
||||||
|
print("Condition Number:", np.linalg.cond(A_tilde))
|
||||||
|
print("""
|
||||||
|
By Preconditioning the Gradient Methods are much faster.
|
||||||
|
This is especially visible for small accuracies in the conjugate gradient method.
|
||||||
|
""")
|
||||||
|
|
||||||
|
all_steps = np.concatenate((steps, cond_steps), axis=0)
|
||||||
|
fmts = ['b-d', 'g-*', 'y-^', 'b--d', 'g--*', 'y--^']
|
||||||
|
names = [x.__name__ for x in solvers] + ["Conditioned " + x.__name__ for x in solvers]
|
||||||
|
plot_steps(epsilons, all_steps, names, fmts)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main1()
|
||||||
|
main2()
|
||||||
|
|
113
week3/solvers.py
Normal file
113
week3/solvers.py
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def diff(a, b):
|
||||||
|
return np.amax(np.abs(a-b))
|
||||||
|
|
||||||
|
def jacobi(A, b, eps):
|
||||||
|
A = np.array(A, dtype=np.float64)
|
||||||
|
b = np.array(b, dtype=np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
D = np.diag(A)
|
||||||
|
L = -np.tril(A, -1)
|
||||||
|
U = -np.triu(A, 1)
|
||||||
|
|
||||||
|
D_inv = np.diagflat(np.reciprocal(D))
|
||||||
|
|
||||||
|
# initially x_f = x_(i-1)
|
||||||
|
# this changes when in the loop
|
||||||
|
x_i = np.dot(D_inv, b)
|
||||||
|
x_f = np.zeros(len(A))
|
||||||
|
k = 1
|
||||||
|
|
||||||
|
while diff(x_i, x_f) >= eps:
|
||||||
|
k += 1
|
||||||
|
|
||||||
|
# Save the previous solution vector as x_f
|
||||||
|
x_f = x_i
|
||||||
|
|
||||||
|
# Create new solution vector
|
||||||
|
x_i = np.dot(np.dot(D_inv, ( L + U )), x_f ) + np.dot(D_inv, b)
|
||||||
|
|
||||||
|
return x_i, k
|
||||||
|
|
||||||
|
def steepest_descent(A, b, eps):
|
||||||
|
A = np.array(A, dtype=np.float64)
|
||||||
|
b = np.array(b, dtype=np.float64)
|
||||||
|
|
||||||
|
# initially x_f = x_(i-1)
|
||||||
|
# this changes when in the loop
|
||||||
|
x_f = np.zeros(len(A), dtype=np.float64)
|
||||||
|
k = 1
|
||||||
|
|
||||||
|
v_f = b
|
||||||
|
t = np.dot(v_f, v_f) / np.dot(v_f, np.dot(A, v_f))
|
||||||
|
x_i = x_f + t*v_f
|
||||||
|
|
||||||
|
while diff(x_i, x_f) >= eps:
|
||||||
|
k += 1
|
||||||
|
|
||||||
|
# Pre calculate v_f and t
|
||||||
|
v_f = b - np.dot(A, x_i)
|
||||||
|
|
||||||
|
t = np.dot(v_f, v_f) / np.dot(v_f, np.dot(A, v_f))
|
||||||
|
|
||||||
|
# Save the previous solution vector as x_f
|
||||||
|
x_f = x_i
|
||||||
|
|
||||||
|
# Create new solution vector
|
||||||
|
x_i = x_f + t * v_f
|
||||||
|
|
||||||
|
return x_i, k
|
||||||
|
|
||||||
|
def conjugate_gradient(A, b, eps):
|
||||||
|
A = np.array(A, dtype=np.float64)
|
||||||
|
b = np.array(b, dtype=np.float64)
|
||||||
|
|
||||||
|
# initially x_f = x_(i-1)
|
||||||
|
# this changes when in the loop
|
||||||
|
x_f = np.zeros(len(A), dtype=np.float64)
|
||||||
|
r_f = b - np.dot(A, x_f)
|
||||||
|
v_f = r_f
|
||||||
|
|
||||||
|
k = 1
|
||||||
|
|
||||||
|
# Calculate first iteration
|
||||||
|
t = np.dot(r_f, r_f) / np.dot(v_f, np.dot(A, v_f))
|
||||||
|
x_i = x_f + t*v_f
|
||||||
|
|
||||||
|
r_i = r_f - t * np.dot(A, v_f)
|
||||||
|
s = np.dot(r_i, r_i) / np.dot(r_f, r_f)
|
||||||
|
|
||||||
|
v_i = r_i - s*v_f
|
||||||
|
|
||||||
|
# Set r and v vectors for next loop
|
||||||
|
r_f = r_i
|
||||||
|
v_f = v_i
|
||||||
|
|
||||||
|
while diff(x_i, x_f) >= eps:
|
||||||
|
k += 1
|
||||||
|
|
||||||
|
t = np.dot(r_f, r_f) / np.dot(v_f, np.dot(A, v_f))
|
||||||
|
# Save the previous solution vector as x_f
|
||||||
|
x_f = x_i
|
||||||
|
|
||||||
|
# Create new solution vector
|
||||||
|
x_i = x_f + t*v_f
|
||||||
|
|
||||||
|
# Calculate r and v vectors
|
||||||
|
|
||||||
|
r_i = r_f - t * np.dot(A, v_f)
|
||||||
|
s = np.dot(r_i, r_i) / np.dot(r_f, r_f)
|
||||||
|
v_i = r_i - s*v_f
|
||||||
|
|
||||||
|
# Save r and v vectors for next loop
|
||||||
|
r_f = r_i
|
||||||
|
v_f = v_i
|
||||||
|
|
||||||
|
|
||||||
|
return x_i, k
|
||||||
|
|
||||||
|
|
Reference in a new issue