Problems when implementing metamodel within a loop (UQPyLab)

Hello,

I’m currently working on a project where I’m using UQPyLab to implement metamodels for a system characterized by a set of matrices.

System Description: The system is characterized by a 3x4 matrix A, representing parameters that may or may not be modeled as uncertain parameters depending on the given combination.

Model Evaluation: I’ve developed a model function that accurately computes the system’s response (Yvals) based on variations in A.

Metamodel Implementation: Using Polynomial Chaos Expansion (PCE), I’m building a metamodel (myPCE) to approximate the system’s behavior for faster computations and sensitivity analyses.

Issue Encountered: While the model evaluations (Yvals) are computing correctly for each sample, the metamodel (myPCE) seems to only capture variations in one specific matrix element ((0,0)), despite correctly sampling variations in other elements (Xvals). This discrepancy occurs specifically within the loop iterating over different combinations of parameters.

Outside Loop Behavior: Interestingly, outside of the loop, changing any of the matrix elements works fine, and the metamodel captures variations correctly. This observation suggests that the issue might be related to the loop or the way samples are processed within it.

I’m seeking insights or suggestions to understand why the metamodel behaves differently inside and outside the loop. Any alternative approaches or best practices for implementing metamodels in UQPyLab would be appreciated. Below, is a snippet of the loop that is causing issues.

Thank you in advance,

Eilidh

> indices_to_labels = {
>     (0, 0): 'X_u', (0, 1): 'X_w', (0, 2): 'X_q', (0, 3): 'X_theta',
>     (1, 0): 'Z_u', (1, 1): 'Z_w', (1, 2): 'Z_q', (1, 3): 'Z_theta',
>     (2, 0): 'M_u', (2, 1): 'M_w', (2, 2): 'M_q', (2, 3): 'M_theta'
> }
> 
>  for idx, (combo, constants) in enumerate(ordered_combinations, start=1):
>     print('-----------------')
>     print('Combo:', combo)
>     print('Constant derivatives:', constants)
> 
>     uq = create_session()  # Reset session
> 
>     myModel = uq.createModel(ModelOpts)
> 
>     InputOpts = {'Marginals': []}
>     all_indices = list(combo.keys()) + list(constants.keys())
> 
>     # Set up marginals based on the current combination and constants
>     for index in all_indices:
>         label = (index[0], index[1])
>         value = A[index].item()  # Convert numpy data type to Python native type
>         dist_type = 'Gaussian' if index in combo else 'Constant'
>         parameters = [value, float(abs(value * COV))] if dist_type == 'Gaussian' else [value]
> 
>         InputOpts['Marginals'].append({
>             'Name': str(label),
>             'Type': dist_type,
>             'Parameters': parameters
>         })
> 
>     # Create input, get samples
>     myInput = uq.createInput(InputOpts)
>     Nval = 1
>     samples = uq.getSample(myInput, Nval)
>     Yvals = []
> 
>     ##  Initialize the sampled matrices with original values of A for each sample set
>     sampled_matrices = np.tile(A[:, :, np.newaxis], (1, 1, Nval))
> 
>     Xvals = []
>     # Inject sampled values only for the varying parameters
>     for param_idx, index in enumerate(all_indices):
>         if index in combo:
>             sampled_matrices[index[0], index[1], :] = samples[:, param_idx]
> 
>     print(f'Sampled Values for Combo {idx}:')
>     for i in range(Nval):
>         print(f'Sample {i+1}:')
>         print(sampled_matrices[:, :, i])
>         print('...........')
> 
>         # Evaluate the model for each sample
>         sample_eval = uq.evalModel(myModel, sampled_matrices[:, :, i].flatten())  # Flatten if needed
>         Yvals.append(sample_eval)
> 
>         # Append the sampled matrix to the list
>         Xvals.append(sampled_matrices[:, :, i])
> 
>     print(Yvals)
>     print(type(Yvals))
>     print(np.shape(Yvals))
>  
>        # Define the metamodel options
>     MetaOpts = {
>         'Type': 'Metamodel',
>         'MetaType': 'PCE',
>         'TruncOptions': {'qNorm': 0.75},
>         'Degree': np.arange(2, 11).tolist(),
>         'ExpDesign': {
>             "NSamples": 120,
>             "Sampling": "LHS"
>         }
>     }
> 
>     print('EXP DESIGN')
>     print('CREATING METAMODEL')
>     # print('samples for metamodel', Xvals)
>     myPCE = uq.createModel(MetaOpts)
>     print(myPCE)
>     
>           
>     print('EVALUATING METAMODEL')
>     # print('sampled matrices shape', Xvals.shape)   
>     print('Xvals for metamodel', Xvals) 
>     YPC = uq.evalModel(myPCE, Xvals)
>     print('YPC', YPC)

