From b15f4942932dd4040364c986127f6b46d83e51dc Mon Sep 17 00:00:00 2001
From: kohlhaasrebecca <rebecca.kohlhaas@outlook.com>
Date: Mon, 29 Jan 2024 11:12:16 +0100
Subject: [PATCH] Finalized UMBridge example

UMBridge examples cleaned and pylink 'single-valued' removed
---
 .../example_umbridge.py                       | 183 ------------------
 .../example_umbridge_testmodel.py             |  24 +--
 .../example_umbridge_tsunamitutorial.py       | 183 ++++++++++++++++++
 .../umbridge_tsunamitutorial/testmodel.pk1    | Bin 12063 -> 0 bytes
 .../umbridge_tsunamitutorial/testmodel.py     |  20 --
 .../umbridge_tsunamitutorial/tsunami_model.py |   4 +-
 .../tsunami_model1.py                         |   4 +-
 src/bayesvalidrox/pylink/pylink.py            |  45 +++--
 8 files changed, 221 insertions(+), 242 deletions(-)
 delete mode 100644 examples/umbridge_tsunamitutorial/example_umbridge.py
 create mode 100644 examples/umbridge_tsunamitutorial/example_umbridge_tsunamitutorial.py
 delete mode 100644 examples/umbridge_tsunamitutorial/testmodel.pk1
 delete mode 100644 examples/umbridge_tsunamitutorial/testmodel.py

