diff --git a/EIVPackage/EIVData/cubic.py b/EIVPackage/EIVData/cubic.py
new file mode 100644
index 0000000000000000000000000000000000000000..dce6ea452fba08280d457e995295d5fe87d46d6d
--- /dev/null
+++ b/EIVPackage/EIVData/cubic.py
@@ -0,0 +1,103 @@
+import torch
+import sys
+from torch.utils.data import TensorDataset
+
+from EIVGeneral.manipulate_tensors import add_noise, normalize_tensor,\
+        unnormalize_tensor
+
+total_number_of_datapoints = 1000
+input_range = [-1,1]
+slope = 1.0
+intercept = 0.0
+x_noise_strength = 0.1 
+y_noise_strength = 0.1
+func = lambda true_x: slope * true_x**3 + intercept 
+
+def load_data(seed=0, splitting_part=0.8, normalize=True,
+        return_ground_truth=False,
+        return_normalized_func=False,
+        fixed_seed = 0):
+    """
+    Loads one-dimensional data, cubic data as in Hernandez-Lobato, Adams 2015.
+    :param seed: Seed for shuffling the data before splitting.
+    :param splitting_part: Which fraction of the data to use as training
+    data. Defaults to 0.8.
+    :param normalize: Whether to normalize the data, defaults to True.
+    :param return_ground_truth: Boolean. If True, the unnoisy ground truth will
+    also be returned. Defaults to False.
+    :param return_normalized_func: Boolean (default False). If True, the
+    normalized version of the used function is returned as a last element.
+    :param fixed_seed: Used to generate the full dataset (test and train).
+    Defaults to 0.
+    :returns: cubic_trainset, cubic_testset, (, normalized_func) if
+    return_ground_truth is False,
+    else cubic_trainset, cubic_testset,  true_cubic_trainset,
+    true_cubic_testset, (, normalized_func). The "true" datasets each return
+    **four tensors**: The true x,y and their noisy counterparts.
+    """
+    # draw different seeds for noise and splitting
+    random_generator = torch.Generator().manual_seed(fixed_seed)
+    seeds = [int(t) for t in torch.randint(0,sys.maxsize,(3,),\
+            generator=random_generator)]
+
+    # create new generators from tensor seeds
+    true_x = input_range[0] + (input_range[1]-input_range[0])\
+                  * torch.rand((total_number_of_datapoints,1),
+                          generator=torch.Generator().manual_seed(seeds[0]))
+    true_y = func(true_x)
+
+    # add noise and normalize x and y
+    (noisy_x, noisy_y), (true_x, true_y), normalization_list = add_noise(
+            tensor_list=(true_x, true_y),
+            noise_strength_list=(x_noise_strength, y_noise_strength),
+            seed_list=seeds[1:3],
+            normalize=normalize,
+            return_normalization=True)
+    if normalize:
+        def normalized_func(x):
+            unnormalized_x = unnormalize_tensor(x, normalization_list[0])
+            y = func(unnormalized_x)
+            normalized_y = normalize_tensor(y, normalization_list[1])
+            return normalized_y
+    else:
+        def normalized_func(x):
+            return func(x)
+    dataset_len = noisy_x.shape[0]
+
+    # shuffle via seed
+    new_order = torch.randperm(dataset_len,
+            generator=torch.Generator().manual_seed(seed))
+    true_x = true_x[new_order, ...]
+    true_y = true_y[new_order, ...]
+    noisy_x = noisy_x[new_order, ...]
+    noisy_y = noisy_y[new_order, ...]
+
+    # create datasets
+    train_len = int(dataset_len*splitting_part)
+    test_len = dataset_len - train_len
+    true_train_x, true_test_x = torch.split(true_x, [train_len, test_len])
+    true_train_y, true_test_y = torch.split(true_y, [train_len, test_len])
+    noisy_train_x, noisy_test_x = torch.split(noisy_x, [train_len, test_len])
+    noisy_train_y, noisy_test_y = torch.split(noisy_y, [train_len, test_len])
+    cubic_trainset = TensorDataset(noisy_train_x, noisy_train_y)
+    cubic_testset = TensorDataset(noisy_test_x, noisy_test_y)
+    true_cubic_trainset = TensorDataset(true_train_x, true_train_y,
+            noisy_train_x, noisy_train_y)
+    true_cubic_testset = TensorDataset(true_test_x, true_test_y,
+            noisy_test_x, noisy_test_y)
+
+
+    # return different objects, depending on Booleans
+    if not return_ground_truth:
+        if not return_normalized_func:
+            return cubic_trainset, cubic_testset
+        else:
+            return cubic_trainset, cubic_testset, normalized_func
+    else:
+        if not return_normalized_func:
+            return cubic_trainset, cubic_testset, true_cubic_trainset,\
+                true_cubic_testset
+        else: 
+            return cubic_trainset, cubic_testset, true_cubic_trainset,\
+                true_cubic_testset, normalized_func
+
diff --git a/EIVPackage/EIVData/linear.py b/EIVPackage/EIVData/linear.py
index 75c7e400e558b3825b700ce9d4108786e4a9bc62..97d2cd951e0d86650c9ab85af17d5ceb96312375 100644
--- a/EIVPackage/EIVData/linear.py
+++ b/EIVPackage/EIVData/linear.py
@@ -2,47 +2,77 @@ import torch
 import sys
 from torch.utils.data import TensorDataset
 
-from EIVGeneral.manipulate_tensors import add_noise
+from EIVGeneral.manipulate_tensors import add_noise, normalize_tensor,\
+        unnormalize_tensor
 
-total_number_of_datapoints = 2000
+total_number_of_datapoints = 500
 input_range = [-1,1]
 slope = 1.0
 intercept = 0.0
-x_noise_strength = 0.05
+x_noise_strength = 0.1
 y_noise_strength = 0.1
+func = lambda true_x: slope * true_x + intercept 
 
 def load_data(seed=0, splitting_part=0.8, normalize=True,
-        return_ground_truth=False):
+        return_ground_truth=False,
+        return_normalized_func=False,
+        fixed_seed = 0):
     """
     Loads one-dimensional data
-    :param seed: Seed for drawing and splitting the data.
+    :param seed: Seed for shuffling the data before splitting.
     :param splitting_part: Which fraction of the data to use as training
     data. Defaults to 0.8.
     :param normalize: Whether to normalize the data, defaults to True.
     :param return_ground_truth: Boolean. If True, the unnoisy ground truth will
     also be returned. Defaults to False.
-    :returns: linear_trainset, linear_testset if return_ground_truth is False,
+    :param return_normalized_func: Boolean (default False). If True, the
+    normalized version of the used function is returned as a last element.
+    :param fixed_seed: Used to generate the full dataset (test and train).
+    Defaults to 0.
+    :returns: linear_trainset, linear_testset, (, normalized_func) if
+    return_ground_truth is False,
     else linear_trainset, linear_testset,  true_linear_trainset,
-    true_linear_testset. The later two return **four tensors**: The true x,y and
-    their noisy counterparts.
+    true_linear_testset, (, normalized_func). The "true" datasets each return
+    **four tensors**: The true x,y and their noisy counterparts.
     """
-    random_generator = torch.Generator().manual_seed(seed)
     # draw different seeds for noise and splitting
