Repository: benchopt/benchopt
Pull request #716 (2024-05-22 10:10:36)

Fix solver complex params
Fix for #715 

- Allow to use a dictionary in solver list in config file.
- Fix display issue with template_parameter when an argument is a dictionary.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/benchopt/benchopt/pull/716?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=benchopt) Report
Attention: Patch coverage is `75.00000%` with `2 lines` in your changes are missing coverage. Please review.
> Project coverage is 80.78%. Comparing base [(`932e529`)](https://app.codecov.io/gh/benchopt/benchopt/commit/932e529b67cdd7c6a98adf40c5cc47e3ac8cafcf?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=benchopt) to head [(`8f3bfea`)](https://app.codecov.io/gh/benchopt/benchopt/pull/716?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=benchopt).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #716      +/-   ##
==========================================
- Coverage   80.82%   80.78%   -0.04%     
==========================================
  Files          45       45              
  Lines        3019     3024       +5     
==========================================
+ Hits         2440     2443       +3     
- Misses        579      581       +2     
```



</details>

Patch:
diff --git a/benchopt/cli/main.py b/benchopt/cli/main.py
index 9801cd9..45a51bc 100644
--- a/benchopt/cli/main.py
+++ b/benchopt/cli/main.py
@@ -232,8 +232,12 @@ def run(config_file=None, **kwargs):
         datasets = benchmark.check_dataset_patterns(dataset_names)
         objectives = benchmark.check_objective_filters(objective_filters)
         # pyyaml returns tuples: make sure everything is a list
+        if isinstance(solver_names, dict):
+            solver_names = [solver_names]
+        elif isinstance(solver_names, tuple):
+            solver_names = list(solver_names)
         solvers = benchmark.check_solver_patterns(
-            list(solver_names) + list(forced_solvers)
+            solver_names + list(forced_solvers)
         )
 
         run_benchmark(
diff --git a/benchopt/utils/parametrized_name_mixin.py b/benchopt/utils/parametrized_name_mixin.py
index 7dcbc82..95decfd 100644
--- a/benchopt/utils/parametrized_name_mixin.py
+++ b/benchopt/utils/parametrized_name_mixin.py
@@ -19,11 +19,6 @@ class ParametrizedNameMixin():
         _parameters = next(product_param(self.parameters))
         _parameters.update(parameters)
         self._parameters = _parameters
-        if not hasattr(self, 'parameter_template'):
-            # sort parameters to make sure the representation is deterministic
-            self.parameter_template = ",".join([
-                f"{k}={_parameters[k]}" for k in sorted(_parameters)
-            ])
         for k, v in _parameters.items():
             if not hasattr(self, k):
                 setattr(self, k, v)
@@ -64,7 +59,16 @@ class ParametrizedNameMixin():
         """Compute the parametrized name of the instance."""
         out = f"{self.name}"
         if len(self._parameters) > 0:
-            out += f"[{self.parameter_template}]".format(**self._parameters)
+            if not hasattr(self, 'parameter_template'):
+                # sort parameters to make sure the representation
+                # is deterministic
+                param_fmt = ",".join([
+                    f"{k}={self._parameters[k]}"
+                    for k in sorted(self._parameters)
+                ])
+            else:
+                param_fmt = self.parameter_template.format(**self._parameters)
+            out += f"[{param_fmt}]"
         return out
 
     @classmethod


Test patch:
diff --git a/benchopt/tests/test_run_config_files.py b/benchopt/tests/test_run_config_files.py
index e7cb389..5886a3f 100644
--- a/benchopt/tests/test_run_config_files.py
+++ b/benchopt/tests/test_run_config_files.py
@@ -136,9 +136,9 @@ def test_config_solver_with_params_str_list(no_debug_test):
                 *f'--config {config_file}'.split()
             ], standalone_mode=False)
         out.check_output(r"RUN\(0\)", repetition=2)
-        out.check_output(r"param1=None", repetition=0)
-        out.check_output(r"param1=0,param2=None", repetition=2)
-        out.check_output(r"param1=1,param2=None", repetition=2)
+        out.check_output("param1=None", repetition=0)
+        out.check_output("param1=0,param2=None", repetition=2)
+        out.check_output("param1=1,param2=None", repetition=2)
 
 
 def test_config_solver_with_params_str_value(no_debug_test):
@@ -156,9 +156,9 @@ def test_config_solver_with_params_str_value(no_debug_test):
                 *f'--config {config_file}'.split()
             ], standalone_mode=False)
         out.check_output(r"RUN\(0\)", repetition=1)
-        out.check_output(r"param1=None", repetition=0)
-        out.check_output(r"param2=None", repetition=0)
-        out.check_output(r"param1=0,param2=1", repetition=2)
+        out.check_output("param1=None", repetition=0)
+        out.check_output("param2=None", repetition=0)
+        out.check_output("param1=0,param2=1", repetition=2)
 
 
 def test_config_solver_with_params_yaml_list(no_debug_test):
@@ -176,9 +176,9 @@ def test_config_solver_with_params_yaml_list(no_debug_test):
                 *f'--config {config_file}'.split()
             ], standalone_mode=False)
         out.check_output(r"RUN\(0\)", repetition=2)
-        out.check_output(r"param1=None", repetition=0)
-        out.check_output(r"param1=0,param2=None", repetition=2)
-        out.check_output(r"param1=1,param2=None", repetition=2)
+        out.check_output("param1=None", repetition=0)
+        out.check_output("param1=0,param2=None", repetition=2)
+        out.check_output("param1=1,param2=None", repetition=2)
 
 
 def test_config_solver_with_params_yaml_items(no_debug_test):
@@ -199,9 +199,9 @@ def test_config_solver_with_params_yaml_items(no_debug_test):
                 *f'--config {config_file}'.split()
             ], standalone_mode=False)
         out.check_output(r"RUN\(0\)", repetition=2)
-        out.check_output(r"param1=None", repetition=0)
-        out.check_output(r"param1=0", repetition=2)
-        out.check_output(r"param1=1", repetition=2)
+        out.check_output("param1=None", repetition=0)
+        out.check_output("param1=0", repetition=2)
+        out.check_output("param1=1", repetition=2)
 
 
 def test_config_solver_with_params_yaml_value(no_debug_test):
@@ -221,9 +221,9 @@ def test_config_solver_with_params_yaml_value(no_debug_test):
                 *f'--config {config_file}'.split()
             ], standalone_mode=False)
         out.check_output(r"RUN\(0\)", repetition=1)
-        out.check_output(r"param1=None", repetition=0)
-        out.check_output(r"param2=None", repetition=0)
-        out.check_output(r"param1=0,param2=1", repetition=2)
+        out.check_output("param1=None", repetition=0)
+        out.check_output("param2=None", repetition=0)
+        out.check_output("param1=0,param2=1", repetition=2)
 
 
 def test_config_solver_with_params_complex_param(no_debug_test):
@@ -235,7 +235,6 @@ def test_config_solver_with_params_complex_param(no_debug_test):
     params = dict(param1=[None], param2=[None])
     with temp_benchmark(solvers=[
         TEST_SOLVER.replace("# PARAMETERS", f"parameters = {params}")
-        .replace("solver1'", "solver1'\n        parameter_template = ''")
         .replace("RUN({n_iter})", "{self._parameters}")
     ]) as benchmark:
         config_file = benchmark.benchmark_dir / "run_config.yml"
@@ -245,7 +244,7 @@ def test_config_solver_with_params_complex_param(no_debug_test):
                 str(benchmark.benchmark_dir),
                 *f'--config {config_file}'.split()
             ], standalone_mode=False)
-        out.check_output(r"param1=None", repetition=0)
+        out.check_output("param1=None", repetition=0)
         out.check_output(
             "{'param1': {'test': 0, 'test2': 1}, 'param2': None}",
             repetition=1
@@ -268,9 +267,9 @@ def test_config_solver_with_one_params_list(no_debug_test):
                 *f'--config {config_file}'.split()
             ], standalone_mode=False)
         out.check_output(r"RUN\(0\)", repetition=2)
-        out.check_output(r"param1=None", repetition=0)
-        out.check_output(r"param1=0", repetition=2)
-        out.check_output(r"param1=1", repetition=2)
+        out.check_output("param1=None", repetition=0)
+        out.check_output("param1=0", repetition=2)
+        out.check_output("param1=1", repetition=2)
 
 
 def test_config_solver_with_one_params_yaml_list(no_debug_test):
@@ -289,9 +288,9 @@ def test_config_solver_with_one_params_yaml_list(no_debug_test):
                 *f'--config {config_file}'.split()
             ], standalone_mode=False)
         out.check_output(r"RUN\(0\)", repetition=2)
-        out.check_output(r"param1=None", repetition=0)
-        out.check_output(r"param1=0", repetition=2)
-        out.check_output(r"param1=1", repetition=2)
+        out.check_output("param1=None", repetition=0)
+        out.check_output("param1=0", repetition=2)
+        out.check_output("param1=1", repetition=2)
 
 
 def test_config_solver_double_param_yaml_list(no_debug_test):
@@ -312,9 +311,9 @@ def test_config_solver_double_param_yaml_list(no_debug_test):
                 *f'--config {config_file}'.split()
             ], standalone_mode=False)
         out.check_output(r"RUN\(0\)", repetition=1)
-        out.check_output(r"param1=None", repetition=0)
-        out.check_output(r"param2=None", repetition=0)
-        out.check_output(r"param1=0,param2=1", repetition=2)
+        out.check_output("param1=None", repetition=0)
+        out.check_output("param2=None", repetition=0)
+        out.check_output("param1=0,param2=1", repetition=2)
 
 
 def test_config_solver_double_param_solver_yaml(no_debug_test):
@@ -333,5 +332,5 @@ def test_config_solver_double_param_solver_yaml(no_debug_test):
                 *f'--config {config_file}'.split()
             ], standalone_mode=False)
         out.check_output(r"RUN\(0\)", repetition=1)
-        out.check_output(r"param1=None", repetition=0)
-        out.check_output(r"param1=0,param2=None", repetition=2)
+        out.check_output("param1=None", repetition=0)
+        out.check_output("param1=0,param2=None", repetition=2)