diff --git a/examples/umbridge_tsunamitutorial/example_umbridge.py b/examples/umbridge_tsunamitutorial/example_umbridge.py
deleted file mode 100644
index b83175cf7..000000000
--- a/examples/umbridge_tsunamitutorial/example_umbridge.py
+++ /dev/null
@@ -1,183 +0,0 @@
-# -*- 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 - level 0
-    model0 = bv.PyLinkForwardModel()
-    #model.link_type = 'Function'
-    model0.link_type = 'umbridge'
-    model0.py_file = 'tsunami_model'
-    model0.name = 'tsunami_model'
-    model0.Output.names = ['T1', 'T2', 'H1', 'H2']
-    #model.observations = data_dict1_times
-    
-    # Define the model - level 1
-    model1 = bv.PyLinkForwardModel()
-    #model.link_type = 'Function'
-    model1.link_type = 'umbridge'
-    model1.py_file = 'tsunami_model1'
-    model1.name = 'tsunami_model1'
-    model1.Output.names = ['T1', 'T2', 'H1', 'H2']
-    #model.observations = data_dict1_times
-    
-    # Create the surrogate
-    surrogate_opts0 = bv.MetaModel(priors, model0)
-    
-    # Select your metamodel method
-    surrogate_opts0.meta_model_type = 'aPCE'
-    surrogate_opts0.pce_reg_method = 'FastARD'
-    surrogate_opts0.pce_deg = np.arange(1, 5)
-    surrogate_opts0.pce_q_norm = 0.4#1.0
-
-    # Select your metamodel method         
-    surrogate_opts0.add_ExpDesign()
-    surrogate_opts0.ExpDesign.method = 'normal'
-    surrogate_opts0.ExpDesign.n_init_samples = 50
-    surrogate_opts0.ExpDesign.sampling_method = 'latin-hypercube'
-    surrogate0 = surrogate_opts0.create_metamodel()
-    print('Surrogate 0 completed')
-    print('')
-    
-    # Create the surrogate
-    surrogate_opts1 = bv.MetaModel(priors, model1)
-    
-    # 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 = 50
-    surrogate_opts1.ExpDesign.sampling_method = 'latin-hypercube'
-    surrogate1 = surrogate_opts1.create_metamodel()
-    print('Surrogate 1 completed')
-    print('')
-
-    # Save surrogate
-    with open('tsunami0.pk1', 'wb') as output:
-        joblib.dump(surrogate0, output, 2)
-        
-    # Save surrogate
-    with open('tsunami1.pk1', 'wb') as output:
-        joblib.dump(surrogate1, output, 2)
-    
-    if 0:
-        # 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]]))
-    model0.observations = true_data
-    model1.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]]))
-    
-    # Bayesian Inference
-    if 0:
-        # Set uncertainty
-        import pandas as pd
-        obsData = pd.DataFrame(true_data_nox, columns=model0.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_{model0.name}.pkl', 'wb') as output:
-            joblib.dump(Bayes, output, 2)
-            
-    # Model Comparison
-    if 1:
-        # Set the models
-        meta_models = {
-            "M0": surrogate0,
-            "M1": surrogate1,
-            }
-
-        # BME Bootstrap options
-        opts_bootstrap = {
-            "bootstrap": True,
-            "n_samples": 1000,
-            "Discrepancy": DiscrepancyOpts,
-            "emulator": True,
-            "plot_post_pred": False
-            }
-
-        # Run model comparison
-        BayesOpts = bv.BayesModelComparison(
-            justifiability=True,
-            n_bootstarp=10,
-            just_n_meas=2
-            )
-        output_dict = BayesOpts.create_model_comparison(
-            meta_models,
-            opts_bootstrap
-            )
-                
-        import pickle as pkl
-        # save model comparison results
-        with open('ModelComparison.pkl', 'wb') as f:
-            pkl.dump(output_dict, f)
-        
\ No newline at end of file
diff --git a/examples/umbridge_tsunamitutorial/example_umbridge_testmodel.py b/examples/umbridge_tsunamitutorial/example_umbridge_testmodel.py
index 95ec964fa..22a0f750e 100644
--- a/examples/umbridge_tsunamitutorial/example_umbridge_testmodel.py
+++ b/examples/umbridge_tsunamitutorial/example_umbridge_testmodel.py
@@ -1,12 +1,12 @@
 # -*- coding: utf-8 -*-
 """
-Created on Mon Dec 11 14:03:23 2023
+Example and test for using UM-Bridge with bayesvalidrox.
+This example uses the `testmodel` and sets the model type explicitly as 
+UM-Bridge for the pylink model.
 
-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
+An example for using UM-Bridge with an explicitly defined model can be found in 
+`example_umbridge_tsunamimodel.py`
 
-@author: rkohlhaas
 """
 
 import joblib
@@ -31,32 +31,30 @@ if __name__ == '__main__':
     priors.Marginals[0].name = 'x'
     priors.Marginals[0].input_data = np.random.uniform(50,150,n_prior_sample)  
     
-    # Define the model - level 0
+    # Define the model directly via Pylink
     model = PyLinkForwardModel()
-    #model.link_type = 'Function'
-    #model.py_file = 'testmodel'
-    #model.name = 'testmodel'
     model.link_type = 'umbridge'
     model.host = 'http://testmodel.linusseelinger.de'
     model.modelparams = {}
     model.name = 'testmodel'
     model.Output.names = ['y']
-    model.output_type = 'single-valued'
+    model.x_values = np.array([0])
     
     # Create the surrogate
     surrogate_opts = MetaModel(priors)
     
-    # Select your metamodel method
+    # Select the surrogate type and properties
     surrogate_opts.meta_model_type = 'aPCE'
     surrogate_opts.pce_reg_method = 'FastARD'
     surrogate_opts.pce_deg = np.arange(1, 5)
     surrogate_opts.pce_q_norm = 0.4
 
-    # Select your metamodel method    
+    # Define the experimental design and sampling methods
     ExpDesign = ExpDesigns(priors)     
     ExpDesign.n_init_samples = 50
     ExpDesign.sampling_method = 'latin-hypercube'
     
+    # Start and run the engine
     engine = Engine(surrogate_opts, model, ExpDesign)
     engine.start_engine()
     engine.train_normal(parallel = False)
@@ -70,8 +68,10 @@ if __name__ == '__main__':
     # Post processing
     L2_PostPCE = PostProcessing(engine)
     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()
diff --git a/examples/umbridge_tsunamitutorial/example_umbridge_tsunamitutorial.py b/examples/umbridge_tsunamitutorial/example_umbridge_tsunamitutorial.py
new file mode 100644
index 000000000..c456787d0
--- /dev/null
+++ b/examples/umbridge_tsunamitutorial/example_umbridge_tsunamitutorial.py
@@ -0,0 +1,183 @@
+# -*- coding: utf-8 -*-
+"""
+Example and test for using UM-Bridge with bayesvalidrox.
+Example model is the tsunami model described here:
+    https://um-bridge-benchmarks.readthedocs.io/en/docs/inverse-benchmarks/exahype-tsunami.html
+It needs to run separately while starting this example. 
+    docker run -it -p 4242:4242 -v ~/tsunami_output:/output linusseelinger/model-exahype-tsunami
+    
+This example uses models that are exmplicitly defined as functions outside of
+PyLink. An example for the link_type 'umbdridge' can be found in 
+`example_umbridge_testmodel.py`.
+
+"""
+
+import copy
+import joblib
+import numpy as np 
+import pandas as pd
+import umbridge
+
+import bayesvalidrox as bv
+from tsunami_model import tsunami_model
+
+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 - level 0
+    model0 = bv.PyLinkForwardModel()
+    model0.link_type = 'function'
+    model0.py_file = 'tsunami_model'
+    model0.name = 'tsunami_model'
+    model0.Output.names = ['T1', 'T2', 'H1', 'H2']
+    #model.observations = data_dict1_times
+    
+    # Define the model - level 1
+    model1 = bv.PyLinkForwardModel()
+    model1.link_type = 'function'
+    model1.py_file = 'tsunami_model1'
+    model1.name = 'tsunami_model1'
+    model1.Output.names = ['T1', 'T2', 'H1', 'H2']
+    #model.observations = data_dict1_times
+    
+    # Create the surrogate
+    surrogate_opts0 = bv.MetaModel(priors, model0)
+    
+    # Select your metamodel method
+    surrogate_opts0.meta_model_type = 'aPCE'
+    surrogate_opts0.pce_reg_method = 'FastARD'
+    surrogate_opts0.pce_deg = np.arange(1, 5)
+    surrogate_opts0.pce_q_norm = 0.4#1.0
+
+    # Define ExpDesign - this is the same for both models       
+    ExpDesign0 = bv.ExpDesigns(priors)
+    ExpDesign0.method = 'normal'
+    ExpDesign0.n_init_samples = 50
+    ExpDesign0.sampling_method = 'latin-hypercube'
+    ExpDesign1 = copy.deepcopy(ExpDesign0)
+    
+    # Start and run the engine
+    engine0 = bv.Engine(surrogate_opts0, model0, ExpDesign0)
+    engine0.start_engine()
+    engine0.train_normal(parallel = False)
+    print('Surrogate 0 completed')
+    print('')
+    
+    # Create the surrogate
+    surrogate_opts1 = bv.MetaModel(priors, model1)
+    
+    # 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
+
+    # Start and run the engine
+    engine1 = bv.Engine(surrogate_opts1, model1, ExpDesign1)
+    engine1.start_engine()
+    engine1.train_normal(parallel = False)
+    print('Surrogate 1 completed')
+    print('')
+
+    # Save surrogate
+    with open('tsunami0.pk1', 'wb') as output:
+        joblib.dump(engine0.MetaModel, output, 2)
+        
+    # Save surrogate
+    with open('tsunami1.pk1', 'wb') as output:
+        joblib.dump(engine1.MetaModel, output, 2)
+    
+    # Post processing on model 1
+    L2_PostPCE = bv.PostProcessing(engine1)
+    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()
+
+    # Get reference evaluation as 'true data'
+    true_data = tsunami_model(np.array([[90.0,60.0]]))
+    model0.observations = true_data
+    model1.observations = true_data
+    true_data_nox = copy.deepcopy(true_data)
+    true_data_nox.pop('x_values')
+    
+    # Direct surrogate evaluation
+    mean, std = engine1.MetaModel.eval_metamodel(np.array([[90.0,60.0]]))
+    
+    # Bayesian Inference
+    # Set uncertainty
+    obsData = pd.DataFrame(true_data_nox, columns=model0.Output.names)
+    DiscrepancyOpts = bv.Discrepancy('')
+    DiscrepancyOpts.type = 'Gaussian'
+    DiscrepancyOpts.parameters = (obsData*0.15)**2
+    
+    # Parameter estimation / single model validation via TOM for model 1
+    BayesOpts = bv.BayesInference(engine1)
+    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_{model0.name}.pkl', 'wb') as output:
+        joblib.dump(Bayes, output, 2)
+            
+    # Model Comparison
+    # Set the models
+    meta_models = {
+        "M0": engine0,
+        "M1": engine1,
+        }
+
+    # BME Bootstrap options
+    opts_bootstrap = {
+        "bootstrap": True,
+        "n_samples": 1000,
+        "Discrepancy": DiscrepancyOpts,
+        "emulator": True,
+        "plot_post_pred": False
+        }
+
+    # Run model comparison
+    BayesOpts = bv.BayesModelComparison(
+        justifiability=True,
+        n_bootstarp=10,
+        just_n_meas=2
+        )
+    output_dict = BayesOpts.create_model_comparison(
+        meta_models,
+        opts_bootstrap
+        )
+            
+    # save model comparison results
+    with open('ModelComparison.pkl', 'wb') as f:
+        joblib.dump(output_dict, f)
+    
\ No newline at end of file
diff --git a/examples/umbridge_tsunamitutorial/testmodel.pk1 b/examples/umbridge_tsunamitutorial/testmodel.pk1
deleted file mode 100644
index 700c643fc23e631a23cb23dd0972954904268377..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 12063
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
ULH8_6w`rJ;EflFKay{w)03D*=fdBvi

diff --git a/examples/umbridge_tsunamitutorial/testmodel.py b/examples/umbridge_tsunamitutorial/testmodel.py
deleted file mode 100644
index b213d2a3a..000000000
--- a/examples/umbridge_tsunamitutorial/testmodel.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Runs the umbridge command for the tsunami model
-
-@author: Rebecca Kohlhaas
-"""
-import umbridge
-import numpy as np
-
-def testmodel(params):
-    # Get the model
-    model = umbridge.HTTPModel('http://testmodel.linusseelinger.de', 'forward')
-    # Run the model
-    params = np.ndarray.tolist(params)
-    out = np.array(model(params))
-    #print(out)
-    return {'y':out[:,0],'x_values':[0]}
-    
-    
-    
\ No newline at end of file
diff --git a/examples/umbridge_tsunamitutorial/tsunami_model.py b/examples/umbridge_tsunamitutorial/tsunami_model.py
index 7f8e54170..2d837a9ae 100644
--- a/examples/umbridge_tsunamitutorial/tsunami_model.py
+++ b/examples/umbridge_tsunamitutorial/tsunami_model.py
@@ -1,8 +1,6 @@
 # -*- coding: utf-8 -*-
 """
-Runs the umbridge command for the tsunami model
-
-@author: Rebecca Kohlhaas
+Runs the umbridge command for the tsunami model.
 """
 import umbridge
 import numpy as np
diff --git a/examples/umbridge_tsunamitutorial/tsunami_model1.py b/examples/umbridge_tsunamitutorial/tsunami_model1.py
index 3e56801e5..239670773 100644
--- a/examples/umbridge_tsunamitutorial/tsunami_model1.py
+++ b/examples/umbridge_tsunamitutorial/tsunami_model1.py
@@ -1,8 +1,6 @@
 # -*- coding: utf-8 -*-
 """
-Runs the umbridge command for the tsunami model
-
-@author: Rebecca Kohlhaas
+Runs the umbridge command for the tsunami model on level 1.
 """
 import umbridge
 import numpy as np
diff --git a/src/bayesvalidrox/pylink/pylink.py b/src/bayesvalidrox/pylink/pylink.py
index 5084f1cf1..8899447c5 100644
--- a/src/bayesvalidrox/pylink/pylink.py
+++ b/src/bayesvalidrox/pylink/pylink.py
@@ -473,6 +473,8 @@ class PyLinkForwardModel(object):
         # 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':
+            if not hasattr(self, 'x_values'):
+                raise AttributeError('For model type `umbridge` the attribute `x_values` needs to be set for the model!')
             # Init model
             #model = umbridge.HTTPModel('http://localhost:4242', 'forward')
             self.model = umbridge.HTTPModel(self.host, 'forward') # TODO: is this always forward?
@@ -608,30 +610,31 @@ class PyLinkForwardModel(object):
         out_dict = {}
         cnt = 0
         for key in self.Output.names:
-            # If needed resort into single-value outputs
-            if self.output_type == 'single-valued':
-                if out.shape[1]>1:  # TODO: this doesn't fully seem correct??
-                    for i in range(out[:,key]): # TODO: this doesn't fully seem correct??
-                        new_key = key+str(i)
-                        if new_key not in self.Output.names:
-                            self.Output.names.append(new_key)
-                            if i == 0:
-                                self.Ouptut.names.remove(key)
-                        out_dict[new_key] = out[:,cnt,i] # TODO: not sure about this, need to test
-                else: 
-                    out_dict[key] = out[:,cnt]
-                    
-                
-            else:
-                out_dict[key] = out[:,cnt]
+        #    # If needed resort into single-value outputs
+        #    if self.output_type == 'single-valued':
+        #        if out.shape[1]>1:  # TODO: this doesn't fully seem correct??
+        #            for i in range(out[:,key]): # TODO: this doesn't fully seem correct??
+        #                new_key = key+str(i)
+        #                if new_key not in self.Output.names:
+        #                    self.Output.names.append(new_key)
+        #                    if i == 0:
+        #                        self.Ouptut.names.remove(key)
+        #                out_dict[new_key] = out[:,cnt,i] # TODO: not sure about this, need to test
+        #        else: 
+        #            out_dict[key] = out[:,cnt]
+        #            
+        #        
+        #    else:
+            out_dict[key] = out[:,cnt]
             cnt += 1
         
             
-        # TODO: how to deal with the x-values?
-        if self.output_type == 'single-valued':
-            out_dict['x_values'] = [0]
-        else:
-            out_dict['x_values'] = np.arange(0,out[:,0].shape[0],1)
+        ## TODO: how to deal with the x-values?
+        #if self.output_type == 'single-valued':
+        #    out_dict['x_values'] = [0]
+        #else:
+        #    out_dict['x_values'] = np.arange(0,out[:,0].shape[0],1)
+        out_dict['x_values'] = self.x_values
         
         #return {'T1':out[:,0], 'T2':out[:,1], 'H1':out[:,2], 'H2':out[:,3], 
        #         'x_values':[0]}
-- 
GitLab