easyvvuq.utils.discrete_validation

Utilities for handling discrete distribution validation in EasyVVUQ.

This module provides enhanced validation capabilities for discrete distributions when used with Stochastic Collocation (SC) and Polynomial Chaos Expansion (PCE) methods.

  1"""
  2Utilities for handling discrete distribution validation in EasyVVUQ.
  3
  4This module provides enhanced validation capabilities for discrete distributions
  5when used with Stochastic Collocation (SC) and Polynomial Chaos Expansion (PCE) methods.
  6"""
  7import numpy as np
  8import chaospy as cp
  9from typing import Any, Dict, Union
 10
 11
 12def is_integer_valued(value: Any) -> bool:
 13    """
 14    Check if a value represents an integer, even if it's stored as a float.
 15    
 16    This is crucial for handling chaospy output where discrete distributions
 17    may return float arrays for numerical consistency with continuous distributions.
 18    
 19    Parameters
 20    ----------
 21    value : Any
 22        The value to check
 23        
 24    Returns
 25    -------
 26    bool
 27        True if the value represents an integer
 28    """
 29    try:
 30        # Handle numpy arrays and scalars
 31        if hasattr(value, '__iter__') and not isinstance(value, (str, bytes)):
 32            # For arrays, check if all elements are integer-valued
 33            return all(is_integer_valued(v) for v in value)
 34        
 35        # Convert to float first to handle various numeric types
 36        float_val = float(value)
 37        
 38        # Check if it's close to an integer
 39        return abs(float_val - round(float_val)) < 1e-10
 40    except (ValueError, TypeError):
 41        return False
 42
 43
 44def convert_to_integer(value: Any) -> Union[int, Any]:
 45    """
 46    Convert a value to integer if it represents an integer, otherwise return as-is.
 47    
 48    Parameters
 49    ----------
 50    value : Any
 51        The value to convert
 52        
 53    Returns
 54    -------
 55    Union[int, Any]
 56        Integer value if conversion is valid, otherwise the original value
 57    """
 58    if is_integer_valued(value):
 59        return int(round(float(value)))
 60    return value
 61
 62
 63def validate_discrete_parameter(value: Any, param_def: Dict[str, Any]) -> bool:
 64    """
 65    Validate a parameter value for discrete distributions.
 66    
 67    This function handles the case where chaospy returns float values
 68    for discrete distributions, especially in mixed discrete/continuous scenarios.
 69    
 70    Parameters
 71    ----------
 72    value : Any
 73        The parameter value to validate
 74    param_def : Dict[str, Any]
 75        Parameter definition from the campaign
 76        
 77    Returns
 78    -------
 79    bool
 80        True if the value is valid for this parameter
 81    """
 82    param_type = param_def.get('type', 'float')
 83    
 84    # For integer parameters, check if the value is integer-valued
 85    if param_type == 'integer':
 86        if not is_integer_valued(value):
 87            return False
 88        
 89        # Check bounds if specified
 90        int_val = convert_to_integer(value)
 91        if 'min' in param_def and int_val < param_def['min']:
 92            return False
 93        if 'max' in param_def and int_val > param_def['max']:
 94            return False
 95    
 96    return True
 97
 98
 99def is_discrete_distribution(distribution) -> bool:
