Skip to article frontmatterSkip to article content

6. Multivariate inference

Authors
Affiliations
TNO
TNO
TNO

Markov Chain Monte Carlo methods

  • MCMC methods can be used to estimate the posterior in an efficient way

  • In MCMC, the goal is to generate chains that after some iterations start sampling from the posterior

  • The posterior densities are then obtained in a Monte Carlo way: ρδ(θ)=m(θδ)/n\rho_{\delta}({ \mathbf{\theta}}) = m({\mathbf{\theta}_{\delta}})/n

  • A popular approach for MCMC is the Metropolis-Hastings algorithm:

mcm algo

Imports

import matplotlib.pyplot as plt
import numpy as np

# Problem definition
from probeye.definition.inverse_problem import InverseProblem
from probeye.definition.forward_model import ForwardModelBase
from probeye.definition.distribution import Normal, Uniform
from probeye.definition.sensor import Sensor
from probeye.definition.likelihood_model import GaussianLikelihoodModel
from probeye.definition.correlation_model import ExpModel

# Inference
from probeye.inference.emcee.solver import EmceeSolver

# Postprocessing
from probeye.postprocessing.sampling_plots import create_pair_plot
from probeye.postprocessing.sampling_plots import create_posterior_plot
from probeye.postprocessing.sampling_plots import create_trace_plot

Problem setup

# Fixed parameters
I = 1e9  # mm^4
L = 10_000  # mm

# Measurements
x_sensors = [2500, 5000]  # mm  (always from lower to higher, bug in inv_cov_vec_1D in tripy)
d_sensors = [35, 50]  # mm
sigma_model = 2.5  # mm
pearson = 0.5
l_corr = -np.abs(x_sensors[1] - x_sensors[0]) / np.log(pearson)  # mm (assuming exponential correlation)

# Prior
E_mean = 60  # GPa
E_std = 20  # GPa
Q_mean = 60  # kN
Q_std = 30  # kN
Q_loc_low = 0  # mm
Q_loc_high = 10000  # mm

Define forward model

def beam_deflection(E, Q, a, x):  # a is load position, x is sensor position
    if x < a:
        b = L - a
        return Q * b * x * (L ** 2 - b ** 2 - x ** 2) / (6 * E * I * L)
    
    return Q * a * (L - x) * (2 * L * x - x ** 2 - a ** 2) / (6 * E * I * L)
## Write your code where the three dots are

class BeamModel(ForwardModelBase):
    def interface(self):
        self.parameters = ...
        self.input_sensors = Sensor("x")
        self.output_sensors = Sensor("y", std_model="sigma")

    def response(self, inp: dict) -> dict:
        return {...}

Define inverse problem

## Write your code where the three dots are

# Instantiate the inverse problem
problem = InverseProblem(...)

# Add latent parameters
problem.add_parameter(
    ...
)
problem.add_parameter(
    ...
)
problem.add_parameter(
  ...
)

# Add fixed parameters
problem.add_parameter(
 ...
)
problem.add_parameter(
...
)

# Add measurement data
problem.add_experiment(
...
)
                 
# Add forward model
problem.add_forward_model(...)

# Add likelihood model
likelihood_model = GaussianLikelihoodModel(
        ...
        )
problem.add_likelihood_model(likelihood_model)

Solve with MCMC

emcee_solver = EmceeSolver(problem, show_progress=True)
inference_data = emcee_solver.run(n_steps=2000, n_initial_steps=2000)

Posterior plot

post_plot_array = create_posterior_plot(
    inference_data,
    emcee_solver.problem,
    kind="kde",
    title="Kernel density estimate of the posterior distribution",
)

Pair plot

pair_plot_array = create_pair_plot(
    inference_data,
    emcee_solver.problem,
    focus_on_posterior=True,
    show_legends=True,
    title="Pair plot of the posterior distribution",
)

Trace plot

## Write your code where the three dots are

trace_plot_array = create_trace_plot(
   ...
)

Posterior predictives

## Write your code where the three dots are

 # Extract samples
posterior_samples = inference_data.posterior.to_array()  # parameters, chains, samples
posterior_samples = np.array(posterior_samples)
posterior_samples = posterior_samples.reshape(posterior_samples.shape[0], -1).T  # samples, parameters

# Make predictions for x in the range of the beam
x_range = np.linspace(0, 10000, 100)
predictions = np.zeros((len(posterior_samples), len(x_range)))

for i, sample in enumerate(posterior_samples):
    ...

# Calculate mean and 95% intervals
mean_pred = ...
lower_bound = ...  # 2.5th percentile
upper_bound = ...  # 97.5th percentile

Plot posterior predictive

plt.figure(figsize=(12, 4))
plt.plot(x_range, -mean_pred, label='Mean Prediction')
plt.fill_between(x_range, -lower_bound, -upper_bound, color='lightblue', alpha=0.5, label='95% Interval')
plt.xlabel('x (mm)')
plt.ylabel('Deflection')
plt.title('Posterior Predictive Deflections along the Beam')
plt.legend()
plt.show()