Dear @Eilidh_Radcliff

It is difficult to reason about code without being able to execute it. To localize the problem and find out if the problem is on the UQPyLab side, a minimal, reproducible example would be helpful.

Best regards
Styfen

Dear Styfen,

Here is a simple reproducible example that highlights the main problem.

import numpy as np
from uqpylab import sessions

A = np.array([
    [-4.99878311e-02, -2.00347137e-02, 6.73251372e+00, -9.76028258e+00],
    [2.37724702e-03, -9.17131968e-01, 4.97635920e+01, 9.52506205e-01],
    [8.16452866e-03, 1.50314100e-02, -1.28856094e+00, 0.00000000e+00],
    [0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 0.00000000e+00]
])



combinations_with_distributions = [
    {(0, 0): 'Gaussian', (0, 1): 'Constant', (0, 2): 'Constant', (0, 3): 'Constant', (1, 0): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'},
    {(0, 1): 'Gaussian', (0, 0): 'Constant', (0, 2): 'Constant', (0, 3): 'Constant', (1, 0): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'},
    # {(0, 2): 'Gaussian', (0, 0): 'Constant', (0, 1): 'Constant', (0, 3): 'Constant', (1, 0): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'},
    # {(0, 3): 'Gaussian', (0, 0): 'Constant', (0, 1): 'Constant', (0, 2): 'Constant', (1, 0): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'},
    # {(1, 0): 'Gaussian', (0, 0): 'Constant', (0, 1): 'Constant', (0, 2): 'Constant', (0, 3): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'}
]



# UQCloud credentials and instance configuration
myToken = 'enter token here'
UQCloud_instance = 'https://uqcloud.ethz.ch'

# Initialize UQCloud session
mySession = sessions.cloud(host=UQCloud_instance, token=myToken)
uq = mySession.cli


def reset_input(comb_with_dist, A_matrix):
    total_output_list = []
    for dic in comb_with_dist:
        InputOpts = {'Marginals': []}
        for key, value in dic.items():
            a_value = (A_matrix[key])  
            np.set_printoptions(precision=15, suppress=False)
            
            marginal = {
                'Name': key,
                'Type': value,
                'Parameters': [a_value, abs(a_value * 0.4)] if value == 'Gaussian' else [a_value]
            }
            print('marginal:', marginal['Parameters'])
            InputOpts['Marginals'].append(marginal)
            # print('InputOpts:', InputOpts)
        myInput =  uq.createInput(InputOpts)
        total_output_list.append(myInput)

    return total_output_list


# Iterate over combinations and reset metamodel
if __name__ == '__main__':
    
    myInput_ = reset_input(combinations_with_distributions,A)
    
    
    # Generate samples
    Nval = 3
    for input in myInput_:
        Xval = uq.getSample(Input=input, N=Nval)    
        print( 'Xval', Xval)
    


    

The ‘combinations_with_distributions’ defines a list of dictionaries to assign elements in the matrix the name, type and parameter for the input. However, the Xval always shows that the element of the matrix is disregarded (it only changes element (0,0). I have tried multiple ways to solve this but cannot find a way. This is the root of my problem when evaluating the model and metamodel later in the code.

Kind regards,
Eilidh

Hi @Eilidh_Radcliff

Thanks for the nice reproducer.

I am not sure if I have understood correctly. If I run your example, the first column Xval follows a Gaussian distribution for the first input dictionair (first dictionair in combinations_with_distributions). For the second input dictionair, the first column in Xval also follows a Gaussian distribution, although the input variable named (0, 0) is defined as constant. Do I understand correctly that you expected this column to be constant?
If this is the case, the problem is that UQLab does not look at the name of the input variables, but at the order in which they are declared. It is therefore not enough to swap the names of the marginals; you also have to swap their position in the dictionary. For example, you must do the following:

combinations_with_distributions = [
    {
        (0, 0): "Gaussian",
        (0, 1): "Constant",
    },
    {
        (0, 0): "Constant",
        (0, 1): "Gaussian",
    },
]

intead of

combinations_with_distributions = [
    {
        (0, 0): "Gaussian",
        (0, 1): "Constant",
    },
    {
        (0, 1): "Gaussian",
        (0, 0): "Constant",
    },
]

Does this solve your problem or did I misunderstand it?

Ah Yes! That does indeed solve the issue - I misunderstood that UQLab only looks at the order in which they are declared. Thank you!

This leads me to the next issue: I am looping through these combinations (which I now declare in the sorted order) - Yval is calculating correctly but YPC only calculates for the last loop. How can I fix this?

This is the usage:


import numpy as np
from uqpylab import sessions
import time
import matplotlib.pyplot as plt


A = np.array([
    [-4.99878311e-02, -2.00347137e-02, 6.73251372e+00, -9.76028258e+00],
    [2.37724702e-03, -9.17131968e-01, 4.97635920e+01, 9.52506205e-01],
    [8.16452866e-03, 1.50314100e-02, -1.28856094e+00, 0.00000000e+00],
    [0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 0.00000000e+00]
])

combinations_with_distributions = [
    {(0, 0): 'Gaussian', (0, 1): 'Constant', (0, 2): 'Constant', (0, 3): 'Constant', (1, 0): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'},
    {(0, 1): 'Gaussian', (0, 0): 'Constant', (0, 2): 'Constant', (0, 3): 'Constant', (1, 0): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'},
#     # {(0, 2): 'Gaussian', (0, 0): 'Constant', (0, 1): 'Constant', (0, 3): 'Constant', (1, 0): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'},
#     # {(0, 3): 'Gaussian', (0, 0): 'Constant', (0, 1): 'Constant', (0, 2): 'Constant', (1, 0): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'},
#     # {(1, 0): 'Gaussian', (0, 0): 'Constant', (0, 1): 'Constant', (0, 2): 'Constant', (0, 3): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'}
]



# UQCloud credentials and instance configuration
myToken = 'enter token'
UQCloud_instance = 'https://uqcloud.ethz.ch'

# Initialize UQCloud session
mySession = sessions.cloud(host=UQCloud_instance, token=myToken)
uq = mySession.cli

# Define model options
ModelOpts = {
    'Type': 'Model',
    'ModelFun': 'quickness_extraction_model.model',
    'isVectorized': 'true'
}

def reset_session():    
    mySession.reset()    
    myModel = uq.createModel(ModelOpts)
    return myModel

def reset_input(comb_with_dist, A_matrix):
    uq.rng(0,'twister');
    total_output_list = []
    gaussian_params_list = []

    for dic in comb_with_dist:
        gaussian_params = [key for key, value in dic.items() if value == 'Gaussian']
        gaussian_params_list.append(gaussian_params)

        InputOpts = {'Marginals': []}
        for key, value in dic.items():
            a_value = (A_matrix[key])  
            np.set_printoptions(precision=15, suppress=False)
            
            marginal = {
                'Name': key,
                'Type': value,
                'Parameters': [a_value, abs(a_value * 0.5)] if value == 'Gaussian' else [a_value]
            }
            
            # print('marginal( unsorted):', marginal['Parameters'])
            InputOpts['Marginals'].append(marginal)
            # print('InputOpts:', InputOpts)
        
        # Sort marginals in order of index
        sorted_marginals = sorted(InputOpts['Marginals'], key=lambda x: list(x['Name']))
        InputOpts['Marginals'] = sorted_marginals
        
                    
        myInput =  uq.createInput(InputOpts)
        total_output_list.append(myInput)

    return total_output_list, gaussian_params_list

def reset_metamodel():
    # Define meta model options
    MetaOpts = {
        'Type': 'Metamodel',
        'MetaType': 'PCE'
    }
    MetaOpts['TruncOptions'] = {'qNorm': 0.75}
    MetaOpts['Degree'] = np.arange(2, 11).tolist()
    MetaOpts['ExpDesign'] = {
        "NSamples": 120,
        "Sampling": "LHS"
    }
    # Create meta model in UQPyLab
    myPCE = uq.createModel(MetaOpts)
    return myPCE

# Iterate over combinations and reset metamodel
if __name__ == '__main__':
    
    myModel_ = reset_session()
    
    myInput_, gaussian_params_list_ = reset_input(combinations_with_distributions, A)
    myPCE_ = reset_metamodel()
    
    # Generate samples
    Nval = 10
    for index, input_combination in enumerate(myInput_):       
      
        
        print("Combination:", index)
        print("Gaussian Parameters:", gaussian_params_list_[index])
        
        Xval = uq.getSample(Input=input_combination, N=Nval)

        Yval = uq.evalModel(Model=myModel_, X=Xval)

        print('Yval:', Yval)       
        print('Yval quickness variance:', np.var(Yval[:, 1]))
        print('Yval theta variance:', np.var(Yval[:, 0]))  
        
        
        
        YPC_start_time = time.time()
        print('Xval for YPC:', Xval)
        YPC = uq.evalModel(Model=myPCE_, X=Xval)
        YPC_end_time = time.time()        
        print('YPC', YPC)
        print('YPC quickness variance:', np.var(YPC[:, 1]))
        print('YPC theta variance:', np.var(YPC[:, 0]))        

      
        x_min = min(np.min(Yval[:, 1]), np.min(YPC[:, 1]))
        x_max = max(np.max(Yval[:, 1]), np.max(YPC[:, 1]))
        y_min = min(np.min(Yval[:, 0]), np.min(YPC[:, 0]))
        y_max = max(np.max(Yval[:, 0]), np.max(YPC[:, 0]))

        # Plot the results side by side with same axes
        plt.figure(figsize=(12, 6))

        # Plot Yval
        plt.subplot(1, 2, 1)
        plt.scatter(Yval[:, 1], Yval[:, 0], label='Yval')
        plt.xlabel('Yval Y-axis label')
        plt.ylabel('Yval X-axis label')
        plt.legend()
        plt.xlim(x_min, x_max)
        plt.ylim(y_min, y_max)

        # Plot YPC
        plt.subplot(1, 2, 2)
        plt.scatter(YPC[:, 1], YPC[:, 0], label='YPC')
        plt.xlabel('YPC Y-axis label')
        plt.ylabel('YPC X-axis label')
        plt.legend()
        plt.xlim(x_min, x_max)
        plt.ylim(y_min, y_max)

        plt.tight_layout()
        plt.show()

and the model:

import numpy as np
import control




B = np.array([[11.00004293, -6.11870228],
              [44.67572957, -139.99937581],
              [-7.55522163, 4.20254285],
              [0.0, 0.0]])

np.random.seed(0)

import numpy as np
import control

def model(X):
    quickness_results = []
    theta_10_results = []
    X = np.array(X)
    
    for i in range(X.shape[0]):
        # Extract row-wise parameters
        Xu, Xw, Xq, Xtheta, Zu, Zw, Zq, Ztheta, Mu, Mw, Mq, Mtheta = X[i]

        # State-space matrices construction
        A = np.array([[Xu, Xw, Xq, Xtheta],
                      [Zu, Zw, Zq, Ztheta],
                      [Mu, Mw, Mq, Mtheta],
                      [0, 0, 1, 0]])

        C = np.array([[0, 0, 1, 0], [0, 0, 0, 1]])
        D = np.array([[0, 0], [0, 0]])

        # Time and input setup
        t = np.arange(0,8,0.1)
        cyclic_input = 1  # degrees
        duration = 2  # seconds

        # Creating doublet input
        u_single = np.zeros_like(t)  # Initialize a zero array for control input
        u_single[(t > 1) & (t < (1 + duration))] = np.radians(cyclic_input)  # Apply cyclic input
        u = np.vstack((u_single, np.zeros_like(t)))  # Stack the control inputs

        # System response
        t_out, y_out = control.forced_response(control.ss(A, B, C, D), t, u)
        y_out = np.rad2deg(-y_out)

        # Quickness calculation
        max_val_y0 = np.max(y_out[0, :])
        max_val_y1 = np.max(y_out[1, :])
        

        
        
        index_max_val_y0 = np.argmax(y_out[0])
        indices_below_10_percent = np.where(y_out[0][index_max_val_y0:] <= 0.1 * max_val_y0)[0]

        if len(indices_below_10_percent) > 0:
         
            index_one_tenth_val_y0 = indices_below_10_percent[0] + index_max_val_y0
            theta_10_percent = y_out[1][index_one_tenth_val_y0]
            t_10_percent = t_out[index_one_tenth_val_y0]
        else:
        # Handle the case where the output does not decrease to 10% of the peak
            theta_10_percent = None
            t_10_percent = None

        max_val_y1 = np.max(y_out[1])
        quickness = max_val_y0 / max_val_y1 if max_val_y1 != 0 else None
        quickness_results.append(quickness)
        theta_10_results.append(theta_10_percent)

    
    combined_output = np.column_stack((quickness_results, theta_10_results)) 

    return combined_output

Can you provide a minimal reproduction program again? Unfortunately I can’t run your code as you are using modules that are not available to me. Or can you at least share the output you get (the figures) and explain what you expected to see?

Hi Styfen,

I assume the problem is the control python module. Here is a simpler model that only uses numpy.

import numpy as np

def model(X):
    outputs = []
    
    for params in X:
        # Extract parameters
        Xu, Xw, Xq, Xtheta, Zu, Zw, Zq, Ztheta, Mu, Mw, Mq, Mtheta = params

        # Compute output 1 using all 12 derivatives
        output1 = Xu + Xw + Xq + Xtheta + Zu + Zw + Zq + Ztheta + Mu + Mw + Mq + Mtheta

        # Compute output 2 using all 12 derivatives
        output2 = 2 * (Xu + Xw + Xq + Xtheta + Zu + Zw + Zq + Ztheta + Mu + Mw + Mq + Mtheta)

        outputs.append([output1, output2])

    return np.array(outputs)

when using this in the ModelOpts in here:

import numpy as np
from uqpylab import sessions
import matplotlib.pyplot as plt


A = np.array([
    [-4.99878311e-02, -2.00347137e-02, 6.73251372e+00, -9.76028258e+00],
    [2.37724702e-03, -9.17131968e-01, 4.97635920e+01, 9.52506205e-01],
    [8.16452866e-03, 1.50314100e-02, -1.28856094e+00, 0.00000000e+00],
    [0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 0.00000000e+00]
])

combinations_with_distributions = [
    {(0, 0): 'Gaussian', (0, 1): 'Constant', (0, 2): 'Constant', (0, 3): 'Constant', (1, 0): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'},
    {(0, 1): 'Gaussian', (0, 0): 'Constant', (0, 2): 'Constant', (0, 3): 'Constant', (1, 0): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'},
    {(0, 2): 'Gaussian', (0, 0): 'Constant', (0, 1): 'Constant', (0, 3): 'Constant', (1, 0): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'},
#     # {(0, 3): 'Gaussian', (0, 0): 'Constant', (0, 1): 'Constant', (0, 2): 'Constant', (1, 0): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'},
#     # {(1, 0): 'Gaussian', (0, 0): 'Constant', (0, 1): 'Constant', (0, 2): 'Constant', (0, 3): 'Constant', (1, 1): 'Constant', (1, 2): 'Constant', (1, 3): 'Constant', (2, 0): 'Constant', (2, 1): 'Constant', (2, 2): 'Constant', (2, 3): 'Constant'}
]

print('type:', type(combinations_with_distributions))
print('shape', len(combinations_with_distributions))    

# UQCloud credentials and instance configuration
myToken = 'token'
UQCloud_instance = 'https://uqcloud.ethz.ch'

# Initialize UQCloud session
mySession = sessions.cloud(host=UQCloud_instance, token=myToken)
uq = mySession.cli

# Define model options
ModelOpts = {
    'Type': 'Model',
    'ModelFun': 'model1.model',  #give model name
    'isVectorized': 'true'
}

def reset_session():    
    mySession.reset()    
    myModel = uq.createModel(ModelOpts)
    return myModel

def reset_input(comb_with_dist, A_matrix):
    uq.rng(0,'twister');
    total_output_list = []
    gaussian_params_list = []

    for dic in comb_with_dist:
        gaussian_params = [key for key, value in dic.items() if value == 'Gaussian']
        gaussian_params_list.append(gaussian_params)

        InputOpts = {'Marginals': []}
        for key, value in dic.items():
            a_value = (A_matrix[key])  
            np.set_printoptions(precision=15, suppress=False)
            
            marginal = {
                'Name': key,
                'Type': value,
                'Parameters': [a_value, abs(a_value * 0.5)] if value == 'Gaussian' else [a_value]
            }
            
            # print('marginal( unsorted):', marginal['Parameters'])
            InputOpts['Marginals'].append(marginal)
            # print('InputOpts:', InputOpts)
        
        # Sort marginals in order of index
        sorted_marginals = sorted(InputOpts['Marginals'], key=lambda x: list(x['Name']))
        InputOpts['Marginals'] = sorted_marginals
        
                    
        myInput =  uq.createInput(InputOpts)
        total_output_list.append(myInput)

    return total_output_list, gaussian_params_list

def reset_metamodel():
    # Define meta model options
    MetaOpts = {
        'Type': 'Metamodel',
        'MetaType': 'PCE'
    }
    MetaOpts['TruncOptions'] = {'qNorm': 0.75}
    MetaOpts['Degree'] = np.arange(2, 11).tolist()
    MetaOpts['ExpDesign'] = {
        "NSamples": 120,
        "Sampling": "LHS"
    }
    # Create meta model in UQPyLab
    myPCE = uq.createModel(MetaOpts)
    return myPCE

if __name__ == '__main__':

    myModel_ = reset_session()
    myInput_, gaussian_params_list_ = reset_input(combinations_with_distributions, A)

    myPCE_ = reset_metamodel()
    print('myPCE type:', type(myPCE_))

    # Generate samples and evaluate metamodel for each combination
    Nval = 10
    for index, input_combination in enumerate(myInput_):
        print("Combination:", index)
        print("Gaussian Parameters:", gaussian_params_list_[index])

        Xval = uq.getSample(Input=input_combination, N=Nval)
        Yval = uq.evalModel(Model=myModel_, X=Xval)

        # Plot Yval
        plt.figure(figsize=(12, 6))
        plt.subplot(1, 2, 1)
        plt.scatter(Yval[:, 1], Yval[:, 0], label='Yval')
        plt.xlabel('Yval Y-axis label')
        plt.ylabel('Yval X-axis label')
        plt.legend()

        
        YPC = uq.evalModel(Model=myPCE_, X=Xval)

        # Plot YPC
        plt.subplot(1, 2, 2)
        plt.scatter(YPC[:, 1], YPC[:, 0], label='YPC')
        plt.xlabel('YPC Y-axis label')
        plt.ylabel('YPC X-axis label')
        plt.legend()

        plt.tight_layout()
        plt.show()

. The graphs show that YPC is only evaluating correctly for the final loop of combinations. For all previous combination, the YPC values are constant.

It seems to me that there is a conceptual mistake here and your result is absolutely as expected. UQLab trains the PCE for the last created input (combinations_with_distributions[2]). Consequently, each input variable except (0, 2) is constant and is therefore ignored by the PCE model (inspect e.g. myPCE_["PCE"][0]). In your evaluation loop, you then evaluate the PCE model for data with non-constant input variables that happened to be constant when the model was trained. Of course, these now have no effect on the prediction. The last iteration is the only one that will give you the results you wanted to see because it is the only input that keeps your promise of constant/non-constant input variables.

I see the problem! Thank you very much for your help.