Repository: automl/SMAC3

Issue:
.pcs read and write error
#### Description
.pcs file read and write does not work

#### Steps/Code to Reproduce
```
#!/usr/bin/env python

from smac.configspace import (ConfigurationSpace,
                              Configuration)
from ConfigSpace.hyperparameters import (CategoricalHyperparameter,
                                         UniformFloatHyperparameter,
                                         UniformIntegerHyperparameter, )
from ConfigSpace.read_and_write import pcs
from smac.utils.io.output_writer import OutputWriter
from smac.utils.io.input_reader import InputReader



cs = ConfigurationSpace()
hyp = UniformFloatHyperparameter('A',
                                 0.0,
                                 1.0,
                                 default_value=0.5)
cs.add_hyperparameters([hyp])

o = OutputWriter()
o.save_configspace(cs, 'configspace.pcs', 'pcs_new')

i = InputReader()
cs = i.read_pcs_file('configspace.pcs')
# first error, empty output, parameter is not read in, in read_pcs_file the file is not opened
# with open(fn) as file:
#     file_string = file.readlines()
print(cs)

# Optional solution also not working: Seems like the pcs format changed at some point
# If 'real' is removed it works
with open('configspace.pcs') as file:
    file_string = file.readlines()
    cs = pcs.read(file_string)
print(cs)
```

#### Versions
0.10.0

Thanks!


Discussion:

Patch:
diff --git a/smac/utils/io/cmd_reader.py b/smac/utils/io/cmd_reader.py
index 74d6f2f7b..129480a2f 100644
--- a/smac/utils/io/cmd_reader.py
+++ b/smac/utils/io/cmd_reader.py
@@ -134,19 +134,8 @@ class ReadPCSFileAction(Action):
         fn = values
         if fn:
             if os.path.isfile(fn):
-                # Three possible formats: json, pcs and pcs_new. We prefer json.
-                with open(fn) as fp:
-                    if fn.endswith('.json'):
-                        parsed_scen_args['cs'] = pcs_json.read(fp.read())
-                        logger.debug("Loading pcs as json from: %s", fn)
-                    else:
-                        pcs_str = fp.readlines()
-                        try:
-                            parsed_scen_args["cs"] = pcs.read(pcs_str)
-                        except:
-                            logger.debug("Could not parse pcs file with old format; trying new format ...")
-                            parsed_scen_args["cs"] = pcs_new.read(pcs_str)
-                    parsed_scen_args["cs"].seed(42)
+                parsed_scen_args['cs'] = in_reader.read_pcs_file(fn)
+                parsed_scen_args["cs"].seed(42)
             else:
                 parser.exit(1, "Could not find pcs file: {}".format(fn))
         setattr(namespace, self.dest, values)
diff --git a/smac/utils/io/input_reader.py b/smac/utils/io/input_reader.py
index 794aa825d..de7a723c3 100644
--- a/smac/utils/io/input_reader.py
+++ b/smac/utils/io/input_reader.py
@@ -1,5 +1,6 @@
 import numpy as np
-from smac.configspace import pcs
+from smac.configspace import pcs, pcs_new
+from smac.configspace import json as pcs_json
 
 __author__ = "Marius Lindauer"
 __copyright__ = "Copyright 2015, ML4AAD"
@@ -172,8 +173,11 @@ class InputReader(object):
                 instances[tmp[0]] = np.array(tmp[1:], dtype=np.double)
         return [f.strip() for f in lines[0].rstrip("\n").split(",")[1:]], instances
 
-    def read_pcs_file(self, fn: str):
-        """Encapsulates generating configuration space object
+    @staticmethod
+    def read_pcs_file(fn: str, logger=None):
+        """Encapsulates generating configuration space object from file.
+
+        Automatically detects whether the cs is saved in json, pcs or pcs_new.
 
         Parameters
         ----------