+    random_generator = torch.Generator().manual_seed(fixed_seed)
     seeds = [int(t) for t in torch.randint(0,sys.maxsize,(3,),\
             generator=random_generator)]
+
     # create new generators from tensor seeds
     true_x = input_range[0] + (input_range[1]-input_range[0])\
                   * torch.rand((total_number_of_datapoints,1),
                           generator=torch.Generator().manual_seed(seeds[0]))
-    true_y = slope * true_x + intercept 
+    true_y = func(true_x)
+
     # add noise and normalize x and y
-    (noisy_x, noisy_y), (true_x, true_y) = add_noise(
+    (noisy_x, noisy_y), (true_x, true_y), normalization_list = add_noise(
             tensor_list=(true_x, true_y),
             noise_strength_list=(x_noise_strength, y_noise_strength),
             seed_list=seeds[1:3],
-            normalize=normalize)
-    # create datasets
+            normalize=normalize,
+            return_normalization=True)
+    if normalize:
+        def normalized_func(x):
+            unnormalized_x = unnormalize_tensor(x, normalization_list[0])
+            y = func(unnormalized_x)
+            normalized_y = normalize_tensor(y, normalization_list[1])
+            return normalized_y
+    else:
+        def normalized_func(x):
+            return func(x)
     dataset_len = noisy_x.shape[0]
+
+    # shuffle via seed
+    new_order = torch.randperm(dataset_len,
+            generator=torch.Generator().manual_seed(seed))
+    true_x = true_x[new_order, ...]
+    true_y = true_y[new_order, ...]
+    noisy_x = noisy_x[new_order, ...]
+    noisy_y = noisy_y[new_order, ...]
+
+    # create datasets
     train_len = int(dataset_len*splitting_part)
     test_len = dataset_len - train_len
     true_train_x, true_test_x = torch.split(true_x, [train_len, test_len])
@@ -55,8 +85,19 @@ def load_data(seed=0, splitting_part=0.8, normalize=True,
             noisy_train_x, noisy_train_y)
     true_linear_testset = TensorDataset(true_test_x, true_test_y,
             noisy_test_x, noisy_test_y)
+
+
+    # return different objects, depending on Booleans
     if not return_ground_truth:
-        return linear_trainset, linear_testset
+        if not return_normalized_func:
+            return linear_trainset, linear_testset
+        else:
+            return linear_trainset, linear_testset, normalized_func
     else:
-        return linear_trainset, linear_testset, true_linear_trainset,\
-            true_linear_testset
+        if not return_normalized_func:
+            return linear_trainset, linear_testset, true_linear_trainset,\
+                true_linear_testset
+        else: 
+            return linear_trainset, linear_testset, true_linear_trainset,\
+                true_linear_testset, normalized_func
+
diff --git a/EIVPackage/EIVData/quadratic.py b/EIVPackage/EIVData/quadratic.py
index a42148205f481665dc420172e5797b84ffaab4ad..655e6bf8455a73f537795782fa61fde6fb0b5cec 100644
--- a/EIVPackage/EIVData/quadratic.py
+++ b/EIVPackage/EIVData/quadratic.py
@@ -2,47 +2,77 @@ import torch
 import sys
 from torch.utils.data import TensorDataset
 
-from EIVGeneral.manipulate_tensors import add_noise
+from EIVGeneral.manipulate_tensors import add_noise, normalize_tensor,\
+        unnormalize_tensor
 
-total_number_of_datapoints = 2000
+total_number_of_datapoints = 500
 input_range = [-1,1]
 slope = 1.0
 intercept = 0.0
-x_noise_strength = 0.05
+x_noise_strength = 0.1
 y_noise_strength = 0.1
+func = lambda true_x: slope * true_x**2 + intercept 
 
 def load_data(seed=0, splitting_part=0.8, normalize=True,
-        return_ground_truth=False):
+        return_ground_truth=False,
+        return_normalized_func=False,
+        fixed_seed = 0):
     """
     Loads one-dimensional data
-    :param seed: Seed for drawing and splitting the data.
+    :param seed: Seed for shuffling the data before splitting.
     :param splitting_part: Which fraction of the data to use as training
     data. Defaults to 0.8.
     :param normalize: Whether to normalize the data, defaults to True.
     :param return_ground_truth: Boolean. If True, the unnoisy ground truth will
     also be returned. Defaults to False.
-    :returns: quadratic_trainset, quadratic_testset if return_ground_truth is False,
+    :param return_normalized_func: Boolean (default False). If True, the
+    normalized version of the used function is returned as a last element.
+    :param fixed_seed: Used to generate the full dataset (test and train).
+    Defaults to 0.
+    :returns: quadratic_trainset, quadratic_testset, (, normalized_func) if
+    return_ground_truth is False,
     else quadratic_trainset, quadratic_testset,  true_quadratic_trainset,
-    true_quadratic_testset. The later two return **four tensors**: The true x,y and
-    their noisy counterparts.
+    true_quadratic_testset, (, normalized_func). The "true" datasets each return
+    **four tensors**: The true x,y and their noisy counterparts.
     """
-    random_generator = torch.Generator().manual_seed(seed)
     # draw different seeds for noise and splitting
+    random_generator = torch.Generator().manual_seed(fixed_seed)
     seeds = [int(t) for t in torch.randint(0,sys.maxsize,(3,),\
             generator=random_generator)]
+
     # create new generators from tensor seeds
     true_x = input_range[0] + (input_range[1]-input_range[0])\
                   * torch.rand((total_number_of_datapoints,1),
                           generator=torch.Generator().manual_seed(seeds[0]))
-    true_y = slope * true_x**2 + intercept 
+    true_y = func(true_x)
+
     # add noise and normalize x and y
-    (noisy_x, noisy_y), (true_x, true_y) = add_noise(
+    (noisy_x, noisy_y), (true_x, true_y), normalization_list = add_noise(
             tensor_list=(true_x, true_y),
             noise_strength_list=(x_noise_strength, y_noise_strength),
             seed_list=seeds[1:3],
-            normalize=normalize)
-    # create datasets
+            normalize=normalize,
+            return_normalization=True)
+    if normalize:
+        def normalized_func(x):
+            unnormalized_x = unnormalize_tensor(x, normalization_list[0])
+            y = func(unnormalized_x)
+            normalized_y = normalize_tensor(y, normalization_list[1])
+            return normalized_y
+    else:
+        def normalized_func(x):
+            return func(x)
     dataset_len = noisy_x.shape[0]
+
+    # shuffle via seed
+    new_order = torch.randperm(dataset_len,
+            generator=torch.Generator().manual_seed(seed))
+    true_x = true_x[new_order, ...]
+    true_y = true_y[new_order, ...]
+    noisy_x = noisy_x[new_order, ...]
+    noisy_y = noisy_y[new_order, ...]
+
+    # create datasets
     train_len = int(dataset_len*splitting_part)
     test_len = dataset_len - train_len
     true_train_x, true_test_x = torch.split(true_x, [train_len, test_len])
@@ -55,8 +85,19 @@ def load_data(seed=0, splitting_part=0.8, normalize=True,
             noisy_train_x, noisy_train_y)
     true_quadratic_testset = TensorDataset(true_test_x, true_test_y,
             noisy_test_x, noisy_test_y)
