1D Rayleigh wave phase velocity inversion#

Open In Colab

If you are running this notebook locally, make sure you’ve followed steps here to set up the environment. (This environment.yml file specifies a list of packages required to run the notebooks)


What we do in this notebook#

Here we look at applying CoFI to the inversion of Rayleigh wave phase velocities for a 1D layered earth.

Learning outcomes

  • A demonstration of CoFI’s ability to switch between parameter estimation and ensemble methods.

  • A comparison between different McMC samplers that is fixed-d and trans-d samplers

  • An application of CoFI to field data

# -------------------------------------------------------- #
#                                                          #
#     Uncomment below to set up environment on "colab"     #
#                                                          #
# -------------------------------------------------------- #

# !pip install -U cofi git+https://github.com/inlab-geo/pysurf96.git
# !git clone https://github.com/inlab-geo/cofi-examples.git
# %cd cofi-examples/examples/sw_rf_joint
import numpy as np
import scipy
import matplotlib.pyplot as plt

from pysurf96 import surf96
import bayesbay
import cofi

Problem description#

Here we illustrate the range of inversion methods made avaialbe by CoFI. That is we first define a base problem and then explore the use of an iterative non linear apporach to find the MAP solution and then employ a range of Markov Chain Monte Carlo strategies to recover the posterior distribution. The forward problem is solved using pysurf 96 (miili/pysurf96) and the field data example is taken from (https://www.eas.slu.edu/eqc/eqc_cps/TUTORIAL/STRUCT/index.html) and we will be inverting observed rayleigh wave phase velocities

Inference problem

# display theory on the inference problem
from IPython.display import display, Markdown

with open("../../theory/geo_surface_wave_dispersion.md", "r") as f:
    content = f.read()

display(Markdown(content))
<IPython.core.display.Markdown object>

Solving methods

# display theory on the optimisation approach
with open("../../theory/inv_optimisation.md", "r") as f:
    content = f.read()

display(Markdown(content))
<IPython.core.display.Markdown object>
# display theory on the optimisation approach
with open("../../theory/inv_mcmc.md", "r") as f:
    content = f.read()

display(Markdown(content))
<IPython.core.display.Markdown object>

Further reading

https://en.wikipedia.org/wiki/Surface_wave_inversion

Utilities#

1D model paramterisation#

# display theory on the 1D model parameterisation
with open("../../theory/misc_1d_model_parameterisation.md", "r") as f:
    content = f.read()

display(Markdown(content))
<IPython.core.display.Markdown object>
# layercake model utilities
def form_layercake_model(thicknesses, vs):
    model = np.zeros((len(vs)*2-1,))
    model[1::2] = thicknesses
    model[::2] = vs
    return model

def split_layercake_model(model):
    thicknesses = model[1::2]
    vs = model[::2]
    return thicknesses, vs
# voronoi model utilities
def form_voronoi_model(voronoi_sites, vs):
    return np.hstack((vs, voronoi_sites))

def split_voronoi_model(model):
    voronoi_sites = model[len(model)//2:]
    vs = model[:len(model)//2]
    return voronoi_sites, vs
def voronoi_to_layercake(voronoi_vector: np.ndarray) -> np.ndarray:
    n_layers = len(voronoi_vector) // 2
    velocities = voronoi_vector[:n_layers]
    voronoi_sites = voronoi_vector[n_layers:]
    depths = (voronoi_sites[:-1] + voronoi_sites[1:]) / 2
    thicknesses = depths - np.insert(depths[:-1], 0, 0)
    layercake_vector = np.zeros((2*n_layers-1,))
    layercake_vector[::2] = velocities
    layercake_vector[1::2] = thicknesses
    return layercake_vector

def layercake_to_voronoi(layercake_vector: np.ndarray, first_voronoi_site: float = 0.0) -> np.ndarray:
    n_layers = len(layercake_vector) // 2 + 1
    thicknesses = layercake_vector[1::2]
    velocities = layercake_vector[::2]
    depths = np.cumsum(thicknesses)
    voronoi_sites = np.zeros((n_layers,))
    for i in range(1,len(voronoi_sites)):
        voronoi_sites[i] = 2 * depths[i-1] - voronoi_sites[i-1]
    voronoi_vector = np.hstack((velocities, voronoi_sites))
    return voronoi_vector

Forward solver#

# display theory on the using the forward solver
with open("../../theory/geo_surface_wave_dispersion2.md", "r") as f:
    content = f.read()

display(Markdown(content))
<IPython.core.display.Markdown object>
# Constants
VP_VS = 1.77
RHO_VP_K = 0.32
RHO_VP_B = 0.77
# forward through pysurf96
def forward_sw(model, periods):
    thicknesses, vs = split_layercake_model(model)
    thicknesses = np.append(thicknesses, 10)
    vp = vs * VP_VS
    rho = RHO_VP_K * vp + RHO_VP_B
    return surf96(
        thicknesses,
        vp,
        vs,
        rho,
        periods,
        wave="rayleigh",
        mode=1,
        velocity="phase",
        flat_earth=False,
    )

# numerical jacobian
def jacobian_sw(model, periods, fwd=forward_sw, relative_step=0.01):
    jacobian = np.zeros((len(periods), len(model)))
    original_dpred = fwd(model, periods)
    for i in range(len(model)):
        perturbed_model = model.copy()
        step = relative_step * model[i]
        perturbed_model[i] += step
        perturbed_dpred = fwd(perturbed_model, periods)
        derivative = (perturbed_dpred - original_dpred) / abs(step)
        jacobian[:, i] = derivative
    return jacobian

Visualisation#

For conveninece we also implement two functions to plot the data here the Rayleigh wave phase velocity and a model given in the layer based parametrisation.

def plot_model(model, ax=None, title="model", **kwargs):
    # process data
    thicknesses = np.append(model[1::2], max(model[1::2]))
    velocities = model[::2]
    y = np.insert(np.cumsum(thicknesses), 0, 0)
    x = np.insert(velocities, 0, velocities[0])

    # plot depth profile
    if ax is None:
        _, ax = plt.subplots()
    plotting_style = {
        "linewidth": kwargs.pop("linewidth", kwargs.pop("lw", 0.5)),
        "alpha": 0.2,
        "color": kwargs.pop("color", kwargs.pop("c", "blue")),
    }
    plotting_style.update(kwargs)
    ax.step(x, y, where="post", **plotting_style)
    if ax.get_ylim()[0] < ax.get_ylim()[1]:
        ax.invert_yaxis()
    ax.set_xlabel("Vs (km/s)")
    ax.set_ylabel("Depth (km)")
    ax.set_title(title)
    return ax
def plot_data(rayleigh_phase_velocities, periods, ax=None, scatter=False,
              title="data", **kwargs):
    if ax is None:
        _, ax = plt.subplots()
    plotting_style = {
        "linewidth": kwargs.pop("linewidth", kwargs.pop("lw", 1)),
        "alpha": 1,
        "color": kwargs.pop("color", kwargs.pop("c", "blue")),
    }
    plotting_style.update(**kwargs)
    if scatter:
        ax.scatter(periods, rayleigh_phase_velocities, **plotting_style)
    else:
        ax.plot(periods, rayleigh_phase_velocities, **plotting_style)
    ax.set_xlabel("Periods (s)")
    ax.set_ylabel("Rayleigh phase velocities (km/s)")
    ax.set_title(title)
    return ax
def plot_model_and_data(model, label_model, color_model,
                        forward_func, periods, label_d_pred, color_d_pred,
                        axes=None, light_thin=False):
    if axes is None:
        _, axes = plt.subplots(1, 2, figsize=(10, 4), gridspec_kw={"width_ratios": [1, 2.5]})
    ax1, ax2 = axes
    if light_thin:
        plot_model(model, ax=ax1, color=color_model, alpha=0.2, lw=0.5, label=label_model)
        plot_data(forward_func(model, periods), periods, ax=ax2, color=color_d_pred, alpha=0.2, lw=0.5, label=label_d_pred)
    else:
        plot_model(model, ax=ax1, color=color_model, alpha=1, lw=1, label=label_model)
        plot_data(forward_func(model, periods), periods, ax=ax2, color=color_d_pred, label=label_d_pred)
    ax1.legend()
    ax2.legend()
    return ax1, ax2

Synthetic example#

Prior to inverting any field data it is good practice to test an inversion method using sythetic exmaples where we know the true model. It is also recommended to prior to this idnepently test any forward solver that is being used and verify the Jacobian, as problems related to the forward sovler are diffiuclt to identify and diagnose once they are integrated in an inversion methodology.

Generate synthetic data#

synth_d_periods = np.geomspace(3, 80, 20)

true_thicknesses = np.array([10, 10, 15, 20, 20, 20, 20, 20])
true_vs = np.array([3.38, 3.44, 3.66, 4.25, 4.35, 4.32, 4.315, 4.38, 4.5])
true_model = form_layercake_model(true_thicknesses, true_vs)
# plot true model and d_pred from true model
_, ax2 = plot_model_and_data(model=true_model, label_model="true model", color_model="orange",
                    forward_func=forward_sw, periods=synth_d_periods,
                    label_d_pred="true data (noiseless)", color_d_pred="orange")

# plot d_obs
plot_data(d_obs, synth_d_periods, ax=ax2, scatter=True, color="red", s=20, label="observed data (noisy)")
ax2.legend();
model, data
<matplotlib.legend.Legend object at 0x7fd0a7ff0410>

Optimisation#

Prepare ``BaseProblem`` for optimisation

n_dims = 17

init_thicknesses = np.ones((n_dims//2,)) * 15
init_vs = np.ones((n_dims//2+1,)) * 4.0
init_model = form_layercake_model(init_thicknesses, init_vs)
# plot the model and d_pred for starting model
axes = plot_model_and_data(model=init_model, label_model="starting model", color_model="purple",
                           forward_func=forward_sw, periods=synth_d_periods,
                           label_d_pred="data predictions from starting model", color_d_pred="purple")

# plot the model and d_pred for true model
plot_model_and_data(model=true_model, label_model="true model", color_model="orange",
                    forward_func=forward_sw, periods=synth_d_periods,
                    label_d_pred="true data (noiseless)", color_d_pred="orange", axes=axes)

# plot d_obs
plot_data(d_obs, synth_d_periods, ax=axes[1], scatter=True, color="red", s=20, label="d_obs")
axes[1].legend();
model, data
<matplotlib.legend.Legend object at 0x7fd24e265950>
my_reg = cofi.utils.QuadraticReg(
    weighting_matrix="damping",
    model_shape=(n_dims,),
    reference_model=init_model
)
def my_objective(model, fwd, periods, d_obs, lamda=1.0):
    d_pred = fwd(model, periods)
    data_misfit = np.sum((d_obs - d_pred) ** 2)
    reg = my_reg(model)
    return data_misfit + lamda * reg

def my_objective_gradient(model, fwd, periods, d_obs, lamda=1.0):
    d_pred = fwd(model, periods)
    jac = jacobian_sw(model, periods, fwd)
    data_misfit_grad = -2 * jac.T @ (d_obs - d_pred)
    reg_grad = my_reg.gradient(model)
    return data_misfit_grad + lamda * reg_grad

def my_objective_hessian(model, fwd, periods, d_obs, lamda=1.0):
    jac = jacobian_sw(model, periods, fwd)
    data_misfit_hess = 2 * jac.T @ jac
    reg_hess = my_reg.hessian(model)
    return data_misfit_hess + lamda * reg_hess

Optimisation with no damping#

lamda = 0

kwargs = {
    "fwd": forward_sw,
    "periods": synth_d_periods,
    "d_obs": d_obs,
    "lamda": lamda
}
sw_problem_no_reg = cofi.BaseProblem()
sw_problem_no_reg.set_objective(my_objective, kwargs=kwargs)
sw_problem_no_reg.set_gradient(my_objective_gradient, kwargs=kwargs)
sw_problem_no_reg.set_hessian(my_objective_hessian, kwargs=kwargs)
sw_problem_no_reg.set_initial_model(init_model)

Define ``InversionOptions``

inv_options_optimiser = cofi.InversionOptions()
inv_options_optimiser.set_tool("scipy.optimize.minimize")
inv_options_optimiser.set_params(method="trust-exact")

Define ``Inversion`` and run

inv_optimiser_no_reg = cofi.Inversion(sw_problem_no_reg, inv_options_optimiser)
inv_result_optimiser_no_reg = inv_optimiser_no_reg.run()

Plot results

# plot the model and d_pred for starting model
axes = plot_model_and_data(model=init_model, label_model="starting model", color_model="black",
                           forward_func=forward_sw, periods=synth_d_periods,
                           label_d_pred="d_pred from starting model", color_d_pred="black")

# plot the model and d_pred for true model
plot_model_and_data(model=true_model, label_model="true model", color_model="orange",
                    forward_func=forward_sw, periods=synth_d_periods,
                    label_d_pred="d_pred from true model", color_d_pred="orange", axes=axes)

# plot the model and d_pred for inverted model
plot_model_and_data(model=inv_result_optimiser_no_reg.model, label_model="inverted model", color_model="purple",
                    forward_func=forward_sw, periods=synth_d_periods,
                    label_d_pred="d_pred from inverted model", color_d_pred="purple", axes=axes);

# plot d_obs
plot_data(d_obs, synth_d_periods, ax=axes[1], scatter=True, color="red", s=20, label="d_obs")
axes[1].legend();
model, data
<matplotlib.legend.Legend object at 0x7fd24e886450>

Optimal damping#

Obviously we get a very skewed 1D model out of an optimisation that solely tries to minimise the data misfit. We would like to add a damping term to our objective function, but we are not sure which factor suits the problem well.

In this situation, the InversionPool from CoFI can be handy.

lambdas = np.logspace(-6, 6, 15)

my_lcurve_problems = []
for lamb in lambdas:
    my_problem = cofi.BaseProblem()
    kwargs = {
        "fwd": forward_sw,
        "periods": synth_d_periods,
        "d_obs": d_obs,
        "lamda": lamb
    }
    my_problem.set_objective(my_objective, kwargs=kwargs)
    my_problem.set_gradient(my_objective_gradient, kwargs=kwargs)
    my_problem.set_hessian(my_objective_hessian, kwargs=kwargs)
    my_problem.set_initial_model(init_model)
    my_lcurve_problems.append(my_problem)

def my_callback(inv_result, i):
    m = inv_result.model
    res_norm = np.linalg.norm(forward_sw(m, synth_d_periods) - d_obs)
    reg_norm = np.sqrt(my_reg(m))
    print(f"Finished inversion with lambda={lambdas[i]}: {res_norm}, {reg_norm}")
    return res_norm, reg_norm

my_inversion_pool = cofi.utils.InversionPool(
    list_of_inv_problems=my_lcurve_problems,
    list_of_inv_options=inv_options_optimiser,
    callback=my_callback,
    parallel=False
)
all_res, all_cb_returns = my_inversion_pool.run()

l_curve_points = list(zip(*all_cb_returns))
Finished inversion with lambda=1e-06: 0.024553006135234645, 4.99805835268218
Finished inversion with lambda=7.196856730011514e-06: 0.02580846716011747, 2.4911951853445333
Finished inversion with lambda=5.1794746792312125e-05: 0.027008633410123793, 1.2840642724354572
Finished inversion with lambda=0.0003727593720314938: 0.02762718792392687, 1.1669353971336833
Finished inversion with lambda=0.0026826957952797246: 0.0286667339637953, 1.1432552848797986
Finished inversion with lambda=0.019306977288832496: 0.04006872346784902, 1.1088903308334361
Finished inversion with lambda=0.1389495494373136: 0.14882281456160756, 0.9734184900709462
Finished inversion with lambda=1.0: 0.4947597385867233, 0.6567054865218048
Finished inversion with lambda=7.196856730011514: 1.0965644431078774, 0.31353590132833054
Finished inversion with lambda=51.79474679231202: 1.6228845583705995, 0.06766337615005368
Finished inversion with lambda=372.7593720314938: 1.7479562697932292, 0.010122391380161093
Finished inversion with lambda=2682.6957952797275: 1.7668611464779829, 0.0014208105468626062
Finished inversion with lambda=19306.977288832455: 1.7695205273135028, 0.0001977765631969541
Finished inversion with lambda=138949.5494373136: 1.7698955895615756, 2.7484668777127396e-05
Finished inversion with lambda=1000000.0: 1.7699472844137434, 3.819054748877029e-06
# print all the lambdas
lambdas
array([1.00000000e-06, 7.19685673e-06, 5.17947468e-05, 3.72759372e-04,
       2.68269580e-03, 1.93069773e-02, 1.38949549e-01, 1.00000000e+00,
       7.19685673e+00, 5.17947468e+01, 3.72759372e+02, 2.68269580e+03,
       1.93069773e+04, 1.38949549e+05, 1.00000000e+06])

Plot L-curve

# plot the L-curve
res_norm, reg_norm = l_curve_points
plt.plot(reg_norm, res_norm, '.-')
plt.xlabel(r'Norm of regularization term $||Wm||_2$')
plt.ylabel(r'Norm of residual $||g(m)-d||_2$')
for i in range(0, len(lambdas), 2):
    plt.annotate(f'{lambdas[i]:.1e}', (reg_norm[i], res_norm[i]), fontsize=8)
1D rayleigh wave phase velocity inversion

Optimisation with damping#

From the L-curve plot above, it seems that a damping factor of around 0.02 would be good.

lamda = 0.02

kwargs = {
    "fwd": forward_sw,
    "periods": synth_d_periods,
    "d_obs": d_obs,
    "lamda": lamda
}
sw_problem = cofi.BaseProblem()
sw_problem.set_objective(my_objective, kwargs=kwargs)
sw_problem.set_gradient(my_objective_gradient, kwargs=kwargs)
sw_problem.set_hessian(my_objective_hessian, kwargs=kwargs)
sw_problem.set_initial_model(init_model)

Define ``Inversion`` and run

inv_optimiser = cofi.Inversion(sw_problem, inv_options_optimiser)
inv_result_optimiser = inv_optimiser.run()

Plot results

# plot the model and d_pred for starting model
axes = plot_model_and_data(model=init_model, label_model="starting model", color_model="black",
                           forward_func=forward_sw, periods=synth_d_periods,
                           label_d_pred="d_pred from starting model", color_d_pred="black")

# plot the model and d_pred for true model
plot_model_and_data(model=true_model, label_model="true model", color_model="orange",
                    forward_func=forward_sw, periods=synth_d_periods,
                    label_d_pred="d_pred from true model", color_d_pred="orange", axes=axes)

# plot the model and d_pred for damped solution, and d_obs
plot_model_and_data(model=inv_result_optimiser.model, label_model="damped solution", color_model="purple",
                    forward_func=forward_sw, periods=synth_d_periods,
                    label_d_pred="d_pred from damped solution", color_d_pred="purple", axes=axes);

# plot d_obs
plot_data(d_obs, synth_d_periods, ax=axes[1], scatter=True, color="red", s=20, label="d_obs")
axes[1].legend();
model, data
<matplotlib.legend.Legend object at 0x7fd0a8115cd0>

Fixed-dimensional sampling#

Prepare ``BaseProblem`` for fixed-dimensional sampling

thick_min = 5
thick_max = 30
vs_min = 2
vs_max = 5

def my_log_prior(model):
    thicknesses, vs = split_layercake_model(model)
    thicknesses_out_of_bounds = (thicknesses < thick_min) | (thicknesses > thick_max)
    vs_out_of_bounds = (vs < vs_min) | (vs > vs_max)
    if np.any(thicknesses_out_of_bounds) or np.any(vs_out_of_bounds):
        return float("-inf")
    log_prior = -np.log(thick_max - thick_min) * len(thicknesses) - np.log(vs_max - vs_min) * len(vs)
    return log_prior
Cdinv = np.eye(len(d_obs))/(noise_level**2)      # inverse data covariance matrix

def my_log_likelihood(model):
    try:
        d_pred = forward_sw(model, synth_d_periods)
    except:
        return float("-inf")
    residual = d_obs - d_pred
    return -0.5 * residual @ (Cdinv @ residual).T
n_walkers = 40

my_walkers_start = np.ones((n_walkers, n_dims)) * inv_result_optimiser.model
for i in range(n_walkers):
    my_walkers_start[i,:] += np.random.normal(0, 0.3, n_dims)
sw_problem.set_log_prior(my_log_prior)
sw_problem.set_log_likelihood(my_log_likelihood)

Define ``InversionOptions``

inv_options_fixed_d_sampling = cofi.InversionOptions()
inv_options_fixed_d_sampling.set_tool("emcee")
inv_options_fixed_d_sampling.set_params(
    nwalkers=n_walkers,
    nsteps=2_000,
    initial_state=my_walkers_start,
    skip_initial_state_check=True,
    progress=True
)

Define ``Inversion`` and run

We will disable the display of warnings temporarily due to the unavoidable existence of -inf in our prior.

dfm/emcee#370

np.seterr(all="ignore");
{'divide': 'ignore', 'over': 'ignore', 'under': 'ignore', 'invalid': 'ignore'}

Sample the prior#

prior_sampling_problem = cofi.BaseProblem()
prior_sampling_problem.set_log_posterior(my_log_prior)
prior_sampling_problem.set_model_shape(init_model.shape)
prior_sampler = cofi.Inversion(prior_sampling_problem, inv_options_fixed_d_sampling)
prior_results = prior_sampler.run()
  0%|          | 0/2000 [00:00<?, ?it/s]
  6%|▌         | 110/2000 [00:00<00:01, 1093.98it/s]
 11%|█         | 221/2000 [00:00<00:01, 1099.40it/s]
 17%|█▋        | 335/2000 [00:00<00:01, 1114.09it/s]
 22%|██▏       | 448/2000 [00:00<00:01, 1119.40it/s]
 28%|██▊       | 562/2000 [00:00<00:01, 1123.78it/s]
 34%|███▍      | 677/2000 [00:00<00:01, 1130.14it/s]
 40%|███▉      | 792/2000 [00:00<00:01, 1134.52it/s]
 45%|████▌     | 907/2000 [00:00<00:00, 1138.02it/s]
 51%|█████     | 1023/2000 [00:00<00:00, 1143.49it/s]
 57%|█████▋    | 1139/2000 [00:01<00:00, 1145.91it/s]
 63%|██████▎   | 1256/2000 [00:01<00:00, 1150.52it/s]
 69%|██████▊   | 1372/2000 [00:01<00:00, 1152.33it/s]
 74%|███████▍  | 1488/2000 [00:01<00:00, 1151.12it/s]
 80%|████████  | 1606/2000 [00:01<00:00, 1157.24it/s]
 86%|████████▌ | 1723/2000 [00:01<00:00, 1158.45it/s]
 92%|█████████▏| 1839/2000 [00:01<00:00, 1157.40it/s]
 98%|█████████▊| 1956/2000 [00:01<00:00, 1160.76it/s]
100%|██████████| 2000/2000 [00:01<00:00, 1144.46it/s]
import arviz as az

labels = ["v0", "t0", "v1", "t1", "v2", "t2", "v3", "t3", "v4", "t4", "v5", "t5", "v6", "t6", "v7", "t7", "v8"]

prior_results_sampler = prior_results.sampler
az_idata_prior = az.from_emcee(prior_results_sampler, var_names=labels)

axes = az.plot_trace(
    az_idata_prior,
    backend_kwargs={"constrained_layout":True},
    figsize=(10,20),
)

for i, axes_pair in enumerate(axes):
    ax1 = axes_pair[0]
    ax2 = axes_pair[1]
    ax1.set_xlabel("parameter value")
    ax1.set_ylabel("density value")
    ax2.set_xlabel("number of iterations")
    ax2.set_ylabel("parameter value")
t0, t0, t1, t1, t2, t2, t3, t3, t4, t4, t5, t5, t6, t6, t7, t7, v0, v0, v1, v1, v2, v2, v3, v3, v4, v4, v5, v5, v6, v6, v7, v7, v8, v8

Sample the posterior#

inversion_fixed_d_sampler = cofi.Inversion(sw_problem, inv_options_fixed_d_sampling)
inv_result_fixed_d_sampler = inversion_fixed_d_sampler.run()
  0%|          | 0/2000 [00:00<?, ?it/s]
  0%|          | 6/2000 [00:00<00:36, 54.24it/s]
  1%|          | 12/2000 [00:00<00:38, 51.48it/s]
  1%|          | 18/2000 [00:00<00:39, 49.84it/s]
  1%|          | 23/2000 [00:00<00:41, 47.93it/s]
  1%|▏         | 28/2000 [00:00<00:40, 48.47it/s]
  2%|▏         | 33/2000 [00:00<00:40, 48.67it/s]
  2%|▏         | 38/2000 [00:00<00:40, 48.85it/s]
  2%|▏         | 43/2000 [00:00<00:40, 48.92it/s]
  2%|▏         | 49/2000 [00:00<00:39, 49.59it/s]
  3%|▎         | 55/2000 [00:01<00:39, 49.82it/s]
  3%|▎         | 61/2000 [00:01<00:38, 50.25it/s]
  3%|▎         | 67/2000 [00:01<00:36, 52.40it/s]
  4%|▎         | 73/2000 [00:01<00:36, 52.13it/s]
  4%|▍         | 79/2000 [00:01<00:36, 53.22it/s]
  4%|▍         | 85/2000 [00:01<00:35, 54.39it/s]
  5%|▍         | 91/2000 [00:01<00:34, 55.56it/s]
  5%|▍         | 97/2000 [00:01<00:34, 55.64it/s]
  5%|▌         | 103/2000 [00:01<00:33, 56.56it/s]
  5%|▌         | 109/2000 [00:02<00:32, 57.31it/s]
  6%|▌         | 115/2000 [00:02<00:32, 57.95it/s]
  6%|▌         | 122/2000 [00:02<00:31, 59.57it/s]
  6%|▋         | 129/2000 [00:02<00:31, 60.17it/s]
  7%|▋         | 136/2000 [00:02<00:29, 62.51it/s]
  7%|▋         | 143/2000 [00:02<00:29, 62.73it/s]
  8%|▊         | 150/2000 [00:02<00:29, 62.32it/s]
  8%|▊         | 157/2000 [00:02<00:29, 63.44it/s]
  8%|▊         | 165/2000 [00:02<00:27, 65.93it/s]
  9%|▊         | 172/2000 [00:03<00:28, 64.89it/s]
  9%|▉         | 179/2000 [00:03<00:27, 65.22it/s]
  9%|▉         | 186/2000 [00:03<00:28, 64.71it/s]
 10%|▉         | 193/2000 [00:03<00:27, 65.36it/s]
 10%|█         | 200/2000 [00:03<00:27, 65.78it/s]
 10%|█         | 207/2000 [00:03<00:27, 65.61it/s]
 11%|█         | 214/2000 [00:03<00:27, 66.03it/s]
 11%|█         | 221/2000 [00:03<00:26, 67.10it/s]
 11%|█▏        | 228/2000 [00:03<00:26, 66.84it/s]
 12%|█▏        | 236/2000 [00:04<00:26, 67.61it/s]
 12%|█▏        | 243/2000 [00:04<00:26, 67.16it/s]
 13%|█▎        | 251/2000 [00:04<00:25, 68.99it/s]
 13%|█▎        | 259/2000 [00:04<00:24, 70.02it/s]
 13%|█▎        | 266/2000 [00:04<00:25, 69.08it/s]
 14%|█▎        | 273/2000 [00:04<00:24, 69.24it/s]
 14%|█▍        | 281/2000 [00:04<00:24, 69.78it/s]
 14%|█▍        | 288/2000 [00:04<00:25, 68.01it/s]
 15%|█▍        | 295/2000 [00:04<00:24, 68.52it/s]
 15%|█▌        | 303/2000 [00:04<00:24, 70.23it/s]
 16%|█▌        | 311/2000 [00:05<00:23, 71.20it/s]
 16%|█▌        | 319/2000 [00:05<00:23, 70.43it/s]
 16%|█▋        | 327/2000 [00:05<00:23, 70.86it/s]
 17%|█▋        | 335/2000 [00:05<00:23, 71.23it/s]
 17%|█▋        | 343/2000 [00:05<00:23, 70.04it/s]
 18%|█▊        | 351/2000 [00:05<00:23, 70.82it/s]
 18%|█▊        | 359/2000 [00:05<00:23, 70.90it/s]
 18%|█▊        | 367/2000 [00:05<00:22, 73.29it/s]
 19%|█▉        | 376/2000 [00:05<00:21, 75.44it/s]
 19%|█▉        | 384/2000 [00:06<00:21, 75.76it/s]
 20%|█▉        | 392/2000 [00:06<00:21, 73.93it/s]
 20%|██        | 401/2000 [00:06<00:21, 75.54it/s]
 20%|██        | 409/2000 [00:06<00:21, 74.27it/s]
 21%|██        | 417/2000 [00:06<00:21, 74.89it/s]
 21%|██▏       | 426/2000 [00:06<00:20, 76.31it/s]
 22%|██▏       | 435/2000 [00:06<00:20, 77.89it/s]
 22%|██▏       | 443/2000 [00:06<00:20, 77.65it/s]
 23%|██▎       | 452/2000 [00:06<00:19, 78.91it/s]
 23%|██▎       | 460/2000 [00:07<00:19, 77.57it/s]
 23%|██▎       | 468/2000 [00:07<00:20, 75.16it/s]
 24%|██▍       | 476/2000 [00:07<00:20, 75.36it/s]
 24%|██▍       | 485/2000 [00:07<00:19, 77.76it/s]
 25%|██▍       | 493/2000 [00:07<00:19, 78.01it/s]
 25%|██▌       | 502/2000 [00:07<00:18, 79.17it/s]
 26%|██▌       | 511/2000 [00:07<00:18, 81.49it/s]
 26%|██▌       | 520/2000 [00:07<00:17, 82.63it/s]
 26%|██▋       | 529/2000 [00:07<00:17, 83.33it/s]
 27%|██▋       | 538/2000 [00:08<00:17, 84.63it/s]
 27%|██▋       | 547/2000 [00:08<00:17, 84.95it/s]
 28%|██▊       | 556/2000 [00:08<00:17, 82.65it/s]
 28%|██▊       | 565/2000 [00:08<00:17, 80.78it/s]
 29%|██▊       | 574/2000 [00:08<00:17, 81.16it/s]
 29%|██▉       | 583/2000 [00:08<00:17, 81.31it/s]
 30%|██▉       | 592/2000 [00:08<00:16, 82.85it/s]
 30%|███       | 601/2000 [00:08<00:16, 82.32it/s]
 30%|███       | 610/2000 [00:08<00:17, 81.04it/s]
 31%|███       | 619/2000 [00:09<00:17, 80.66it/s]
 31%|███▏      | 628/2000 [00:09<00:17, 80.38it/s]
 32%|███▏      | 637/2000 [00:09<00:17, 79.31it/s]
 32%|███▏      | 646/2000 [00:09<00:16, 80.86it/s]
 33%|███▎      | 655/2000 [00:09<00:16, 80.91it/s]
 33%|███▎      | 664/2000 [00:09<00:16, 81.14it/s]
 34%|███▎      | 673/2000 [00:09<00:16, 80.08it/s]
 34%|███▍      | 682/2000 [00:09<00:16, 80.20it/s]
 35%|███▍      | 691/2000 [00:09<00:16, 81.15it/s]
 35%|███▌      | 700/2000 [00:10<00:16, 80.18it/s]
 35%|███▌      | 709/2000 [00:10<00:16, 80.09it/s]
 36%|███▌      | 718/2000 [00:10<00:16, 75.63it/s]
 36%|███▋      | 726/2000 [00:10<00:17, 74.22it/s]
 37%|███▋      | 734/2000 [00:10<00:17, 74.18it/s]
 37%|███▋      | 742/2000 [00:10<00:16, 75.46it/s]
 38%|███▊      | 750/2000 [00:10<00:16, 75.23it/s]
 38%|███▊      | 758/2000 [00:10<00:16, 74.65it/s]
 38%|███▊      | 766/2000 [00:10<00:16, 75.86it/s]
 39%|███▊      | 774/2000 [00:11<00:16, 76.11it/s]
 39%|███▉      | 782/2000 [00:11<00:15, 76.90it/s]
 40%|███▉      | 790/2000 [00:11<00:15, 76.44it/s]
 40%|███▉      | 798/2000 [00:11<00:15, 76.38it/s]
 40%|████      | 806/2000 [00:11<00:15, 75.05it/s]
 41%|████      | 815/2000 [00:11<00:15, 76.80it/s]
 41%|████      | 823/2000 [00:11<00:15, 76.48it/s]
 42%|████▏     | 831/2000 [00:11<00:15, 75.14it/s]
 42%|████▏     | 839/2000 [00:11<00:15, 75.49it/s]
 42%|████▏     | 848/2000 [00:12<00:15, 76.79it/s]
 43%|████▎     | 856/2000 [00:12<00:15, 75.02it/s]
 43%|████▎     | 865/2000 [00:12<00:14, 77.03it/s]
 44%|████▎     | 874/2000 [00:12<00:14, 78.43it/s]
 44%|████▍     | 883/2000 [00:12<00:13, 80.33it/s]
 45%|████▍     | 892/2000 [00:12<00:14, 78.19it/s]
 45%|████▌     | 900/2000 [00:12<00:14, 76.44it/s]
 45%|████▌     | 909/2000 [00:12<00:14, 77.42it/s]
 46%|████▌     | 917/2000 [00:12<00:13, 77.63it/s]
 46%|████▋     | 925/2000 [00:13<00:14, 75.36it/s]
 47%|████▋     | 934/2000 [00:13<00:13, 79.32it/s]
 47%|████▋     | 943/2000 [00:13<00:13, 80.46it/s]
 48%|████▊     | 953/2000 [00:13<00:12, 83.51it/s]
 48%|████▊     | 963/2000 [00:13<00:12, 85.98it/s]
 49%|████▊     | 973/2000 [00:13<00:11, 88.98it/s]
 49%|████▉     | 983/2000 [00:13<00:11, 91.03it/s]
 50%|████▉     | 993/2000 [00:13<00:11, 90.56it/s]
 50%|█████     | 1003/2000 [00:13<00:11, 88.83it/s]
 51%|█████     | 1013/2000 [00:13<00:11, 88.94it/s]
 51%|█████     | 1022/2000 [00:14<00:11, 87.88it/s]
 52%|█████▏    | 1031/2000 [00:14<00:10, 88.43it/s]
 52%|█████▏    | 1041/2000 [00:14<00:10, 89.95it/s]
 52%|█████▎    | 1050/2000 [00:14<00:10, 89.05it/s]
 53%|█████▎    | 1059/2000 [00:14<00:10, 88.15it/s]
 53%|█████▎    | 1068/2000 [00:14<00:10, 85.45it/s]
 54%|█████▍    | 1077/2000 [00:14<00:10, 84.84it/s]
 54%|█████▍    | 1087/2000 [00:14<00:10, 88.10it/s]
 55%|█████▍    | 1096/2000 [00:14<00:10, 87.93it/s]
 55%|█████▌    | 1106/2000 [00:15<00:10, 88.94it/s]
 56%|█████▌    | 1115/2000 [00:15<00:10, 86.39it/s]
 56%|█████▌    | 1124/2000 [00:15<00:10, 87.28it/s]
 57%|█████▋    | 1134/2000 [00:15<00:09, 90.14it/s]
 57%|█████▋    | 1144/2000 [00:15<00:09, 90.27it/s]
 58%|█████▊    | 1154/2000 [00:15<00:09, 90.81it/s]
 58%|█████▊    | 1164/2000 [00:15<00:09, 90.63it/s]
 59%|█████▊    | 1174/2000 [00:15<00:09, 90.80it/s]
 59%|█████▉    | 1184/2000 [00:15<00:09, 89.91it/s]
 60%|█████▉    | 1193/2000 [00:16<00:09, 88.64it/s]
 60%|██████    | 1203/2000 [00:16<00:08, 91.67it/s]
 61%|██████    | 1213/2000 [00:16<00:08, 90.47it/s]
 61%|██████    | 1223/2000 [00:16<00:08, 90.66it/s]
 62%|██████▏   | 1233/2000 [00:16<00:08, 88.48it/s]
 62%|██████▏   | 1243/2000 [00:16<00:08, 90.14it/s]
 63%|██████▎   | 1253/2000 [00:16<00:08, 88.79it/s]
 63%|██████▎   | 1262/2000 [00:16<00:08, 87.08it/s]
 64%|██████▎   | 1271/2000 [00:16<00:08, 85.98it/s]
 64%|██████▍   | 1281/2000 [00:17<00:08, 86.99it/s]
 64%|██████▍   | 1290/2000 [00:17<00:08, 85.44it/s]
 65%|██████▍   | 1299/2000 [00:17<00:08, 84.40it/s]
 65%|██████▌   | 1308/2000 [00:17<00:08, 85.69it/s]
 66%|██████▌   | 1317/2000 [00:17<00:07, 86.10it/s]
 66%|██████▋   | 1326/2000 [00:17<00:07, 86.69it/s]
 67%|██████▋   | 1335/2000 [00:17<00:07, 87.10it/s]
 67%|██████▋   | 1345/2000 [00:17<00:07, 88.59it/s]
 68%|██████▊   | 1354/2000 [00:17<00:07, 88.83it/s]
 68%|██████▊   | 1363/2000 [00:17<00:07, 86.27it/s]
 69%|██████▊   | 1373/2000 [00:18<00:07, 88.47it/s]
 69%|██████▉   | 1383/2000 [00:18<00:06, 90.11it/s]
 70%|██████▉   | 1393/2000 [00:18<00:06, 90.83it/s]
 70%|███████   | 1403/2000 [00:18<00:06, 90.80it/s]
 71%|███████   | 1413/2000 [00:18<00:06, 90.66it/s]
 71%|███████   | 1423/2000 [00:18<00:06, 88.33it/s]
 72%|███████▏  | 1433/2000 [00:18<00:06, 91.00it/s]
 72%|███████▏  | 1443/2000 [00:18<00:06, 89.36it/s]
 73%|███████▎  | 1452/2000 [00:18<00:06, 89.25it/s]
 73%|███████▎  | 1462/2000 [00:19<00:05, 90.27it/s]
 74%|███████▎  | 1472/2000 [00:19<00:05, 92.46it/s]
 74%|███████▍  | 1482/2000 [00:19<00:05, 92.25it/s]
 75%|███████▍  | 1492/2000 [00:19<00:05, 94.29it/s]
 75%|███████▌  | 1502/2000 [00:19<00:05, 94.24it/s]
 76%|███████▌  | 1512/2000 [00:19<00:05, 90.75it/s]
 76%|███████▌  | 1522/2000 [00:19<00:05, 90.63it/s]
 77%|███████▋  | 1532/2000 [00:19<00:05, 90.08it/s]
 77%|███████▋  | 1542/2000 [00:19<00:05, 87.32it/s]
 78%|███████▊  | 1552/2000 [00:20<00:05, 89.42it/s]
 78%|███████▊  | 1561/2000 [00:20<00:04, 89.17it/s]
 78%|███████▊  | 1570/2000 [00:20<00:04, 88.48it/s]
 79%|███████▉  | 1580/2000 [00:20<00:04, 91.49it/s]
 80%|███████▉  | 1590/2000 [00:20<00:04, 93.91it/s]
 80%|████████  | 1600/2000 [00:20<00:04, 93.83it/s]
 80%|████████  | 1610/2000 [00:20<00:04, 92.49it/s]
 81%|████████  | 1620/2000 [00:20<00:04, 91.78it/s]
 82%|████████▏ | 1630/2000 [00:20<00:04, 91.40it/s]
 82%|████████▏ | 1640/2000 [00:20<00:03, 92.69it/s]
 82%|████████▎ | 1650/2000 [00:21<00:03, 92.52it/s]
 83%|████████▎ | 1660/2000 [00:21<00:03, 93.87it/s]
 84%|████████▎ | 1670/2000 [00:21<00:03, 92.54it/s]
 84%|████████▍ | 1680/2000 [00:21<00:03, 93.90it/s]
 84%|████████▍ | 1690/2000 [00:21<00:03, 95.04it/s]
 85%|████████▌ | 1700/2000 [00:21<00:03, 95.48it/s]
 86%|████████▌ | 1710/2000 [00:21<00:03, 92.08it/s]
 86%|████████▌ | 1720/2000 [00:21<00:03, 91.86it/s]
 86%|████████▋ | 1730/2000 [00:21<00:02, 90.85it/s]
 87%|████████▋ | 1740/2000 [00:22<00:02, 91.10it/s]
 88%|████████▊ | 1750/2000 [00:22<00:02, 91.60it/s]
 88%|████████▊ | 1760/2000 [00:22<00:02, 91.66it/s]
 88%|████████▊ | 1770/2000 [00:22<00:02, 92.10it/s]
 89%|████████▉ | 1780/2000 [00:22<00:02, 92.53it/s]
 90%|████████▉ | 1790/2000 [00:22<00:02, 91.77it/s]
 90%|█████████ | 1800/2000 [00:22<00:02, 91.64it/s]
 90%|█████████ | 1810/2000 [00:22<00:02, 93.29it/s]
 91%|█████████ | 1820/2000 [00:22<00:01, 90.77it/s]
 92%|█████████▏| 1830/2000 [00:23<00:01, 91.63it/s]
 92%|█████████▏| 1840/2000 [00:23<00:01, 92.17it/s]
 93%|█████████▎| 1851/2000 [00:23<00:01, 95.04it/s]
 93%|█████████▎| 1861/2000 [00:23<00:01, 94.69it/s]
 94%|█████████▎| 1871/2000 [00:23<00:01, 93.22it/s]
 94%|█████████▍| 1881/2000 [00:23<00:01, 91.52it/s]
 95%|█████████▍| 1891/2000 [00:23<00:01, 90.68it/s]
 95%|█████████▌| 1901/2000 [00:23<00:01, 92.39it/s]
 96%|█████████▌| 1911/2000 [00:23<00:00, 89.69it/s]
 96%|█████████▌| 1921/2000 [00:24<00:00, 90.79it/s]
 97%|█████████▋| 1931/2000 [00:24<00:00, 91.76it/s]
 97%|█████████▋| 1941/2000 [00:24<00:00, 93.58it/s]
 98%|█████████▊| 1951/2000 [00:24<00:00, 94.35it/s]
 98%|█████████▊| 1961/2000 [00:24<00:00, 94.72it/s]
 99%|█████████▊| 1971/2000 [00:24<00:00, 92.06it/s]
 99%|█████████▉| 1981/2000 [00:24<00:00, 90.92it/s]
100%|█████████▉| 1991/2000 [00:24<00:00, 91.74it/s]
100%|██████████| 2000/2000 [00:24<00:00, 80.36it/s]
sampler = inv_result_fixed_d_sampler.sampler
az_idata = az.from_emcee(sampler, var_names=labels)
az_idata.get("posterior")
<xarray.Dataset> Size: 11MB
Dimensions:  (chain: 40, draw: 2000)
Coordinates:
  * chain    (chain) int64 320B 0 1 2 3 4 5 6 7 8 ... 31 32 33 34 35 36 37 38 39
  * draw     (draw) int64 16kB 0 1 2 3 4 5 6 ... 1994 1995 1996 1997 1998 1999
Data variables: (12/17)
    t0       (chain, draw) float64 640kB 15.29 15.35 15.35 ... 13.24 13.21 13.21
    t1       (chain, draw) float64 640kB 15.07 15.13 15.13 ... 5.63 5.062 5.062
    t2       (chain, draw) float64 640kB 14.89 15.14 15.14 ... 14.81 14.22 14.22
    t3       (chain, draw) float64 640kB 15.05 15.21 15.21 ... 14.64 15.42 15.42
    t4       (chain, draw) float64 640kB 15.48 15.05 15.05 ... 16.52 16.45 16.45
    t5       (chain, draw) float64 640kB 15.24 15.07 15.07 ... 22.81 25.11 25.11
    ...       ...
    v3       (chain, draw) float64 640kB 4.361 4.171 4.171 ... 3.854 3.704 3.704
    v4       (chain, draw) float64 640kB 4.114 4.297 4.297 ... 4.648 4.685 4.685
    v5       (chain, draw) float64 640kB 3.979 3.89 3.89 ... 4.046 4.19 4.19
    v6       (chain, draw) float64 640kB 4.4 4.229 4.229 ... 4.67 4.649 4.649
    v7       (chain, draw) float64 640kB 4.228 4.164 4.164 ... 4.72 4.706 4.706
    v8       (chain, draw) float64 640kB 4.349 4.417 4.417 ... 4.399 4.378 4.378
Attributes:
    created_at:                 2024-04-19T05:04:03.959899+00:00
    arviz_version:              0.18.0
    inference_library:          emcee
    inference_library_version:  3.1.5


# plot the model and d_pred for starting model
axes = plot_model_and_data(model=init_model, label_model="initial model for damped solution", color_model="black",
                           forward_func=forward_sw, periods=synth_d_periods,
                           label_d_pred="d_pred from initial model for damped solution", color_d_pred="black")

# plot the model and d_pred for true model
plot_model_and_data(model=true_model, label_model="true model", color_model="orange",
                    forward_func=forward_sw, periods=synth_d_periods,
                    label_d_pred="d_pred from true model", color_d_pred="orange", axes=axes)

# plot the model and d_pred for damped solution, and d_obs
plot_model_and_data(model=inv_result_optimiser.model, label_model="damped solution", color_model="green",
                    forward_func=forward_sw, periods=synth_d_periods,
                    label_d_pred="d_pred from damped solution", color_d_pred="green", axes=axes);

# plot randomly selected samples and data predictions from samples
flat_samples = sampler.get_chain(discard=500, thin=500, flat=True)
rand_indices = np.random.randint(len(flat_samples), size=100)
for idx in rand_indices:
    sample = flat_samples[idx]
    label_model = "sample models" if idx == 0 else None
    label_d_pred = "d_pred from samples" if idx == 0 else None
    plot_model_and_data(model=sample, label_model=label_model, color_model="gray",
                        forward_func=forward_sw, periods=synth_d_periods,
                        label_d_pred=label_d_pred, color_d_pred="gray", axes=axes, light_thin=True)

# plot d_obs
plot_data(d_obs, synth_d_periods, ax=axes[1], scatter=True, color="red", s=20, label="d_obs")

axes[0].set_ylim(170)
axes[0].legend(loc="lower center", bbox_to_anchor=(0.5, -0.46))
axes[1].legend(loc="lower center", bbox_to_anchor=(0.5, -0.46));
model, data
<matplotlib.legend.Legend object at 0x7fd0a9095cd0>
axes = az.plot_trace(
    az_idata,
    backend_kwargs={"constrained_layout":True},
    figsize=(10,20)
)

for i, axes_pair in enumerate(axes):
    ax1 = axes_pair[0]
    ax2 = axes_pair[1]
    ax1.axvline(true_model[i], linestyle='dotted', color='red')
    ax1.set_xlabel("parameter value")
    ax1.set_ylabel("density value")
    ax2.set_xlabel("number of iterations")
    ax2.set_ylabel("parameter value")
t0, t0, t1, t1, t2, t2, t3, t3, t4, t4, t5, t5, t6, t6, t7, t7, v0, v0, v1, v1, v2, v2, v3, v3, v4, v4, v5, v5, v6, v6, v7, v7, v8, v8

More steps?

Due to time restrictions, we have only run 2_000 steps above, which might be enough for illustration purpose and sanity check, but are not enough for an actual inversion.

On a seperate experiment, we ran 200_000 steps instead and produced the following samples plot.

Fixed-dimensional sampling results with 200_000 steps

Fixed-dimensional sampling results with 200_000 steps#

Trans-dimensional sampling#

Prepare utilities for trans-dimensional sampling

def forward_for_bayesbay(state):
    vs = state["voronoi"]["vs"]
    voronoi_sites = state["voronoi"]["discretization"]
    depths = (voronoi_sites[:-1] + voronoi_sites[1:]) / 2
    thicknesses = depths - np.insert(depths[:-1], 0, 0)
    model = form_layercake_model(thicknesses, vs)
    return forward_sw(model, synth_d_periods)
targets = [bayesbay.Target("rayleigh", d_obs, covariance_mat_inv=1/noise_level**2)]
fwd_funcs = [forward_for_bayesbay]
my_log_likelihood = bayesbay.LogLikelihood(targets, fwd_funcs)
param_vs = bayesbay.prior.UniformPrior(
    name="vs",
    vmin=[2.7, 3.2, 3.75],
    vmax=[4, 4.75, 5],
    position=[0, 40, 80],
    perturb_std=0.15
)

def param_vs_initialize(param, positions):
    vmin, vmax = param.get_vmin_vmax(positions)
    sorted_vals = np.sort(np.random.uniform(vmin, vmax, positions.size))
    for i in range (len(sorted_vals)):
        val = sorted_vals[i]
        vmin_i = vmin if np.isscalar(vmin) else vmin[i]
        vmax_i = vmax if np.isscalar(vmax) else vmax[i]
        if val < vmin_i or val > vmax_i:
            if val > vmax_i: sorted_vals[i] = vmax_i
            if val < vmin_i: sorted_vals[i] = vmin_i
    return sorted_vals

param_vs.set_custom_initialize(param_vs_initialize)
parameterization = bayesbay.parameterization.Parameterization(
    bayesbay.discretization.Voronoi1D(
        name="voronoi",
        vmin=0,
        vmax=150,
        perturb_std=10,
        n_dimensions=None,
        n_dimensions_min=4,
        n_dimensions_max=15,
        parameters=[param_vs],
    )
)
my_perturbation_funcs = parameterization.perturbation_functions
n_chains=12
walkers_start = []
for i in range(n_chains):
    walkers_start.append(parameterization.initialize())

Define ``InversionOptions``

inv_options_trans_d_sampling = cofi.InversionOptions()
inv_options_trans_d_sampling.set_tool("bayesbay")
inv_options_trans_d_sampling.set_params(
    walkers_starting_states=walkers_start,
    perturbation_funcs=my_perturbation_funcs,
    log_like_ratio_func=my_log_likelihood,
    n_chains=n_chains,
    n_iterations=3_000,
    burnin_iterations=1_000,
    verbose=False,
    save_every=200,
)

Define ``Inversion`` and run

inversion_trans_d_sampler = cofi.Inversion(sw_problem, inv_options_trans_d_sampling)
inv_result_trans_d_sampler = inversion_trans_d_sampler.run()
inverted_models = inv_result_trans_d_sampler.models
samples = []
for v, vs in zip(inverted_models["voronoi.discretization"], inverted_models["voronoi.vs"]):
    sample = form_voronoi_model(v, vs)
    samples.append(voronoi_to_layercake(sample))
# plot the model and d_pred for starting model
axes = plot_model_and_data(model=init_model, label_model="initial model for damped solution", color_model="black",
                           forward_func=forward_sw, periods=synth_d_periods,
                           label_d_pred="d_pred from initial model for damped solution", color_d_pred="black")

# plot the model and d_pred for true model
plot_model_and_data(model=true_model, label_model="true model", color_model="orange",
                    forward_func=forward_sw, periods=synth_d_periods,
                    label_d_pred="d_pred from true model", color_d_pred="orange", axes=axes)

# plot the model and d_pred for damped solution, and d_obs
plot_model_and_data(model=inv_result_optimiser.model, label_model="damped solution", color_model="green",
                    forward_func=forward_sw, periods=synth_d_periods,
                    label_d_pred="d_pred from damped solution", color_d_pred="green", axes=axes);

# plot randomly selected samples and data predictions from samples
for i, sample in enumerate(samples):
    label_model = "sample models" if i == 0 else None
    label_d_pred = "d_pred from samples" if i == 0 else None
    plot_model_and_data(model=sample, label_model=label_model, color_model="gray",
                        forward_func=forward_sw, periods=synth_d_periods,
                        label_d_pred=label_d_pred, color_d_pred="gray", axes=axes, light_thin=True)

# plot d_obs
plot_data(d_obs, synth_d_periods, ax=axes[1], scatter=True, color="red", s=20, label="d_obs")

axes[0].set_ylim(170)
axes[0].legend(loc="lower center", bbox_to_anchor=(0.5, -0.46))
axes[1].legend(loc="lower center", bbox_to_anchor=(0.5, -0.46));
model, data
<matplotlib.legend.Legend object at 0x7fd245d45a10>

Field data example#

Read data#

Rayleigh observations

file_surf_data = "../../data/sw_rf_joint/data/SURF/nnall.dsp"

with open(file_surf_data, "r") as file:
    lines = file.readlines()
    surf_data = []
    for line in lines:
        row = line.strip().split()
        if "C" in row:
            surf_data.append([float(e) for e in row[5:8]])

field_d = np.array(surf_data)
field_d_periods = field_d[:,0]
field_d_obs = field_d[:,1]
ax = plot_data(field_d_obs, field_d_periods, color="brown", s=5, scatter=True,
             label="d_obs")
ax.legend();
data
<matplotlib.legend.Legend object at 0x7fd25f7d5ed0>

Reference good model

file_end_mod = "../../data/sw_rf_joint/data/SURF/end.mod"

with open(file_end_mod, "r") as file:
    lines = file.readlines()
    ref_good_model = []
    for line in lines[12:]:
        row = line.strip().split()
        ref_good_model.append([float(row[0]), float(row[2])])

ref_good_model = np.array(ref_good_model)
ref_good_model = form_layercake_model(ref_good_model[:-1,0], ref_good_model[:,1])
_, ax = plt.subplots(figsize=(4,6))
plot_model(ref_good_model, ax=ax, alpha=1);
model
<Axes: title={'center': 'model'}, xlabel='Vs (km/s)', ylabel='Depth (km)'>

Modified forward utility#

def forward_sw_interp(model, periods=field_d_periods):
    pysurf_periods = np.logspace(
        np.log(np.min(periods)),
        np.log(np.max(periods+1)),
        60,
        base=np.e,
    )
    pysurf_dpred = forward_sw(model, pysurf_periods)
    interp_func = scipy.interpolate.interp1d(pysurf_periods,
                                             pysurf_dpred,
                                             fill_value="extrapolate")
    dpred = interp_func(periods)
    return dpred

Optimisation#

n_dims = 29

init_thicknesses = np.ones((n_dims//2,)) * 10
init_vs = np.ones((n_dims//2+1,)) * 4.0
init_model = form_layercake_model(init_thicknesses, init_vs)
my_reg = cofi.utils.QuadraticReg(
    weighting_matrix="damping",
    model_shape=(n_dims,),
    reference_model=init_model
)

Optimisation with no damping#

lamda = 0

kwargs = {
    "fwd": forward_sw_interp,
    "periods": field_d_periods,
    "d_obs": field_d_obs,
    "lamda": lamda,
}
sw_field_problem_no_reg = cofi.BaseProblem()
sw_field_problem_no_reg.set_objective(my_objective, kwargs=kwargs)
sw_field_problem_no_reg.set_gradient(my_objective_gradient, kwargs=kwargs)
sw_field_problem_no_reg.set_hessian(my_objective_hessian, kwargs=kwargs)
sw_field_problem_no_reg.set_initial_model(init_model)

Define ``Inversion`` and run

inv_sw_field_problem_no_reg = cofi.Inversion(sw_field_problem_no_reg, inv_options_optimiser)
inv_result_sw_field_no_reg = inv_sw_field_problem_no_reg.run()

Plot results

# plot the model and d_pred for starting model
axes = plot_model_and_data(model=init_model, label_model="starting model", color_model="black",
                           forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                           label_d_pred="d_pred from starting model", color_d_pred="black")

# plot the model and d_pred for true model
plot_model_and_data(model=ref_good_model, label_model="reference good model", color_model="red",
                    forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                    label_d_pred="d_pred from reference good model", color_d_pred="red", axes=axes)

# plot the model and d_pred for inverted model, and d_obs
plot_model_and_data(model=inv_result_sw_field_no_reg.model,
                    label_model="inverted model from field data", color_model="purple",
                    forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                    label_d_pred="d_pred from inverted model", color_d_pred="purple", axes=axes)

# plot d_obs
plot_data(field_d_obs, field_d_periods, ax=axes[1], scatter=True, color="orange", s=8, label="d_obs")

axes[0].set_ylim(100, 0)
axes[0].legend(loc="lower center", bbox_to_anchor=(0.5, -0.4))
axes[1].legend(loc="lower center", bbox_to_anchor=(0.5, -0.46));
model, data
<matplotlib.legend.Legend object at 0x7fd245b278d0>

Optimal damping#

Again, we would like to find a good regularisation factor.

lambdas = np.logspace(-6, 6, 15)

my_lcurve_problems = []
for lamb in lambdas:
    my_problem = cofi.BaseProblem()
    kwargs = {
        "fwd": forward_sw_interp,
        "periods": field_d_periods,
        "d_obs": field_d_obs,
        "lamda": lamb,
    }
    my_problem.set_objective(my_objective, kwargs=kwargs)
    my_problem.set_gradient(my_objective_gradient, kwargs=kwargs)
    my_problem.set_hessian(my_objective_hessian, kwargs=kwargs)
    my_problem.set_initial_model(init_model)
    my_lcurve_problems.append(my_problem)

def my_callback(inv_result, i):
    m = inv_result.model
    res_norm = np.linalg.norm(forward_sw_interp(m, field_d_periods) - field_d_obs)
    reg_norm = np.sqrt(my_reg(m))
    print(f"Finished inversion with lambda={lambdas[i]}: {res_norm}, {reg_norm}")
    return res_norm, reg_norm

my_inversion_pool = cofi.utils.InversionPool(
    list_of_inv_problems=my_lcurve_problems,
    list_of_inv_options=inv_options_optimiser,
    callback=my_callback,
    parallel=False
)
all_res, all_cb_returns = my_inversion_pool.run()

l_curve_points = list(zip(*all_cb_returns))
Finished inversion with lambda=1e-06: 1.9540941311396671, 26.760380619787956
Finished inversion with lambda=7.196856730011514e-06: 1.9548537779696775, 23.084178681310398
Finished inversion with lambda=5.1794746792312125e-05: 1.9672756794905297, 10.950500473754316
Finished inversion with lambda=0.0003727593720314938: 1.9699341235211631, 7.634041352559792
Finished inversion with lambda=0.0026826957952797246: 1.9782390541679031, 4.552509100476203
Finished inversion with lambda=0.019306977288832496: 2.0050549438091747, 2.3442273803041136
Finished inversion with lambda=0.1389495494373136: 2.0393073369018655, 1.6477206878151607
Finished inversion with lambda=1.0: 2.112939266124699, 1.3870331002558811
Finished inversion with lambda=7.196856730011514: 2.7878693578372693, 0.9907601514482579
Finished inversion with lambda=51.79474679231202: 4.890126438918003, 0.34475194170977674
Finished inversion with lambda=372.7593720314938: 5.957321022746526, 0.06129962536156985
Finished inversion with lambda=2682.6957952797275: 6.160087164630214, 0.008874921981364005
Finished inversion with lambda=19306.977288832455: 6.189744923132511, 0.001240946449950785
Finished inversion with lambda=138949.5494373136: 6.193898179384247, 0.0001725411516554573
Finished inversion with lambda=1000000.0: 6.194457488485488, 2.3974628324356084e-05
# print all the lambdas
lambdas
array([1.00000000e-06, 7.19685673e-06, 5.17947468e-05, 3.72759372e-04,
       2.68269580e-03, 1.93069773e-02, 1.38949549e-01, 1.00000000e+00,
       7.19685673e+00, 5.17947468e+01, 3.72759372e+02, 2.68269580e+03,
       1.93069773e+04, 1.38949549e+05, 1.00000000e+06])
# plot the L-curve
res_norm, reg_norm = l_curve_points
plt.plot(reg_norm, res_norm, '.-')
plt.xlabel(r'Norm of regularization term $||Wm||_2$')
plt.ylabel(r'Norm of residual $||g(m)-d||_2$')
for i in range(0, len(lambdas), 2):
    plt.annotate(f'{lambdas[i]:.1e}', (reg_norm[i], res_norm[i]), fontsize=8)
1D rayleigh wave phase velocity inversion

Optimisation with damping#

lamda = 0.14

kwargs = {
    "fwd": forward_sw_interp,
    "periods": field_d_periods,
    "d_obs": field_d_obs,
    "lamda": lamda,
}
sw_field_problem = cofi.BaseProblem()
sw_field_problem.set_objective(my_objective, kwargs=kwargs)
sw_field_problem.set_gradient(my_objective_gradient, kwargs=kwargs)
sw_field_problem.set_hessian(my_objective_hessian, kwargs=kwargs)
sw_field_problem.set_initial_model(init_model)

Define ``Inversion`` and run

inv_sw_field_problem = cofi.Inversion(sw_field_problem, inv_options_optimiser)
inv_result_sw_field = inv_sw_field_problem.run()

Plot results

# plot the model and d_pred for starting model
axes = plot_model_and_data(model=init_model, label_model="starting model", color_model="black",
                           forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                           label_d_pred="d_pred from starting model", color_d_pred="black")

# plot the model and d_pred for true model
plot_model_and_data(model=ref_good_model, label_model="reference good model", color_model="red",
                    forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                    label_d_pred="d_pred from reference good model", color_d_pred="red", axes=axes)

# plot the model and d_pred for inverted model, and d_obs
plot_model_and_data(model=inv_result_sw_field.model,
                    label_model="inverted model from field data", color_model="purple",
                    forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                    label_d_pred="d_pred from inverted model", color_d_pred="purple", axes=axes)

# plot d_obs
plot_data(field_d_obs, field_d_periods, ax=axes[1], scatter=True, color="orange", s=8, label="d_obs")

axes[0].set_ylim(100, 0)
axes[0].legend(loc="lower center", bbox_to_anchor=(0.5, -0.4))
axes[1].legend(loc="lower center", bbox_to_anchor=(0.5, -0.46));
model, data
<matplotlib.legend.Legend object at 0x7fd25f6f6ed0>

Fixed-dimensional sampling#

We are going to use the same sets of log prior, and we will rewrite the log likelihood function to apply on the field data.

thick_min = 3
thick_max = 10
vs_min = 2
vs_max = 5.5

def my_log_prior(model):
    thicknesses, vs = split_layercake_model(model)
    thicknesses_out_of_bounds = (thicknesses < thick_min) | (thicknesses > thick_max)
    vs_out_of_bounds = (vs < vs_min) | (vs > vs_max)
    if np.any(thicknesses_out_of_bounds) or np.any(vs_out_of_bounds):
        return float("-inf")
    log_prior = - np.log(thick_max - thick_min) * len(thicknesses) \
                - np.log(vs_max - vs_min) * len(vs)
    return log_prior
# estimate the data noise
d_pred_from_optimiser = forward_sw_interp(inv_result_sw_field.model, field_d_periods)
noise_level = np.std(field_d_obs - d_pred_from_optimiser)
Cdinv = np.eye(len(field_d_obs))/(noise_level**2)

print(f"Estimated noise level: {noise_level}")
Estimated noise level: 0.10587785944399027
def my_log_likelihood(model):
    try:
        d_pred = forward_sw_interp(model, field_d_periods)
    except:
        return float("-inf")
    residual = field_d_obs - d_pred
    return -0.5 * residual @ (Cdinv @ residual).T
n_walkers = 60

my_walkers_start = np.ones((n_walkers, n_dims)) * inv_result_sw_field.model
for i in range(n_walkers):
    my_walkers_start[i,:] += np.random.normal(0, 0.3, n_dims)
sw_field_problem.set_log_prior(my_log_prior)
sw_field_problem.set_log_likelihood(my_log_likelihood)

Define ``InversionOptions``

inv_options_fixed_d_sampling = cofi.InversionOptions()
inv_options_fixed_d_sampling.set_tool("emcee")
inv_options_fixed_d_sampling.set_params(
    nwalkers=n_walkers,
    nsteps=20_000,
    initial_state=my_walkers_start,
    skip_initial_state_check=True,
    progress=True
)

Sample the posterior#

inversion_fixed_d_sampler_field = cofi.Inversion(sw_field_problem,
                                                 inv_options_fixed_d_sampling)
inv_result_fixed_d_sampler_field = inversion_fixed_d_sampler_field.run()
  0%|          | 0/20000 [00:00<?, ?it/s]
  0%|          | 92/20000 [00:00<00:21, 914.29it/s]
  1%|          | 185/20000 [00:00<00:21, 918.70it/s]
  1%|▏         | 277/20000 [00:00<00:21, 917.00it/s]
  2%|▏         | 370/20000 [00:00<00:21, 919.11it/s]
  2%|▏         | 462/20000 [00:00<00:21, 913.80it/s]
  3%|▎         | 554/20000 [00:00<00:21, 915.57it/s]
  3%|▎         | 646/20000 [00:00<00:21, 908.01it/s]
  4%|▎         | 738/20000 [00:00<00:21, 911.56it/s]
  4%|▍         | 830/20000 [00:00<00:20, 913.42it/s]
  5%|▍         | 923/20000 [00:01<00:20, 916.00it/s]
  5%|▌         | 1015/20000 [00:01<00:20, 916.90it/s]
  6%|▌         | 1107/20000 [00:01<00:20, 913.38it/s]
  6%|▌         | 1199/20000 [00:01<00:20, 912.21it/s]
  6%|▋         | 1292/20000 [00:01<00:20, 914.73it/s]
  7%|▋         | 1384/20000 [00:01<00:20, 915.09it/s]
  7%|▋         | 1476/20000 [00:01<00:20, 914.98it/s]
  8%|▊         | 1568/20000 [00:01<00:20, 915.52it/s]
  8%|▊         | 1660/20000 [00:01<00:20, 913.92it/s]
  9%|▉         | 1752/20000 [00:01<00:19, 912.73it/s]
  9%|▉         | 1844/20000 [00:02<00:19, 913.30it/s]
 10%|▉         | 1936/20000 [00:02<00:19, 912.39it/s]
 10%|█         | 2028/20000 [00:02<00:19, 908.79it/s]
 11%|█         | 2120/20000 [00:02<00:19, 909.94it/s]
 11%|█         | 2211/20000 [00:02<00:19, 908.03it/s]
 12%|█▏        | 2303/20000 [00:02<00:19, 911.11it/s]
 12%|█▏        | 2395/20000 [00:02<00:19, 910.93it/s]
 12%|█▏        | 2487/20000 [00:02<00:19, 908.81it/s]
 13%|█▎        | 2578/20000 [00:02<00:19, 908.70it/s]
 13%|█▎        | 2670/20000 [00:02<00:19, 911.03it/s]
 14%|█▍        | 2762/20000 [00:03<00:18, 909.54it/s]
 14%|█▍        | 2854/20000 [00:03<00:18, 911.38it/s]
 15%|█▍        | 2946/20000 [00:03<00:18, 902.34it/s]
 15%|█▌        | 3037/20000 [00:03<00:18, 904.04it/s]
 16%|█▌        | 3130/20000 [00:03<00:18, 909.01it/s]
 16%|█▌        | 3221/20000 [00:03<00:18, 908.83it/s]
 17%|█▋        | 3312/20000 [00:03<00:18, 904.98it/s]
 17%|█▋        | 3403/20000 [00:03<00:18, 902.52it/s]
 17%|█▋        | 3495/20000 [00:03<00:18, 904.86it/s]
 18%|█▊        | 3587/20000 [00:03<00:18, 908.59it/s]
 18%|█▊        | 3680/20000 [00:04<00:17, 912.24it/s]
 19%|█▉        | 3772/20000 [00:04<00:17, 914.34it/s]
 19%|█▉        | 3864/20000 [00:04<00:17, 915.93it/s]
 20%|█▉        | 3956/20000 [00:04<00:17, 915.18it/s]
 20%|██        | 4049/20000 [00:04<00:17, 917.05it/s]
 21%|██        | 4141/20000 [00:04<00:17, 917.04it/s]
 21%|██        | 4233/20000 [00:04<00:17, 916.25it/s]
 22%|██▏       | 4325/20000 [00:04<00:17, 916.52it/s]
 22%|██▏       | 4417/20000 [00:04<00:16, 917.36it/s]
 23%|██▎       | 4510/20000 [00:04<00:16, 918.50it/s]
 23%|██▎       | 4603/20000 [00:05<00:16, 919.29it/s]
 23%|██▎       | 4695/20000 [00:05<00:16, 918.99it/s]
 24%|██▍       | 4787/20000 [00:05<00:16, 915.33it/s]
 24%|██▍       | 4879/20000 [00:05<00:16, 908.22it/s]
 25%|██▍       | 4972/20000 [00:05<00:16, 912.08it/s]
 25%|██▌       | 5064/20000 [00:05<00:16, 912.65it/s]
 26%|██▌       | 5156/20000 [00:05<00:16, 913.08it/s]
 26%|██▌       | 5248/20000 [00:05<00:16, 914.59it/s]
 27%|██▋       | 5340/20000 [00:05<00:16, 915.81it/s]
 27%|██▋       | 5432/20000 [00:05<00:15, 914.26it/s]
 28%|██▊       | 5524/20000 [00:06<00:15, 910.87it/s]
 28%|██▊       | 5616/20000 [00:06<00:15, 912.40it/s]
 29%|██▊       | 5708/20000 [00:06<00:15, 912.00it/s]
 29%|██▉       | 5800/20000 [00:06<00:15, 912.95it/s]
 29%|██▉       | 5893/20000 [00:06<00:15, 915.52it/s]
 30%|██▉       | 5986/20000 [00:06<00:15, 916.99it/s]
 30%|███       | 6078/20000 [00:06<00:15, 916.37it/s]
 31%|███       | 6170/20000 [00:06<00:15, 915.07it/s]
 31%|███▏      | 6262/20000 [00:06<00:15, 910.49it/s]
 32%|███▏      | 6354/20000 [00:06<00:14, 910.29it/s]
 32%|███▏      | 6446/20000 [00:07<00:14, 913.12it/s]
 33%|███▎      | 6539/20000 [00:07<00:14, 915.30it/s]
 33%|███▎      | 6631/20000 [00:07<00:14, 909.49it/s]
 34%|███▎      | 6722/20000 [00:07<00:14, 909.25it/s]
 34%|███▍      | 6813/20000 [00:07<00:14, 908.04it/s]
 35%|███▍      | 6905/20000 [00:07<00:14, 910.35it/s]
 35%|███▍      | 6997/20000 [00:07<00:14, 912.50it/s]
 35%|███▌      | 7089/20000 [00:07<00:14, 913.47it/s]
 36%|███▌      | 7181/20000 [00:07<00:14, 913.43it/s]
 36%|███▋      | 7273/20000 [00:07<00:13, 914.40it/s]
 37%|███▋      | 7365/20000 [00:08<00:13, 910.38it/s]
 37%|███▋      | 7457/20000 [00:08<00:13, 911.91it/s]
 38%|███▊      | 7549/20000 [00:08<00:13, 908.11it/s]
 38%|███▊      | 7640/20000 [00:08<00:13, 906.23it/s]
 39%|███▊      | 7732/20000 [00:08<00:13, 909.39it/s]
 39%|███▉      | 7823/20000 [00:08<00:13, 909.32it/s]
 40%|███▉      | 7915/20000 [00:08<00:13, 910.90it/s]
 40%|████      | 8007/20000 [00:08<00:13, 912.94it/s]
 40%|████      | 8099/20000 [00:08<00:13, 905.25it/s]
 41%|████      | 8191/20000 [00:08<00:13, 907.06it/s]
 41%|████▏     | 8284/20000 [00:09<00:12, 911.34it/s]
 42%|████▏     | 8376/20000 [00:09<00:12, 911.07it/s]
 42%|████▏     | 8468/20000 [00:09<00:12, 910.22it/s]
 43%|████▎     | 8560/20000 [00:09<00:12, 910.55it/s]
 43%|████▎     | 8652/20000 [00:09<00:12, 912.44it/s]
 44%|████▎     | 8744/20000 [00:09<00:12, 912.09it/s]
 44%|████▍     | 8836/20000 [00:09<00:12, 912.37it/s]
 45%|████▍     | 8928/20000 [00:09<00:12, 913.26it/s]
 45%|████▌     | 9020/20000 [00:09<00:12, 913.47it/s]
 46%|████▌     | 9112/20000 [00:09<00:11, 909.15it/s]
 46%|████▌     | 9203/20000 [00:10<00:11, 904.81it/s]
 46%|████▋     | 9295/20000 [00:10<00:11, 906.88it/s]
 47%|████▋     | 9387/20000 [00:10<00:11, 909.84it/s]
 47%|████▋     | 9479/20000 [00:10<00:11, 911.84it/s]
 48%|████▊     | 9571/20000 [00:10<00:11, 912.37it/s]
 48%|████▊     | 9663/20000 [00:10<00:11, 914.43it/s]
 49%|████▉     | 9755/20000 [00:10<00:11, 915.91it/s]
 49%|████▉     | 9847/20000 [00:10<00:11, 915.65it/s]
 50%|████▉     | 9939/20000 [00:10<00:10, 915.40it/s]
 50%|█████     | 10032/20000 [00:10<00:10, 917.14it/s]
 51%|█████     | 10125/20000 [00:11<00:10, 918.33it/s]
 51%|█████     | 10217/20000 [00:11<00:10, 915.95it/s]
 52%|█████▏    | 10309/20000 [00:11<00:10, 913.62it/s]
 52%|█████▏    | 10401/20000 [00:11<00:10, 914.54it/s]
 52%|█████▏    | 10493/20000 [00:11<00:10, 915.82it/s]
 53%|█████▎    | 10585/20000 [00:11<00:10, 913.20it/s]
 53%|█████▎    | 10677/20000 [00:11<00:10, 914.90it/s]
 54%|█████▍    | 10769/20000 [00:11<00:10, 914.17it/s]
 54%|█████▍    | 10861/20000 [00:11<00:09, 915.54it/s]
 55%|█████▍    | 10953/20000 [00:12<00:09, 915.40it/s]
 55%|█████▌    | 11045/20000 [00:12<00:09, 915.32it/s]
 56%|█████▌    | 11138/20000 [00:12<00:09, 916.87it/s]
 56%|█████▌    | 11230/20000 [00:12<00:09, 916.68it/s]
 57%|█████▋    | 11322/20000 [00:12<00:09, 917.67it/s]
 57%|█████▋    | 11414/20000 [00:12<00:09, 917.27it/s]
 58%|█████▊    | 11506/20000 [00:12<00:09, 918.03it/s]
 58%|█████▊    | 11598/20000 [00:12<00:09, 918.10it/s]
 58%|█████▊    | 11690/20000 [00:12<00:09, 918.38it/s]
 59%|█████▉    | 11783/20000 [00:12<00:08, 919.38it/s]
 59%|█████▉    | 11875/20000 [00:13<00:08, 917.64it/s]
 60%|█████▉    | 11967/20000 [00:13<00:08, 917.96it/s]
 60%|██████    | 12059/20000 [00:13<00:08, 916.18it/s]
 61%|██████    | 12151/20000 [00:13<00:08, 913.08it/s]
 61%|██████    | 12243/20000 [00:13<00:08, 915.14it/s]
 62%|██████▏   | 12335/20000 [00:13<00:08, 915.04it/s]
 62%|██████▏   | 12427/20000 [00:13<00:08, 915.47it/s]
 63%|██████▎   | 12519/20000 [00:13<00:08, 915.95it/s]
 63%|██████▎   | 12611/20000 [00:13<00:08, 915.47it/s]
 64%|██████▎   | 12703/20000 [00:13<00:07, 916.80it/s]
 64%|██████▍   | 12795/20000 [00:14<00:07, 915.46it/s]
 64%|██████▍   | 12887/20000 [00:14<00:07, 914.40it/s]
 65%|██████▍   | 12979/20000 [00:14<00:07, 914.41it/s]
 65%|██████▌   | 13071/20000 [00:14<00:07, 912.66it/s]
 66%|██████▌   | 13163/20000 [00:14<00:07, 911.21it/s]
 66%|██████▋   | 13255/20000 [00:14<00:07, 905.98it/s]
 67%|██████▋   | 13346/20000 [00:14<00:07, 901.85it/s]
 67%|██████▋   | 13437/20000 [00:14<00:07, 896.65it/s]
 68%|██████▊   | 13527/20000 [00:14<00:07, 896.56it/s]
 68%|██████▊   | 13618/20000 [00:14<00:07, 899.62it/s]
 69%|██████▊   | 13710/20000 [00:15<00:06, 905.07it/s]
 69%|██████▉   | 13802/20000 [00:15<00:06, 909.25it/s]
 69%|██████▉   | 13893/20000 [00:15<00:06, 900.41it/s]
 70%|██████▉   | 13984/20000 [00:15<00:06, 901.15it/s]
 70%|███████   | 14076/20000 [00:15<00:06, 904.36it/s]
 71%|███████   | 14168/20000 [00:15<00:06, 907.99it/s]
 71%|███████▏  | 14260/20000 [00:15<00:06, 910.32it/s]
 72%|███████▏  | 14352/20000 [00:15<00:06, 912.28it/s]
 72%|███████▏  | 14445/20000 [00:15<00:06, 914.83it/s]
 73%|███████▎  | 14537/20000 [00:15<00:05, 915.91it/s]
 73%|███████▎  | 14629/20000 [00:16<00:05, 915.82it/s]
 74%|███████▎  | 14721/20000 [00:16<00:05, 913.23it/s]
 74%|███████▍  | 14814/20000 [00:16<00:05, 915.52it/s]
 75%|███████▍  | 14906/20000 [00:16<00:05, 912.88it/s]
 75%|███████▍  | 14999/20000 [00:16<00:05, 915.24it/s]
 75%|███████▌  | 15091/20000 [00:16<00:05, 915.43it/s]
 76%|███████▌  | 15183/20000 [00:16<00:05, 916.45it/s]
 76%|███████▋  | 15275/20000 [00:16<00:05, 917.33it/s]
 77%|███████▋  | 15367/20000 [00:16<00:05, 915.35it/s]
 77%|███████▋  | 15460/20000 [00:16<00:04, 916.87it/s]
 78%|███████▊  | 15553/20000 [00:17<00:04, 917.92it/s]
 78%|███████▊  | 15646/20000 [00:17<00:04, 918.73it/s]
 79%|███████▊  | 15739/20000 [00:17<00:04, 919.13it/s]
 79%|███████▉  | 15831/20000 [00:17<00:04, 913.14it/s]
 80%|███████▉  | 15924/20000 [00:17<00:04, 915.53it/s]
 80%|████████  | 16016/20000 [00:17<00:04, 916.71it/s]
 81%|████████  | 16109/20000 [00:17<00:04, 917.68it/s]
 81%|████████  | 16201/20000 [00:17<00:04, 917.68it/s]
 81%|████████▏ | 16293/20000 [00:17<00:04, 917.55it/s]
 82%|████████▏ | 16386/20000 [00:17<00:03, 918.46it/s]
 82%|████████▏ | 16478/20000 [00:18<00:03, 917.59it/s]
 83%|████████▎ | 16570/20000 [00:18<00:03, 917.05it/s]
 83%|████████▎ | 16662/20000 [00:18<00:03, 916.92it/s]
 84%|████████▍ | 16754/20000 [00:18<00:03, 916.19it/s]
 84%|████████▍ | 16846/20000 [00:18<00:03, 915.72it/s]
 85%|████████▍ | 16938/20000 [00:18<00:03, 915.94it/s]
 85%|████████▌ | 17030/20000 [00:18<00:03, 916.37it/s]
 86%|████████▌ | 17123/20000 [00:18<00:03, 917.60it/s]
 86%|████████▌ | 17216/20000 [00:18<00:03, 919.00it/s]
 87%|████████▋ | 17308/20000 [00:18<00:02, 918.36it/s]
 87%|████████▋ | 17400/20000 [00:19<00:02, 916.90it/s]
 87%|████████▋ | 17493/20000 [00:19<00:02, 918.85it/s]
 88%|████████▊ | 17585/20000 [00:19<00:02, 915.16it/s]
 88%|████████▊ | 17677/20000 [00:19<00:02, 913.78it/s]
 89%|████████▉ | 17769/20000 [00:19<00:02, 911.82it/s]
 89%|████████▉ | 17861/20000 [00:19<00:02, 912.69it/s]
 90%|████████▉ | 17954/20000 [00:19<00:02, 914.92it/s]
 90%|█████████ | 18046/20000 [00:19<00:02, 915.91it/s]
 91%|█████████ | 18138/20000 [00:19<00:02, 916.24it/s]
 91%|█████████ | 18230/20000 [00:19<00:01, 914.08it/s]
 92%|█████████▏| 18322/20000 [00:20<00:01, 914.42it/s]
 92%|█████████▏| 18414/20000 [00:20<00:01, 915.67it/s]
 93%|█████████▎| 18506/20000 [00:20<00:01, 912.95it/s]
 93%|█████████▎| 18598/20000 [00:20<00:01, 912.86it/s]
 93%|█████████▎| 18690/20000 [00:20<00:01, 912.41it/s]
 94%|█████████▍| 18782/20000 [00:20<00:01, 914.46it/s]
 94%|█████████▍| 18874/20000 [00:20<00:01, 908.77it/s]
 95%|█████████▍| 18966/20000 [00:20<00:01, 911.74it/s]
 95%|█████████▌| 19058/20000 [00:20<00:01, 911.26it/s]
 96%|█████████▌| 19150/20000 [00:20<00:00, 907.51it/s]
 96%|█████████▌| 19241/20000 [00:21<00:00, 905.61it/s]
 97%|█████████▋| 19332/20000 [00:21<00:00, 903.12it/s]
 97%|█████████▋| 19423/20000 [00:21<00:00, 896.40it/s]
 98%|█████████▊| 19514/20000 [00:21<00:00, 899.39it/s]
 98%|█████████▊| 19604/20000 [00:21<00:00, 897.53it/s]
 98%|█████████▊| 19696/20000 [00:21<00:00, 903.69it/s]
 99%|█████████▉| 19788/20000 [00:21<00:00, 906.77it/s]
 99%|█████████▉| 19880/20000 [00:21<00:00, 909.18it/s]
100%|█████████▉| 19972/20000 [00:21<00:00, 911.65it/s]
100%|██████████| 20000/20000 [00:21<00:00, 912.58it/s]
sampler = inv_result_fixed_d_sampler.sampler
az_idata = az.from_emcee(sampler, var_names=labels)
az_idata.get("posterior")
<xarray.Dataset> Size: 11MB
Dimensions:  (chain: 40, draw: 2000)
Coordinates:
  * chain    (chain) int64 320B 0 1 2 3 4 5 6 7 8 ... 31 32 33 34 35 36 37 38 39
  * draw     (draw) int64 16kB 0 1 2 3 4 5 6 ... 1994 1995 1996 1997 1998 1999
Data variables: (12/17)
    t0       (chain, draw) float64 640kB 15.29 15.35 15.35 ... 13.24 13.21 13.21
    t1       (chain, draw) float64 640kB 15.07 15.13 15.13 ... 5.63 5.062 5.062
    t2       (chain, draw) float64 640kB 14.89 15.14 15.14 ... 14.81 14.22 14.22
    t3       (chain, draw) float64 640kB 15.05 15.21 15.21 ... 14.64 15.42 15.42
    t4       (chain, draw) float64 640kB 15.48 15.05 15.05 ... 16.52 16.45 16.45
    t5       (chain, draw) float64 640kB 15.24 15.07 15.07 ... 22.81 25.11 25.11
    ...       ...
    v3       (chain, draw) float64 640kB 4.361 4.171 4.171 ... 3.854 3.704 3.704
    v4       (chain, draw) float64 640kB 4.114 4.297 4.297 ... 4.648 4.685 4.685
    v5       (chain, draw) float64 640kB 3.979 3.89 3.89 ... 4.046 4.19 4.19
    v6       (chain, draw) float64 640kB 4.4 4.229 4.229 ... 4.67 4.649 4.649
    v7       (chain, draw) float64 640kB 4.228 4.164 4.164 ... 4.72 4.706 4.706
    v8       (chain, draw) float64 640kB 4.349 4.417 4.417 ... 4.399 4.378 4.378
Attributes:
    created_at:                 2024-04-19T05:06:34.889639+00:00
    arviz_version:              0.18.0
    inference_library:          emcee
    inference_library_version:  3.1.5


# plot the model and d_pred for starting model
axes = plot_model_and_data(model=init_model, label_model="starting model", color_model="black",
                           forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                           label_d_pred="d_pred from starting model", color_d_pred="black")

# plot the model and d_pred for true model
plot_model_and_data(model=ref_good_model, label_model="reference good model", color_model="red",
                    forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                    label_d_pred="d_pred from reference good model", color_d_pred="red", axes=axes)

# plot the model and d_pred for inverted model, and d_obs
plot_model_and_data(model=inv_result_sw_field.model,
                    label_model="inverted model from field data", color_model="green",
                    forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                    label_d_pred="d_pred from inverted model", color_d_pred="green", axes=axes)

# plot randomly selected samples and data predictions from samples
flat_samples = sampler.get_chain(discard=1000, thin=300, flat=True)
rand_indices = np.random.randint(len(flat_samples), size=100)
for idx in rand_indices:
    sample = flat_samples[idx]
    label_model = "sample models" if idx == 0 else None
    label_d_pred = "d_pred from samples" if idx == 0 else None
    plot_model_and_data(model=sample, label_model=label_model, color_model="gray",
                        forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                        label_d_pred=label_d_pred, color_d_pred="gray", axes=axes, light_thin=True)

# plot d_obs
plot_data(field_d_obs, field_d_periods, ax=axes[1], scatter=True, color="orange", s=8, label="d_obs")

axes[0].set_ylim(100, 0)
axes[0].legend(loc="lower center", bbox_to_anchor=(0.5, -0.4))
axes[1].legend(loc="lower center", bbox_to_anchor=(0.5, -0.46));
model, data
<matplotlib.legend.Legend object at 0x7fd25fb0f790>

More steps

Similar to our earlier fixed-dimensional sampling run on the synthetic data, we are not sampling enough due to time limit.

On a seperate experiment, we ran 200_000 steps and produced the following samples plot.

Trans-dimensional sampling#

def forward_interp_for_bayesbay(state):
    vs = state["voronoi"]["vs"]
    voronoi_sites = state["voronoi"]["discretization"]
    depths = (voronoi_sites[:-1] + voronoi_sites[1:]) / 2
    thicknesses = depths - np.insert(depths[:-1], 0, 0)
    model = form_layercake_model(thicknesses, vs)
    return forward_sw_interp(model, field_d_periods)
targets = [bayesbay.Target("rayleigh", field_d_obs, covariance_mat_inv=1/noise_level**2)]
fwd_funcs = [forward_interp_for_bayesbay]
my_log_likelihood = bayesbay.LogLikelihood(targets, fwd_funcs)
param_vs = bayesbay.prior.UniformPrior(
    name="vs",
    vmin=[2.7, 3.2, 3.75],
    vmax=[4, 4.75, 5],
    position=[0, 40, 80],
    perturb_std=0.15
)

def param_vs_initialize(param, positions):
    vmin, vmax = param.get_vmin_vmax(positions)
    sorted_vals = np.sort(np.random.uniform(vmin, vmax, positions.size))
    for i in range (len(sorted_vals)):
        val = sorted_vals[i]
        vmin_i = vmin if np.isscalar(vmin) else vmin[i]
        vmax_i = vmax if np.isscalar(vmax) else vmax[i]
        if val < vmin_i or val > vmax_i:
            if val > vmax_i: sorted_vals[i] = vmax_i
            if val < vmin_i: sorted_vals[i] = vmin_i
    return sorted_vals

param_vs.set_custom_initialize(param_vs_initialize)
parameterization = bayesbay.parameterization.Parameterization(
    bayesbay.discretization.Voronoi1D(
        name="voronoi",
        vmin=0,
        vmax=150,
        perturb_std=10,
        n_dimensions=None,
        n_dimensions_min=4,
        n_dimensions_max=20,
        parameters=[param_vs],
    )
)
my_perturbation_funcs = parameterization.perturbation_functions
n_chains=12
walkers_start = []
for i in range(n_chains):
    walkers_start.append(parameterization.initialize())

Define ``InversionOptions``

inv_options_field_trans_d_sampling = cofi.InversionOptions()
inv_options_field_trans_d_sampling.set_tool("bayesbay")
inv_options_field_trans_d_sampling.set_params(
    walkers_starting_states=walkers_start,
    perturbation_funcs=my_perturbation_funcs,
    log_like_ratio_func=my_log_likelihood,
    n_chains=n_chains,
    n_iterations=3_000,
    burnin_iterations=1_000,
    verbose=False,
    save_every=200,
)

Define ``Inversion`` and run

inversion_field_trans_d_sampler = cofi.Inversion(sw_field_problem,
                                                 inv_options_field_trans_d_sampling)
inv_result_field_trans_d_sampler = inversion_field_trans_d_sampler.run()
inverted_models = inv_result_field_trans_d_sampler.models
samples = []
for v, vs in zip(inverted_models["voronoi.discretization"], inverted_models["voronoi.vs"]):
    sample = form_voronoi_model(v, vs)
    samples.append(voronoi_to_layercake(sample))
# plot the model and d_pred for starting model
axes = plot_model_and_data(model=init_model, label_model="starting model", color_model="black",
                           forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                           label_d_pred="d_pred from starting model", color_d_pred="black")

# plot the model and d_pred for true model
plot_model_and_data(model=ref_good_model, label_model="reference good model", color_model="red",
                    forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                    label_d_pred="d_pred from reference good model", color_d_pred="red", axes=axes)

# plot the model and d_pred for inverted model, and d_obs
plot_model_and_data(model=inv_result_sw_field.model,
                    label_model="inverted model from field data", color_model="green",
                    forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                    label_d_pred="d_pred from inverted model", color_d_pred="green", axes=axes)

# plot randomly selected samples and data predictions from samples
flat_samples = sampler.get_chain(discard=1000, thin=300, flat=True)
rand_indices = np.random.randint(len(flat_samples), size=100)
for i, sample in enumerate(samples):
    label_model = "sample models" if i == 0 else None
    label_d_pred = "d_pred from samples" if i == 0 else None
    plot_model_and_data(model=sample, label_model=label_model, color_model="gray",
                        forward_func=forward_sw_interp, periods=field_d_periods_logspace,
                        label_d_pred=label_d_pred, color_d_pred="gray", axes=axes, light_thin=True)

# plot d_obs
plot_data(field_d_obs, field_d_periods, ax=axes[1], scatter=True, color="orange", s=8, label="d_obs")

axes[0].set_ylim(100, 0)
axes[0].legend(loc="lower center", bbox_to_anchor=(0.5, -0.4))
axes[1].legend(loc="lower center", bbox_to_anchor=(0.5, -0.46));
model, data
<matplotlib.legend.Legend object at 0x7fd0a8f0b850>

Watermark#

watermark_list = ["cofi", "numpy", "matplotlib", "scipy", "emcee", "bayesbay"]
for pkg in watermark_list:
    pkg_var = __import__(pkg)
    print(pkg, getattr(pkg_var, "__version__"))
cofi 0.2.8
numpy 1.26.4
matplotlib 3.8.3
scipy 1.12.0
emcee 3.1.5
bayesbay 0.3.0

sphinx_gallery_thumbnail_number = -1

Total running time of the script: (3 minutes 13.920 seconds)

Gallery generated by Sphinx-Gallery