@@ -184,5 +188,18 @@ class InputReader(object):
         -------
             ConfigSpace: ConfigSpace
         """
-        space = pcs.read(fn)
-        return space
+        # Three possible formats: json, pcs and pcs_new. We prefer json.
+        with open(fn) as fp:
+            if fn.endswith('.json'):
+                cs = pcs_json.read(fp.read())
+                if logger:
+                    logger.debug("Loading pcs as json from: %s", fn)
+            else:
+                pcs_str = fp.readlines()
+                try:
+                    cs = pcs.read(pcs_str)
+                except NotImplementedError:
+                    if logger:
+                        logger.debug("Could not parse pcs file with old format; trying new format ...")
+                    cs = pcs_new.read(pcs_str)
+        return cs


Test patch:
diff --git a/test/test_utils/io/test_inputreader.py b/test/test_utils/io/test_inputreader.py
index ee7d5fd11..bcde0ec98 100644
--- a/test/test_utils/io/test_inputreader.py
+++ b/test/test_utils/io/test_inputreader.py
@@ -9,7 +9,11 @@ import logging
 
 import numpy as np
 
+from smac.configspace import ConfigurationSpace
+from ConfigSpace.hyperparameters import UniformFloatHyperparameter
+from smac.configspace import pcs
 from smac.utils.io.input_reader import InputReader
+from smac.utils.io.output_writer import OutputWriter
 
 
 class InputReaderTest(unittest.TestCase):
@@ -24,7 +28,20 @@ class InputReaderTest(unittest.TestCase):
                                                       '..', '..'))
         os.chdir(base_directory)
 
+        # Files that will be created:
+        self.pcs_fn = "test/test_files/configspace.pcs"
+        self.json_fn = "test/test_files/configspace.json"
+
+        self.output_files = [self.pcs_fn, self.json_fn]
+
     def tearDown(self):
+        for output_file in self.output_files:
+            if output_file:
+                try:
+                    os.remove(output_file)
+                except FileNotFoundError:
+                    pass
+
         os.chdir(self.current_dir)
 
     def test_feature_input(self):
@@ -37,3 +54,36 @@ class InputReaderTest(unittest.TestCase):
                           "inst3":[1.7, 1.8, 1.9]}
         for i in feats[1]:
             self.assertEqual(feats_original[i], list(feats[1][i]))
+
+    def test_save_load_configspace(self):
+        """Check if inputreader can load different config-spaces"""
+        cs = ConfigurationSpace()
+        hyp = UniformFloatHyperparameter('A', 0.0, 1.0, default_value=0.5)
+        cs.add_hyperparameters([hyp])
+
+        output_writer = OutputWriter()
+        input_reader = InputReader()
+
+        # pcs_new
+        output_writer.save_configspace(cs, self.pcs_fn, 'pcs_new')
+        restored_cs = input_reader.read_pcs_file(self.pcs_fn)
+        self.assertEqual(cs, restored_cs)
+        restored_cs = input_reader.read_pcs_file(self.pcs_fn, self.logger)
+        self.assertEqual(cs, restored_cs)
+
+        # json
+        output_writer.save_configspace(cs, self.json_fn, 'json')
+        restored_cs = input_reader.read_pcs_file(self.json_fn)
+        self.assertEqual(cs, restored_cs)
+        restored_cs = input_reader.read_pcs_file(self.json_fn, self.logger)
+        self.assertEqual(cs, restored_cs)
+
+        # pcs
+        with open(self.pcs_fn, 'w') as fh:
+            fh.write(pcs.write(cs))
+        restored_cs = input_reader.read_pcs_file(self.pcs_fn)
+        self.assertEqual(cs, restored_cs)
+        restored_cs = input_reader.read_pcs_file(self.pcs_fn)
+        self.assertEqual(cs, restored_cs)
+        restored_cs = input_reader.read_pcs_file(self.pcs_fn, self.logger)
+        self.assertEqual(cs, restored_cs)