From e446db7ddf00378f53c23160a35b761326d3d9e8 Mon Sep 17 00:00:00 2001 From: kohlhaasrebecca <rebecca.kohlhaas@outlook.com> Date: Mon, 11 Dec 2023 17:15:59 +0100 Subject: [PATCH] Allowing for UMBridge models Implemented simple example using the tsunami model from UMBridge --- .../bayes_inference/bayes_inference.py | 18 ++- src/bayesvalidrox/bayes_inference/mcmc.py | 18 +-- .../post_processing/post_processing.py | 6 +- src/bayesvalidrox/pylink/pylink.py | 27 +++-- .../surrogate_models/bayes_linear.py | 2 +- .../surrogate_models/meta_model_engine.py | 4 +- .../surrogate_models/reg_fast_ard.py | 6 +- .../surrogate_models/reg_fast_laplace.py | 2 +- .../surrogate_models/sequential_design.py | 4 +- .../surrogate_models/surrogate_models.py | 14 +-- src/test_umbridge.py | 114 ++++++++++++++++++ src/tsunami.pk1 | Bin 0 -> 21593 bytes src/tsunami_model.py | 16 +++ 13 files changed, 188 insertions(+), 43 deletions(-) create mode 100644 src/test_umbridge.py create mode 100644 src/tsunami.pk1 create mode 100644 src/tsunami_model.py diff --git a/src/bayesvalidrox/bayes_inference/bayes_inference.py b/src/bayesvalidrox/bayes_inference/bayes_inference.py index c8073ba76..3246f4058 100644 --- a/src/bayesvalidrox/bayes_inference/bayes_inference.py +++ b/src/bayesvalidrox/bayes_inference/bayes_inference.py @@ -331,7 +331,7 @@ class BayesInference: for itr_idx, data in tqdm( enumerate(self.perturbed_data), total=self.n_bootstrap_itrs, - desc="Boostraping the BME calculations", ascii=True + desc="Bootstrapping the BME calculations", ascii=True ): # ---------------- Likelihood calculation ---------------- @@ -352,6 +352,16 @@ class BayesInference: output_names[i]: data[j:k] for i, (j, k) in enumerate(indices) } + #print(output_names) + #print(indices) + #print(numbers) + #print(nobs) + #print(self.measured_data) + #for i, (j, k) in enumerate(indices): + # print(i,j,k) + #print(data) + #print(data_dict) + #stop # Unknown sigma2 if opt_sigma == 'C' or hasattr(self, 'sigma2s'): @@ -370,7 +380,7 @@ class BayesInference: # BME (log) log_BME[itr_idx] = np.log( np.nanmean(np.exp(logLikelihoods[:, itr_idx], - dtype=np.float128)) + dtype=np.longdouble))#float128)) ) # BME correction when using Emulator @@ -1042,9 +1052,9 @@ class BayesInference: # Take the first column of Likelihoods (Observation data without noise) if self.just_analysis or self.bayes_loocv: index = self.n_tot_measurement-1 - likelihoods = np.exp(self.log_likes[:, index], dtype=np.float128) + likelihoods = np.exp(self.log_likes[:, index], dtype=np.longdouble)#np.float128) else: - likelihoods = np.exp(self.log_likes[:, 0], dtype=np.float128) + likelihoods = np.exp(self.log_likes[:, 0], dtype=np.longdouble)#np.float128) n_samples = len(likelihoods) norm_ikelihoods = likelihoods / np.max(likelihoods) diff --git a/src/bayesvalidrox/bayes_inference/mcmc.py b/src/bayesvalidrox/bayes_inference/mcmc.py index 6b9ca9482..cc632c2de 100755 --- a/src/bayesvalidrox/bayes_inference/mcmc.py +++ b/src/bayesvalidrox/bayes_inference/mcmc.py @@ -251,7 +251,7 @@ class MCMC: # output current autocorrelation estimate if self.verbose: - print(f"Mean autocorr time estimate: {np.nanmean(tau):.3f}") + print(f"Mean autocorr. time estimate: {np.nanmean(tau):.3f}") list_gr = np.round(self.gelman_rubin(sampler.chain), 3) print("Gelman-Rubin Test*: ", list_gr) @@ -282,15 +282,15 @@ class MCMC: # Print summary print('\n') print('-'*15 + 'Posterior diagnostics' + '-'*15) - print(f"mean auto-correlation time: {np.nanmean(tau):.3f}") - print(f"thin: {thin}") - print(f"burn-in: {burnin}") - print(f"flat chain shape: {finalsamples.shape}") - print(f"Mean acceptance fraction: {acc_fr:.3f}") - print("Gelman-Rubin Test*: ", list_gr) + print(f"Mean auto-correlation time: {np.nanmean(tau):.3f}") + print(f"Thin: {thin}") + print(f"Burn-in: {burnin}") + print(f"Flat chain shape: {finalsamples.shape}") + print(f"Mean acceptance fraction*: {acc_fr:.3f}") + print("Gelman-Rubin Test**: ", list_gr) print("\n* This value must lay between 0.234 and 0.5.") - print("* These values must be smaller than 1.1.") + print("** These values must be smaller than 1.1.") print('-'*50) print(f"\n>>>> Bayesian inference with MCMC for {self.BayesOpts.name} " @@ -777,7 +777,7 @@ class MCMC: ) if ~np.isfinite(tmp['logml']): warnings.warn( - "logml could not be estimated within maxiter, rerunning with " + "Logml could not be estimated within maxiter, rerunning with " "adjusted starting value. Estimate might be more variable than" " usual.") # use geometric mean as starting value diff --git a/src/bayesvalidrox/post_processing/post_processing.py b/src/bayesvalidrox/post_processing/post_processing.py index 4db354bbd..e1795a74c 100644 --- a/src/bayesvalidrox/post_processing/post_processing.py +++ b/src/bayesvalidrox/post_processing/post_processing.py @@ -237,8 +237,8 @@ class PostProcessing: elif samples is not None: self.n_samples = samples.shape[0] else: - raise Exception("Please provide either samples or pass number of " - "samples!") + raise Exception("Please provide either samples or pass the number" + " of samples!") # Generate random samples if necessary Samples = self._get_sample() if samples is None else samples @@ -922,7 +922,7 @@ class PostProcessing: ax = plt.gca() _, p = stats.shapiro(residuals) if p < 0.01: - annText = "The residuals seem to come from Gaussian process." + annText = "The residuals seem to come from a Gaussian Process." else: annText = "The normality assumption may not hold." at = AnchoredText(annText, prop=dict(size=30), frameon=True, diff --git a/src/bayesvalidrox/pylink/pylink.py b/src/bayesvalidrox/pylink/pylink.py index 7af4a14b8..84695119a 100644 --- a/src/bayesvalidrox/pylink/pylink.py +++ b/src/bayesvalidrox/pylink/pylink.py @@ -170,9 +170,9 @@ class PyLinkForwardModel(object): obs = self.observations else: raise Exception("Please provide the observation data as a " - "dictionary via observations attribute or pass" - " the csv-file path to MeasurementFile " - "attribute") + "dictionary via the observations attribute or " + "pass the csv-file path to the MeasurementFile" + " attribute") elif case.lower() == 'valid': if isinstance(self.observations_valid, dict) and \ bool(self.observations_valid): @@ -184,9 +184,9 @@ class PyLinkForwardModel(object): obs = self.observations_valid else: raise Exception("Please provide the observation data as a " - "dictionary via Observations attribute or pass" - " the csv-file path to MeasurementFile " - "attribute") + "dictionary via the observations attribute or " + "pass the csv-file path to the MeasurementFile" + " attribute") # Compute the number of observation n_obs = obs[self.Output.names].notnull().sum().values.sum() @@ -316,9 +316,8 @@ class PyLinkForwardModel(object): Process = os.system(f'./../{command}') if Process != 0: print('\nMessage 1:') - print(f'\tIf value of \'{Process}\' is a non-zero value, ' - 'then compilation problems \n' % Process) - + print(f'\tIf the value of \'{Process}\' is a non-zero value' + ', then compilation problems occur \n' % Process) os.chdir("..") # Read the output @@ -438,6 +437,12 @@ class PyLinkForwardModel(object): # Initilization n_c_points = len(c_points) all_outputs = {} + + # TODO: if umbridge, just run, no parallel from here + # If the link type is UM-Bridge, then no parallel needs to be started from here + if self.link_type.lower() == 'umbridge': + # For now just do same as usual + self.link_type = 'function' # Extract the function if self.link_type.lower() == 'function': @@ -499,7 +504,7 @@ class PyLinkForwardModel(object): if len(NaN_idx) != 0: print('\n') print('*'*20) - print("\nThe following parametersets have been removed:\n", + print("\nThe following parameter sets have been removed:\n", c_points[NaN_idx]) print("\n") print('*'*20) @@ -656,6 +661,6 @@ class PyLinkForwardModel(object): shutil.rmtree(path) print("\n") - print(f'{dir_name}.zip file has been created successfully!\n') + print(f'{dir_name}.zip has been created successfully!\n') return diff --git a/src/bayesvalidrox/surrogate_models/bayes_linear.py b/src/bayesvalidrox/surrogate_models/bayes_linear.py index a7d6b5929..767e6fff7 100644 --- a/src/bayesvalidrox/surrogate_models/bayes_linear.py +++ b/src/bayesvalidrox/surrogate_models/bayes_linear.py @@ -138,7 +138,7 @@ class EBLinearRegression(BayesianLinearRegression): normalize=True, perfect_fit_tol = 1e-6, alpha = 1, copy_X = True, verbose = False): super(EBLinearRegression,self).__init__(n_iter, tol, fit_intercept, copy_X, verbose) if optimizer not in ['em','fp']: - raise ValueError('Optimizer can be either "em" of "fp" ') + raise ValueError('Optimizer can be either "em" or "fp" ') self.optimizer = optimizer self.alpha = alpha self.perfect_fit = False diff --git a/src/bayesvalidrox/surrogate_models/meta_model_engine.py b/src/bayesvalidrox/surrogate_models/meta_model_engine.py index 2df2dee53..c2e9ec409 100644 --- a/src/bayesvalidrox/surrogate_models/meta_model_engine.py +++ b/src/bayesvalidrox/surrogate_models/meta_model_engine.py @@ -1067,7 +1067,7 @@ class MetaModelEngine(): maxfun=max_func_itr) if verbose: - print(f"global minimum: xmin = {Res_Global.x}, " + print(f"Global minimum: xmin = {Res_Global.x}, " f"f(xmin) = {Res_Global.fun:.6f}, nfev = {Res_Global.nfev}") return (Run_No, Res_Global.x) @@ -1237,7 +1237,7 @@ class MetaModelEngine(): elapsed_time = time.time() - start_time print("\n") - print(f"elapsed_time: {round(elapsed_time,2)} sec.") + print(f"Elapsed_time: {round(elapsed_time,2)} sec.") print('-'*20) elif explore_method == 'LOOCV': diff --git a/src/bayesvalidrox/surrogate_models/reg_fast_ard.py b/src/bayesvalidrox/surrogate_models/reg_fast_ard.py index 44073da8e..e6883a3ed 100755 --- a/src/bayesvalidrox/surrogate_models/reg_fast_ard.py +++ b/src/bayesvalidrox/surrogate_models/reg_fast_ard.py @@ -286,9 +286,9 @@ class RegressionFastARD(LinearModel, RegressorMixin): # raise warning in case cholesky fails if warning_flag == 1: warnings.warn(("Cholesky decomposition failed! Algorithm uses " - "pinvh, which is significantly slower, if you " + "pinvh, which is significantly slower. If you " "use RVR it is advised to change parameters of " - "kernel")) + "the kernel!")) # compute quality & sparsity parameters s, q, S, Q = self._sparsity_quality(XX, XXd, XY, XYa, Aa, Ri, @@ -319,7 +319,7 @@ class RegressionFastARD(LinearModel, RegressorMixin): if converged or i == self.n_iter - 1: if converged and self.verbose: - print('Algorithm converged !') + print('Algorithm converged!') break # after last update of alpha & beta update parameters diff --git a/src/bayesvalidrox/surrogate_models/reg_fast_laplace.py b/src/bayesvalidrox/surrogate_models/reg_fast_laplace.py index bdff324ed..84faa6026 100644 --- a/src/bayesvalidrox/surrogate_models/reg_fast_laplace.py +++ b/src/bayesvalidrox/surrogate_models/reg_fast_laplace.py @@ -307,7 +307,7 @@ class RegressionFastLaplace(): # recomputation # zero if regressor has not been chosen yet if not ind_global_to_local[ind_L_max]: - raise Exception('cannot recompute index{0} -- not yet\ + raise Exception('Cannot recompute index{0} -- not yet\ part of the model!'.format(ind_L_max)) Sigma = np.atleast_2d(Sigma) mu = np.atleast_2d(mu) diff --git a/src/bayesvalidrox/surrogate_models/sequential_design.py b/src/bayesvalidrox/surrogate_models/sequential_design.py index fc81dcd45..1e0935dee 100644 --- a/src/bayesvalidrox/surrogate_models/sequential_design.py +++ b/src/bayesvalidrox/surrogate_models/sequential_design.py @@ -1099,7 +1099,7 @@ class SeqDesign(): maxfun=max_func_itr) if verbose: - print(f"global minimum: xmin = {Res_Global.x}, " + print(f"Global minimum: xmin = {Res_Global.x}, " f"f(xmin) = {Res_Global.fun:.6f}, nfev = {Res_Global.nfev}") return (Run_No, Res_Global.x) @@ -1269,7 +1269,7 @@ class SeqDesign(): elapsed_time = time.time() - start_time print("\n") - print(f"elapsed_time: {round(elapsed_time,2)} sec.") + print(f"Elapsed_time: {round(elapsed_time,2)} sec.") print('-'*20) elif explore_method == 'LOOCV': diff --git a/src/bayesvalidrox/surrogate_models/surrogate_models.py b/src/bayesvalidrox/surrogate_models/surrogate_models.py index c51ea8c66..96cb1d8c5 100644 --- a/src/bayesvalidrox/surrogate_models/surrogate_models.py +++ b/src/bayesvalidrox/surrogate_models/surrogate_models.py @@ -136,7 +136,7 @@ class MetaModel(): if self.ExpDesign.method == 'sequential': raise Exception( - "Please use MetaModelEngine class for the sequential design!" + "Please use the MetaModelEngine class for sequential design!" ) elif self.ExpDesign.method == 'normal': @@ -238,7 +238,7 @@ class MetaModel(): if verbose and self.n_bootstrap_itrs > 1: enum_obj = tqdm(range(self.n_bootstrap_itrs), total=self.n_bootstrap_itrs, - desc="Boostraping the metamodel", + desc="Bootstrapping the metamodel", ascii=True) else: enum_obj = range(self.n_bootstrap_itrs) @@ -516,7 +516,7 @@ class MetaModel(): self.ModelOutputDict = ExpDesign.Y else: raise Exception('Please provide either a dictionary or a hdf5' - 'file to ExpDesign.hdf5_file argument.') + 'file as the ExpDesign.hdf5_file argument.') return ED_X_tr, self.ModelOutputDict @@ -882,9 +882,9 @@ class MetaModel(): print(' '*10 + ' Summary of results ') print('='*50) - print("scores:\n", scores) - print("Best score's degree:", self.deg_array[best_deg-1]) - print("NO. of terms:", len(basis_indices)) + print("Scores:\n", scores) + print("Degree of best score:", self.deg_array[best_deg-1]) + print("No. of terms:", len(basis_indices)) print("Sparsity index:", round(len(basis_indices)/P, 3)) print("Best Indices:\n", basis_indices) @@ -980,7 +980,7 @@ class MetaModel(): # only the trace is, to save memory) PsiM = np.dot(psi_sparse, M) - h = np.sum(np.multiply(PsiM, psi_sparse), axis=1, dtype=np.float128) + h = np.sum(np.multiply(PsiM, psi_sparse), axis=1, dtype=np.longdouble)#float128) # ------ Calculate Error Loocv for each measurement point ---- # Residuals diff --git a/src/test_umbridge.py b/src/test_umbridge.py new file mode 100644 index 000000000..8e7c32cf4 --- /dev/null +++ b/src/test_umbridge.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Dec 11 14:03:23 2023 + +Example and test for using umbridge with bayesvalidrox. +Example model is the tsunami model by +docker run -it -p 4242:4242 -v ~/tsunami_output:/output linusseelinger/model-exahype-tsunami + +@author: rkohlhaas +""" + +import joblib +import numpy as np +import umbridge + +import bayesvalidrox as bv + + +if __name__ == '__main__': + + # This model has 2 inputs and four outputs + n_prior_sample = 1000 + priors = bv.Input() + priors.add_marginals() + priors.Marginals[0].name = 'x' + priors.Marginals[0].input_data = np.random.uniform(50,150,n_prior_sample) + priors.add_marginals() + priors.Marginals[1].name = 'y' + priors.Marginals[1].input_data = np.random.uniform(50,150,n_prior_sample) + + # Define the model + model = bv.PyLinkForwardModel() + #model.link_type = 'Function' + model.link_type = 'umbridge' + model.py_file = 'tsunami_model' + model.name = 'tsunami_model' + model.Output.names = ['T1', 'T2', 'H1', 'H2'] + #model.observations = data_dict1_times + + # Create the surrogate + surrogate_opts1 = bv.MetaModel(priors, model) + + # Select your metamodel method + surrogate_opts1.meta_model_type = 'aPCE' + surrogate_opts1.pce_reg_method = 'FastARD' + surrogate_opts1.pce_deg = np.arange(1, 5) + surrogate_opts1.pce_q_norm = 0.4#1.0 + + # Select your metamodel method + surrogate_opts1.add_ExpDesign() + surrogate_opts1.ExpDesign.method = 'normal' + surrogate_opts1.ExpDesign.n_init_samples = 20 + surrogate_opts1.ExpDesign.sampling_method = 'latin-hypercube' + surrogate1 = surrogate_opts1.create_metamodel() + print('Surrogate completed') + print('') + + # Save surrogate + with open('tsunami.pk1', 'wb') as output: + joblib.dump(surrogate1, output, 2) + + # Post processing + L2_PostPCE = bv.PostProcessing(surrogate1) + L2_PostPCE.plot_moments(plot_type='line') + # Plot to check validation visually. + L2_PostPCE.valid_metamodel(n_samples=1) + # Compute and print RMSE error + L2_PostPCE.check_accuracy(n_samples=300) + total_sobol = L2_PostPCE.sobol_indices() + + # Test BayesInference + import copy + from tsunami_model import tsunami_model + true_data = tsunami_model(np.array([[90.0,60.0]])) + model.observations = true_data + true_data_nox = copy.deepcopy(true_data) + true_data_nox.pop('x_values') + + # Test surrogate output shape + mean, std = surrogate1.eval_metamodel(np.array([[90.0,60.0]])) + + # Set uncertainty + import pandas as pd + obsData = pd.DataFrame(true_data_nox, columns=model.Output.names) + DiscrepancyOpts = bv.Discrepancy('') + DiscrepancyOpts.type = 'Gaussian' + DiscrepancyOpts.parameters = (obsData*0.15)**2 + + # Parameter estimation / single model validation via TOM + BayesOpts = bv.BayesInference(surrogate1) + BayesOpts.emulator= True + BayesOpts.plot_post_pred = True + #BayesOpts.inference_method = 'rejection' + BayesOpts.Discrepancy = DiscrepancyOpts + BayesOpts.bootstrap = True + BayesOpts.n_bootstrap_itrs = 10 + BayesOpts.bootstrap_noise = 0.05 + BayesOpts.out_dir = '' + + # Set the MCMC parameters + import emcee + BayesOpts.inference_method = "MCMC" + BayesOpts.mcmc_params = { + 'n_steps': 1e3, + 'n_walkers': 30, + 'moves': emcee.moves.KDEMove(), + 'multiprocessing': False, + 'verbose': False + } + + Bayes = BayesOpts.create_inference() + # Pkl the inference results + with open(f'Bayes_{model.name}.pkl', 'wb') as output: + joblib.dump(Bayes, output, 2) \ No newline at end of file diff --git a/src/tsunami.pk1 b/src/tsunami.pk1 new file mode 100644 index 0000000000000000000000000000000000000000..3f5e6991ba13f525c9e5d63d9e044796c36de9e0 GIT binary patch literal 21593 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¥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 LpLgeHt5p3P0&&UF literal 0 HcmV?d00001 diff --git a/src/tsunami_model.py b/src/tsunami_model.py new file mode 100644 index 000000000..6e9b0e50b --- /dev/null +++ b/src/tsunami_model.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +""" +Runs the umbridge command for the tsunami model + +@author: Rebecca Kohlhaas +""" +import umbridge +import numpy as np + +def tsunami_model(params): + model = umbridge.HTTPModel('http://localhost:4242', 'forward') + out = np.array(model(np.ndarray.tolist(params))) + + return {'T1': out[:,0], 'T2': out[:,1], 'H1': out[:,2], 'H2': out[:,3], 'x_values': [0]} + + \ No newline at end of file -- GitLab