+
+
+    # return different objects, depending on Booleans
     if not return_ground_truth:
-        return quadratic_trainset, quadratic_testset
+        if not return_normalized_func:
+            return quadratic_trainset, quadratic_testset
+        else:
+            return quadratic_trainset, quadratic_testset, normalized_func
     else:
-        return quadratic_trainset, quadratic_testset, true_quadratic_trainset,\
-            true_quadratic_testset
+        if not return_normalized_func:
+            return quadratic_trainset, quadratic_testset, true_quadratic_trainset,\
+                true_quadratic_testset
+        else: 
+            return quadratic_trainset, quadratic_testset, true_quadratic_trainset,\
+                true_quadratic_testset, normalized_func
+
diff --git a/EIVPackage/EIVData/repeated_sampling.py b/EIVPackage/EIVData/repeated_sampling.py
index bff0c9fd73c9c08bb75acfbf82184842f343c1c6..16f27ec288aa7d8fca6f9a0800490bcce428669d 100644
--- a/EIVPackage/EIVData/repeated_sampling.py
+++ b/EIVPackage/EIVData/repeated_sampling.py
@@ -7,7 +7,8 @@ import sys
 import torch
 from torch.utils.data import TensorDataset
 
-from EIVGeneral.manipulate_tensors import add_noise
+from EIVGeneral.manipulate_tensors import add_noise, normalize_tensor,\
+        unnormalize_tensor
 
 class repeated_sampling():
     """
@@ -25,17 +26,35 @@ class repeated_sampling():
     """
     def __init__(self, dataclass, fixed_seed=0):
         self.dataclass = dataclass
+        self.func = dataclass.func
         self.fixed_seed = fixed_seed
         self.x_noise_strength = dataclass.x_noise_strength
         self.y_noise_strength = dataclass.y_noise_strength
 
     def __call__(self,seed=0, splitting_part=0.8, normalize=True,
-            return_ground_truth=False):
+            return_ground_truth=False,
+            return_normalized_func=False):
         _, _, true_trainset, true_testset\
                 = self.dataclass.load_data(
                         seed=self.fixed_seed, splitting_part=splitting_part,
                         normalize=False,
                         return_ground_truth=True)
