Skip to content
Snippets Groups Projects
Commit 639bc6cf authored by Nicolas Aspert's avatar Nicolas Aspert
Browse files

update solution week 5

parent 1006bd30
No related branches found
No related tags found
No related merge requests found
%% Cell type:code id:044b8034 tags:
%% Cell type:code id:5410f063 tags:
``` python
# Initialize Otter
import otter
grader = otter.Notebook("Linear systems - Poisson equation.ipynb")
```
%% Cell type:markdown id:d133bce9-d159-4b1d-a25f-69bed8da493a tags:
# Matrix Analysis 2025 - EE312
## Week 5 - Discrete Poisson equation
[LTS2](https://lts2.epfl.ch)
### Objectives
Apply your knowledge about linear systems to Poisson equation resolution.
%% Cell type:markdown id:654ad4ee-c060-4bc3-b218-3a6709952750 tags:
## Poisson equation
Let $u,v \in \mathbb{R}^n$ represent a physical quantity $f$ and $g: \mathbb{R} \mapsto \mathbb{R}$ sampled at $n$ equi-spaced locations $x_i$, i.e $u_i = f(x_i)$, $v_i = g(x_i)$.
Let us assume that the underlying continuous object satisfies the Poisson equation: $\frac{d^2}{dx^2} f (x)= g(x)$ with constraints $f(x_j) = y_j$ for a subset of $m$ locations $j \in \{i_1, \ldots i_m \}$.
We will assume that **all the $i_k$ are distinct**.
This equation governs a number of physical principles, e.g. gravity, fluid mechanics and electrostatics. In the latter we have $\Delta \varphi = -\frac{\rho}{\varepsilon}$, where $\Delta$ is the Laplacian operator $(\frac{d^2}{dx^2} + \frac{d^2}{dy^2} + \frac{d^2}{dz^2})$, $\varphi$ is the electric potential, $\rho$ the density of electric charges and $\varepsilon$ the electric permittivity.
---
%% Cell type:markdown id:06ac5cfa-62ef-4477-a957-e240ddeb85d8 tags:
### 1D case
%% Cell type:code id:7f3cb74d-72f0-460b-a978-a47f87de8557 tags:
``` python
import numpy as np
import matplotlib.pyplot as plt
```
%% Cell type:markdown id:9fd89d0b-50df-4711-a32b-ec1026e6b122 tags:
<!-- BEGIN QUESTION -->
1. Write down a matrix equation for the discrete version of $\frac{d^2}{dx^2} f (x)= g(x)$, using the finite-difference approximation of the derivative $\frac{d^2}{dx^2} f = f(x_{k+1}) - 2f(x_k) +f(x_{k-1})$. For the boundary conditions we will make the assumption that $x_{-1}=x_0$ and $x_{n-1}=x_n$ (also referred to as Dirichlet boundary conditions)
What is the rank of the corresponding matrix $D$ ?
%% Cell type:markdown id:d1f8bd46 tags:otter_answer_cell
%% Cell type:markdown id:ca746265 tags:otter_answer_cell
_Type your answer here, replacing this text._
%% Cell type:markdown id:a21ed0d8-4b5d-42eb-b1bf-6955938ebe2d tags:otter_assign_solution_cell
If we use zero-padding instead of the constant extension this equations satisfy $Du = v$ with
$D=\begin{pmatrix}
-2 & 1 & 0 & 0& ... & 0\\
1 & -2 & 1 & 0 &... &0\\
0 & 1 & -2 & 1 &... &0\\
& & & ... & & \\
0 & 0 & ... & 1 & -2 & 1\\
0 & 0 & ... & 0 & 1 & -2\\
\end{pmatrix}$
This is the laplacian matrix.
$\mbox{rank }D = n$.
If we use a Dirichlet boundary condition, i.e. constant extension, we have $u_{-1}=u_0$ and $u_n = u_{n+1}$, therefore
$D=\begin{pmatrix}
-1 & 1 & 0 & 0& ... & 0\\
1 & -2 & 1 & 0 &... &0\\
0 & 1 & -2 & 1 &... &0\\
& & & ... & & \\
0 & 0 & ... & 1 & -2 & 1\\
0 & 0 & ... & 0 & 1 & -1\\
\end{pmatrix}$
In this case the rank is $n-1$ since the sum of rows/columns is 0 and if we remove one row/column they become linearly independent.
%% Cell type:markdown id:aed76fe2-8cb5-4b28-bd3b-fa0bea113a46 tags:
<!-- END QUESTION -->
<Your answers here>
%% Cell type:markdown id:cceac118-c43d-4782-9f7e-4392e5ecc451 tags:
2. Implement a function that creates the $D$ matrix (also called Laplacian). The [diag](https://numpy.org/doc/stable/reference/generated/numpy.diag.html) function in numpy might be useful.
%% Cell type:code id:d41105d8-6933-4ef8-b2f0-c6ac053e1f55 tags:otter_assign_solution_cell
``` python
def lapl_matrix(N):
# BEGIN SOLUTION
O = np.ones(N-1)
D = -2*np.eye(N) + np.diag(O, -1) + np.diag(O, 1)
D[0,0] = -1
D[N-1, N-1] = -1
return D
# END SOLUTION
```
%% Cell type:code id:756d9370 tags:
%% Cell type:code id:1abc2b2c tags:
``` python
grader.check("q2")
```
%% Output
q2 results: All test cases passed!
q2 - 1 message: Good, results seem correct
%% Cell type:markdown id:4cea41dc-6f12-42f2-8644-b51e4918504d tags:
<!-- BEGIN QUESTION -->
3. Write down a matrix equation for the discrete version of $f(x_j) = y_j$. What is the rank of the corresponding matrix $B$ ?
%% Cell type:markdown id:fc67bfcb tags:otter_answer_cell
%% Cell type:markdown id:63a9357b tags:otter_answer_cell
_Type your answer here, replacing this text._
%% Cell type:markdown id:d47029a2-bd30-4da8-ae9a-f41240154377 tags:otter_assign_solution_cell
This operation can be represented by a matrix $B\in\mathbb{R}^{n\times m}$ where the coefficients $b_{kp} = \delta_{ki_p}$.
The rank of this matrix is $m$, under the assumption that all $i_k$ are distinct, since every row has a single non-zero value.
Using this matrix we can rewrite the condition $f(x_j) = y_j$ as $Bu=\begin{pmatrix}y_0\\ \vdots \\y_{m-1}\end{pmatrix}$
%% Cell type:markdown id:418bc83f-7d46-41c2-8a7e-df892857320c tags:
<!-- END QUESTION -->
4. Implement a function that creates matrix $B$
%% Cell type:code id:1d294eda-7edc-42c2-9640-13bec779bd48 tags:otter_assign_solution_cell
``` python
def b_matrix(N, idx_list): # idx_list is a list of valid indices, e.g. N=5, idx_list=[1,3]
# BEGIN SOLUTION
m = len(idx_list)
if m<1:
raise ValueError("Supply at least one index")
if not np.all(np.array(idx_list)<N):
raise ValueError("Invalid list of indices supplied")
if not np.all(np.array(idx_list)>=0):
raise ValueError("Negative indices supplied")
if N<1 or m >= N:
raise ValueError("Invalid dimensions")
B = np.zeros((m, N))
for p in zip(range(m), idx_list):
B[p[0], p[1]] = 1.
return B
# END SOLUTION
```
%% Cell type:code id:d592616d tags:
%% Cell type:code id:58dbd947 tags:
``` python
grader.check("q4")
```
%% Output
q4 results: All test cases passed!
q4 - 1 message: Good, results seem correct
%% Cell type:markdown id:b77f215e-e4d0-4976-9ed6-7ee210c689a6 tags:
<!-- BEGIN QUESTION -->
3. Write down a matrix equation for the full problem (Hint: Consider the matrix $C=\begin{pmatrix}D\\B\end{pmatrix}$). Discuss the rank of the matrix and deduce a way to numerically solve the linear system. Implement a function that returns matrix $C$.
%% Cell type:markdown id:70f5637a tags:otter_answer_cell
%% Cell type:markdown id:19ecce3a tags:otter_answer_cell
_Type your answer here, replacing this text._
%% Cell type:markdown id:c9ea268f-a134-43d5-a74f-7853bd8d3547 tags:otter_assign_solution_cell
Let us use the matrix $C=\begin{pmatrix}D\\B\end{pmatrix}$.
The following holds $Cu = z$, with $z=\begin{pmatrix}v_0\\v_1\\...\\v_{n-1}\\y_0\\y_1\\...\\y_{m-1}\end{pmatrix}$.
Let us suppose there exist non zero $\alpha_i$ s.t.
$\sum_{i=0}^{n-1} \alpha_i C_i = 0$, where $C_i$ is the $i$-th column of $C$.
Looking at the bottom part of $C$, we must have $\alpha_i=0, i\in\{i_1,...,i_m\}$. The $\alpha_i$ are can only be non-zero when the lower part of the vector only has 0s. Given a subset of columns of $D$ will have at least one row with a single non zero coefficient making the associated $\alpha_i = 0$, which then propagates to the rest (since columns have disjoint support), the rank of $C$ is $n$.
We can get the solution using $C^+$.
%% Cell type:code id:2eaf5a8c-b83a-4f53-9c4e-6192bf4e6fa4 tags:otter_assign_solution_cell
``` python
def c_matrix(D, B):
# BEGIN SOLUTION
sd = D.shape
sb = B. shape
C = np.zeros((sd[0]+sb[0], sd[1]))
C[:sd[0], :] = D
C[sd[0]:, :] = B
return C
# END SOLUTION
```
%% Cell type:code id:d77116d4 tags:
%% Cell type:code id:c0c17d8b tags:
``` python
grader.check("q4")
```
%% Output
q4 results: All test cases passed!
q4 - 1 message: Good, results seem correct
%% Cell type:markdown id:10a3d136-f5f3-4c1b-9c0a-ece58f891dfe tags:
<!-- END QUESTION -->
<!-- BEGIN QUESTION -->
5. What explicit formula can you use to compute the pseudo-inverse of $C$ (justify)?
%% Cell type:markdown id:ce8aa19a tags:otter_answer_cell
%% Cell type:markdown id:fd6f7561 tags:otter_answer_cell
_Type your answer here, replacing this text._
%% Cell type:markdown id:4daa620b-d617-49e8-9504-41151b02ab00 tags:otter_assign_solution_cell
The rank of $C$ is $n$, the columns of $C$ are independent, therefore the matrix is 1-1. We can use the left pseudo inverse
$C^+=(C^TC)^{-1}C^T$
%% Cell type:markdown id:3726eb03-0c82-4aed-b179-77ff947583c3 tags:
<!-- END QUESTION -->
<!-- BEGIN QUESTION -->
6. Implement a function that return the solution of the problem. You can either use the formula above (you may then use the [linalg.inv](https://numpy.org/doc/stable/reference/generated/numpy.linalg.inv.html) or compute the pseudo-inverse using [linalg.pinv](https://numpy.org/doc/stable/reference/generated/numpy.linalg.pinv.html).
%% Cell type:code id:79f8c6d1-7b92-48b6-bb90-e888aaa41243 tags:otter_assign_solution_cell
``` python
# v is a vector of size N
# u is a vector of size len(idx_list)
def solve_poisson(N, v, idx_list, u):
# BEGIN SOLUTION
D = lapl_matrix(N)
B = b_matrix(N, idx_list)
C = c_matrix(D, B)
z = np.zeros(C.shape[0])
z[:N] = v
for p in zip(range(len(u)), u):
z[N+p[0]] = p[1]
Cinv = np.linalg.pinv(C)
#or
# Cinv = np.linalg.inv(C.T@C)@C.T
return Cinv@z
# END SOLUTION
```
%% Cell type:markdown id:ff6184e1-e308-47d1-8b72-ab4559b48adb tags:
<!-- END QUESTION -->
<!-- BEGIN QUESTION -->
7. Let us now consider the solutions of the particular case $g(x) = 0, \forall x\in\mathbb{R}$. What are the analytical solutions that would solve Poisson equation ?
%% Cell type:markdown id:53e63141 tags:otter_answer_cell
%% Cell type:markdown id:d47ff697 tags:otter_answer_cell
_Type your answer here, replacing this text._
%% Cell type:markdown id:fc80136a-8da3-4fa2-a9c1-c77962761e38 tags:otter_assign_solution_cell
The analytical solutions are polynomial of degree 1 (s.t. their second derivative is 0), i.e. linear functions, i.e. $f(x)=ax+b$.
%% Cell type:markdown id:4a33db39-077d-4775-a55d-ae5bd7400b23 tags:
<!-- END QUESTION -->
<!-- BEGIN QUESTION -->
Let us verify that our implementation of the solution works with a small example. Fill the values for $u$ s.t. they belong to the solution space.
%% Cell type:code id:96381701-e737-41c1-8642-fa1a5c13924c tags:otter_assign_solution_cell
``` python
N = 50
v = np.zeros(N)
idx_list = [10, 20, 30, 40]
u = [10, 20, 30, 40] # SOLUTION
sol = solve_poisson(N, v, idx_list, u)
plt.plot(sol) # plot the result
```
%% Output
[<matplotlib.lines.Line2D at 0x16d950b10>]
[<matplotlib.lines.Line2D at 0x10af67a10>]
%% Cell type:markdown id:04e6fc55-6c8a-4dfa-8ca0-9b45ed2f157c tags:
<!-- END QUESTION -->
<!-- BEGIN QUESTION -->
9. Are the results conform to what we expected ? What happens near the boundaries ?
%% Cell type:markdown id:e1c9c95f tags:otter_answer_cell
%% Cell type:markdown id:41b1af96 tags:otter_answer_cell
_Type your answer here, replacing this text._
%% Cell type:markdown id:5710b3b8-a7be-4263-86a5-81d735062bad tags:otter_assign_solution_cell
We obtain a solution that is linear except near it boundaries. The aspect of the solution near the boundaries is due to the Dirichlet conditions we impose (i.e. solution should be constant outside the interval displayed). The values we had set as constraints are matched closely.
%% Cell type:markdown id:ecf46b87-accd-4043-8ab6-06835830699b tags:
<!-- END QUESTION -->
<!-- BEGIN QUESTION -->
10. Let us use a step function for $f$ and try to solve the system
%% Cell type:code id:d81b4501-b2a5-4412-a266-d04ffa8dd2c7 tags:
``` python
idx_list = [10, 20, 30, 40]
u2 = [1, 1, -1, -1]
sol2 = solve_poisson(N, v, idx_list, u2)
plt.scatter(idx_list, u2, marker='+', color='r')
plt.plot(sol2)
```
%% Output
[<matplotlib.lines.Line2D at 0x16d5b4150>]
[<matplotlib.lines.Line2D at 0x10fe128d0>]
%% Cell type:markdown id:4918d31c-40a2-4df3-88ec-d18a80e879e7 tags:
What do you observe and what is the reason for that ?
%% Cell type:markdown id:1482c041 tags:otter_answer_cell
%% Cell type:markdown id:e0d2ec3d tags:otter_answer_cell
_Type your answer here, replacing this text._
%% Cell type:markdown id:6f86a2fe-3fd2-45ed-871f-8850cbe198a4 tags:otter_assign_solution_cell
If we deliberately choose $f$ to be outside of the solution space, we obtain a poor approximation and our constraints are not met (which is expected).
We can check that the Laplacian of computed solution and the Laplacian of the step function (from which the constraints are sampled) do not match
%% Cell type:code id:21d1f686-af17-4660-a29e-656e6eee670b tags:otter_assign_solution_cell
``` python
D = lapl_matrix(N)
z = D@sol2 # Laplacian of the computed solution
step=np.zeros(50)
step[:25]=1
step[25:]=-1
plt.plot(z)
plt.plot(D@step) # plot the Laplacian of the step
plt.legend(['Lapl. solution', 'Lapl. step'])
```
%% Output
<matplotlib.legend.Legend at 0x1187e2610>
%% Cell type:markdown id:b520b2c6-7c84-4c3e-be16-8eb866648a97 tags:
<!-- END QUESTION -->
<!-- BEGIN QUESTION -->
11. Let us now consider the application of the Poisson equation to electrostatic. Knowing a distribution of electric charges, we can solve it to compute the electric potential. The distribution of electric charges will be very simple, consisting of two punctual charges
%% Cell type:code id:96932aa1-74aa-4f4e-bbf5-df0f0d85c47c tags:
``` python
N = 50
v3 = np.zeros(N)
v3[10] = 1
v3[40] = 1
plt.plot(v3)
```
%% Output
[<matplotlib.lines.Line2D at 0x16c85e810>]
[<matplotlib.lines.Line2D at 0x10feeef50>]
%% Cell type:markdown id:42861911-53e1-4cb3-8dc7-aa617c01a7e8 tags:
- What are the analytical solutions for this problem ? (remember the discrete derivative of a step function is a Dirac function, i.e. 0 everywhere except at one point where it is 1).
- Plot the analytical solution
- Compute and plot the numerical solution
- What do you observe ?
%% Cell type:markdown id:1b19e6d2 tags:otter_answer_cell
%% Cell type:markdown id:ddc8c11f tags:otter_answer_cell
_Type your answer here, replacing this text._
%% Cell type:markdown id:bc86b8ba-89c7-42a4-bae3-4279b8314fee tags:otter_assign_solution_cell
The analytical solution is a piecewise linear solution. Integrating once the 2-peaked will lead to picewise constant function, and piecewise linear if we integrate twice.
If you use the constraints we impose for the numerical example you can compute the theoretical solution of the problem satisfying the constraints (that was not asked in the question so the above answer was sufficient). If you impose the solution to be constant between 10 and 40, it means its derivative is 0 in this interval. Below 10 the derivative is then -1 and above 40 the derivative is 1.
%% Cell type:code id:f42fc23b-c412-4791-b5b6-bef54fa3bab3 tags:otter_assign_solution_cell
``` python
# BEGIN SOLUTION
d=np.zeros(50)
d[:10]=-1
d[40:]=1
plt.plot(d)
# END SOLUTION
```
%% Output
[<matplotlib.lines.Line2D at 0x16d7154d0>]
[<matplotlib.lines.Line2D at 0x10ff78890>]
%% Cell type:markdown id:6aba7c71-0e5b-40de-ae37-bce24524da06 tags:otter_assign_solution_cell
If we solve $-x+a = 1$ for $x=10$ and $x+b=1$ for $x = 40$, the analytical solution that satisfies the constraint is:
- $f(x) = -x+11, x<10$
- $f(x) = 1, 10\le x\le 40$
- $f(x) = x - 39, x>40$
%% Cell type:code id:f2f9bd66-067e-4590-8088-87b7969621bb tags:otter_assign_solution_cell
``` python
# Plot analytical solution
# BEGIN SOLUTION
x1 = np.arange(0, 11)
x2 = np.arange(10, 41)
x3 = np.arange(40, 51)
plt.plot(x1, -x1+11)
plt.plot(x2, np.ones(x2.shape))
plt.plot(x3, x3-39)
# END SOLUTION
```
%% Output
[<matplotlib.lines.Line2D at 0x16cf93250>]
[<matplotlib.lines.Line2D at 0x10fff5990>]
%% Cell type:code id:22eae7ab-4c91-4000-b123-7105cc4c023a tags:otter_assign_solution_cell
``` python
idx_list = [10, 20, 30, 40]
u3 = [1, 1, 1, 1]
sol3 = solve_poisson(N, v3, idx_list, u3)
plt.scatter(idx_list, u3, marker='+', color='r')
plt.plot(sol3)
```
%% Output
[<matplotlib.lines.Line2D at 0x16d7c05d0>]
[<matplotlib.lines.Line2D at 0x10ff7ad90>]
%% Cell type:code id:b0310f39-9bbf-4518-be7d-af0e3f6724a7 tags:otter_assign_solution_cell
``` python
# check the 2nd derivative of the solution computed
plt.plot(lapl_matrix(50)@sol3[:50]) # SOLUTION
```
%% Output
[<matplotlib.lines.Line2D at 0x16d85a910>]
[<matplotlib.lines.Line2D at 0x1180751d0>]
%% Cell type:markdown id:6e71baf6-6e79-4b39-b420-0b29d47444b5 tags:otter_assign_solution_cell
We can see the solution computed is acceptable as its second derivative matches the original distribution (it is not fully constant between the peaks but close enough). The Dirichlet boundary conditions are once again interfering at $x=0$ and $x=50$, and the derivative not being continuous also has an impact on the constant part.
%% Cell type:markdown id:c0f0755f tags:
%% Cell type:markdown id:daf06368 tags:
<!-- END QUESTION -->
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment