Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warning when specifying multiple forward models to generate training data with LatinHypercubeSampler #84

Open
JanKoune opened this issue Jun 7, 2022 · 0 comments

Comments

@JanKoune
Copy link
Collaborator

JanKoune commented Jun 7, 2022

The generate_training_data method of LatinHypercubeSampler requires as input a forward model that has been added to the problem definition and has an experiment associated with it. This results in a InverseProblem with two forward models: one physical (e.g. FE) model and one surrogate. To avoid evaluating the FE model during inference, we can only specify an likelihood model for the surrogate model. This results in the following warning:

| WARNING  | The globally defined experiment 'TestSeriesFemModel' does not appear in any of the likelihood models!

This warning makes it unclear if this is the intended way of using the LatinHypercubeSampler. You can find an example based on a modified version of test_surrogate_model.py below:

import numpy as np
from probeye.definition.inverse_problem import InverseProblem
from probeye.definition.forward_model import ForwardModelBase
from probeye.definition.surrogate_model import SurrogateModelBase
from probeye.definition.sensor import Sensor
from probeye.definition.likelihood_model import GaussianLikelihoodModel
from probeye.surrogate.initial_sampling import LatinHypercubeSampler
from probeye.inference.emcee.solver import EmceeSolver

# ============================================================================ #
#                              Set numeric values                              #
# ============================================================================ #
n_walkers = 20
n_steps = 200
n_init_steps = 100
N_train = 100

# 'true' value of a, and its normal prior parameters
m_true = 2.5
mean_m = 2.0
std_m = 1.0

# 'true' value of b, and its normal prior parameters
b_true = 1.7
mean_b = 1.0
std_b = 1.0

# 'true' value of additive error sd, and its uniform prior parameters
sigma = 0.5
low_sigma = 0.0
high_sigma = 0.8

# the number of generated experiment_names and seed for random numbers
n_tests = 50
seed = 1

# ============================================================================ #
#                           Define the Forward Model                           #
# ============================================================================ #
class ExpensiveModel(ForwardModelBase):
    def interface(self):
        self.parameters = ["m", "b"]
        self.input_sensors = Sensor("x")
        self.output_sensors = Sensor("y", std_model="sigma")

    def response(self, inp: dict) -> dict:
        x = inp["x"]
        m = inp["m"]
        b = inp["b"]
        return {"y": m * x + b}

# ============================================================================ #
#                          Define the Surrogate Model                          #
# ============================================================================ #
class SurrogateModel(ExpensiveModel, SurrogateModelBase):
    """
    The inheritance from ExpensiveModel 'copies' the interface-method from
    ExpensiveModel (the surrogate model should have the same interface as the
    forward model). The inheritance from SurrogateModelBase is required to
    assign a forward model to the surrogate model, see surrogate_model.py.
    """

    def response(self, inp: dict) -> dict:
        x = inp["x"]
        m = inp["m"]
        b = inp["b"]
        return {"y": m * x + b}

# ============================================================================ #
#                         Define the Inference Problem                         #
# ============================================================================ #
# initialize the inverse problem with a useful name
problem = InverseProblem("Using a surrogate model")

# add all parameters to the problem
problem.add_parameter(
    "m",
    "model",
    tex="$m$",
    info="Slope of the graph",
    prior=("normal", {"mean": mean_m, "std": std_m}),
)
problem.add_parameter(
    "b",
    "model",
    info="Intersection of graph with y-axis",
    tex="$b$",
    prior=("normal", {"mean": mean_b, "std": std_b}),
)
problem.add_parameter(
    "sigma",
    "likelihood",
    domain="(0, +oo)",
    tex=r"$\sigma$",
    info="Standard deviation, of zero-mean additive model error",
    prior=("uniform", {"low": low_sigma, "high": high_sigma}),
)

# Add forward model to generate training data
forward_model = ExpensiveModel("ExpensiveModel")
problem.add_forward_model(forward_model)

# ============================================================================ #
#                    Add test data to the Inference Problem                    #
# ============================================================================ #
# data-generation; normal likelihood with constant variance around each point
np.random.seed(seed)
x_test = np.linspace(0.0, 1.0, n_tests)
y_true = forward_model.response(
    {forward_model.input_sensor.name: x_test, "m": m_true, "b": b_true}
)[forward_model.output_sensor.name]
y_test = np.random.normal(loc=y_true, scale=sigma)

# add the experimental data
problem.add_experiment(
    f"TestSeries_FE",
    fwd_model_name="ExpensiveModel",
    sensor_values={
        forward_model.input_sensor.name: x_test,
        forward_model.output_sensor.name: y_test,
    },
)

# Generate training data
sampler = LatinHypercubeSampler(problem=problem)
train_samples, train_data = sampler.generate_training_data(forward_model, N_train)

# Add surrogate model to InverseProblem
surrogate_model = SurrogateModel("FastModel", forward_model=forward_model)
problem.add_forward_model(surrogate_model)

# Add surrogate model to the experiment
problem.add_experiment(
    f"TestSeries_Surrogate",
    fwd_model_name="FastModel",
    sensor_values={
        forward_model.input_sensor.name: x_test,
        forward_model.output_sensor.name: y_test,
    },
)

# ============================================================================ #
#                           Add likelihood model(s)                            #
# ============================================================================ #
# add the likelihood model to the problem
problem.add_likelihood_model(
    GaussianLikelihoodModel(
        prms_def="sigma",
        experiment_name="TestSeries_Surrogate",
        model_error="additive",
        name="SimpleLikelihoodModel",
    )
)

# ============================================================================ #
#                           Run solver                                         #
# ============================================================================ #
emcee_solver = EmceeSolver(
    problem,
    show_progress=True,
)

inference_data = emcee_solver.run_mcmc(
    n_walkers=n_walkers,
    n_steps=n_steps,
    n_initial_steps=n_init_steps,
    vectorize=False
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant