Repository: automl/SMAC3

Issue:
Wrong parameters order
Hi all, 

we have realized that when using fmin_smac, the input parameters given to the function are processed in the wrong order if their count is higher than 9. 

For instance when executing this command: 

x, cost, _ = fmin_smac(func=to_minimize,
                       x0=[0.5, 20, 0.7, 0.15, 10, 0.5, 1.0, 0.1, 0.25, 1.0, 0.5],
                       bounds=[(0, 1), (5, 25), (0.1, 2.0), (0.0, 1.5), (2, 20), (0, 1), (1, 1.3), (0.001, 1), (0.001, 2), (0.05, 9), (0.2, 0.8)],
                       maxfun=2000,
                       rng=3)

the input parameters are swapped in the following way, as reported in the configspace.pcs file:

x1 real [0.0, 1.0] [0.5]
x10 real [0.05, 9.0] [1.0]
x11 real [0.2, 0.8] [0.5]
x2 real [5.0, 25.0] [20.0]
x3 real [0.1, 2.0] [0.7]
x4 real [0.0, 1.5] [0.15]
x5 real [2.0, 20.0] [10.0]
x6 real [0.0, 1.0] [0.5]
x7 real [1.0, 1.3] [1.0]
x8 real [0.001, 1.0] [0.1]
x9 real [0.001, 2.0] [0.25]


Any idea what's happening there? Thanks for your help!

Best, 

L.


Discussion:

Patch:
diff --git a/smac/facade/func_facade.py b/smac/facade/func_facade.py
index c747fd01b..5a5aefc74 100644
--- a/smac/facade/func_facade.py
+++ b/smac/facade/func_facade.py
@@ -52,8 +52,12 @@ def fmin_smac(func: callable,
     """
     # create configuration space
     cs = ConfigurationSpace()
+
+    # Adjust zero padding
+    tmplt = 'x{0:0' + str(len(str(len(bounds)))) + 'd}'
+
     for idx, (lower_bound, upper_bound) in enumerate(bounds):
-        parameter = UniformFloatHyperparameter(name="x%d" % (idx + 1),
+        parameter = UniformFloatHyperparameter(name=tmplt.format(idx + 1),
                                                lower=lower_bound,
                                                upper=upper_bound,
                                                default_value=x0[idx])
@@ -77,11 +81,10 @@ def fmin_smac(func: callable,
     smac = SMAC(scenario=scenario, tae_runner=ta, rng=rng)
     smac.logger = logging.getLogger(smac.__module__ + "." + smac.__class__.__name__)
     incumbent = smac.optimize()
-
     config_id = smac.solver.runhistory.config_ids[incumbent]
     run_key = RunKey(config_id, None, 0)
     incumbent_performance = smac.solver.runhistory.data[run_key]
-    incumbent = np.array([incumbent['x%d' % (idx + 1)]
+    incumbent = np.array([incumbent[tmplt.format(idx + 1)]
                           for idx in range(len(bounds))], dtype=np.float)
     return incumbent, incumbent_performance.cost, \
            smac


Test patch:
diff --git a/test/test_facade/test_func_facade.py b/test/test_facade/test_func_facade.py
index 11ab7c88d..43748ed1c 100644
--- a/test/test_facade/test_func_facade.py
+++ b/test/test_facade/test_func_facade.py
@@ -33,3 +33,20 @@ class TestSMACFacade(unittest.TestCase):
         self.assertEqual(type(f), type(f_s))
 
         self.output_dirs.append(smac.scenario.output_dir)
+
+    def test_parameter_order(self):
+        def func(x):
+            for i in range(len(x)):
+                self.assertLess(i - 1, x[i])
+                self.assertGreater(i, x[i])
+            return 1
+
+        default = [i - 0.5 for i in range(10)]
+        bounds = [(i - 1, i) for i in range(10)]
+        print(default, bounds)
+        _, _, smac = fmin_smac(func=func, x0=default,
+                               bounds=bounds,
+                               maxfun=1)
+
+        self.output_dirs.append(smac.scenario.output_dir)
+