100    """
101    Check if a distribution is discrete.
102    
103    Parameters
104    ----------
105    distribution
106        A chaospy distribution object
107        
108    Returns
109    -------
110    bool
111        True if the distribution is discrete
112    """
113    return isinstance(distribution, cp.DiscreteUniform)
114
115
116def get_discrete_parameter_info(vary: Dict[str, Any]) -> Dict[str, bool]:
117    """
118    Identify which parameters use discrete distributions.
119    
120    Parameters
121    ----------
122    vary : Dict[str, Any]
123        Dictionary of parameter names to chaospy distributions
124        
125    Returns
126    -------
127    Dict[str, bool]
128        Dictionary mapping parameter names to whether they are discrete
129    """
130    discrete_info = {}
131    for param_name, distribution in vary.items():
132        discrete_info[param_name] = is_discrete_distribution(distribution)
133    return discrete_info
134
135
136def process_sampler_output(run_dict: Dict[str, Any], params_spec, vary: Dict[str, Any]) -> Dict[str, Any]:
137    """
138    Process sampler output to handle discrete distributions properly.
139    
140    This function converts float values to integers for parameters that should be integers
141    but were returned as floats by chaospy due to mixed distribution scenarios.
142    
143    Parameters
144    ----------
145    run_dict : Dict[str, Any]
146        Dictionary of parameter values from the sampler
147    params_spec : ParamsSpecification
148        Parameter specification object
149    vary : Dict[str, Any]
150        Dictionary of parameter names to chaospy distributions
151        
152    Returns
153    -------
154    Dict[str, Any]
155        Processed run dictionary with correct types
156    """
157    processed_run = run_dict.copy()
158    
159    # Get discrete parameter information
160    discrete_info = get_discrete_parameter_info(vary)
161    
162    for param_name, value in run_dict.items():
163        if param_name in params_spec.params_dict:
164            param_def = params_spec.params_dict[param_name]
165            param_type = param_def.get('type', 'float')
166            
167            # Convert float to int for integer parameters from discrete distributions
168            if param_type == 'integer' and discrete_info.get(param_name, False):
169                if is_integer_valued(value):
170                    processed_run[param_name] = convert_to_integer(value)
171    
172    return processed_run
def is_integer_valued(value: Any) -> bool:
13def is_integer_valued(value: Any) -> bool:
14    """
15    Check if a value represents an integer, even if it's stored as a float.
16    
17    This is crucial for handling chaospy output where discrete distributions
18    may return float arrays for numerical consistency with continuous distributions.
19    
20    Parameters
21    ----------
22    value : Any
23        The value to check
24        
25    Returns
26    -------
27    bool
28        True if the value represents an integer
29    """
30    try:
31        # Handle numpy arrays and scalars
32        if hasattr(value, '__iter__') and not isinstance(value, (str, bytes)):
33            # For arrays, check if all elements are integer-valued
34            return all(is_integer_valued(v) for v in value)
35        
36        # Convert to float first to handle various numeric types
37        float_val = float(value)
38        
39        # Check if it's close to an integer
40        return abs(float_val - round(float_val)) < 1e-10
41    except (ValueError, TypeError):
42        return False

Check if a value represents an integer, even if it's stored as a float.

This is crucial for handling chaospy output where discrete distributions may return float arrays for numerical consistency with continuous distributions.

Parameters
  • value (Any): The value to check
Returns
  • bool: True if the value represents an integer
def convert_to_integer(value: Any) -> int | Any:
45def convert_to_integer(value: Any) -> Union[int, Any]:
46    """
47    Convert a value to integer if it represents an integer, otherwise return as-is.
48    
49    Parameters
50    ----------
51    value : Any
52        The value to convert
53        
54    Returns
55    -------
56    Union[int, Any]
57        Integer value if conversion is valid, otherwise the original value
58    """
59    if is_integer_valued(value):
60        return int(round(float(value)))
61    return value

Convert a value to integer if it represents an integer, otherwise return as-is.

Parameters
  • value (Any): The value to convert
Returns
  • Union[int, Any]: Integer value if conversion is valid, otherwise the original value
def validate_discrete_parameter(value: Any, param_def: Dict[str, Any]) -> bool:
64def validate_discrete_parameter(value: Any, param_def: Dict[str, Any]) -> bool:
65    """
66    Validate a parameter value for discrete distributions.
67    
68    This function handles the case where chaospy returns float values
69    for discrete distributions, especially in mixed discrete/continuous scenarios.
70    
71    Parameters
72    ----------
73    value : Any
74        The parameter value to validate
75    param_def : Dict[str, Any]
76        Parameter definition from the campaign
77        
78    Returns
79    -------
80    bool
81        True if the value is valid for this parameter
82    """
83    param_type = param_def.get('type', 'float')
84    
85    # For integer parameters, check if the value is integer-valued
86    if param_type == 'integer':
87        if not is_integer_valued(value):
88            return False
89        
90        # Check bounds if specified
91        int_val = convert_to_integer(value)
92        if 'min' in param_def and int_val < param_def['min']:
93            return False
94        if 'max' in param_def and int_val > param_def['max']:
95            return False
96    
97    return True