+        """
+        Loads repeated sampling data
+        :param seed: Seed for the used noise
+        :param splitting_part: Which fraction of the data to use as training
+        data. Defaults to 0.8.
+        :param normalize: Whether to normalize the data, defaults to True.
+        :param return_ground_truth: Boolean. If True, the unnoisy ground truth will
+        also be returned. Defaults to False.
+        :param return_normalized_func: Boolean (default False). If True, the
+        normalized version of the used function is returned as a last element.
+        :returns: trainset, testset, (, normalized_func) if
+        return_ground_truth is False,
+        else trainset, testset,  true_trainset,
+        true_testset, (, normalized_func). The "true" datasets each return
+        **four tensors**: The true x,y and their noisy counterparts.
+        """
         full_noisy_x = torch.concat((true_trainset.tensors[2],
                 true_testset.tensors[2]), dim=0)
         full_noisy_y = torch.concat((true_trainset.tensors[3],
@@ -46,23 +65,45 @@ class repeated_sampling():
         # draw different seeds for noise and splitting
         seeds = [int(t) for t in torch.randint(0,sys.maxsize,(2,),\
                 generator=random_generator)]
-        (noisy_train_x, noisy_train_y), (true_train_x, true_train_y) =\
-                add_noise((true_train_x, true_train_y),
+        # use same normalization for train and test
+        (noisy_train_x, noisy_train_y), (true_train_x, true_train_y),\
+            normalization_list= add_noise((true_train_x, true_train_y),
                         (self.x_noise_strength, self.y_noise_strength), seeds,
                         normalize=normalize,
-                        normalization_list=[full_noisy_x, full_noisy_y])
+                        normalization_list=[full_noisy_x, full_noisy_y],
+                        return_normalization=True)
         (noisy_test_x, noisy_test_y), (true_test_x, true_test_y) =\
                 add_noise((true_test_x, true_test_y),
                         (self.x_noise_strength, self.y_noise_strength), seeds,
                         normalize=normalize,
-                        normalization_list=[full_noisy_x, full_noisy_y])
+                        normalization_list=[full_noisy_x, full_noisy_y],
+                        return_normalization=False) # same normalization
+        if normalize:
+            def normalized_func(x):
+                unnormalized_x = unnormalize_tensor(x, normalization_list[0])
+                y = self.func(unnormalized_x)
+                normalized_y = normalize_tensor(y, normalization_list[1])
+                return normalized_y
+        else:
+            def normalized_func(x):
+                return self.func(x)
         trainset = TensorDataset(noisy_train_x, noisy_train_y)
         testset = TensorDataset(noisy_test_x, noisy_test_y)
         true_trainset = TensorDataset(true_train_x, true_train_y,
                 noisy_train_x, noisy_train_y)
         true_testset = TensorDataset(true_test_x, true_test_y,
                 noisy_test_x, noisy_test_y)
+        # return different objects, depending on Booleans
         if not return_ground_truth:
-            return trainset, testset
+            if not return_normalized_func:
+                return trainset, testset
+            else:
+                return trainset, testset, normalized_func
         else:
-            return trainset, testset, true_trainset, true_testset
+            if not return_normalized_func:
+                return trainset, testset, true_trainset,\
+                    true_testset
+            else: 
+                return trainset, testset, true_trainset,\
+                    true_testset, normalized_func
+
diff --git a/EIVPackage/EIVData/sine.py b/EIVPackage/EIVData/sine.py
new file mode 100644
index 0000000000000000000000000000000000000000..cde80d349eef58cadab44b79f0937d52a90e8d41
--- /dev/null
+++ b/EIVPackage/EIVData/sine.py
@@ -0,0 +1,104 @@
+import torch
+import sys
+from torch.utils.data import TensorDataset
+
+from EIVGeneral.manipulate_tensors import add_noise, normalize_tensor,\
+        unnormalize_tensor
+
+total_number_of_datapoints = 2000
+input_range = [-0.2,0.8]
+intercept = 0.0
+x_noise_strength = 0.02 
+y_noise_strength = 0.02
+func = lambda true_x: true_x +\
+            torch.sin(2 * torch.pi * true_x) +\
+            torch.sin(4 * torch.pi * true_x)
+
+def load_data(seed=0, splitting_part=0.8, normalize=True,
+        return_ground_truth=False,
+        return_normalized_func=False,
+        fixed_seed = 0):
+    """
+    Loads one-dimensional, sine shaped data as in Blundell et al. 2014.
+    :param seed: Seed for shuffling the data before splitting.
+    :param splitting_part: Which fraction of the data to use as training
+    data. Defaults to 0.8.
+    :param normalize: Whether to normalize the data, defaults to True.
+    :param return_ground_truth: Boolean. If True, the unnoisy ground truth will
+    also be returned. Defaults to False.
+    :param return_normalized_func: Boolean (default False). If True, the
+    normalized version of the used function is returned as a last element.
+    :param fixed_seed: Used to generate the full dataset (test and train).
+    Defaults to 0.
+    :returns: sine_trainset, sine_testset, (, normalized_func) if
+    return_ground_truth is False,
+    else sine_trainset, sine_testset,  true_sine_trainset,
+    true_sine_testset, (, normalized_func). The "true" datasets each return
+    **four tensors**: The true x,y and their noisy counterparts.
+    """
+    # draw different seeds for noise and splitting
+    random_generator = torch.Generator().manual_seed(fixed_seed)
+    seeds = [int(t) for t in torch.randint(0,sys.maxsize,(3,),\
+            generator=random_generator)]
+
+    # create new generators from tensor seeds
+    true_x = input_range[0] + (input_range[1]-input_range[0])\
+                  * torch.rand((total_number_of_datapoints,1),
+                          generator=torch.Generator().manual_seed(seeds[0]))
+    true_y = func(true_x)
+
+    # add noise and normalize x and y
+    (noisy_x, noisy_y), (true_x, true_y), normalization_list = add_noise(
+            tensor_list=(true_x, true_y),
+            noise_strength_list=(x_noise_strength, y_noise_strength),
+            seed_list=seeds[1:3],
+            normalize=normalize,
+            return_normalization=True)
+    if normalize:
+        def normalized_func(x):
+            unnormalized_x = unnormalize_tensor(x, normalization_list[0])
+            y = func(unnormalized_x)
+            normalized_y = normalize_tensor(y, normalization_list[1])
+            return normalized_y
+    else:
+        def normalized_func(x):
+            return func(x)
+    dataset_len = noisy_x.shape[0]
+
+    # shuffle via seed
+    new_order = torch.randperm(dataset_len,
+            generator=torch.Generator().manual_seed(seed))
+    true_x = true_x[new_order, ...]
+    true_y = true_y[new_order, ...]
+    noisy_x = noisy_x[new_order, ...]
+    noisy_y = noisy_y[new_order, ...]
+
+    # create datasets
+    train_len = int(dataset_len*splitting_part)
+    test_len = dataset_len - train_len
+    true_train_x, true_test_x = torch.split(true_x, [train_len, test_len])
+    true_train_y, true_test_y = torch.split(true_y, [train_len, test_len])
+    noisy_train_x, noisy_test_x = torch.split(noisy_x, [train_len, test_len])
+    noisy_train_y, noisy_test_y = torch.split(noisy_y, [train_len, test_len])
+    sine_trainset = TensorDataset(noisy_train_x, noisy_train_y)
+    sine_testset = TensorDataset(noisy_test_x, noisy_test_y)
+    true_sine_trainset = TensorDataset(true_train_x, true_train_y,
+            noisy_train_x, noisy_train_y)
+    true_sine_testset = TensorDataset(true_test_x, true_test_y,
+            noisy_test_x, noisy_test_y)
+
+
+    # return different objects, depending on Booleans
+    if not return_ground_truth:
+        if not return_normalized_func:
+            return sine_trainset, sine_testset
+        else:
+            return sine_trainset, sine_testset, normalized_func
+    else:
+        if not return_normalized_func:
+            return sine_trainset, sine_testset, true_sine_trainset,\
+                true_sine_testset
+        else: 
+            return sine_trainset, sine_testset, true_sine_trainset,\
+                true_sine_testset, normalized_func
+
diff --git a/EIVPackage/EIVGeneral/manipulate_datasets.py b/EIVPackage/EIVGeneral/manipulate_datasets.py
index f04280e3a84f374cef1705a3e53a6607a6d0e394..553db1672f547837309c20383e788d35953933d8 100644
--- a/EIVPackage/EIVGeneral/manipulate_datasets.py
+++ b/EIVPackage/EIVGeneral/manipulate_datasets.py
@@ -19,3 +19,7 @@ class VerticalCut(Dataset):
 
     def __len__(self):
         return len(self.dataset)
+
+    @property
+    def tensors(self):
+        return itemgetter(*self.components_to_pick)(self.dataset.tensors)
diff --git a/EIVPackage/EIVGeneral/manipulate_tensors.py b/EIVPackage/EIVGeneral/manipulate_tensors.py
index 20c37a63ed2be6cc6a618b0d9c7a712ba8c81df7..57392fdfe722a0afc6250d29f2388a121242f7b7 100644
--- a/EIVPackage/EIVGeneral/manipulate_tensors.py
+++ b/EIVPackage/EIVGeneral/manipulate_tensors.py
@@ -18,9 +18,17 @@ def normalize_tensor(t, mean_std):
     """
     return (t-mean_std[0])/mean_std[1]
 
+def unnormalize_tensor(t, mean_std):
+    """
+    Unnormalize the tensor `t` by the mean `mean_std[0]` and the standard
+    devation `mean_std[1]`. The inverse of `normalize_tensor`.
+    """
+    return t * mean_std[1] + mean_std[0]
+
+
 
 def add_noise(tensor_list, noise_strength_list, seed_list, normalize=True,
-        normalization_list = None):
+        normalization_list = None, return_normalization = False):
     """
     Takes the tensors in `tensor_list`, adds random noise using the standard
     deviations in `noise_strength_list` and the seeds in `seed_list`, then, if
@@ -35,10 +43,14 @@ def add_noise(tensor_list, noise_strength_list, seed_list, normalize=True,
     :param normalization_list: Either None (default) or a list of tensors.
     If the latter, these tensors will be used for normalization and `normalize`
     is assumed to be True.
-    :returns: noisy_tensor_list, unnoisy_tensor_list, both normalized
+    :param list_of_normalization: Boolean. If True (default: False) the
+    used normalizations will be returned.
+    :returns: noisy_tensor_list, unnoisy_tensor_list(, list_of_normalization)
     """
     noisy_t_list = []
     unnoisy_t_list = []
+    # store tuples that were used for normalization in here
+    list_of_normalization = []
     if normalization_list is not None:
         assert len(normalization_list) == len(tensor_list)
     for i, (t,noise,seed) in enumerate(zip(tensor_list, noise_strength_list,\
@@ -51,9 +63,12 @@ def add_noise(tensor_list, noise_strength_list, seed_list, normalize=True,
                         get_normalization(normalization_list[i])
             else:
                 noisy_t_normalization = get_normalization(noisy_t)
+            list_of_normalization.append(noisy_t_normalization)
             noisy_t = normalize_tensor(noisy_t, noisy_t_normalization)
             t = normalize_tensor(t, noisy_t_normalization)
         noisy_t_list.append(noisy_t)
         unnoisy_t_list.append(t)
-    return noisy_t_list, unnoisy_t_list
-
+    if return_normalization:
+        return noisy_t_list, unnoisy_t_list, list_of_normalization
+    else:
+        return noisy_t_list, unnoisy_t_list
diff --git a/Experiments/configurations/eiv_cubic.json b/Experiments/configurations/eiv_cubic.json
new file mode 100644
index 0000000000000000000000000000000000000000..0e9f9e88edb30a99bd73a2814a92c4ca504218fd
--- /dev/null
+++ b/Experiments/configurations/eiv_cubic.json
@@ -0,0 +1,22 @@
+{
+	"long_dataname": "cubic",
+	"short_dataname": "cubic",
+	"normalize": false,
+	"lr": 1e-3,
+	"batch_size": 16,
+	"test_batch_size": 800,
+	"number_of_epochs": 100,
+	"unscaled_reg": 10,
+	"report_point": 5,
+	"p": 0.1,
+	"lr_update": 20,
+	"std_y_update_points": [1,40],
+	"eiv_prediction_number_of_draws": [100,5],
+	"eiv_prediction_number_of_batches": 10,
+	"init_std_y_list": [0.5],
+	"gamma": 0.5,
+	"hidden_layers": [128, 128, 128, 128],
+	"fixed_std_x": 0.10,
+	"seed_range": [0,10],
+	"gpu_number": 1
+}
diff --git a/Experiments/configurations/eiv_linear.json b/Experiments/configurations/eiv_linear.json
index a83fc15d5a0d1ba30017465b984ab6b79035b60b..8b7ebe0767411186d51ca18dde9d2d6aeb92de06 100644
--- a/Experiments/configurations/eiv_linear.json
+++ b/Experiments/configurations/eiv_linear.json
@@ -1,8 +1,9 @@
 {
 	"long_dataname": "linear",
 	"short_dataname": "linear",
+	"normalize": false,
 	"lr": 1e-3,
-	"batch_size": 64,
+	"batch_size": 16,
 	"test_batch_size": 800,
 	"number_of_epochs": 100,
 	"unscaled_reg": 10,
@@ -15,7 +16,7 @@
 	"init_std_y_list": [0.5],
 	"gamma": 0.5,
 	"hidden_layers": [128, 128, 128, 128],
-	"fixed_std_x": 0.05,
+	"fixed_std_x": 0.10,
 	"seed_range": [0,10],
 	"gpu_number": 1
 }
diff --git a/Experiments/configurations/eiv_quadratic.json b/Experiments/configurations/eiv_quadratic.json
index 9b5c52e663e597e08114038a2336d5ae7b8c6659..c774bbed09beb17e65c475fdfedfca1c795682a7 100644
--- a/Experiments/configurations/eiv_quadratic.json
+++ b/Experiments/configurations/eiv_quadratic.json
@@ -1,8 +1,9 @@
 {
 	"long_dataname": "quadratic",
 	"short_dataname": "quadratic",
+	"normalize": false,
 	"lr": 1e-3,
-	"batch_size": 64,
+	"batch_size": 16,
 	"test_batch_size": 800,
 	"number_of_epochs": 100,
 	"unscaled_reg": 10,
@@ -15,7 +16,7 @@
 	"init_std_y_list": [0.5],
 	"gamma": 0.5,
 	"hidden_layers": [128, 128, 128, 128],
-	"fixed_std_x": 0.05,
+	"fixed_std_x": 0.10,
 	"seed_range": [0,10],
 	"gpu_number": 1
 }
diff --git a/Experiments/configurations/eiv_sine.json b/Experiments/configurations/eiv_sine.json
new file mode 100644
index 0000000000000000000000000000000000000000..1195ea0891ab3dc49745b0c9b310416cc2ee0ba9
--- /dev/null
+++ b/Experiments/configurations/eiv_sine.json
@@ -0,0 +1,23 @@
+{
+	"long_dataname": "sine",
+	"short_dataname": "sine",
+	"normalize": false,
+	"lr": 1e-3,
+	"batch_size": 16,
+	"test_batch_size": 800,
+	"number_of_epochs": 100,
+	"unscaled_reg": 10,
+	"report_point": 5,
+	"p": 0.1,
+	"lr_update": 20,
+	"std_y_update_points": [1,40],
+	"eiv_number_of_forward_draws": 10,
+	"eiv_prediction_number_of_draws": [100,5],
+	"eiv_prediction_number_of_batches": 10,
+	"init_std_y_list": [0.1],
+	"gamma": 0.5,
+	"hidden_layers": [128, 128, 128, 128],
+	"fixed_std_x": 0.02,
+	"seed_range": [0,10],
+	"gpu_number": 1
+}
diff --git a/Experiments/configurations/noneiv_cubic.json b/Experiments/configurations/noneiv_cubic.json
new file mode 100644
index 0000000000000000000000000000000000000000..6f71af1d77db1a357f0d68edfe192cc06578d3af
--- /dev/null
+++ b/Experiments/configurations/noneiv_cubic.json
@@ -0,0 +1,21 @@
+{
+	"long_dataname": "cubic",
+	"short_dataname": "cubic",
+	"normalize": false,
+	"lr": 1e-3,
+	"batch_size": 16,
+	"test_batch_size": 800,
+	"number_of_epochs": 100,
+	"unscaled_reg": 10,
+	"report_point": 5,
+	"p": 0.1,
+	"lr_update": 20,
+	"std_y_update_points": [1,40] ,
+	"noneiv_prediction_number_of_draws": 100,
+	"noneiv_prediction_number_of_batches": 10,
+	"init_std_y_list": [0.5],
+	"gamma": 0.5,
+	"hidden_layers": [128, 128, 128, 128],
+	"seed_range": [0,10],
+	"gpu_number": 1
+}
diff --git a/Experiments/configurations/noneiv_linear.json b/Experiments/configurations/noneiv_linear.json
index 1b2110a6149f8125fba9752a95cc134fac7267c2..ae3040e2b157bc7bd10cfd73d9f4f5448f4cf023 100644
--- a/Experiments/configurations/noneiv_linear.json
+++ b/Experiments/configurations/noneiv_linear.json
@@ -1,8 +1,9 @@
 {
 	"long_dataname": "linear",
 	"short_dataname": "linear",
+	"normalize": false,
 	"lr": 1e-3,
-	"batch_size": 64,
+	"batch_size": 16,
 	"test_batch_size": 800,
 	"number_of_epochs": 100,
 	"unscaled_reg": 10,
diff --git a/Experiments/configurations/noneiv_quadratic.json b/Experiments/configurations/noneiv_quadratic.json
index 573d7877bc9def4ce8b1ffba8a847495d09b7f47..405263797ff8b9ffa3ef692540b0db5edc506cda 100644
--- a/Experiments/configurations/noneiv_quadratic.json
+++ b/Experiments/configurations/noneiv_quadratic.json
@@ -1,8 +1,9 @@
 {
 	"long_dataname": "quadratic",
 	"short_dataname": "quadratic",
+	"normalize": false,
 	"lr": 1e-3,
-	"batch_size": 64,
+	"batch_size": 16,
 	"test_batch_size": 800,
 	"number_of_epochs": 100,
 	"unscaled_reg": 10,
diff --git a/Experiments/configurations/noneiv_sine.json b/Experiments/configurations/noneiv_sine.json
new file mode 100644
index 0000000000000000000000000000000000000000..cc889194d725b1e167b1129d444e12720e2610a0
--- /dev/null
+++ b/Experiments/configurations/noneiv_sine.json
@@ -0,0 +1,21 @@
+{
+	"long_dataname": "sine",
+	"short_dataname": "sine",
+	"normalize": false,
+	"lr": 1e-3,
+	"batch_size": 16,
+	"test_batch_size": 800,
+	"number_of_epochs": 100,
+	"unscaled_reg": 10,
+	"report_point": 5,
+	"p": 0.1,
+	"lr_update": 20,
+	"std_y_update_points": [1,40] ,
+	"noneiv_prediction_number_of_draws": 100,
+	"noneiv_prediction_number_of_batches": 10,
+	"init_std_y_list": [0.1],
+	"gamma": 0.5,
+	"hidden_layers": [128, 128, 128, 128],
+	"seed_range": [0,10],
+	"gpu_number": 1
+}
diff --git a/Experiments/create_tabular.py b/Experiments/create_tabular.py
index 8b8373de4c5735c380cde7b9079b39e224a0af53..cc15d7c84243bc517954c38ec9e5c92d744bc2c4 100644
--- a/Experiments/create_tabular.py
+++ b/Experiments/create_tabular.py
@@ -2,7 +2,7 @@ import os
 import glob
 import json
 
-metrics_to_display = ['rmse','coverage_theory','coverage_numerical','true_coverage_numerical','avg_bias']
+metrics_to_display = ['rmse','logdens','coverage_numerical','true_coverage_numerical']
 show_incomplete = True
 
 list_of_result_files = glob.glob(os.path.join('results','*.json'))
diff --git a/Experiments/evaluate_metrics.py b/Experiments/evaluate_metrics.py
index bf052e872d2e42d830e827526e52649da4d67be4..6912c013b2fa24ed916632321303f616f0d7c767 100644
--- a/Experiments/evaluate_metrics.py
+++ b/Experiments/evaluate_metrics.py
@@ -31,6 +31,12 @@ with open(os.path.join('configurations',f'eiv_{data}.json'),'r') as conf_file:
     eiv_conf_dict = json.load(conf_file)
 with open(os.path.join('configurations',f'noneiv_{data}.json'),'r') as conf_file:
     noneiv_conf_dict = json.load(conf_file)
+try:
+    normalize = eiv_conf_dict['normalize']
+    assert normalize == noneiv_conf_dict['normalize']
+except KeyError:
+    # normalize by default
+    normalize = True
 
 long_dataname = eiv_conf_dict["long_dataname"]
 short_dataname = eiv_conf_dict["short_dataname"]
@@ -40,7 +46,7 @@ print(f"Evaluating {long_dataname}")
 scale_outputs = False 
 load_data = importlib.import_module(f'EIVData.{long_dataname}').load_data
 
-train_data, test_data = load_data()
+train_data, test_data = load_data(normalize=normalize)
 input_dim = train_data[0][0].numel()
 output_dim = train_data[0][1].numel()
 
@@ -240,7 +246,8 @@ def collect_metrics(x_y_pairs, seed=0,
 def collect_full_seed_range_metrics(load_data,
         seed_range,test_batch_size = 100, test_samples = 10,
         noneiv_number_of_draws=100, eiv_number_of_draws=[100,5], device=device,
-        scale_outputs=scale_outputs):
+        scale_outputs=scale_outputs,
+        normalize=normalize):
     """
     Collect metrics that need all seeds for their computation.
     :param load_data: load_data map should take seed as an argument and,
@@ -257,6 +264,7 @@ def collect_full_seed_range_metrics(load_data,
     :param device: The torch.device to use
     :param scale_output: Boolean, scale the outputs for some metrics. Defaults
     to False.
+    :param normalize: Boolean, whether to normalize the data
     :returns: Dictionaries noneiv_metrics, eiv_metrics
     """
     noneiv_metrics = {}
@@ -267,9 +275,10 @@ def collect_full_seed_range_metrics(load_data,
         # load data according toseed
         try:
             train_data, test_data, true_train_data, true_test_data \
-                    = load_data(seed=seed, return_ground_truth=True)
+                    = load_data(seed=seed, return_ground_truth=True,
+                            normalize=normalize)
         except TypeError:
-            train_data, test_data = load_data(seed=seed)
+            train_data, test_data = load_data(seed=seed, normalize=normalize)
             true_train_data, true_test_data = None, None
 
         ## Compute x-dependant bias
@@ -460,9 +469,10 @@ number_of_test_samples = 2
 for seed in tqdm(seed_list):
     try:
         train_data, test_data, true_train_data, true_test_data \
-                = load_data(seed=seed, return_ground_truth=True)
+                = load_data(seed=seed, return_ground_truth=True,
+                        normalize=normalize)
     except TypeError:
-        train_data, test_data = load_data(seed=seed)
+        train_data, test_data = load_data(seed=seed, normalize=normalize)
         true_train_data, true_test_data = None, None
     if true_test_data is None:
         test_dataloader = DataLoader(test_data,
diff --git a/Experiments/plot_coverage.py b/Experiments/plot_coverage.py
index b7a511645c34f2dd62f693e037db36ec05111696..d8f548547240a0ba991ed5e4987386d9bf792361 100644
--- a/Experiments/plot_coverage.py
+++ b/Experiments/plot_coverage.py
@@ -48,6 +48,11 @@ def compute_coverages(data, eiv, number_of_draws,
 
     long_dataname = conf_dict["long_dataname"]
     short_dataname = conf_dict["short_dataname"]
+    try:
+        normalize = conf_dict['normalize']
+    except KeyError:
+        # normalize by default
+        normalize = True
 
 
     load_data = importlib.import_module(f'EIVData.{long_dataname}').load_data
@@ -73,12 +78,13 @@ def compute_coverages(data, eiv, number_of_draws,
         # test whether there is a ground truth
         try:
             train_data, _, _,_  \
-                    = load_data(seed=0, return_ground_truth=True)
+                    = load_data(seed=0, return_ground_truth=True,
+                            normalize=normalize)
         except TypeError:
         # if not, end function
             return None,None
     else:
-        train_data, _ = load_data()
+        train_data, _ = load_data(normalize=normalize)
 
     print(f"Computing {'EiV' if eiv else 'non-EiV'} coverage for {long_dataname}")
 
@@ -145,14 +151,15 @@ def compute_coverages(data, eiv, number_of_draws,
         """
         for seed in seed_list:
             if not use_ground_truth:
-                _, test_data = load_data(seed=seed)
+                _, test_data = load_data(seed=seed, normalize=normalize)
                 test_dataloader = DataLoader(test_data, 
                         batch_size=batch_size,
                         shuffle=True)
                 yield test_dataloader
             else:
                 _, _, _, true_test =\
-                        load_data(seed=seed, return_ground_truth=True)
+                        load_data(seed=seed, return_ground_truth=True,
+                                normalize=normalize)
                 # take noisy x but unnoisy y
                 cut_true_test = VerticalCut(true_test,
                         components_to_pick=[2,1])
@@ -362,8 +369,10 @@ plt.xlabel('q')
 plt.ylabel('EiV cov - nonEiV-cov')
 
 # datasets to plot and their coloring
-datasets = ['linear', 'quadratic','yacht','wine','power',
-        'protein','concrete','california','energy','kin8nm','msd','naval']
+datasets = ['linear', 'quadratic','cubic','sine',
+        'yacht','wine','power','protein','concrete',
+        'california','energy','kin8nm','msd','naval']
+
 colors = cm.rainbow(np.linspace(0,1,len(datasets)))
 
 # loop through data
diff --git a/Experiments/plot_prediction.py b/Experiments/plot_prediction.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0e1817fc8d20a865f9f236a5da0b3e5ecb95725
--- /dev/null
+++ b/Experiments/plot_prediction.py
@@ -0,0 +1,263 @@
+"""
+Plot predictions with uncertainties for (simulated) datasets with a ground 
+truth. At the moment it is assumed that the used datasets have an output
+dimension equal to 1. Plots are only produced for datasets with input dimension
+and output dimension 1.
+"""
+import importlib
+import os
+import json
+
+import torch
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+from EIVArchitectures import Networks
+from EIVTrainingRoutines import train_and_store
+
+# coverage factor
+k = 1.96
+
+def compute_predictions_and_uncertainties(data, x_range, eiv, number_of_draws,
+        x_noise_seed = 0, y_noise_seed = 1,
+        plotting_seed = 0, number_of_test_datapoints = 100):
+    """
+    Iterate through networks associated with `data` (short dataname) 
+    throuth their JSON configuration and evaluate their prediction and
+    uncertainties for the (true) x in `x_range`. The results returned are as numpy
+    arrays included in a `plotting_dictionary` that contains the predictions
+    and uncertainties via the keys "prediction" and "uncertainty" but also 
+    - A tuple of `x_range` and the corresponding values of y (key
+            "range_points")
+    - the noisified version of `x_range` and the corresponding y values (key
+    "noisy_range_points") and 
+    - `number_of_test_datapoints` points from the test
+    dataset with seed `plotting_seed` (key "test_data_points") and
+    - the input dimension (key "input_dim").
+
+    **Note**: The output of the neural networks are assumed to be
+    one-dimensional .
+
+    :data: String, short dataname. The corresponding module should contain
+    `x_noise_strength`, `y_noise_strength`.
+    :x_range: An iterator yielding the (true) x values to consider
+    :eiv: Boolean. If True an EiV model is used, else an non-EiV model.
+    :number_of_draws: Number of draws to use for prediction. Take an int for
+    non-EiV models and a two-element list for EiV models.
+    :x_noise_seed: An integer. Will be used as a seed to generate the noise put
+    on `x_range`.
+    :y_noise_seed: An integer. Will be used as a seed to generate the noise put
+    on `normalized_func(x_range)` that will be returned with `range_values` in the
+    `plotting_dictionary`.
+    :plotting_seed: An integer. Needed for choosing which of the test datasets
+    will be used to returning the test datapoints.
+    :number_of_test_datapoints: A positive integer. Number of test_datapoints
+    :returns: plotting_dictionary
+    """
+    plotting_dictionary = {}
+    # load configuration file
+    if eiv:
+        with open(os.path.join('configurations',f'eiv_{data}.json'),'r') as\
+                conf_file:
+            conf_dict = json.load(conf_file)
+    else:
+        with open(os.path.join('configurations',f'noneiv_{data}.json'),'r') as\
+                conf_file:
+            conf_dict = json.load(conf_file)
+
+    # get datanames
+    long_dataname = conf_dict["long_dataname"]
+    short_dataname = conf_dict["short_dataname"]
+    try:
+        normalize = conf_dict['normalize']
+    except KeyError:
+        # normalize by default
+        normalize = True
+
+
+    # load hyperparameters
+    load_data = importlib.import_module(f'EIVData.{long_dataname}').load_data
+    x_noise_strength =\
+        importlib.import_module(f'EIVData.{long_dataname}').x_noise_strength
+    y_noise_strength =\
+        importlib.import_module(f'EIVData.{long_dataname}').y_noise_strength
+    seed_list = range(conf_dict["seed_range"][0],
+            conf_dict["seed_range"][1])
+
+    # switch to gpu, if possible
+    try:
+        gpu_number = conf_dict["gpu_number"]
+        device = torch.device(f'cuda:{gpu_number}')
+        try:
+            torch.tensor([0.0]).to(device)
+        except RuntimeError:
+            if torch.cuda.is_available():
+                print('Switched to GPU 0')
+                device = torch.device('cuda:0')
+            else:
+                print('No cuda available, using CPU')
+                device = torch.device('cpu')
+    except KeyError:
+        device = torch.device('cpu')
+
+
+    # determine dimensions
+    _, test_data, normalized_func = load_data(seed=plotting_seed,
+            return_ground_truth=False,
+            return_normalized_func=True,
+            normalize=normalize)
+    input_dim = test_data[0][0].numel()
+    output_dim = test_data[0][1].numel()
+    assert output_dim == 1
+    # store in plotting_dictionary
+    plotting_dictionary['input_dim'] = input_dim
+
+    # store test datapoints
+    test_x, test_y = test_data[:number_of_test_datapoints]
+    plotting_dictionary['test_data_points'] = (test_x.detach().cpu().numpy(),
+            test_y.detach().cpu().numpy())
+
+
+    # iterator for looping through networks
+    def net_iterator(eiv=eiv, seed_list=seed_list):
+        """
+        Yields EiV models (if `eiv`) or
+        non-EiV models (if not `eiv`) for the seeds in
+        `seed_list` and `data`.
+        """
+        if eiv:
+            # load parameters
+            init_std_y = conf_dict["init_std_y_list"][0]
+            unscaled_reg = conf_dict["unscaled_reg"]
+            p = conf_dict["p"]
+            hidden_layers = conf_dict["hidden_layers"]
+            fixed_std_x = conf_dict["fixed_std_x"]
+            net = Networks.FNNEIV(p=p, init_std_y=init_std_y,
+                    h=[input_dim, *hidden_layers, output_dim],
+                    fixed_std_x=fixed_std_x).to(device)
+            for seed in seed_list:
+                # load network paramaters
+                saved_file = os.path.join('saved_networks',
+                        f'eiv_{short_dataname}'\
+                                f'_init_std_y_{init_std_y:.3f}'\
+                                f'_ureg_{unscaled_reg:.1f}'\
+                                f'_p_{p:.2f}_fixed_std_x_{fixed_std_x:.3f}'\
+                                f'_seed_{seed}.pkl')
+                train_and_store.open_stored_training(saved_file=saved_file,
+                        net=net, device=device)
+                yield net
+        else:
+            # load parameters
+            init_std_y = conf_dict["init_std_y_list"][0]
+            unscaled_reg = conf_dict["unscaled_reg"]
+            p = conf_dict["p"]
+            hidden_layers = conf_dict["hidden_layers"]
+            net = Networks.FNNBer(p=p, init_std_y=init_std_y,
+                    h=[input_dim, *hidden_layers, output_dim]).to(device)
+            for seed in seed_list:
+                saved_file = os.path.join('saved_networks',
+                            f'noneiv_{short_dataname}'\
+                                    f'_init_std_y_{init_std_y:.3f}_ureg_{unscaled_reg:.1f}'\
+                                    f'_p_{p:.2f}_seed_{seed}.pkl')
+                # load network paramaters
+                train_and_store.open_stored_training(saved_file=saved_file,
+                        net=net, device=device)
+                yield net
+    
+    # add feature dimension (if necessary) 
+    x_range = x_range.view((-1, input_dim))
+    y_range = normalized_func(x_range)
+    # noisify
+    noisy_x_range = x_range + x_noise_strength * torch.randn(x_range.shape,
+            generator=torch.Generator().manual_seed(x_noise_seed))
+    # move to device for later processing
+    noisy_x_range = noisy_x_range.to(device) 
+    # y values for noisy_x_range (not on device)
+    noisy_y_range = y_range + y_noise_strength *\
+    torch.randn(x_range.shape,
+            generator=torch.Generator().manual_seed(y_noise_seed))
+    # save in plotting_dictionary
+    plotting_dictionary['noisy_range_points'] =\
+            (noisy_x_range.detach().cpu().numpy(),
+            noisy_y_range.detach().cpu().numpy())
+    plotting_dictionary['range_points'] =\
+            (x_range.detach().cpu().numpy(),
+            y_range.detach().cpu().numpy())
+
+
+    # loop through networks and predict
+    mean_collection, unc_collection = [], []
+    for net in net_iterator(eiv=eiv):
+        mean, unc = net.predict_mean_and_unc(noisy_x_range,
+                number_of_draws=number_of_draws)[:2]
+        if len(mean.shape) > 1:
+                # assume only batch and output dimension
+                assert len(mean.shape) == 2
+                assert len(unc.shape) == 2
+                # assume one-dimensional outputs
+                assert mean.shape[1] == 1
+                assert unc.shape[1] == 1
+                # make one-dimensional
+        mean = mean.view((-1,))
+        unc = unc.view((-1,))
+        mean_collection.append(mean)
+        unc_collection.append(unc)
+
+    # stack collections along a new, first dimension
+    mean_collection = torch.stack(mean_collection, dim=0)
+    unc_collection = torch.stack(unc_collection, dim=0)
+
+    # save average in plotting_dictionary
+    plotting_dictionary['prediction'] =\
+            torch.mean(mean_collection, dim=0).detach().cpu().numpy()
+    plotting_dictionary['uncertainty'] =\
+            torch.mean(unc_collection, dim=0).detach().cpu().numpy()
+    return plotting_dictionary
+
+
+data_list = ['linear','quadratic','cubic','sine'] # short datanames
+list_x_range = [torch.linspace(-1.0,1.0, 50),
+                torch.linspace(-1.0,1.0, 50),
+                torch.linspace(-1.0,1.0, 50),
+        torch.linspace(-0.2,0.8, 50)]
+list_color = [('red','blue')] * len(data_list)
+list_number_of_draws = [((100,5), 100)] * len(data_list)
+for i, (data, x_range, color, number_of_draws) in enumerate(zip(data_list,
+    list_x_range, list_color, list_number_of_draws)):
+    eiv_plotting_dictionary = compute_predictions_and_uncertainties(
+            data=data,
+            x_range=x_range,
+            eiv=True,
+            number_of_draws=number_of_draws[0])
+    noneiv_plotting_dictionary = compute_predictions_and_uncertainties(
+            data=data,
+            x_range=x_range,
+            eiv=False,
+            number_of_draws=number_of_draws[1])
+    input_dim = eiv_plotting_dictionary['input_dim']
+    if input_dim == 1:
+        plt.figure(i+1)
+        plt.clf()
+        x_values, y_values = eiv_plotting_dictionary['range_points']
+        plt.plot(x_values.flatten(), y_values.flatten(),'-', color='k')
+        eiv_pred = eiv_plotting_dictionary['prediction']
+        eiv_unc = eiv_plotting_dictionary['uncertainty']
+        plt.plot(x_values, eiv_pred,'-',
+                color=color[0])
+        plt.fill_between(x_values.flatten(), eiv_pred-k * eiv_unc,
+                eiv_pred + k * eiv_unc,
+                color=color[0], alpha=0.5)
+        noneiv_pred = noneiv_plotting_dictionary['prediction']
+        noneiv_unc = noneiv_plotting_dictionary['uncertainty']
+        plt.plot(x_values.flatten(), noneiv_pred,'-',
+                color=color[1])
+        plt.fill_between(x_values.flatten(), noneiv_pred-k * noneiv_unc,
+                noneiv_pred + k * noneiv_unc,
+                color=color[1], alpha=0.5)
+    else:
+        # multidimensional handling not included yet
+        pass
+
+
+plt.show()
diff --git a/Experiments/train_eiv.py b/Experiments/train_eiv.py
index 116408f467339a8bcef4e1b63c8b7896f693c87b..973fc59ddc003f4e0577b1f718b4ad91cd518dd7 100644
--- a/Experiments/train_eiv.py
+++ b/Experiments/train_eiv.py
@@ -41,15 +41,26 @@ p = conf_dict["p"]
 lr_update = conf_dict["lr_update"]
 # offset before updating sigma_y after each epoch
 std_y_update_points = conf_dict["std_y_update_points"]
-# will be used to predict the RMSE and update sigma_y accordingly
+
+# will be used to predict, to compute the RMSE and update sigma_y accordingly
+try:
+    eiv_number_of_forward_draws = conf_dict['eiv_number_of_forward_draws']
+except KeyError:
+    eiv_number_of_forward_draws = 5
 eiv_prediction_number_of_draws = conf_dict["eiv_prediction_number_of_draws"]
 eiv_prediction_number_of_batches = \
         conf_dict["eiv_prediction_number_of_batches"]
+
 init_std_y_list = conf_dict["init_std_y_list"]
 fixed_std_x = conf_dict['fixed_std_x']
 gamma = conf_dict["gamma"]
 hidden_layers = conf_dict["hidden_layers"]
 seed_range = conf_dict['seed_range']
+try:
+    normalize = conf_dict['normalize']
+except KeyError:
+    # normalize by default
+    normalize = True
 
 print(f"Training on {long_dataname} data")
 
@@ -199,7 +210,7 @@ def train_on_data(init_std_y, seed):
     set_seeds(seed)
     # load Datasets
     train_data, test_data = load_data(seed=seed, splitting_part=0.8,
-            normalize=True)
+            normalize=normalize)
     # make dataloaders
     train_dataloader = DataLoader(train_data, batch_size=batch_size, 
             shuffle=True)
@@ -220,7 +231,9 @@ def train_on_data(init_std_y, seed):
     # regularization
     reg = unscaled_reg/len(train_data)
     # create epoch_map
-    criterion = loss_functions.nll_eiv
+    def criterion(net, x, y, reg):
+        return loss_functions.nll_eiv(net, x, y, reg,
+                number_of_draws=eiv_number_of_forward_draws)
     epoch_map = UpdatedTrainEpoch(train_dataloader=train_dataloader,
             test_dataloader=test_dataloader,
             criterion=criterion, std_y_map=std_y_map, std_x_map=std_x_map,
diff --git a/Experiments/train_noneiv.py b/Experiments/train_noneiv.py
index a585ce62db5287d8972210fc7604656678138440..f18617a87ae9e6f7ed3befd079607d0693810120 100644
--- a/Experiments/train_noneiv.py
+++ b/Experiments/train_noneiv.py
@@ -49,6 +49,11 @@ init_std_y_list = conf_dict["init_std_y_list"]
 gamma = conf_dict["gamma"]
 hidden_layers = conf_dict["hidden_layers"]
 seed_range = conf_dict['seed_range']
+try:
+    normalize = conf_dict['normalize']
+except KeyError:
+    # normalize by default
+    normalize = True
 
 print(f"Training on {long_dataname} data")
 
@@ -190,7 +195,7 @@ def train_on_data(init_std_y, seed):
     set_seeds(seed)
     # load Datasets
     train_data, test_data = load_data(seed=seed, splitting_part=0.8,
-            normalize=True)
+            normalize=normalize)
     # make dataloaders
     train_dataloader = DataLoader(train_data, batch_size=batch_size, 
             shuffle=True)