Repository: damar-wicaksono/uqtestfuns (language: python)

Issue:
list_functions() returns an empty result with any input dimension filter.
Consider the following:

```python
>>> import uqtestfuns as uqtf
>>> uqtf.list_functions()
+-------+-------------------------------+-----------+------------+----------+---------------+--------------------------------+
|  No.  |          Constructor          |  # Input  |  # Output  |  Param.  |  Application  | Description                    |
+=======+===============================+===========+============+==========+===============+================================+
|   1   |           Ackley()            |     M     |     1      |   True   | optimization, | Optimization test function     |
|       |                               |           |            |          | metamodeling  | from Ackley (1987)             |
+-------+-------------------------------+-----------+------------+----------+---------------+--------------------------------+
|   2   |        Alemazkoor20D()        |    20     |     1      |  False   | metamodeling  | High-dimensional low-degree    |
|       |                               |           |            |          |               | polynomial from Alemazkoor &   |
|       |                               |           |            |          |               | Meidani (2018)                 |
+-------+-------------------------------+-----------+------------+----------+---------------+--------------------------------+
...
```

Clearly there is at least one 20 dimensional function.
But when I type

```python
>>> uqtf.list_functions(input_dimension=20)
```

nothing is returned. Something is clearly amiss: input dimension is an int but internally everything is a string (to handle "m") so the comparison used in the filtering does not work.





Discussion / hints:

Fix patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 409480f..146c4dc 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -42,7 +42,7 @@ jobs:
         run: tox -e typecheck
   test:
     name: Test
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-20.04
     strategy:
       matrix:
         python:
@@ -54,6 +54,10 @@ jobs:
             toxenv: "py39"
           - version: "3.11"
             toxenv: "py311"
+          - version: "3.12"
+            toxenv: "py312"
+          - version: "3.13"
+            toxenv: "py313"
     steps:
       - uses: actions/checkout@v3
       - uses: actions/setup-python@v4.0.0
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a4122ff..776aa49 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -52,6 +52,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
   for metamodeling exercise.
 - The 8-dimensional robot arm function for metamodeling exercises.
 
+## Fixed
+
+- The argument `input_dimension` to filter the output of `list_functions()`
+  is now effective and does not always return zero result.
+
 ## [0.5.0] - 2024-11-18
 
 ### Added


Test patch:
diff --git a/src/uqtestfuns/core/prob_input/univariate_distributions/triangular.py b/src/uqtestfuns/core/prob_input/univariate_distributions/triangular.py
index d48369b..096b026 100644
--- a/src/uqtestfuns/core/prob_input/univariate_distributions/triangular.py
+++ b/src/uqtestfuns/core/prob_input/univariate_distributions/triangular.py
@@ -6,7 +6,7 @@
 These are the lower bound, upper bound, and the mid-point (i.e., modal value),
 respectively.
 
-The density value at the modal value is computed such that the are under
+The density value at the modal value is computed such that the area under
 the distribution is 1.0.
 """
 
@@ -238,6 +238,6 @@ def icdf(
     yy[idx_nan] = np.nan
 
     # Check if values are within the set bounds
-    yy = postprocess_icdf(yy, lower_bound, upper_bound)
+    yy_post = postprocess_icdf(yy, lower_bound, upper_bound)
 
-    return yy
+    return yy_post
diff --git a/src/uqtestfuns/core/prob_input/univariate_distributions/uniform.py b/src/uqtestfuns/core/prob_input/univariate_distributions/uniform.py
index 60204da..5a8c63e 100644
--- a/src/uqtestfuns/core/prob_input/univariate_distributions/uniform.py
+++ b/src/uqtestfuns/core/prob_input/univariate_distributions/uniform.py
@@ -198,9 +198,9 @@ def icdf(
     xx[xx > 1.0] = np.nan
 
     # Compute the ICDF
-    yy = lower_bound + np.diff(parameters) * xx
+    yy = (lower_bound + np.diff(parameters) * xx).astype(np.float64)
 
     # Check if values are within the set bounds
-    yy = postprocess_icdf(yy, lower_bound, upper_bound)
+    yy_post = postprocess_icdf(yy, lower_bound, upper_bound)
 
-    return yy
+    return yy_post
diff --git a/src/uqtestfuns/helpers.py b/src/uqtestfuns/helpers.py
index bd167e4..c75e051 100644
--- a/src/uqtestfuns/helpers.py
+++ b/src/uqtestfuns/helpers.py
@@ -255,8 +255,9 @@ def _verify_input_args(
 def _filter_on_input_dim(data, input_dimension):
     """Filter the dictionary of test functions data based on the input dim."""
     if input_dimension is not None:
-        if isinstance(input_dimension, str):
-            input_dimension = input_dimension.upper()
+        # Make the input dimension a string and upper case;
+        # the result is either a numeric string or the string "M"
+        input_dimension = str(input_dimension).upper()
         data = {
             k: v for k, v in data.items() if v["input_dim"] == input_dimension
         }
diff --git a/tests/builtin_test_functions/test_sobol_levitan.py b/tests/builtin_test_functions/test_sobol_levitan.py
index e16a191..b33c311 100644
--- a/tests/builtin_test_functions/test_sobol_levitan.py
+++ b/tests/builtin_test_functions/test_sobol_levitan.py
@@ -63,7 +63,7 @@ def test_compute_variance(input_dimension, parameters_id):
     )
 
     # Compute the variance via Monte Carlo
-    xx = my_fun.prob_input.get_sample(500000)
+    xx = my_fun.prob_input.get_sample(1000000)
     yy = my_fun(xx)
 
     var_mc = np.var(yy)
diff --git a/tests/core/prob_input/test_univariate_input.py b/tests/core/prob_input/test_univariate_input.py
index b7a51d4..58a6de8 100644
--- a/tests/core/prob_input/test_univariate_input.py
+++ b/tests/core/prob_input/test_univariate_input.py
@@ -26,13 +26,15 @@ def univariate_input(
     # to avoid awkward yet unrealistic values)
     name = create_random_alphanumeric(8)
     distribution = request.param
-    if request.param == "uniform":
+    if distribution == "uniform":
         parameters = np.sort(np.round(np.random.rand(2), decimals=5))
-    elif request.param == "beta":
+    elif distribution == "beta":
         parameters = np.sort(np.round(np.random.rand(4), decimals=5))
     elif distribution == "exponential":
         # Single parameter, must be strictly positive
-        parameters = 1 + np.round(np.random.rand(1), decimals=5)
+        parameters = (1 + np.round(np.random.rand(1), decimals=5)).astype(
+            np.float64
+        )
     elif distribution == "triangular":
         parameters = np.sort(1 + 2 * np.round(np.random.rand(2), decimals=5))
         # Append the mid point
@@ -49,9 +51,13 @@ def univariate_input(
         parameters = np.insert(parameters, 1, np.random.rand(1))
     elif distribution == "lognormal":
         # Limit the size of the parameters
-        parameters = 1 + np.round(np.random.rand(2), decimals=5)
+        parameters = (1 + np.round(np.random.rand(2), decimals=5)).astype(
+            np.float64
+        )
     else:
-        parameters = 5 * np.round(np.random.rand(2), decimals=5)
+        parameters = (5 * np.round(np.random.rand(2), decimals=5)).astype(
+            np.float64
+        )
         parameters[1] += 1.0
 
     specs = {