Validate a parameter value for discrete distributions.

This function handles the case where chaospy returns float values for discrete distributions, especially in mixed discrete/continuous scenarios.

Parameters
  • value (Any): The parameter value to validate
  • param_def (Dict[str, Any]): Parameter definition from the campaign
Returns
  • bool: True if the value is valid for this parameter
def is_discrete_distribution(distribution) -> bool:
100def is_discrete_distribution(distribution) -> bool:
101    """
102    Check if a distribution is discrete.
103    
104    Parameters
105    ----------
106    distribution
107        A chaospy distribution object
108        
109    Returns
110    -------
111    bool
112        True if the distribution is discrete
113    """
114    return isinstance(distribution, cp.DiscreteUniform)

Check if a distribution is discrete.

Parameters
  • distribution: A chaospy distribution object
Returns
  • bool: True if the distribution is discrete
def get_discrete_parameter_info(vary: Dict[str, Any]) -> Dict[str, bool]:
117def get_discrete_parameter_info(vary: Dict[str, Any]) -> Dict[str, bool]:
118    """
119    Identify which parameters use discrete distributions.
120    
121    Parameters
122    ----------
123    vary : Dict[str, Any]
124        Dictionary of parameter names to chaospy distributions
125        
126    Returns
127    -------
128    Dict[str, bool]
129        Dictionary mapping parameter names to whether they are discrete
130    """
131    discrete_info = {}
132    for param_name, distribution in vary.items():
133        discrete_info[param_name] = is_discrete_distribution(distribution)
134    return discrete_info

Identify which parameters use discrete distributions.

Parameters
  • vary (Dict[str, Any]): Dictionary of parameter names to chaospy distributions
Returns
  • Dict[str, bool]: Dictionary mapping parameter names to whether they are discrete
def process_sampler_output( run_dict: Dict[str, Any], params_spec, vary: Dict[str, Any]) -> Dict[str, Any]:
137def process_sampler_output(run_dict: Dict[str, Any], params_spec, vary: Dict[str, Any]) -> Dict[str, Any]:
138    """
139    Process sampler output to handle discrete distributions properly.
140    
141    This function converts float values to integers for parameters that should be integers
142    but were returned as floats by chaospy due to mixed distribution scenarios.
143    
144    Parameters
145    ----------
146    run_dict : Dict[str, Any]
147        Dictionary of parameter values from the sampler
148    params_spec : ParamsSpecification
149        Parameter specification object
150    vary : Dict[str, Any]
151        Dictionary of parameter names to chaospy distributions
152        
153    Returns
154    -------
155    Dict[str, Any]
156        Processed run dictionary with correct types
157    """
158    processed_run = run_dict.copy()
159    
160    # Get discrete parameter information
161    discrete_info = get_discrete_parameter_info(vary)
162    
163    for param_name, value in run_dict.items():
164        if param_name in params_spec.params_dict:
165            param_def = params_spec.params_dict[param_name]
166            param_type = param_def.get('type', 'float')
167            
168            # Convert float to int for integer parameters from discrete distributions
169            if param_type == 'integer' and discrete_info.get(param_name, False):
170                if is_integer_valued(value):
171                    processed_run[param_name] = convert_to_integer(value)
172    
173    return processed_run

Process sampler output to handle discrete distributions properly.

This function converts float values to integers for parameters that should be integers but were returned as floats by chaospy due to mixed distribution scenarios.

Parameters
  • run_dict (Dict[str, Any]): Dictionary of parameter values from the sampler
  • params_spec (ParamsSpecification): Parameter specification object
  • vary (Dict[str, Any]): Dictionary of parameter names to chaospy distributions
Returns
  • Dict[str, Any]: Processed run dictionary with correct types