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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
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