Skip to content
Snippets Groups Projects
Commit f3180b6f authored by Ulrich Kerzel's avatar Ulrich Kerzel
Browse files

add RootFinding Exercise

parent febf9791
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id: tags:
# Newton's Method for Root Finding
In many applications, we need to find the root of a function, i.e. the point where the function crosses the $x$-axis: $f(x_r) = 0$
A variety of methods exist for this problem, in this examle we want to use Newton's method. The general idea is the following:
We start at some point, our initial guess $x_0$. Then, we calculate the value of the function at point $x_n$ (starting from the initial guess) $f(x_n)$, as well as the derivative $f'(x_n)$, the derivative is the slope of the tangent line to the function $f(x)$ at this point $x_n$:
$$ y = f(x_n) + f'(x_n)(x-x_n)$$
We now want to find the point where the tangent line intersects with the $x$-axis, i.e. we set $y=0$, leading to:
$$f'(x_n)(x-x_n) = -f(x_n)$$
Assuming $f'(x_n) \neq 0$, we can divide both sides by $f'(x_n)$, solve for $x$ and then iterate.
More concisely, the overall approach is:
1. Choose an initial guess $ x_0 $.
2. Iterate using the formula:
$$x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)}$$
where $ n = 0, 1, 2, \ldots $
The process continues until the difference between successive approximations is less than a predetermined tolerance level or until a maximum number of iterations is reached.
Note that if our initial guess $x_0$ is not suitable, the method may not converge.
One of the underrated features of modern deep learning frameworks is the automatic differentiation. In "conventional" deep learning, we use this as a tool behind the scenes to train a neural network and do not really interact with this. However, this method is useful in a range of applications, such as physics-informed neural networks or, indeed, this example of finding the root of a function efficiently.
While we perceive deep-learning frameworks such as [PyTorch](https://pytorch.org/) or [TensorFlow](https://www.tensorflow.org/) primarily as libraries for deep learning (and we do indeed use them for this purpose), they are, essentially, heavily optimised libraries for matrix operations and numerical handling of equations that can, in addition, levarage the computation power of GPUs.
Note that while we would ideally work with functions where we can caluclate the derivative analytically, this is not necessary.
We will use the example of a conic steel vessel discussed in the lecture "Numerical Models in Processing" by [PD Dr. W. Lenz](https://www.iob.rwth-aachen.de/habilitation-von-dr-wolfgang-lenz/). In this example, a numerical solution is derived which we will use as starting point.
First, we will start with a motivating generic example to get familiar with the method and general code structure before then turning to the concrete example.
%% Cell type:code id: tags:
``` python
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import torch
from datetime import datetime
```
%% Cell type:markdown id: tags:
## General Example
We start with a generic example using the function
$f(x) = \cos(x) -x$.
First, we plot the function.
Note that we directly use [torch.tensor](https://pytorch.org/docs/stable/tensors.html) as we will later on use the automatic differentiation to implement Newton's method for finding roots.
%% Cell type:code id: tags:
``` python
def f(x):
return # YOUR CODE HERE
```
%% Cell type:markdown id: tags:
Let's first make a plot of this function.
Assuming that we already know that the root of the function is at $x=0.755$, we add a vertical line to indicate this root.
%% Cell type:code id: tags:
``` python
x_space = np.linspace(-10, 10, 500)
y_space = f(torch.tensor(x_space))
sns.lineplot(x=x_space, y=y_space, label='f(x)')
plt.axhline(y=0, color='black', linestyle='--', label='y=0')
# we use the value we have found below for illustration.
plt.axvline(x=0.755, color='red', linestyle='--', label='x=0.755')
plt.title('Plot of f(x) = cos(x) - x')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.legend()
plt.show()
```
%% Output
%% Cell type:code id: tags:
``` python
x = torch.tensor([0.1], requires_grad=True)
tolerance = 1e-6
max_iterations = 100
t_start = datetime.now()
for i in range(max_iterations):
y = f(x)
# YOUR CODE HERE
with torch.no_grad():
# Replacing in-place copy with out-of-place operation
x_new = # YOUR CODE HERE
if torch.abs(x_new - x).item() < tolerance: #add .item() to get a python number
t_stop = datetime.now()
print(f'Converged after {i+1} iterations.')
print(f'Time taken: {t_stop - t_start}')
break
x = x_new.clone().detach().requires_grad_(True) # Create a new tensor with gradient enabled
print(f'Root approximated at x = {x.item()}')
print(f'Function value at root approximation f(x) = {f(x).item()}')
```
%% Output
Converged after 5 iterations.
Time taken: 0:00:00.001611
Root approximated at x = 0.7390851378440857
Function value at root approximation f(x) = 0.0
%% Cell type:markdown id: tags:
# With Optimiser
In the above code, we have implemented Newton's method directly.
However, modern deep learning packages include poweful optimisers that perform the calculation of the gradient, as well as the subsequent updates of the parameters.
As an exercise, re-write the code to use the [Adam](https://pytorch.org/docs/stable/generated/torch.optim.Adam.html) optimiser.
*Hint*: You need to think of a suitable loss function;
*Note*: Depending on the problem at hand, using an optimiser and loss function may (or may not) improve convergence. You may find that the standard approach works sufficiently well for your problem.
%% Cell type:markdown id: tags:
**Exercise**
Modify the above code to use the Adam optimiser
%% Cell type:code id: tags:
``` python
##
## Your code goes here
##
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment