From f42b24d02d2dbf8b8883d22a92bed23f8996320d Mon Sep 17 00:00:00 2001 From: Damar Wicaksono Date: Fri, 8 Nov 2024 14:24:16 +0100 Subject: [PATCH 1/2] Refactor UQTestFunABC class hierarchy `UQTestFunABC` class is further subclassed into two distinct abstract base classes: `UQTestFunFixDimABC` and `UQTestFunVarDim` to distinguish and deal with the construction of UQ test functions of fixed and variable dimension, respectively. Furthermore, redundant attributes has been removed from the concrete classes. This commit should resolve Issue #372. --- CHANGELOG.md | 6 + README.md | 3 +- docs/api/overview.md | 10 +- docs/api/uqtestfun-abc.rst | 18 + .../adding-test-function-implementation.md | 21 +- .../tutorial-custom-functions.md | 2 +- docs/getting-started/tutorial-reliability.md | 4 +- docs/getting-started/tutorial-sensitivity.md | 4 +- src/uqtestfuns/__init__.py | 10 +- src/uqtestfuns/core/__init__.py | 9 +- src/uqtestfuns/core/uqtestfun_abc.py | 639 ++++++++++++------ src/uqtestfuns/helpers.py | 9 +- src/uqtestfuns/test_functions/ackley.py | 7 +- src/uqtestfuns/test_functions/alemazkoor.py | 6 +- src/uqtestfuns/test_functions/borehole.py | 7 +- src/uqtestfuns/test_functions/bratley1992.py | 17 +- .../test_functions/cantilever_beam_2d.py | 5 +- .../test_functions/circular_pipe_crack.py | 5 +- .../test_functions/convex_fail_domain.py | 5 +- .../test_functions/damped_cosine.py | 4 +- .../test_functions/damped_oscillator.py | 12 +- src/uqtestfuns/test_functions/flood.py | 5 +- src/uqtestfuns/test_functions/forrester.py | 5 +- src/uqtestfuns/test_functions/four_branch.py | 7 +- src/uqtestfuns/test_functions/franke.py | 21 +- src/uqtestfuns/test_functions/friedman.py | 8 +- src/uqtestfuns/test_functions/gayton_hat.py | 4 +- src/uqtestfuns/test_functions/gramacy2007.py | 5 +- src/uqtestfuns/test_functions/hyper_sphere.py | 4 +- src/uqtestfuns/test_functions/ishigami.py | 7 +- src/uqtestfuns/test_functions/mclain.py | 18 +- src/uqtestfuns/test_functions/oakley2002.py | 5 +- src/uqtestfuns/test_functions/otl_circuit.py | 6 +- src/uqtestfuns/test_functions/piston.py | 7 +- src/uqtestfuns/test_functions/portfolio_3d.py | 7 +- .../test_functions/rs_circular_bar.py | 4 +- src/uqtestfuns/test_functions/rs_quadratic.py | 4 +- src/uqtestfuns/test_functions/sobol_g.py | 9 +- .../test_functions/speed_reducer_shaft.py | 5 +- src/uqtestfuns/test_functions/sulfur.py | 5 +- src/uqtestfuns/test_functions/webster.py | 5 +- src/uqtestfuns/test_functions/welch1992.py | 5 +- src/uqtestfuns/test_functions/wing_weight.py | 5 +- tests/builtin_test_functions/test_ishigami.py | 8 +- .../test_otl_circuit.py | 4 +- tests/builtin_test_functions/test_piston.py | 4 +- .../test_portfolio_3d.py | 8 +- tests/builtin_test_functions/test_sobol_g.py | 6 +- .../test_test_functions.py | 17 +- 49 files changed, 603 insertions(+), 398 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c65d4e1..bd362bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Two new abstract base classes are added, namely `UQTestFunFixDimABC` and + `UQTestFunVarDimABC` to deal with the construction of UQ test functions of + fixed and variable dimensions, respectively. Both abstract classes are derived + from `UQTestFunABC` such that the interfaces remain consistent. - `function_id` and `input_id` are now property of `ProbInput`. - `output_dimension` is now property of `UQTestFunBareABC` and inherited to all concrete classes of UQ test functions. @@ -26,6 +30,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Application tags are now displayed when an instance of test function is + printed on the terminal. - `list_functions()` is now printed in grid format and include information regarding the output dimension and the parameterization. Furthermore, filtering can be done based on the input dimension, output dimension, diff --git a/README.md b/README.md index 9fc9061..d7f1181 100644 --- a/README.md +++ b/README.md @@ -60,10 +60,11 @@ and sensitivity analysis purposes; to create an instance of this test function: >>> my_testfun = uqtf.Borehole() >>> print(my_testfun) Function ID : Borehole -Input Dimension : 8 +Input Dimension : 8 (fixed) Output Dimension : 1 Parameterized : False Description : Borehole function from Harper and Gupta (1983) +Applications : ['metamodeling', 'sensitivity'] ``` The probabilistic input specification of this test function is built-in: diff --git a/docs/api/overview.md b/docs/api/overview.md index 43ea1d5..ed7841a 100644 --- a/docs/api/overview.md +++ b/docs/api/overview.md @@ -8,9 +8,13 @@ To make sense of how the objects in UQTestFuns are organized, let's start from the top, the {ref}`built-in test functions `: - Each of the built-in UQ test functions is a concrete implementation of the - abstract base class {ref}`UQTestFunABC `. - The base class, in turns, is derived - from {ref}`UQTestFunABC `). + abstract base classes: {ref}`UQTestFunFixDimABC ` + (for UQ test functions with fixed dimension) or + {ref}`UQTestFunVarDimABC ` + (for UQ test functions with variable dimension). +- Both of those abstract classes are derived from {ref}`UQTestFunABC `. + This base class, in turn, is derived + from {ref}`UQTestFunBareABC `). Therefore, all the instances share the same underlying interfaces. In particular, all instances share, among other things, the ``evaluate()`` method, the ``prob_input`` property, and the ``parameters`` property [^essence]. diff --git a/docs/api/uqtestfun-abc.rst b/docs/api/uqtestfun-abc.rst index 9bcd5f6..fce2a49 100644 --- a/docs/api/uqtestfun-abc.rst +++ b/docs/api/uqtestfun-abc.rst @@ -22,3 +22,21 @@ Abstract Base Classes .. autoclass:: uqtestfuns.core.uqtestfun_abc.UQTestFunABC :members: + + +.. _api_reference_uqtestfun_fix_dim_abc: + +``UQTestFunFixDimABC`` Abstract Base Class +------------------------------------ + +.. autoclass:: uqtestfuns.core.uqtestfun_abc.UQTestFunVarDimABC + :members: + + +.. _api_reference_uqtestfun_var_dim_abc: + +``UQTestFunVarDimABC`` Abstract Base Class +------------------------------------ + +.. autoclass:: uqtestfuns.core.uqtestfun_abc.UQTestFunFixDimABC + :members: diff --git a/docs/development/adding-test-function-implementation.md b/docs/development/adding-test-function-implementation.md index d1902ba..d273bf0 100644 --- a/docs/development/adding-test-function-implementation.md +++ b/docs/development/adding-test-function-implementation.md @@ -110,7 +110,7 @@ Here are the few things we usually use: import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs, FunParamSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Branin"] ``` @@ -118,14 +118,20 @@ __all__ = ["Branin"] Here are some explanations: - NumPy is usually a must, especially for implementing the evaluation function. -- All built-in test functions are concrete implementations of the abstract base - class `UQTestFunABC`. +- Because Branin test function is a test function with a fixed number of (input) + dimensions, the class will be derived from the abstract base class + {ref}`UQTestFunFixDimABC `. - To assist specifying the data for probabilistic input model the custom type `ProbInputSpecs` can be used; these are supposed to help you specifying all the required data via the typechecker. - Similarly, the custom type `FunParamSpecs` is used for specifying the function parameters. +```{notes} +In case the function is of variable dimension, use the abstract base class +{ref}`UQTestFunVarDimABC ` instead. +``` + ### Implementing a concrete evaluation function For an implementation of a test function, create a top module-level function @@ -285,8 +291,8 @@ A concrete implementation of this base class requires the following: - a static method named `evaluate()` - several class-level properties, namely: `_tags`, `_description`, - `_available_inputs`, `_available_parameters`, `_default_input_dimension`, - `_default_input`, and `_default_parameters`. + `_available_inputs`, `_available_parameters`, `_default_input_id`, + and `_default_parameters_id`. The full definition of the class for the Branin test function is shown below. @@ -298,9 +304,8 @@ class Branin(UQTestFunABC): _description = "Branin function from Dixon and Szegö (1978)" # Short description _available_inputs = AVAILABLE_INPUTS # As defined above _available_parameters = AVAILABLE_PARAMETERS # As defined above - _default_input_dimension = 2 # input dimension of the function - _default_input = "Dixon1978" # Optional, if only one input is available - _default_parameters = "Dixon1978" # Optional, if only one set of parameters is available + _default_input_id = "Dixon1978" # Optional, if only one input is available + _default_parameters_id = "Dixon1978" # Optional, if only one set of parameters is available evaluate = staticmethod(evaluate) # assuming `evaluate()` has been defined ``` diff --git a/docs/getting-started/tutorial-custom-functions.md b/docs/getting-started/tutorial-custom-functions.md index afb3cb9..3cb8a9c 100644 --- a/docs/getting-started/tutorial-custom-functions.md +++ b/docs/getting-started/tutorial-custom-functions.md @@ -150,7 +150,7 @@ marginals = [ uqtf.Marginal(distribution="uniform", parameters=[0, 15], name="x2"), ] # Create a probabilistic input -my_input = uqtf.ProbInput(marginals=marginals, name="Branin-Input") +my_input = uqtf.ProbInput(marginals=marginals, function_id="Branin", input_id="custom") ``` To verify if the instance has been created successfully, diff --git a/docs/getting-started/tutorial-reliability.md b/docs/getting-started/tutorial-reliability.md index 50d8091..e0d90c4 100644 --- a/docs/getting-started/tutorial-reliability.md +++ b/docs/getting-started/tutorial-reliability.md @@ -220,13 +220,13 @@ The input variables $w$ and $h$ are probabilistically defined according to the table below. ```{code-cell} ipython3 -cantilever.prob_input +print(cantilever.prob_input) ``` The default values of the parameters $E$ and $l$ are: ```{code-cell} ipython3 -cantilever.parameters +print(cantilever.parameters) ``` For reproducibility of this tutorial, set the seed number of the diff --git a/docs/getting-started/tutorial-sensitivity.md b/docs/getting-started/tutorial-sensitivity.md index 121a7a3..75f7dc4 100644 --- a/docs/getting-started/tutorial-sensitivity.md +++ b/docs/getting-started/tutorial-sensitivity.md @@ -316,13 +316,13 @@ The input variables of the function are probabilistically defined according to the table below. ```{code-cell} ipython3 -ishigami.prob_input +print(ishigami.prob_input) ``` Finally, the default values for the parameters $a$ and $b$ are: ```{code-cell} ipython3 -ishigami.parameters +print(ishigami.parameters) ``` For reproducibility of this tutorial, set the seed number for the pseudo-random diff --git a/src/uqtestfuns/__init__.py b/src/uqtestfuns/__init__.py index 2a409d3..b177438 100644 --- a/src/uqtestfuns/__init__.py +++ b/src/uqtestfuns/__init__.py @@ -6,7 +6,12 @@ from .core import Marginal from .core import ProbInput -from .core import UQTestFunBareABC, UQTestFunABC +from .core import ( + UQTestFunBareABC, + UQTestFunABC, + UQTestFunFixDimABC, + UQTestFunVarDimABC, +) from .core import UQTestFun from .core import FunParams @@ -30,9 +35,10 @@ "Marginal", "ProbInput", "FunParams", - "UQTestFunABC", "UQTestFunBareABC", "UQTestFunABC", + "UQTestFunFixDimABC", + "UQTestFunVarDimABC", "UQTestFun", "test_functions", "UQMetaFunSpec", diff --git a/src/uqtestfuns/core/__init__.py b/src/uqtestfuns/core/__init__.py index f0f7de8..7b086fa 100644 --- a/src/uqtestfuns/core/__init__.py +++ b/src/uqtestfuns/core/__init__.py @@ -5,7 +5,12 @@ from .parameters import FunParams from .prob_input.marginal import Marginal from .prob_input.probabilistic_input import ProbInput -from .uqtestfun_abc import UQTestFunBareABC, UQTestFunABC +from .uqtestfun_abc import ( + UQTestFunBareABC, + UQTestFunABC, + UQTestFunFixDimABC, + UQTestFunVarDimABC, +) from .uqtestfun import UQTestFun __all__ = [ @@ -14,5 +19,7 @@ "FunParams", "UQTestFunBareABC", "UQTestFunABC", + "UQTestFunFixDimABC", + "UQTestFunVarDimABC", "UQTestFun", ] diff --git a/src/uqtestfuns/core/uqtestfun_abc.py b/src/uqtestfuns/core/uqtestfun_abc.py index 66b9fb2..4c12ea9 100644 --- a/src/uqtestfuns/core/uqtestfun_abc.py +++ b/src/uqtestfuns/core/uqtestfun_abc.py @@ -3,11 +3,13 @@ """ import abc +from abc import ABC + import numpy as np from copy import deepcopy from inspect import signature -from typing import Callable, cast, List, Optional +from typing import Callable, cast, List, Optional, Type from .prob_input.marginal import Marginal from .prob_input.probabilistic_input import ProbInput @@ -20,14 +22,18 @@ ) from .utils import create_canonical_uniform_input -__all__ = ["UQTestFunBareABC", "UQTestFunABC"] +__all__ = [ + "UQTestFunBareABC", + "UQTestFunABC", + "UQTestFunFixDimABC", + "UQTestFunVarDimABC", +] CLASS_HIDDEN_ATTRIBUTES = [ "_tags", "_description", "_available_inputs", "_available_parameters", - "_default_input_dimension", ] DEFAULT_DIMENSION = 2 @@ -205,7 +211,7 @@ def _eval(self, xx) -> np.ndarray: return self.__class__.evaluate(xx, **self.parameters.as_dict()) -class UQTestFunABC(UQTestFunBareABC): +class UQTestFunABC(UQTestFunBareABC, ABC): """An abstract class for (published) UQ test functions. Parameters @@ -217,11 +223,11 @@ class UQTestFunABC(UQTestFunBareABC): In the case of functions with variable dimension, the default dimension is set to 2. This is a keyword only parameter. - prob_input_selection : str, optional + input_id : str, optional The selection of probabilistic input model; this is used when there are multiple input specifications in the literature. This is a keyword only parameter. - parameters_selection : str, optional + parameters_id : str, optional The selection of parameters set; this is used when there are multiple sets of parameters available in the literature. This is a keyword only parameter. @@ -244,61 +250,298 @@ class UQTestFunABC(UQTestFunBareABC): a fixed dimension. """ - _default_input: Optional[str] = None - _default_parameters: Optional[str] = None + @classproperty + def tags(cls) -> List[str]: + """Tags to classify different UQ test functions.""" + return cls._tags # type: ignore + + @classproperty + def available_inputs(cls) -> ProbInputSpecs: + """All the keys to the available probabilistic input specifications.""" + return cls._available_inputs # type: ignore + + @classproperty + def default_input_id(cls) -> Optional[str]: + """The key to the default probabilistic input specification.""" + return cls._default_input_id # type: ignore + + @classproperty + def available_parameters(cls) -> Optional[FunParamSpecs]: + """All the keys to the available set of parameter values.""" + return cls._available_parameters # type: ignore + + @classproperty + def default_parameters_id(cls) -> Optional[str]: + """The key to the default set of parameters.""" + return cls._default_parameters_id # type: ignore + + @classproperty + def description(cls) -> Optional[str]: + """Short description of the UQ test function.""" + return cls._description # type: ignore + + @property + @abc.abstractmethod + def variable_dimension(self): + pass + + def __str__(self): + if self.variable_dimension: + input_dimension = f"{self.input_dimension} (variable)" + else: + input_dimension = f"{self.input_dimension} (fixed)" + out = ( + f"Function ID : {self.function_id}\n" + f"Input Dimension : {input_dimension}\n" + f"Output Dimension : {self.output_dimension}\n" + f"Parameterized : {bool(self.parameters)}\n" + f"Description : {self.description}\n" + f"Applications : {self.tags}" + ) + + return out + + def _verify_input_id(self, input_id: Optional[str]) -> str: + """Verify the 'input_id' argument. + + Parameters + ---------- + input_id : Optional[str] + The ID of the probabilistic input specification. + + Returns + ------- + str + The verified ID of the probabilistic input specification. + """ + # --- Verify the input + if input_id is None: + input_id = self.default_input_id + input_id = cast(str, input_id) + + available_inputs = self.available_inputs + if input_id not in available_inputs: + raise KeyError( + f"Input ID {input_id} is not in the available " + f"specifications {list(available_inputs.keys())}" + ) + + return input_id + + def _verify_parameters_id(self, parameters_id: Optional[str]) -> str: + """Verify the 'parameters_id' argument. + + Parameters + ---------- + parameters_id : Optional[str] + The ID of the function parameters specification. + + Returns + ------- + str + The verified ID of the function parameters specification. + """ + available_parameters = self.available_parameters + + if self.available_parameters is None and parameters_id is not None: + raise ValueError("No parameters are available") + + if self.available_parameters is None: + return "" + + if parameters_id is None: + parameters_id = self.default_parameters_id + parameters_id = cast(str, parameters_id) + + if parameters_id not in available_parameters: + raise KeyError( + f"Input ID {parameters_id} is not in the available " + f"specifications {list(available_parameters.keys())}" + ) + + return parameters_id + + +class UQTestFunFixDimABC(UQTestFunABC, ABC): + """An abstract class for (published) fixed-dimension UQ test functions. + + Parameters + ---------- + input_id : str, optional + The selection of probabilistic input model; this is used when there are + multiple input specifications in the literature. + This is a keyword only parameter. + parameters_id : str, optional + The selection of parameters set; this is used when there are multiple + sets of parameters available in the literature. + This is a keyword only parameter. + function_id : str, optional + The ID of the UQ test function. + If not given, `None` is used as the function ID. + This is a keyword only parameter. + + Notes + ----- + - A published UQ test function includes a couple of additional metadata, + namely tags and description. + + Raises + ------ + KeyError + If selection is not in the list of available inputs and parameters. + TypeError + If input dimension is specified for a UQ test function with + a fixed dimension. + """ + + _default_input_id: Optional[str] = None + _default_parameters_id: Optional[str] = None def __init__( self, *, - input_dimension: Optional[int] = None, - prob_input_selection: Optional[str] = None, - parameters_selection: Optional[str] = None, + input_id: Optional[str] = None, + parameters_id: Optional[str] = None, function_id: Optional[str] = None, ): # --- Create a probabilistic input model - # Select the probabilistic input model - available_inputs = self.available_inputs - if not prob_input_selection: - prob_input_selection = self.default_input - prob_input_selection = cast(str, prob_input_selection) - if prob_input_selection not in available_inputs: - print(prob_input_selection) - raise KeyError( - "Input selection is not in the available specifications." - ) + input_id = self._verify_input_id(input_id) + prob_input = self._create_prob_input(input_id) - if self.default_input_dimension is None: - if not input_dimension: - input_dimension = DEFAULT_DIMENSION - prob_input_data = _parse_input_vardim( - input_dimension, - prob_input_selection, - available_inputs, - ) - else: - if input_dimension: - raise TypeError("Fixed test dimension!") - prob_input_data = _parse_input_fixdim( - prob_input_selection, - available_inputs, - ) + # --- Create a set of function parameters + parameters_id = self._verify_parameters_id(parameters_id) + fun_params = self._create_fun_params(parameters_id) - prob_input = ProbInput(**prob_input_data) + # --- Process the default ID + if function_id is None: + function_id = self.__class__.__name__ - # --- Select the parameters set, when applicable + # --- Initialize the parent class + super().__init__( + prob_input=prob_input, + parameters=fun_params, + function_id=function_id, + ) + + def __init_subclass__(cls, **kwargs): + """Verify if concrete class has all the required hidden attributes.""" + _init_subclass(cls) + + @property + def variable_dimension(self) -> bool: + """Return ``False`` due to fixed dimension function.""" + return False + + def _create_prob_input(self, input_id: str) -> ProbInput: + """Create an instance of probabilistic input. + + Parameters + ---------- + input_id : str + The ID of the available probabilistic input models. + + Returns + ------- + ProbInput + The probabilistic input model based on the selected ID. + """ + # Get the input (copy to avoid mutation) + raw_data = deepcopy(self.available_inputs[input_id]) + + # Process the marginals + marginals = [] + for marginal in raw_data["marginals"]: + marginals.append(Marginal(**marginal)) + + # Recast the type to satisfy type checker + input_data = cast(ProbInputArgs, raw_data) + input_data["input_id"] = input_id + input_data["marginals"] = marginals + + return ProbInput(**input_data) + + def _create_fun_params(self, parameters_id: Optional[str]) -> FunParams: + """Create an instance of function parameters. + + Parameters + ---------- + parameters_id : str + The ID of the available function parameters. + + Returns + ------- + FunParams + The set of function parameters based on the selected ID. + """ if self.available_parameters is None: - parameters = FunParams() - else: - if parameters_selection is None: - parameter_id = self.default_parameters - else: - parameter_id = parameters_selection - parameters = _create_parameters( - self.__class__.__name__, - parameter_id, - self.available_parameters, - input_dimension, - ) + return FunParams() + + # Must be deep-copied due to modification + param_data = deepcopy(self.available_parameters[parameters_id]) + + # Prepare the dictionary as input + param_data = cast(FunParamsArgs, param_data) # for type checker + param_data["parameter_id"] = parameters_id + + return FunParams(**param_data) + + +class UQTestFunVarDimABC(UQTestFunABC, ABC): + """An abstract class for (published) variable-dimension UQ test functions. + + Parameters + ---------- + input_dimension : int, optional + The input dimension of the UQ test function. + This is used only when the function supports variable dimension; + otherwise, if specified, an exception is raised. + In the case of functions with variable dimension, the default dimension + is set to 2. + input_id : str, optional + The selection of probabilistic input model; this is used when there are + multiple input specifications in the literature. + This is a keyword only parameter. + parameters_id : str, optional + The selection of parameters set; this is used when there are multiple + sets of parameters available in the literature. + This is a keyword only parameter. + function_id : str, optional + The ID of the UQ test function. + If not given, `None` is used as the function ID. + This is a keyword only parameter. + + Notes + ----- + - A published UQ test function includes a couple of additional metadata, + namely tags and description. + + Raises + ------ + KeyError + If selection is not in the list of available inputs and parameters. + TypeError + If input dimension is specified for a UQ test function with + a fixed dimension. + """ + + _default_input_id: Optional[str] = None + _default_parameters_id: Optional[str] = None + + def __init__( + self, + input_dimension: int = DEFAULT_DIMENSION, + *, + input_id: Optional[str] = None, + parameters_id: Optional[str] = None, + function_id: Optional[str] = None, + ): + # --- Create a probabilistic input model + input_id = self._verify_input_id(input_id) + prob_input = self._create_prob_input(input_id, input_dimension) + + # --- Select the parameters set, when applicable + parameters_id = self._verify_parameters_id(parameters_id) + fun_params = self._create_fun_params(parameters_id, input_dimension) # --- Process the default ID if function_id is None: @@ -307,110 +550,147 @@ def __init__( # --- Initialize the parent class super().__init__( prob_input=prob_input, - parameters=parameters, + parameters=fun_params, function_id=function_id, ) - @classmethod - def __init_subclass__(cls): - """Verify if concrete class has all the required hidden attributes. - - Raises - ------ - NotImplementedError - If required attributes are not implemented in the concrete class. - ValueError - If default input and parameters selections are not specified - when there are multiple of them. - KeyError - If the selections for the default input and parameters set are - not available. + def __init_subclass__(cls, **kwargs): + """Verify if concrete class has all the required hidden attributes.""" + _init_subclass(cls) + + @property + def variable_dimension(self) -> bool: + return True + + def _create_prob_input(self, input_id: str, input_dim: int) -> ProbInput: + """Create an instance of probabilistic input model. + + Parameters + ---------- + input_id : str + The ID of the available probabilistic input models. + input_dim : int + The input dimension of the UQ test function. + + Returns + ------- + ProbInput + The probabilistic input model based on the selected ID. """ - for class_hidden_attribute in CLASS_HIDDEN_ATTRIBUTES: - # Some class attributes must be specified - if not hasattr(cls, class_hidden_attribute): - raise NotImplementedError( - f"Class {cls} lacks required {class_hidden_attribute!r} " - f"class attribute." - ) - # Parse default input selection - if cls.default_input: - if cls.default_input not in cls.available_inputs: - raise KeyError("Input selection is not available!") - else: - if len(cls.available_inputs) > 1: - raise ValueError( - "There are multiple available input specifications, " - "the default input selection must be specified!" - ) - else: - # If only one is available, use it without being specified - cls._default_input = list(cls.available_inputs.keys())[0] + # Get the input + raw_data = deepcopy(self.available_inputs[input_id]) - # Parse default parameters set selection - if cls.available_parameters: - if cls.default_parameters: - if cls.default_parameters not in cls.available_parameters: - raise KeyError("Parameters selection is not available!") + # Process the marginals + marginals = [] + for i in range(input_dim): + marginal = raw_data["marginals"][0].copy() + marginal["name"] = f"{marginal['name']}{i}" + marginals.append(Marginal(**marginal)) - else: - if len(cls.available_parameters) > 1: - raise ValueError( - "There are multiple available parameters sets, " - "the default input selection must be specified!" - ) - else: - # If only one is available, use it without being specified - cls._default_parameters = list( - cls.available_parameters.keys() - )[0] + # Recast the type to satisfy type checker + input_data = cast(ProbInputArgs, raw_data) + input_data["input_id"] = input_id + input_data["marginals"] = marginals - @classproperty - def tags(cls) -> List[str]: - """Tags to classify different UQ test functions.""" - return cls._tags # type: ignore + return ProbInput(**input_data) - @classproperty - def available_inputs(cls) -> ProbInputSpecs: - """All the keys to the available probabilistic input specifications.""" - return cls._available_inputs # type: ignore + def _create_fun_params( + self, + parameters_id: Optional[str], + input_dim: int, + ) -> FunParams: + """Create an instance of function parameters. - @classproperty - def default_input(cls) -> Optional[str]: - """The key to the default probabilistic input specification.""" - return cls._default_input + Parameters + ---------- + parameters_id : str + The ID of the available function parameters. + input_dim : int + The input dimension of the UQ test function. - @classproperty - def available_parameters(cls) -> Optional[FunParamSpecs]: - """All the keys to the available set of parameter values.""" - return cls._available_parameters # type: ignore + Returns + ------- + FunParams + The set of function parameters based on the selected ID. + """ - @classproperty - def default_parameters(cls) -> Optional[str]: - """The key to the default set of parameters.""" - return cls._default_parameters # type: ignore + if self.available_parameters is None: + return FunParams() - @classproperty - def default_input_dimension(cls) -> Optional[int]: - """To store the default dimension of a test function.""" - return cls._default_input_dimension # type: ignore + # Must be deep-copied due to modification + param_data = deepcopy(self.available_parameters[parameters_id]) - @classproperty - def description(cls) -> Optional[str]: - """Short description of the UQ test function.""" - return cls._description # type: ignore + # Prepare the dictionary as input + param_data = cast(FunParamsArgs, param_data) # for type checker + param_data["parameter_id"] = parameters_id - def __str__(self): - out = ( - f"Function ID : {self.function_id}\n" - f"Input Dimension : {self.input_dimension}\n" - f"Output Dimension : {self.output_dimension}\n" - f"Parameterized : {bool(self.parameters)}\n" - f"Description : {self.description}" - ) + # Deal with variable dimension parameter + declared_parameters = param_data["declared_parameters"] + for declared_parameter in declared_parameters: + value = declared_parameter["value"] + if isinstance(value, Callable): # type: ignore + func_signature = signature(value).parameters + if "input_dimension" in func_signature: + parameter_value = value(input_dimension=input_dim) + declared_parameter["value"] = parameter_value - return out + return FunParams(**param_data) + + +def _init_subclass(cls: Type[UQTestFunABC]): + """Verify if concrete class has all the required hidden attributes. + + Raises + ------ + NotImplementedError + If required attributes are not implemented in the concrete class. + ValueError + If default input and parameters selections are not specified + when there are multiple of them. + KeyError + If the selections for the default input and parameters set are + not available. + """ + for class_hidden_attribute in CLASS_HIDDEN_ATTRIBUTES: + # Some class attributes must be specified + if not hasattr(cls, class_hidden_attribute): + raise NotImplementedError( + f"Class {cls} lacks required {class_hidden_attribute!r} " + f"class attribute." + ) + + # Parse default input selection + if cls.default_input_id: + if cls.default_input_id not in cls.available_inputs: + raise KeyError("Input selection is not available!") + else: + if len(cls.available_inputs) > 1: + raise ValueError( + "There are multiple available input specifications, " + "the default input selection must be specified!" + ) + else: + # If only one is available, use it without being specified + cls._default_input_id = list(cls.available_inputs.keys())[0] + + # Parse default parameters set selection + if cls.available_parameters: + if cls.default_parameters_id: + if cls.default_parameters_id not in cls.available_parameters: + raise KeyError("Parameters selection is not available!") + + else: + if len(cls.available_parameters) > 1: + raise ValueError( + "There are multiple available parameters sets, " + "the default input selection must be specified!" + ) + else: + # If only one is available, use it without being specified + cls._default_parameters_id = list( + cls.available_parameters.keys() + )[0] def _verify_sample_shape(xx: np.ndarray, num_cols: int): @@ -460,86 +740,3 @@ def _verify_sample_domain(xx: np.ndarray, min_value: float, max_value: float): f"One or more values are outside the domain " f"[{min_value}, {max_value}]!" ) - - -def _parse_input_vardim( - input_dimension: int, - input_id: str, - available_inputs: ProbInputSpecs, -) -> ProbInputArgs: - """Get the selected input.""" - # Get the input - raw_data = available_inputs[input_id].copy() - - # Process the marginals - marginals = [] - for i in range(input_dimension): - marginal = raw_data["marginals"][0].copy() - marginal["name"] = f"{marginal['name']}{i}" - marginals.append(Marginal(**marginal)) - - # Recast the type to satisfy type checker - input_data = cast(ProbInputArgs, raw_data) - input_data["input_id"] = input_id - input_data["marginals"] = marginals - - return input_data - - -def _parse_input_fixdim( - input_id: str, - available_inputs: ProbInputSpecs, -) -> ProbInputArgs: - """Get the selected input.""" - # Get the input - raw_data = available_inputs[input_id].copy() - - # Process the marginals - marginals = [] - for marginal in raw_data["marginals"]: - marginals.append(Marginal(**marginal)) - - # Recast the type to satisfy type checker - input_data = cast(ProbInputArgs, raw_data) - input_data["input_id"] = input_id - input_data["marginals"] = marginals - - return input_data - - -def _create_parameters( - function_id: str, - parameter_id: str, - available_parameters: FunParamSpecs, - input_dimension: Optional[int] = None, -) -> FunParams: - """Create the Parameter set of a UQ test function.""" - - # Verify if the selection is valid - if parameter_id not in available_parameters: - raise KeyError( - f"Parameter set {parameter_id} is not in the available sets." - ) - - # Must be deepcopied due to modification - param_dict = deepcopy(available_parameters[parameter_id]) - - # Verify if the function_id agrees - if function_id != param_dict["function_id"]: - raise ValueError() - - # Prepare the dictionary as input - param_dict = cast(FunParamsArgs, param_dict) # Recasting for type checker - param_dict["parameter_id"] = parameter_id - - # Deal with variable dimension parameter - declared_parameters = param_dict["declared_parameters"] - for declared_parameter in declared_parameters: - value = declared_parameter["value"] - if isinstance(value, Callable): # type: ignore - func_signature = signature(value).parameters - if "input_dimension" in func_signature: - parameter_value = value(input_dimension=input_dimension) - declared_parameter["value"] = parameter_value - - return FunParams(**param_dict) diff --git a/src/uqtestfuns/helpers.py b/src/uqtestfuns/helpers.py index 8116cc0..b9155e0 100644 --- a/src/uqtestfuns/helpers.py +++ b/src/uqtestfuns/helpers.py @@ -356,15 +356,14 @@ def _parse_modules_data(package): instance: UQTestFunABC = class_path() # Get the dimension - default_input_dimension = class_path.default_input_dimension - if default_input_dimension is None: - default_input_dimension = "M" + if instance.variable_dimension: + input_dimension = "M" else: - default_input_dimension = instance.input_dimension + input_dimension = instance.input_dimension data[available_class] = { "constructor": available_class + "()", - "input_dim": default_input_dimension, + "input_dim": input_dimension, "output_dim": instance.output_dimension, "parameterized": True if instance.parameters else False, "tags": ", ".join(instance.tags), diff --git a/src/uqtestfuns/test_functions/ackley.py b/src/uqtestfuns/test_functions/ackley.py index ca03a97..4328565 100644 --- a/src/uqtestfuns/test_functions/ackley.py +++ b/src/uqtestfuns/test_functions/ackley.py @@ -22,7 +22,7 @@ import numpy as np -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunVarDimABC from uqtestfuns.core.custom_typing import ProbInputSpecs, FunParamSpecs __all__ = ["Ackley"] @@ -30,7 +30,7 @@ AVAILABLE_INPUTS: ProbInputSpecs = { "Ackley1987": { - "function_id": "Ackley1987", + "function_id": "Ackley", "description": ( "Search domain for the Ackley function from Ackley (1987)" ), @@ -110,13 +110,12 @@ def evaluate(xx: np.ndarray, a: float, b: float, c: float) -> np.ndarray: return yy -class Ackley(UQTestFunABC): +class Ackley(UQTestFunVarDimABC): """A concrete implementation of the M-dimensional Ackley test function.""" _tags = ["optimization", "metamodeling"] _description = "Optimization test function from Ackley (1987)" _available_inputs = AVAILABLE_INPUTS _available_parameters = AVAILABLE_PARAMETERS - _default_input_dimension = None # Indicate that this is variable dim. evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/alemazkoor.py b/src/uqtestfuns/test_functions/alemazkoor.py index b6ab6e1..1633235 100644 --- a/src/uqtestfuns/test_functions/alemazkoor.py +++ b/src/uqtestfuns/test_functions/alemazkoor.py @@ -20,7 +20,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Alemazkoor2D", "Alemazkoor20D"] @@ -94,7 +94,7 @@ def evaluate_2d(xx: np.ndarray) -> np.ndarray: return yy -class Alemazkoor2D(UQTestFunABC): +class Alemazkoor2D(UQTestFunFixDimABC): """An implementation of the 2D function of Alemazkoor & Meidani (2018).""" _tags = ["metamodeling"] @@ -129,7 +129,7 @@ def evaluate_20d(xx: np.ndarray) -> np.ndarray: return yy -class Alemazkoor20D(UQTestFunABC): +class Alemazkoor20D(UQTestFunFixDimABC): """An implementation of the 20D function of Alemazkoor & Meidani (2018).""" _tags = ["metamodeling"] diff --git a/src/uqtestfuns/test_functions/borehole.py b/src/uqtestfuns/test_functions/borehole.py index fc47f61..5477f98 100644 --- a/src/uqtestfuns/test_functions/borehole.py +++ b/src/uqtestfuns/test_functions/borehole.py @@ -25,7 +25,7 @@ import numpy as np from uqtestfuns.core.custom_typing import MarginalSpecs, ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Borehole"] @@ -154,14 +154,13 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return yy -class Borehole(UQTestFunABC): +class Borehole(UQTestFunFixDimABC): """A concrete implementation of the Borehole function.""" _tags = ["metamodeling", "sensitivity"] _description = "Borehole function from Harper and Gupta (1983)" _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input = DEFAULT_INPUT_SELECTION - _default_input_dimension = 8 + _default_input_id = DEFAULT_INPUT_SELECTION evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/bratley1992.py b/src/uqtestfuns/test_functions/bratley1992.py index 3786948..52a1253 100644 --- a/src/uqtestfuns/test_functions/bratley1992.py +++ b/src/uqtestfuns/test_functions/bratley1992.py @@ -40,7 +40,7 @@ from .sobol_g import evaluate as evaluate_sobol_g from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunVarDimABC __all__ = ["Bratley1992a", "Bratley1992b", "Bratley1992c", "Bratley1992d"] @@ -68,7 +68,6 @@ _tags=["integration", "sensitivity"], _available_inputs=AVAILABLE_INPUTS, _available_parameters=None, - _default_input_dimension=None, _description="from Bratley et al. (1992)", ) @@ -95,7 +94,7 @@ def evaluate_bratley1992a(xx: np.ndarray) -> np.ndarray: return yy -class Bratley1992a(UQTestFunABC): +class Bratley1992a(UQTestFunVarDimABC): """An implementation of the test function 1 from Bratley et al. (1992). The function (used as an integrand) is a product of an absolute function. @@ -111,7 +110,6 @@ class Bratley1992a(UQTestFunABC): ) _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = None evaluate = staticmethod(evaluate_bratley1992a) # type: ignore @@ -141,7 +139,7 @@ def evaluate_bratley1992b(xx: np.ndarray) -> np.ndarray: return yy -class Bratley1992b(UQTestFunABC): +class Bratley1992b(UQTestFunVarDimABC): """An implementation of the test function 2 from Bratley et al. (1992). The function (used as an integrand) is a product of a trigonometric @@ -154,7 +152,6 @@ class Bratley1992b(UQTestFunABC): ) _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = None evaluate = staticmethod(evaluate_bratley1992b) # type: ignore @@ -186,8 +183,8 @@ def evaluate_bratley1992c(xx: np.ndarray) -> np.ndarray: return yy -class Bratley1992c(UQTestFunABC): - """An implementation of the test function 2 from Bratley et al. (1992). +class Bratley1992c(UQTestFunVarDimABC): + """An implementation of the test function 3 from Bratley et al. (1992). The function (used as an integrand) is a product of a trigonometric function. @@ -199,7 +196,6 @@ class Bratley1992c(UQTestFunABC): ) _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = None evaluate = staticmethod(evaluate_bratley1992c) # type: ignore @@ -230,7 +226,7 @@ def evaluate_bratley1992d(xx: np.ndarray) -> np.ndarray: return yy -class Bratley1992d(UQTestFunABC): +class Bratley1992d(UQTestFunVarDimABC): """An implementation of the test function 4 from Bratley et al. (1992). The function (used as an integrand) is a sum of products. @@ -242,6 +238,5 @@ class Bratley1992d(UQTestFunABC): ) _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = None evaluate = staticmethod(evaluate_bratley1992d) # type: ignore diff --git a/src/uqtestfuns/test_functions/cantilever_beam_2d.py b/src/uqtestfuns/test_functions/cantilever_beam_2d.py index bf248fa..92eac35 100644 --- a/src/uqtestfuns/test_functions/cantilever_beam_2d.py +++ b/src/uqtestfuns/test_functions/cantilever_beam_2d.py @@ -29,7 +29,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs, FunParamSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["CantileverBeam2D"] @@ -117,7 +117,7 @@ def evaluate(xx: np.ndarray, modulus: float, span: float) -> np.ndarray: return yy -class CantileverBeam2D(UQTestFunABC): +class CantileverBeam2D(UQTestFunFixDimABC): """Concrete implementation of the 2D cantilever beam reliability.""" _tags = ["reliability"] @@ -127,6 +127,5 @@ class CantileverBeam2D(UQTestFunABC): ) _available_inputs = AVAILABLE_INPUTS _available_parameters = AVAILABLE_PARAMETERS - _default_input_dimension = 2 evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/circular_pipe_crack.py b/src/uqtestfuns/test_functions/circular_pipe_crack.py index 65ddda2..5da5d06 100644 --- a/src/uqtestfuns/test_functions/circular_pipe_crack.py +++ b/src/uqtestfuns/test_functions/circular_pipe_crack.py @@ -24,7 +24,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs, FunParamSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["CircularPipeCrack"] @@ -126,7 +126,7 @@ def evaluate( return yy -class CircularPipeCrack(UQTestFunABC): +class CircularPipeCrack(UQTestFunFixDimABC): """A concrete implementation of the circular pipe crack problem.""" _tags = ["reliability"] @@ -135,6 +135,5 @@ class CircularPipeCrack(UQTestFunABC): ) _available_inputs = AVAILABLE_INPUTS _available_parameters = AVAILABLE_PARAMETERS - _default_input_dimension = 2 evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/convex_fail_domain.py b/src/uqtestfuns/test_functions/convex_fail_domain.py index 7ea28a0..c7565c8 100644 --- a/src/uqtestfuns/test_functions/convex_fail_domain.py +++ b/src/uqtestfuns/test_functions/convex_fail_domain.py @@ -18,7 +18,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["ConvexFailDomain"] @@ -76,7 +76,7 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return yy -class ConvexFailDomain(UQTestFunABC): +class ConvexFailDomain(UQTestFunFixDimABC): """Concrete implementation of the Convex failure domain reliability.""" _tags = ["reliability"] @@ -85,6 +85,5 @@ class ConvexFailDomain(UQTestFunABC): ) _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input_dimension = 2 evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/damped_cosine.py b/src/uqtestfuns/test_functions/damped_cosine.py index c3fa53b..f9f163f 100644 --- a/src/uqtestfuns/test_functions/damped_cosine.py +++ b/src/uqtestfuns/test_functions/damped_cosine.py @@ -14,7 +14,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["DampedCosine"] @@ -60,7 +60,7 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return yy -class DampedCosine(UQTestFunABC): +class DampedCosine(UQTestFunFixDimABC): """An implementation of the 1D damped cosine from Santner et al. (2018).""" _tags = ["metamodeling"] diff --git a/src/uqtestfuns/test_functions/damped_oscillator.py b/src/uqtestfuns/test_functions/damped_oscillator.py index 8620804..7c45668 100644 --- a/src/uqtestfuns/test_functions/damped_oscillator.py +++ b/src/uqtestfuns/test_functions/damped_oscillator.py @@ -46,7 +46,7 @@ FunParamSpecs, MarginalSpecs, ) -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC from .utils import lognorm2norm_mean, lognorm2norm_std __all__ = ["DampedOscillator", "DampedOscillatorReliability"] @@ -269,7 +269,7 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return np.sqrt(xx_s) -class DampedOscillator(UQTestFunABC): +class DampedOscillator(UQTestFunFixDimABC): """A concrete implementation of the Damped oscillator test function.""" _tags = ["metamodeling", "sensitivity"] @@ -278,7 +278,6 @@ class DampedOscillator(UQTestFunABC): ) _available_inputs = AVAILABLE_INPUTS_BASE _available_parameters = None - _default_input_dimension = 8 evaluate = staticmethod(evaluate) # type: ignore @@ -310,7 +309,7 @@ def evaluate_reliability(xx: np.ndarray, pf: float): return yy -class DampedOscillatorReliability(UQTestFunABC): +class DampedOscillatorReliability(UQTestFunFixDimABC): """A concrete implementation of the Damped oscillator reliability func.""" _tags = ["reliability"] @@ -319,8 +318,7 @@ class DampedOscillatorReliability(UQTestFunABC): ) _available_inputs = AVAILABLE_INPUTS_RELIABILITY _available_parameters = AVAILABLE_PARAMETERS_RELIABILITY - _default_input_dimension = 8 - _default_input = "DerKiureghian1990a" - _default_parameters = "DerKiureghian1990" + _default_input_id = "DerKiureghian1990a" + _default_parameters_id = "DerKiureghian1990" evaluate = staticmethod(evaluate_reliability) # type: ignore diff --git a/src/uqtestfuns/test_functions/flood.py b/src/uqtestfuns/test_functions/flood.py index 6f1e649..7d7ef99 100644 --- a/src/uqtestfuns/test_functions/flood.py +++ b/src/uqtestfuns/test_functions/flood.py @@ -34,7 +34,7 @@ import numpy as np from uqtestfuns.core.custom_typing import MarginalSpecs, ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Flood"] @@ -139,13 +139,12 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return ss -class Flood(UQTestFunABC): +class Flood(UQTestFunFixDimABC): """Concrete implementation of the Flood model test function.""" _tags = ["metamodeling", "sensitivity"] _description = "Flood model from Iooss and Lemaître (2015)" _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input_dimension = 8 evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/forrester.py b/src/uqtestfuns/test_functions/forrester.py index fafb233..3fd35ef 100644 --- a/src/uqtestfuns/test_functions/forrester.py +++ b/src/uqtestfuns/test_functions/forrester.py @@ -17,7 +17,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Forrester2008"] @@ -63,13 +63,12 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return yy -class Forrester2008(UQTestFunABC): +class Forrester2008(UQTestFunFixDimABC): """An implementation of the 1D function of Forrester et al. (2008).""" _tags = ["optimization", "metamodeling"] _description = "One-dimensional function from Forrester et al. (2008)" _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input_dimension = 1 evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/four_branch.py b/src/uqtestfuns/test_functions/four_branch.py index f4df679..843364e 100644 --- a/src/uqtestfuns/test_functions/four_branch.py +++ b/src/uqtestfuns/test_functions/four_branch.py @@ -35,7 +35,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs, FunParamSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["FourBranch"] @@ -137,7 +137,7 @@ def evaluate(xx: np.ndarray, p: float) -> np.ndarray: return np.min(yy, axis=0) -class FourBranch(UQTestFunABC): +class FourBranch(UQTestFunFixDimABC): """A concrete implementation of the four-branch test function.""" _tags = ["reliability"] @@ -146,7 +146,6 @@ class FourBranch(UQTestFunABC): ) _available_inputs = AVAILABLE_INPUTS _available_parameters = AVAILABLE_PARAMETERS - _default_input_dimension = 2 - _default_parameters = "Schueremans2005" + _default_parameters_id = "Schueremans2005" evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/franke.py b/src/uqtestfuns/test_functions/franke.py index 6aeab8b..036aca5 100644 --- a/src/uqtestfuns/test_functions/franke.py +++ b/src/uqtestfuns/test_functions/franke.py @@ -43,7 +43,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Franke1", "Franke2", "Franke3", "Franke4", "Franke5", "Franke6"] @@ -79,7 +79,6 @@ _tags=["metamodeling"], _available_inputs=AVAILABLE_INPUTS, _available_parameters=None, - _default_input_dimension=2, _description="from Franke (1979)", ) @@ -117,7 +116,7 @@ def evaluate_franke1(xx: np.ndarray): return yy -class Franke1(UQTestFunABC): +class Franke1(UQTestFunFixDimABC): """A concrete implementation of the (1st) Franke function. The function features two Gaussian peaks and a Gaussian dip. @@ -127,7 +126,6 @@ class Franke1(UQTestFunABC): _description = f"(1st) Franke function {COMMON_METADATA['_description']}" _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = COMMON_METADATA["_default_input_dimension"] evaluate = staticmethod(evaluate_franke1) # type: ignore @@ -153,7 +151,7 @@ def evaluate_franke2(xx: np.ndarray): return yy -class Franke2(UQTestFunABC): +class Franke2(UQTestFunFixDimABC): """A concrete implementation of the (2nd) Franke function. The function features two plateaus joined by a steep hill. @@ -163,7 +161,6 @@ class Franke2(UQTestFunABC): _description = f"(2nd) Franke function {COMMON_METADATA['_description']}" _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = COMMON_METADATA["_default_input_dimension"] evaluate = staticmethod(evaluate_franke2) # type: ignore @@ -192,7 +189,7 @@ def evaluate_franke3(xx: np.ndarray): return yy -class Franke3(UQTestFunABC): +class Franke3(UQTestFunFixDimABC): """A concrete implementation of the (3rd) Franke function. The function features a saddle shaped surface. @@ -202,7 +199,6 @@ class Franke3(UQTestFunABC): _description = f"(3rd) Franke function {COMMON_METADATA['_description']}" _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = COMMON_METADATA["_default_input_dimension"] evaluate = staticmethod(evaluate_franke3) # type: ignore @@ -231,7 +227,7 @@ def evaluate_franke4(xx: np.ndarray): return yy -class Franke4(UQTestFunABC): +class Franke4(UQTestFunFixDimABC): """A concrete implementation of the (4th) Franke function. The function features a gentle Gaussian hill. @@ -241,7 +237,6 @@ class Franke4(UQTestFunABC): _description = f"(4th) Franke function {COMMON_METADATA['_description']}" _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = COMMON_METADATA["_default_input_dimension"] evaluate = staticmethod(evaluate_franke4) # type: ignore @@ -270,7 +265,7 @@ def evaluate_franke5(xx: np.ndarray): return yy -class Franke5(UQTestFunABC): +class Franke5(UQTestFunFixDimABC): """A concrete implementation of the (5th) Franke function. The function features a steep Gaussian hill. @@ -280,7 +275,6 @@ class Franke5(UQTestFunABC): _description = f"(5th) Franke function {COMMON_METADATA['_description']}" _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = COMMON_METADATA["_default_input_dimension"] evaluate = staticmethod(evaluate_franke5) # type: ignore @@ -310,7 +304,7 @@ def evaluate_franke6(xx: np.ndarray): return yy -class Franke6(UQTestFunABC): +class Franke6(UQTestFunFixDimABC): """A concrete implementation of the (6th) Franke function. The function features a part of a sphere. @@ -320,6 +314,5 @@ class Franke6(UQTestFunABC): _description = f"(6th) Franke function {COMMON_METADATA['_description']}" _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = COMMON_METADATA["_default_input_dimension"] evaluate = staticmethod(evaluate_franke6) # type: ignore diff --git a/src/uqtestfuns/test_functions/friedman.py b/src/uqtestfuns/test_functions/friedman.py index 2b23da3..5ec5ddf 100644 --- a/src/uqtestfuns/test_functions/friedman.py +++ b/src/uqtestfuns/test_functions/friedman.py @@ -27,7 +27,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Friedman6D", "Friedman10D"] @@ -98,25 +98,23 @@ def evaluate_friedman(xx: np.ndarray) -> np.ndarray: return yy_1 + yy_2 + yy_3 + yy_4 -class Friedman6D(UQTestFunABC): +class Friedman6D(UQTestFunFixDimABC): """A concrete implementation of the 6D Friedman et al. (1983) function.""" _tags = ["metamodeling", "sensitivity"] _description = "Six-dimensional function from Friedman et al. (1983)" _available_inputs = AVAILABLE_INPUTS_6D _available_parameters = None - _default_input_dimension = 6 evaluate = staticmethod(evaluate_friedman) # type: ignore -class Friedman10D(UQTestFunABC): +class Friedman10D(UQTestFunFixDimABC): """A concrete implementation of the 10D Friedman (1991) function.""" _tags = ["metamodeling"] _description = "Ten-dimensional function from Friedman (1991)" _available_inputs = AVAILABLE_INPUTS_10D _available_parameters = None - _default_input_dimension = 10 evaluate = staticmethod(evaluate_friedman) # type: ignore diff --git a/src/uqtestfuns/test_functions/gayton_hat.py b/src/uqtestfuns/test_functions/gayton_hat.py index cfc83af..73d4b2a 100644 --- a/src/uqtestfuns/test_functions/gayton_hat.py +++ b/src/uqtestfuns/test_functions/gayton_hat.py @@ -17,7 +17,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["GaytonHat"] @@ -68,7 +68,7 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return yy -class GaytonHat(UQTestFunABC): +class GaytonHat(UQTestFunFixDimABC): """A concrete implementation of the Gayton Hat test function.""" _tags = ["reliability"] diff --git a/src/uqtestfuns/test_functions/gramacy2007.py b/src/uqtestfuns/test_functions/gramacy2007.py index 6866b54..402de60 100644 --- a/src/uqtestfuns/test_functions/gramacy2007.py +++ b/src/uqtestfuns/test_functions/gramacy2007.py @@ -21,7 +21,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Gramacy1DSine"] @@ -76,13 +76,12 @@ def evaluate_1dsine(xx: np.ndarray) -> np.ndarray: return yy -class Gramacy1DSine(UQTestFunABC): +class Gramacy1DSine(UQTestFunFixDimABC): """A concrete implementation of the 1D Gramacy (2007) Sine function.""" _tags = ["metamodeling"] _description = "One-dimensional sine function from Gramacy (2007)" _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input_dimension = 1 evaluate = staticmethod(evaluate_1dsine) # type: ignore diff --git a/src/uqtestfuns/test_functions/hyper_sphere.py b/src/uqtestfuns/test_functions/hyper_sphere.py index f22b9d3..54d2e53 100644 --- a/src/uqtestfuns/test_functions/hyper_sphere.py +++ b/src/uqtestfuns/test_functions/hyper_sphere.py @@ -16,7 +16,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["HyperSphere"] @@ -67,7 +67,7 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return yy -class HyperSphere(UQTestFunABC): +class HyperSphere(UQTestFunFixDimABC): """A concrete implementation of the hyper-sphere reliability problem.""" _tags = ["reliability"] diff --git a/src/uqtestfuns/test_functions/ishigami.py b/src/uqtestfuns/test_functions/ishigami.py index dbc4b77..84ab8ca 100644 --- a/src/uqtestfuns/test_functions/ishigami.py +++ b/src/uqtestfuns/test_functions/ishigami.py @@ -35,7 +35,7 @@ ProbInputSpecs, FunParamSpecs, ) -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Ishigami"] @@ -150,14 +150,13 @@ def evaluate(xx: np.ndarray, a: float, b: float) -> np.ndarray: return yy -class Ishigami(UQTestFunABC): +class Ishigami(UQTestFunFixDimABC): """An implementation of the Ishigami test function.""" _tags = ["sensitivity"] _description = "Ishigami function from Ishigami and Homma (1991)" _available_inputs = AVAILABLE_INPUTS _available_parameters = AVAILABLE_PARAMETERS - _default_parameters = DEFAULT_PARAMETERS_SELECTION - _default_input_dimension = 3 + _default_parameters_id = DEFAULT_PARAMETERS_SELECTION evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/mclain.py b/src/uqtestfuns/test_functions/mclain.py index 49b03b8..4f03282 100644 --- a/src/uqtestfuns/test_functions/mclain.py +++ b/src/uqtestfuns/test_functions/mclain.py @@ -31,7 +31,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["McLainS1", "McLainS2", "McLainS3", "McLainS4", "McLainS5"] @@ -65,7 +65,6 @@ _tags=["metamodeling"], _available_inputs=AVAILABLE_INPUTS, _available_parameters=None, - _default_input_dimension=2, _description="from McLain (1974)", ) @@ -91,7 +90,7 @@ def evaluate_mclain_s1(xx: np.ndarray) -> np.ndarray: return yy -class McLainS1(UQTestFunABC): +class McLainS1(UQTestFunFixDimABC): """A concrete implementation of the McLain S1 function. The function features a part of a sphere. @@ -101,7 +100,6 @@ class McLainS1(UQTestFunABC): _description = f"McLain S1 function {COMMON_METADATA['_description']}" _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = COMMON_METADATA["_default_input_dimension"] evaluate = staticmethod(evaluate_mclain_s1) # type: ignore @@ -126,7 +124,7 @@ def evaluate_mclain_s2(xx: np.ndarray) -> np.ndarray: return yy -class McLainS2(UQTestFunABC): +class McLainS2(UQTestFunFixDimABC): """A concrete implementation of the McLain S2 function. The function features a steep hill rising from a plain. @@ -136,7 +134,6 @@ class McLainS2(UQTestFunABC): _description = f"McLain S2 function {COMMON_METADATA['_description']}" _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = COMMON_METADATA["_default_input_dimension"] evaluate = staticmethod(evaluate_mclain_s2) # type: ignore @@ -161,7 +158,7 @@ def evaluate_mclain_s3(xx: np.ndarray) -> np.ndarray: return yy -class McLainS3(UQTestFunABC): +class McLainS3(UQTestFunFixDimABC): """A concrete implementation of the McLain S3 function. The function features a less steep hill (compared to S2). @@ -171,7 +168,6 @@ class McLainS3(UQTestFunABC): _description = f"McLain S3 function {COMMON_METADATA['_description']}" _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = COMMON_METADATA["_default_input_dimension"] evaluate = staticmethod(evaluate_mclain_s3) # type: ignore @@ -199,7 +195,7 @@ def evaluate_mclain_s4(xx: np.ndarray) -> np.ndarray: return yy -class McLainS4(UQTestFunABC): +class McLainS4(UQTestFunFixDimABC): """A concrete implementation of the McLain S4 function. The function features a long narrow hill. @@ -209,7 +205,6 @@ class McLainS4(UQTestFunABC): _description = f"McLain S4 function {COMMON_METADATA['_description']}" _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = COMMON_METADATA["_default_input_dimension"] evaluate = staticmethod(evaluate_mclain_s4) # type: ignore @@ -234,7 +229,7 @@ def evaluate_mclain_s5(xx: np.ndarray) -> np.ndarray: return yy -class McLainS5(UQTestFunABC): +class McLainS5(UQTestFunFixDimABC): """A concrete implementation of the McLain S5 function. The function features two plateaus separated by a steep cliff. @@ -244,6 +239,5 @@ class McLainS5(UQTestFunABC): _description = f"McLain S5 function {COMMON_METADATA['_description']}" _available_inputs = COMMON_METADATA["_available_inputs"] _available_parameters = COMMON_METADATA["_available_parameters"] - _default_input_dimension = COMMON_METADATA["_default_input_dimension"] evaluate = staticmethod(evaluate_mclain_s5) # type: ignore diff --git a/src/uqtestfuns/test_functions/oakley2002.py b/src/uqtestfuns/test_functions/oakley2002.py index b02bead..630e658 100644 --- a/src/uqtestfuns/test_functions/oakley2002.py +++ b/src/uqtestfuns/test_functions/oakley2002.py @@ -18,7 +18,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Oakley1D"] @@ -64,13 +64,12 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return yy -class Oakley1D(UQTestFunABC): +class Oakley1D(UQTestFunFixDimABC): """An implementation of the 1D function from Oakley & O'Hagan (2002).""" _tags = ["metamodeling"] _description = "One-dimensional function from Oakley and O'Hagan (2002)" _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input_dimension = 1 evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/otl_circuit.py b/src/uqtestfuns/test_functions/otl_circuit.py index ff17cbf..4b840e1 100644 --- a/src/uqtestfuns/test_functions/otl_circuit.py +++ b/src/uqtestfuns/test_functions/otl_circuit.py @@ -26,7 +26,7 @@ from copy import deepcopy from uqtestfuns.core.custom_typing import MarginalSpecs, ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["OTLCircuit"] @@ -148,7 +148,7 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return vm -class OTLCircuit(UQTestFunABC): +class OTLCircuit(UQTestFunFixDimABC): """A concrete implementation of the OTL circuit test function.""" _tags = ["metamodeling", "sensitivity"] @@ -159,6 +159,6 @@ class OTLCircuit(UQTestFunABC): ) _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input = DEFAULT_INPUT_SELECTION + _default_input_id = DEFAULT_INPUT_SELECTION evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/piston.py b/src/uqtestfuns/test_functions/piston.py index c101105..2e893c3 100644 --- a/src/uqtestfuns/test_functions/piston.py +++ b/src/uqtestfuns/test_functions/piston.py @@ -26,7 +26,7 @@ from copy import deepcopy from uqtestfuns.core.custom_typing import MarginalSpecs, ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Piston"] @@ -161,14 +161,13 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return cc -class Piston(UQTestFunABC): +class Piston(UQTestFunFixDimABC): """A concrete implementation of the Piston simulation test function.""" _tags = ["metamodeling", "sensitivity"] _description = "Piston simulation model from Ben-Ari and Steinberg (2007)" _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input_dimension = 7 - _default_input = DEFAULT_INPUT_SELECTION + _default_input_id = DEFAULT_INPUT_SELECTION evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/portfolio_3d.py b/src/uqtestfuns/test_functions/portfolio_3d.py index bd83143..4d3e01c 100644 --- a/src/uqtestfuns/test_functions/portfolio_3d.py +++ b/src/uqtestfuns/test_functions/portfolio_3d.py @@ -20,7 +20,7 @@ ProbInputSpecs, FunParamSpecs, ) -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Portfolio3D"] @@ -179,14 +179,13 @@ def evaluate(xx: np.ndarray, cs: float, ct: float, cj: float) -> np.ndarray: return yy -class Portfolio3D(UQTestFunABC): +class Portfolio3D(UQTestFunFixDimABC): """An implementation of the simple portfolio model test function.""" _tags = ["sensitivity"] _description = "Simple portfolio model from Saltelli et al. (2004)" _available_inputs = AVAILABLE_INPUTS _available_parameters = AVAILABLE_PARAMETERS - _default_parameters = DEFAULT_PARAMETERS_SELECTION - _default_input_dimension = 3 + _default_parameters_id = DEFAULT_PARAMETERS_SELECTION evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/rs_circular_bar.py b/src/uqtestfuns/test_functions/rs_circular_bar.py index d3a05b7..6e48528 100644 --- a/src/uqtestfuns/test_functions/rs_circular_bar.py +++ b/src/uqtestfuns/test_functions/rs_circular_bar.py @@ -15,7 +15,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs, FunParamSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["RSCircularBar"] @@ -93,7 +93,7 @@ def evaluate(xx: np.ndarray, bar_diameter: float) -> np.ndarray: return yy -class RSCircularBar(UQTestFunABC): +class RSCircularBar(UQTestFunFixDimABC): """Concrete implementation of the circular bar RS reliability problem.""" _tags = ["reliability"] diff --git a/src/uqtestfuns/test_functions/rs_quadratic.py b/src/uqtestfuns/test_functions/rs_quadratic.py index d5b0bf5..c7493a2 100644 --- a/src/uqtestfuns/test_functions/rs_quadratic.py +++ b/src/uqtestfuns/test_functions/rs_quadratic.py @@ -15,7 +15,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["RSQuadratic"] @@ -66,7 +66,7 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return yy -class RSQuadratic(UQTestFunABC): +class RSQuadratic(UQTestFunFixDimABC): """Concrete implementation of the quadratic RS reliability problem.""" _tags = ["reliability"] diff --git a/src/uqtestfuns/test_functions/sobol_g.py b/src/uqtestfuns/test_functions/sobol_g.py index 219ebc1..98e5bd3 100644 --- a/src/uqtestfuns/test_functions/sobol_g.py +++ b/src/uqtestfuns/test_functions/sobol_g.py @@ -59,14 +59,14 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs, FunParamSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunVarDimABC __all__ = ["SobolG"] AVAILABLE_INPUTS: ProbInputSpecs = { "Saltelli1995": { - "function_id": "Sobol-G", + "function_id": "SobolG", "description": ( "Probabilistic input model for the Sobol'-G function " "from Saltelli and Sobol' (1995)" @@ -386,14 +386,13 @@ def evaluate(xx: np.ndarray, aa: np.ndarray): return yy -class SobolG(UQTestFunABC): +class SobolG(UQTestFunVarDimABC): """An implementation of the M-dimensional Sobol'-G test function.""" _tags = ["sensitivity", "integration"] _description = "Sobol'-G function from Saltelli and Sobol' (1995)" _available_inputs = AVAILABLE_INPUTS _available_parameters = AVAILABLE_PARAMETERS - _default_parameters = DEFAULT_PARAMETERS_SELECTION - _default_input_dimension = None + _default_parameters_id = DEFAULT_PARAMETERS_SELECTION evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/speed_reducer_shaft.py b/src/uqtestfuns/test_functions/speed_reducer_shaft.py index e429aa6..4814f59 100644 --- a/src/uqtestfuns/test_functions/speed_reducer_shaft.py +++ b/src/uqtestfuns/test_functions/speed_reducer_shaft.py @@ -24,7 +24,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC from .utils import gumbel_max_mu, gumbel_max_beta __all__ = ["SpeedReducerShaft"] @@ -104,7 +104,7 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return yy -class SpeedReducerShaft(UQTestFunABC): +class SpeedReducerShaft(UQTestFunFixDimABC): """A concrete implementation of the speed reducer shaft function.""" _tags = ["reliability"] @@ -114,6 +114,5 @@ class SpeedReducerShaft(UQTestFunABC): ) _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input_dimension = 5 evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/sulfur.py b/src/uqtestfuns/test_functions/sulfur.py index 2673815..6460a7e 100644 --- a/src/uqtestfuns/test_functions/sulfur.py +++ b/src/uqtestfuns/test_functions/sulfur.py @@ -62,7 +62,7 @@ import numpy as np from uqtestfuns.core.custom_typing import MarginalSpecs, ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Sulfur"] @@ -197,13 +197,12 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return dd_f -class Sulfur(UQTestFunABC): +class Sulfur(UQTestFunFixDimABC): """A concrete implementation of the Sulfur model test function.""" _tags = ["metamodeling", "sensitivity"] _description = "Sulfur model from Charlson et al. (1992)" _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input_dimension = 9 evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/webster.py b/src/uqtestfuns/test_functions/webster.py index 5890d09..77f315d 100644 --- a/src/uqtestfuns/test_functions/webster.py +++ b/src/uqtestfuns/test_functions/webster.py @@ -17,7 +17,7 @@ import numpy as np from uqtestfuns.core.custom_typing import ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Webster2D"] @@ -68,13 +68,12 @@ def evaluate(xx: np.ndarray): return yy -class Webster2D(UQTestFunABC): +class Webster2D(UQTestFunFixDimABC): """A concrete implementation of the function from Webster et al. (1996).""" _tags = ["metamodeling"] _description = "2D polynomial function from Webster et al. (1996)." _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input_dimension = 2 evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/welch1992.py b/src/uqtestfuns/test_functions/welch1992.py index c86415f..80dd89d 100644 --- a/src/uqtestfuns/test_functions/welch1992.py +++ b/src/uqtestfuns/test_functions/welch1992.py @@ -22,7 +22,7 @@ import numpy as np from uqtestfuns.core.custom_typing import MarginalSpecs, ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC __all__ = ["Welch1992"] @@ -96,13 +96,12 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return yy -class Welch1992(UQTestFunABC): +class Welch1992(UQTestFunFixDimABC): """A concrete implementation of the Welch et al. (1992) test function.""" _tags = ["metamodeling", "sensitivity", "integration"] _description = "20-Dimensional function from Welch et al. (1992)" _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input_dimension = 20 evaluate = staticmethod(evaluate) # type: ignore diff --git a/src/uqtestfuns/test_functions/wing_weight.py b/src/uqtestfuns/test_functions/wing_weight.py index 48784f9..406a256 100644 --- a/src/uqtestfuns/test_functions/wing_weight.py +++ b/src/uqtestfuns/test_functions/wing_weight.py @@ -23,7 +23,7 @@ import numpy as np from uqtestfuns.core.custom_typing import MarginalSpecs, ProbInputSpecs -from uqtestfuns.core.uqtestfun_abc import UQTestFunABC +from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC from .utils import deg2rad __all__ = ["WingWeight"] @@ -135,13 +135,12 @@ def evaluate(xx: np.ndarray) -> np.ndarray: return yy -class WingWeight(UQTestFunABC): +class WingWeight(UQTestFunFixDimABC): """A concrete implementation of the wing weight test function.""" _tags = ["metamodeling", "sensitivity"] _description = "Wing weight model from Forrester et al. (2008)" _available_inputs = AVAILABLE_INPUTS _available_parameters = None - _default_input_dimension = 10 evaluate = staticmethod(evaluate) # type: ignore diff --git a/tests/builtin_test_functions/test_ishigami.py b/tests/builtin_test_functions/test_ishigami.py index b719c28..4d26490 100644 --- a/tests/builtin_test_functions/test_ishigami.py +++ b/tests/builtin_test_functions/test_ishigami.py @@ -18,7 +18,7 @@ @pytest.fixture(params=available_parameters) def ishigami_fun(request): - ishigami = Ishigami(parameters_selection=request.param) + ishigami = Ishigami(parameters_id=request.param) return ishigami @@ -61,8 +61,8 @@ def test_different_parameters(param_selection): """Test selecting different built-in parameters.""" # Create an instance of Ishigami function with a specified param. selection - my_testfun_1 = Ishigami(parameters_selection=param_selection) - my_testfun_2 = Ishigami(parameters_selection=param_selection) + my_testfun_1 = Ishigami(parameters_id=param_selection) + my_testfun_2 = Ishigami(parameters_id=param_selection) # Assertion assert my_testfun_1.parameters == my_testfun_2.parameters @@ -71,4 +71,4 @@ def test_different_parameters(param_selection): def test_wrong_param_selection(): """Test a wrong selection of the parameters.""" with pytest.raises(KeyError): - Ishigami(parameters_selection="marelli1") + Ishigami(parameters_id="marelli1") diff --git a/tests/builtin_test_functions/test_otl_circuit.py b/tests/builtin_test_functions/test_otl_circuit.py index b3d7736..ef2610b 100644 --- a/tests/builtin_test_functions/test_otl_circuit.py +++ b/tests/builtin_test_functions/test_otl_circuit.py @@ -14,8 +14,8 @@ def test_inert_inputs(): """Test whether the inputs from 'Moon' specification are indeed inert.""" - otl_ben_ari = OTLCircuit(prob_input_selection="BenAri2007") - otl_moon = OTLCircuit(prob_input_selection="Moon2010") + otl_ben_ari = OTLCircuit(input_id="BenAri2007") + otl_moon = OTLCircuit(input_id="Moon2010") # Assertions: ProbInput is assigned assert otl_ben_ari.prob_input is not None diff --git a/tests/builtin_test_functions/test_piston.py b/tests/builtin_test_functions/test_piston.py index 4965d4a..0ee52a4 100644 --- a/tests/builtin_test_functions/test_piston.py +++ b/tests/builtin_test_functions/test_piston.py @@ -14,8 +14,8 @@ def test_inert_inputs(): """Test whether the inputs from 'Moon' specification are indeed inert.""" - piston_ben_ari = Piston(prob_input_selection="BenAri2007") - piston_moon = Piston(prob_input_selection="Moon2010") + piston_ben_ari = Piston(input_id="BenAri2007") + piston_moon = Piston(input_id="Moon2010") # Assert that the ProbInput is correctly attached assert piston_ben_ari.prob_input is not None diff --git a/tests/builtin_test_functions/test_portfolio_3d.py b/tests/builtin_test_functions/test_portfolio_3d.py index 782d4ae..a163799 100644 --- a/tests/builtin_test_functions/test_portfolio_3d.py +++ b/tests/builtin_test_functions/test_portfolio_3d.py @@ -18,7 +18,7 @@ @pytest.fixture(params=available_parameters) def portfolio3d_fun(request): - portfolio3d = Portfolio3D(parameters_selection=request.param) + portfolio3d = Portfolio3D(parameters_id=request.param) return portfolio3d @@ -74,8 +74,8 @@ def test_different_parameters(param_selection): """Test selecting different built-in parameters.""" # Create an instance of Ishigami function with a specified param. selection - my_testfun_1 = Portfolio3D(parameters_selection=param_selection) - my_testfun_2 = Portfolio3D(parameters_selection=param_selection) + my_testfun_1 = Portfolio3D(parameters_id=param_selection) + my_testfun_2 = Portfolio3D(parameters_id=param_selection) # Assertion: The parameter sets are identical assert my_testfun_1.parameters == my_testfun_2.parameters @@ -84,4 +84,4 @@ def test_different_parameters(param_selection): def test_wrong_param_selection(): """Test a wrong selection of the parameters.""" with pytest.raises(KeyError): - Portfolio3D(parameters_selection="marelli1") + Portfolio3D(parameters_id="marelli1") diff --git a/tests/builtin_test_functions/test_sobol_g.py b/tests/builtin_test_functions/test_sobol_g.py index b4c352c..dae810a 100644 --- a/tests/builtin_test_functions/test_sobol_g.py +++ b/tests/builtin_test_functions/test_sobol_g.py @@ -18,7 +18,7 @@ def test_wrong_param_selection(): """Test a wrong selection of the parameters.""" with pytest.raises(KeyError): - SobolG(parameters_selection="marelli1") + SobolG(parameters_id="marelli1") # ATTENTION: some parameters choice (e.g., "sobol-1") @@ -31,7 +31,7 @@ def test_compute_mean(input_dimension, params_selection): # Create an instance of Sobol-G test function my_fun = SobolG( input_dimension=input_dimension, - parameters_selection=params_selection, + parameters_id=params_selection, ) # Assert that ProbInput is correctly attached @@ -59,7 +59,7 @@ def test_compute_variance(input_dimension, params_selection): # Create an instance of the Sobol-G test function my_fun = SobolG( input_dimension=input_dimension, - parameters_selection=params_selection, + parameters_id=params_selection, ) # Assert that ProbInput is correctly attached diff --git a/tests/builtin_test_functions/test_test_functions.py b/tests/builtin_test_functions/test_test_functions.py index a2fa9d7..76e03e9 100644 --- a/tests/builtin_test_functions/test_test_functions.py +++ b/tests/builtin_test_functions/test_test_functions.py @@ -117,7 +117,7 @@ def test_available_inputs(builtin_testfun): available_inputs = testfun_class.available_inputs for available_input in available_inputs: - assert_call(testfun_class, prob_input_selection=available_input) + assert_call(testfun_class, input_id=available_input) def test_available_parameters(builtin_testfun): @@ -129,9 +129,7 @@ def test_available_parameters(builtin_testfun): if available_parameters is not None: for available_parameter in available_parameters: - assert_call( - testfun_class, parameters_selection=available_parameter - ) + assert_call(testfun_class, parameters_id=available_parameter) def test_call_instance(builtin_testfun): @@ -154,12 +152,17 @@ def test_str(builtin_testfun): # Create an instance my_fun = builtin_testfun() + if my_fun.variable_dimension: + input_dim = f"{my_fun.input_dimension} (variable)" + else: + input_dim = f"{my_fun.input_dimension} (fixed)" str_ref = ( f"Function ID : {my_fun.function_id}\n" - f"Input Dimension : {my_fun.input_dimension}\n" + f"Input Dimension : {input_dim}\n" f"Output Dimension : {my_fun.output_dimension}\n" f"Parameterized : {bool(my_fun.parameters)}\n" - f"Description : {my_fun.description}" + f"Description : {my_fun.description}\n" + f"Applications : {my_fun.tags}" ) assert my_fun.__str__() == str_ref @@ -271,4 +274,4 @@ def test_evaluate_invalid_input_dim(builtin_testfun): def test_evaluate_invalid_input_selection(builtin_testfun): """Test if an exception is raised if invalid input selection is given.""" with pytest.raises(KeyError): - builtin_testfun(prob_input_selection=100) + builtin_testfun(input_id=100) From 8e749b9d15737092cfe56a13c43d93dca18b0a7f Mon Sep 17 00:00:00 2001 From: Damar Wicaksono Date: Fri, 8 Nov 2024 14:31:39 +0100 Subject: [PATCH 2/2] Fix type checker issues --- src/uqtestfuns/core/uqtestfun_abc.py | 9 +++------ src/uqtestfuns/helpers.py | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/uqtestfuns/core/uqtestfun_abc.py b/src/uqtestfuns/core/uqtestfun_abc.py index 4c12ea9..5462741 100644 --- a/src/uqtestfuns/core/uqtestfun_abc.py +++ b/src/uqtestfuns/core/uqtestfun_abc.py @@ -250,6 +250,9 @@ class UQTestFunABC(UQTestFunBareABC, ABC): a fixed dimension. """ + _default_input_id: Optional[str] = None + _default_parameters_id: Optional[str] = None + @classproperty def tags(cls) -> List[str]: """Tags to classify different UQ test functions.""" @@ -394,9 +397,6 @@ class UQTestFunFixDimABC(UQTestFunABC, ABC): a fixed dimension. """ - _default_input_id: Optional[str] = None - _default_parameters_id: Optional[str] = None - def __init__( self, *, @@ -524,9 +524,6 @@ class UQTestFunVarDimABC(UQTestFunABC, ABC): a fixed dimension. """ - _default_input_id: Optional[str] = None - _default_parameters_id: Optional[str] = None - def __init__( self, input_dimension: int = DEFAULT_DIMENSION, diff --git a/src/uqtestfuns/helpers.py b/src/uqtestfuns/helpers.py index b9155e0..bd167e4 100644 --- a/src/uqtestfuns/helpers.py +++ b/src/uqtestfuns/helpers.py @@ -359,7 +359,7 @@ def _parse_modules_data(package): if instance.variable_dimension: input_dimension = "M" else: - input_dimension = instance.input_dimension + input_dimension = str(instance.input_dimension) data[available_class] = { "constructor": available_class + "()",