---
# System prepended metadata

title: 'Course 02975: Introduction to uncertainty quantification for inverse problems'

---

# Course 02975: Introduction to uncertainty quantification for inverse problems

## Installation (extra)
Fenics on colab
```
try:
    import dolfin
except ImportError:
    !wget "https://fem-on-colab.github.io/releases/fenics-install-real.sh" -O "/tmp/fenics-install.sh" && bash "/tmp/fenics-install.sh"
    import dolfin
```

## Chapter 1 code solutions:

### Solutions for Chapter 1: Probably the simplist BIP in the world, The data distribution (on multiplicative noise)

**ex. link:**
https://cuqi-dtu.github.io/CUQI-Book/chapter01/Intro_example.html#id3

##### Question 4: part (What is the corresponding data distribution? )
$$
\begin{align}
b &= Ax + (Ax)e \quad e\sim \text{Gaussian}(0, \sigma^2) \\
b &= Ax(1+e)\\
b &= Ax(e_2) \quad e_2\sim \text{Gaussian}(1, \sigma^2)
\end{align}
$$
by linear transformation of normal distribution (see section [Operations on a single normal variable](https://en.wikipedia.org/wiki/Normal_distribution)) 
(note:We consider $Ax$ constatnt relative to the random variable $b$ or $e_2$)
$$
b \sim \text{Gaussian}(Ax, \sigma^2(Ax)^2)
$$

##### Question 4: part (Define the data distribution in CUQIpy ...)

```python
b_mult = Gaussian(A@x, lambda x : 0.1*(A@x)**2)
b_mult_given_particular_x = b_mult(x=particular_x)
```

##### Question 4: part (Plot the PDF of the data distribution ...)
```python
plot_pdf_1D(b_mult_given_particular_x, -1.5, 6.5)
# note: I am also plotting the b of the additive noise case for comparison 
plot_pdf_1D(b_given_particular_x, -1.5, 6.5)
plt.legend(['b_mult_given_particular_x', 'b_additive_given_particular_x'])
```







# Chapter 2 solutions:


### Section: Prior
##### Subsection: Multivariate Gaussian distribution
```python
# 1.
help(y_samples.plot_ci)
y_samples.plot_ci(90)
# 2. 
y_samples.plot_ci(90, exact=y.mean)
# 3. 
plt.figure()
y_samples.hist_chain([0], bins=50, density=True)
```

##### Multivariate Gaussian distribution: compute the entropy of the distribution numerically
```python
# 1.
y_ent = Gaussian(0, 0.1)
y_ent_entropy = -1*sp_integrate.quad(lambda val: entropy_integrand(y_ent, val),
                                     -np.inf, np.inf)[0]
print(y_ent_entropy)
# 2.
z_ent = Uniform(-3, 3)
z_ent_entropy = -1*sp_integrate.quad(lambda val: entropy_integrand(z_ent, val),
                                     -3, 3)[0]
print(z_ent_entropy)
```

##### Subsection: Markov Random Fields in CUQIpy
```python
# your code here
# 1. 
x_GMRF.sample().plot()

# 2.
plt.figure() 
x_Gaussian = Gaussian(mean=np.zeros(n), prec=50)
x_GMRF.sample().plot()
x_Gaussian.sample().plot()

# 3.
plt.figure()
x_GMRF_samples = x_GMRF.sample(100000)
diff_30_31_samples = Samples((x_GMRF_samples.samples[31] - x_GMRF_samples.samples[30]).reshape(1, -1))
print(diff_30_31_samples.mean())
print(diff_30_31_samples.variance())
```


### Section: Gibbs

##### Subsection: Hierarchical Bayesian model
```python
# Your code here
x_samples =  x(d=100).sample(1000).plot()
```

##### Subsection: Posterior distribution 
```python
# Your code here
# 1.
joint.logd(y=y_data, x=x(d=1000).sample(), d=1000, s=1)

# 2.
marginal_post = posterior(x=x(d=1000).sample(), d=1000)
print(marginal_post)
   
# 3. 
x2 = GMRF(np.zeros(n),prec=1 )
y2 = Gaussian(A@x2, prec=10000)

BP2 = BayesianProblem(x2, y2)
BP2.set_data(y2=y_data)

BP2.UQ(exact=probinfo.exactSolution)
```

##### Subsection: Connection to BayesianProblem class

```python
sigma_post = np.linalg.inv(np.linalg.inv(cov1) + np.linalg.inv(cov_obs))
print("sigma_post: ", sigma_post)
mu_post = sigma_post @ (np.linalg.inv(cov_obs) @ np.array([0.1, 0.2]) + np.linalg.inv(cov1) @ np.array([0, 0]))
x3 = Gaussian(mu_post, sigma_post)
samples_x3 = x3.sample(10000)

# alpha
samples_post.plot_pair()
samples_x3.plot_pair(ax=plt.gca())
```



# Chapter 3

## First notebook

### Poisson target 1
```python=
# your code here
x_true.plot(plot_par=True)
plt.figure()
x_true.plot(plot_par=False)
```
### Poisson target 2
```python=
BP_poisson.UQ(exact=x_true)
```

### Poisson target 3
```python=
target_poisson.logpdf(x=x_true)
```

## Second notebook

### Story 1, part 1
```python=
plot_pdf_2D(target_donut, -4, 4, -4, 4)
MH_fixed_samples.burnthin(1500).plot_pair(
    ax=plt.gca())


MH_sampler.scale = 0.005
MH_fixed_samples_small_scale = MH_sampler.sample(Ns, Nb)
plt.figure()
plot_pdf_2D(target_donut, -4, 4, -4, 4)
MH_fixed_samples_small_scale.burnthin(
    1500).plot_pair(ax=plt.gca())
```
### Story 1, part 2
```python=
print(MH_fixed_samples.compute_ess()/ 1.4)
print(MH_adapted_samples.compute_ess() /1.5)
```



### Story 2
```python=
MH_samples_funvals = MH_samples.funvals
CWMH_samples_funvals = CWMH_samples.funvals

MH_samples_funvals.plot_ci(exact=x_true)
plt.figure()
CWMH_samples_funvals.plot_ci(exact=x_true)

# --- Coefficient space
plt.figure()
MH_samples.plot_ci(exact=x_true, plot_par=True)
plt.figure()
CWMH_samples.plot_ci(exact=x_true, plot_par=True)

# --- ESS
plt.figure()
plt.plot(MH_samples.compute_ess(), label='MH')
plt.plot(CWMH_samples.compute_ess(), label='CWMH')
plt.legend()

# --- Scale
plt.figure()
plt.plot(MH_sampler.scale*np.ones(MH_sampler.dim), label='MH')
plt.plot(CWMH_sampler.scale, label='CWMH')
plt.legend()

```
### Story 3: ULA

```python=
# your code here

# --- Change scale to 0.01
ULA_sampler_2 = ULA(target=target_donut, scale=0.01, x0=np.array([0,0]))
Ns = 1000
ULA_samples_2 = ULA_sampler_2.sample(Ns)
plot_pdf_2D(target_donut, -4, 4, -4, 4)
ULA_samples_2.plot_pair(ax=plt.gca(), scatter_kwargs={'alpha': 1})

# --- Compare with MH
ULA_samples_10 = ULA_sampler_2.sample(10)
MH_10 = MH(target_donut, scale=0.01, x0=np.array([0, 0]))
MH_samples_10 = MH_10.sample(10, 0)

plt.figure()
plot_pdf_2D(target_donut, -4, 4, -4, 4)
ULA_samples_10.plot_pair(ax=plt.gca(), scatter_kwargs={'alpha': 1})

plt.figure()
plot_pdf_2D(target_donut, -4, 4, -4, 4)
MH_samples_10.plot_pair(ax=plt.gca(), scatter_kwargs={'alpha': 1})

# --- Univariate
plt.figure()
x_uni = Gaussian(0, 1)
ULA_uni = ULA(target=x_uni, scale=1, x0=0)
ULA_samples_uni = ULA_uni.sample(40000)
kde_est = gaussian_kde(ULA_samples_uni.samples[0,:])
grid = np.linspace(-4, 4, 1000)
plt.plot(grid, kde_est(grid), label='ULA', color='b')
plot_pdf_1D(x_uni, -4, 4, color='r', label='True')
plt.legend()
```
### Story 5: NUTS 1
```python=
# ESS and chain for the donut distribution 
NUTS_donuts_samples.compute_ess()
NUTS_donuts_samples.plot_trace()

# Draw connecting lines for MALA samples
sampler1 = MALA(target_donut, scale=0.01, x0=np.array([0,0]))
sampler1_samples = sampler1.sample(1000, 500)

plot_pdf_2D(target_donut, -4, 4, -4, 4)
sampler1_samples.plot_pair(
    ax=plt.gca(),
    scatter_kwargs={'alpha': 1, 'c': 'r'})
plt.plot(
    sampler1_samples.samples[0,:],
    sampler1_samples.samples[1,:], 'r', alpha=0.5)

# To visualize the step size and tree size
plt.plot(NUTS_donut.epsilon_list)
plt.figure()
plt.plot(NUTS_donut.num_tree_node_list)
```

### Story 5: NUTS 2
```python=
# NUTS for the Poisson problem
np.random.seed(0) # Fix the seed for reproducibility
target_poisson.enable_FD()
NUTS_poisson = NUTS(
    target=target_poisson, max_depth=7)
Ns = 100
Nb = 10
NUTS_poisson_samples = NUTS_poisson.sample(Ns, Nb)
NUTS_poisson_samples.funvals.plot_ci(exact=x_true)
print(NUTS_poisson_samples.compute_ess())

```