# raise NotImplementedError()
# from collections import OrderedDict, deque
# import copy
# import math
# import StringIO
#
# import numpy as np  # Because the python docs don't recommend log(x, y)
#                     # https://docs.python.org/2/library/math.html#math.log10
# import hyperopt
#
# import HPOlib.format_converter.configuration_space as configuration_space_module
#
# ################################################################################
# # Read functionality
# def read(pyll_string):
#     reader = PyllReader()
#     if isinstance(pyll_string, file):
#         pyll_string = pyll_string.read()
#     elif type(pyll_string) == str:
#         pass
#     else:
#         raise NotImplementedError()
#     return reader.read(pyll_string)
#
#
# class PyllReader(object):
#     # This won't parse AutoWEKA because inside a hp.choice there are lists.
#     # This parser assumes that every choice of an hp.choice is either a
#     # Literal or a dictionaries with exactly one literal, where the literal is
#     # one value of the choice hyperparameter. Adjust
#     # autoweka/src/java/autoweka/tpe/TPEExperimentConstructor.java
#     # Instead of:
#     # hp.choice('auto_param_2', [
#     #    {'attributesearch':'NONE'},
#     #    (
#     #        hp.choice('auto_param_4', [
#     #            {
#     #                'attributesearch':'weka.attributeSelection.BestFirst',
#     #                'assearch_wasbf__00__01_0_D':hp.choice('assearch_wasbf__00__01_0_D', ['2', '1', '0', ]),
#     #                'assearch_wasbf__00__02_1_INT_N':hp.quniform('assearch_wasbf__00__02_1_INT_N', 2.0, 10.0,1.0),
#     #                'assearch_wasbf__00__03_2_S':hp.choice('assearch_wasbf__00__03_2_S', ['0', ]),
#     #            }]))])
#     # write
#     # auto_param2 = hp.choice('auto_param_2', [
#     #    {'attributesearch':'NONE'},
#     #    {'attritubesearch':hp.choice('auto_param_4', [
#     #            {
#     #                'attributesearch':'weka.attributeSelection.BestFirst',
#     #                'assearch_wasbf__00__01_0_D':hp.choice('assearch_wasbf__00__01_0_D', ['2', '1', '0', ]),
#     #                'assearch_wasbf__00__02_1_INT_N':hp.quniform('assearch_wasbf__00__02_1_INT_N', 2.0, 10.0,1.0),
#     #                'assearch_wasbf__00__03_2_S':hp.choice('assearch_wasbf__00__03_2_S', ['0', ]),
#     #            }])}])
#
#
#     def __init__(self):
#         self.hyperparameters = OrderedDict()
#         self.constants = list()
#
#     def read(self, pyll_string):
#         exec pyll_string
#         space = hyperopt.pyll.as_apply(space)   # Space is generated by exec
#
#         if space.name not in ("dict", "pos_args", "switch"):
#             raise ValueError("The configuration space must consist of a dict "
#                              "or pos_args but the "
#                              "root node but is actually %s" % space.name)
#
#         root_values = self.read_object(space, None, condition=None)
#
#         if type(root_values) == dict:
#             for key in root_values:
#                 self.hyperparameters[key] = root_values[key]
#         elif isinstance(root_values, configuration_space_module
#                 .CategoricalHyperparameter):
#             self.hyperparameters[root_values.name] = root_values
#         else:
#             NotImplementedError()
#
#         for key in sorted(self.hyperparameters):
#             print self.hyperparameters[key]
#         return self.hyperparameters
#
#     def read_object(self, expr, label, condition=None):
#         # Useful pyll objects...
#         if isinstance(expr, configuration_space_module.Hyperparameter):
#             return expr
#         elif expr.name == "hyperopt_param":
#             return self.read_hyperopt_param(expr, condition=condition)
#         elif expr.name == "literal":
#             return self.read_literal(expr, label, condition=condition)
#         elif expr.name == "dict":
#             return self.read_dict(expr, condition=condition)
#         elif expr.name == "pos_args":
#             return self.read_container(expr, condition=condition)
#         elif expr.name == "switch":
#             return self.read_switch(expr, condition=condition)
#         elif expr.name == "float":
#             return self.read_float(expr, condition=condition)
#         elif expr.name == "int":
#             return self.read_int(expr, condition=condition)
#         # Hyperopt distributions
#         elif expr.name == "uniform":
#             return self.read_uniform(expr, label, condition=condition)
#         elif expr.name == "quniform":
#             return self.read_quniform(expr, label, condition=condition)
#         elif expr.name == "loguniform":
#             return self.read_loguniform(expr, label, condition=condition)
#         elif expr.name == "qloguniform":
#             return self.read_qloguniform(expr, label, condition=condition)
#         elif expr.name == "normal":
#             return self.read_normal(expr, label, condition=condition)
#         elif expr.name == "qnormal":
#             return self.read_qnormal(expr, label, condition=condition)
#         elif expr.name == "lognormal":
#             return self.read_lognormal(expr, label, condition=condition)
#         elif expr.name == "qlognormal":
#             return self.read_qlognormal(expr, label, condition=condition)
#         # Mathematical operations
#         elif expr.name in ("floordiv", "pow", "div", "mul", "ceildiv", "max",
#                 "add", "sub"):
#             return self.read_mathematical(expr, condition=condition)
#         elif expr.name == "partial":
#             return self.read_dict(expr, condition=condition)
#         elif expr.name == "getitem":
#             return self.read_getitem(expr, label, condition=condition)
#         elif expr.name == "getattr":
#             return self.read_getattr(expr, condition=condition)
#         elif expr.name == "name":
#             raise NotImplementedError()
#         elif len(expr.named_args) > 0 and len(expr.pos_args) == 0:
#             print "WARNING: encountered unknown pyll node %s, but it has " \
#                   "named arguments which will now be processed" % expr.name
#             return self.read_dict(expr)
#         elif len(expr.named_args) == 0 and len(expr.pos_args) > 0:
#             print "WARNING: encountered unknown pyll node %s, but it has " \
#                   "position arguments which will now be processed" % expr.name
#             return self.read_container(expr)
#         elif len(expr.named_args) > 0 and len(expr.pos_args) > 0:
#             print "WARNING: encountered unknown pyll node %s, but it has " \
#                   "both named and positional arguments which will now be " \
#                   "processed" % expr.name
#             return self.read_container(expr)
#         else:
#             raise Exception("Node name %s not known of %s" % (expr.name, expr))
#
#     def read_literal(self, expr, label, condition=None):
#         # This seems to do no harm...
#         if type(expr._obj) == hyperopt.pyll.base.SymbolTableEntry:
#             pass
#         else:
#             if label is None:
#                 label = str(expr._obj)
#             return configuration_space_module.Constant(label, str(expr._obj),
#                                                        condition)
#
#     def read_float(self, expr, condition=None):
#         # 0 float
#         # 1   hyperopt_param
#         # 2     Literal{b}
#         # 3     uniform
#         # 4       Literal{5}
#         # 5       Literal{15}
#         assert len(expr.inputs()) == 1, expr.inputs()
#         name = expr.inputs()[0].name
#         if name == "literal":
#             raise NotImplementedError()
#         elif name in ("hyperopt_param", "switch"):
#             return self.read_object(expr.inputs()[0], None,
#                                     condition=condition)
#         else:
#             print expr
#             raise NotImplementedError()
#
#     def read_int(self, expr, condition=None):
#         # 0 float
#         # 1   hyperopt_param
#         # 2     Literal{b}
#         # 3     uniform
#         # 4       Literal{5}
#         # 5       Literal{15}
#         assert len(expr.inputs()) == 1, expr.inputs()
#         name = expr.inputs()[0].name
#         if name == "literal":
#             raise NotImplementedError()
#         elif name == "float":
#             param = self.read_object(expr.inputs()[0], None,
#                                     condition=condition)
#             if not isinstance(param, configuration_space_module
#                     .IntegerHyperparameter):
#                 # Rounding ints is done this way in pyll
#                 if isinstance(param, configuration_space_module
#                         .UniformFloatHyperparameter):
#                     param.lower = int(param.lower)
#                     param.upper = int(param.upper)
#                 param = param.to_integer()
#             return param
#         else:
#             print expr
#             raise NotImplementedError()
#
#     def read_hyperopt_param(self, expr, condition=None):
#         # 1   hyperopt_param
#         # 2     Literal{b}
#         # 3     uniform
#         # 4       Literal{5}
#         # 5       Literal{15}
#         assert len(expr.inputs()) == 2, expr.inputs()
#         label = expr.inputs()[0]._obj
#         return self.read_object(expr.inputs()[1], label, condition=condition)
#
#     def read_container(self, expr, condition=None):
#         choices = {}
#         descendants = deque()
#         # descendants.extend(expr.pos_args)
#         descendants.extend(expr.inputs())
#         while len(descendants) > 0:
#             descendant = descendants.popleft()
#             child = self.read_object(descendant, None, condition=condition) #  label not known
#             if child is None:
#                 # this seems to do no harm and can happen e.g. here:
#                 # 0 pos_args
#                 # 1   Literal{<hyperopt.pyll.base.SymbolTableEntry object at 0x7f6afce5b790>}
#                 # 2   switch
#                 continue
#
#             if isinstance(child, dict):
#                 descendants.extend(child.values())
#                 continue
#
#             try:
#                 choices[child.name] = child
#             except AttributeError as e:
#                 print
#                 print expr, child
#                 raise e
#
#         return choices
#
#     def read_dict(self, expr, condition=None):
#         # Only one constant per dict is allowed!
#         values = {}
#         descendants = deque()
#         descendants.extend(expr.named_args)
#         while len(descendants) > 0:
#             descendant = descendants.popleft()
#             child = self.read_object(descendant[1], descendant[0],
#                                      condition=condition)
#             if isinstance(child, dict):
#                 descendants.extend(child.items())
#                 continue
#             try:
#                 values[child.name] = child
#             except AttributeError as e:
#                 for hp_key in self.hyperparameters:
#                     print self.hyperparameters[hp_key]
#                 print "Child", child
#                 print "Expr", expr
#                 raise e
#
#             # The dict must not add values to self.hyperparameters as a
#             # parent switch must be able to control whether this is a real
#             # hyperparameter or just a constant
#
#         # if num_constants != 1:
#         #     raise ValueError("More than one constant value in dict %s" % expr)
#         return values
#
#     def read_switch(self, expr, condition=None):
#         # These checks are really restrictive, they can be relaxed if necessary
#         # e.g. scope.switch(hp.uniform(), []) would be raise an Error but is
#         # perfectly legal
#         # The switch throws away all constants, as the name/value relation of
#         #  them is not unique. Only constants inside a switch, where the key
#         # is the same as the switch name are kept and used as the name of the
#         #  choice
#         assert expr.inputs()[0].name in ("hyperopt_param", "int"), \
#             expr.inputs()[0]
#         randint_node = expr.pos_args[0]
#         if len(randint_node.inputs()) == 1:
#             raise NotImplementedError("You probably try to use a switch "
#                                       "outside a hp.choice, this is not "
#                                       "implemented yet.")
#         if randint_node.inputs()[1].name == "categorical":
#             raise NotImplementedError("This expression seems to be a pchoice:"
#                                       "%s" % expr)
#         assert randint_node.inputs()[1].name == "randint", randint_node.inputs()[1]
#         assert randint_node.inputs()[1].inputs()[0].name == "literal", \
#             randint_node.inputs()[1].inputs()[0]
#         label = randint_node.inputs()[0]._obj
#
#         choices = []
#
#         for i, descendant in enumerate(expr.inputs()[1:]):
#             child = self.read_object(descendant, None) #  label not known
#             choice_name = [str(i)]
#             if type(child) == dict:
#                 # Unfortunately, we have to iterate twice...
#                 for key in child:
#                     if isinstance(child[key], configuration_space_module
#                             .Constant):
#                         if child[key].name == label:
#                             choice_name.append(child[key].value)
#                         else:
#                             print "WARNING: Constants (%s) without a " \
#                                   "hyperparameter will be ignored." % child[key]
#                             self.constants.append(child[key])
#                 if len(choice_name) > 2:
#                     raise ValueError("There must be only one constant per "
#                                      "dictionary for %s and names %s" %
#                                      (expr, choice_name[1:]))
#                 for key in child:
#                     child[key].append_condition(["%s == %s" %
#                                                   (label, choice_name[-1])])
#                     if isinstance(child[key], configuration_space_module
#                             .Constant):
#                         pass
#                     elif self.hyperparameters.has_key(child[key].name):
#                         if child[key] == self.hyperparameters[child[key].name]:
#                             self.hyperparameters[child[key].name]\
#                                 .append_conditions(child[key].conditions)
#                         else:
#                             print "A different hyperparameter with the same " \
#                                   "name has been encountered " \
#                                   "before:\n%s\nnew%s" % \
#                                   (self.hyperparameters[child[key].name], child[key])
#                             raise ValueError()
#                     else:
#                         self.hyperparameters[child[key].name] = child[key]
#
#
#             elif isinstance(child, configuration_space_module.Constant):
#                 child.condition = [["%s == %s" % (label, i)]]
#                 choice_name.append(child.name)
#             elif isinstance(child, configuration_space_module
#                     .NumericalHyperparameter):
#                 child.condition = [["%s == %s" % (label, i)]]
#                 choice_name.append(child.name)
#                 if self.hyperparameters.has_key(child.name):
#                     if child == self.hyperparameters[child.name]:
#                         self.hyperparameters[child.name]\
#                             .append_conditions(child.conditions)
#                     else:
#                         print "A different hyperparameter with the same " \
#                               "name has been encountered " \
#                               "before:\n%s\nnew%s" % \
#                               (self.hyperparameters[child.name], child)
#                         raise ValueError()
#                 else:
#                     self.hyperparameters[child.name] = child
#
#             else:
#                 raise ValueError("This pyll reader only supports dictionaries or"
#                                  " constants "
#                                  "as choices in hp.choice, you provided a %s "
#                                  "for %s in %s" % (type(child), descendant,
#                                                    expr))
#             choices.append(choice_name[-1])
#
#         return configuration_space_module.CategoricalHyperparameter(label, choices)
#
#     def read_getitem(self, expr, label, condition=None):
#         # channels=pyll_getattr(Xcm, 'shape')[1]
#         assert len(expr.pos_args) == 2
#         assert len(expr.named_args) == 0
#         return self.read_container(expr, condition)
#
#     def read_getattr(self, expr, condition=None):
#         assert len(expr.pos_args) == 2
#         assert len(expr.named_args) == 0
#         return self.read_container(expr, condition)
#
#     def read_mathematical(self, expr, condition=None):
#         assert len(expr.pos_args) == 2
#         assert len(expr.named_args) == 0
#         return self.read_container(expr, condition)
#
#     def read_uniform(self, expr, label, condition=None):
#         assert len(expr.inputs()) == 2
#         assert all([input.name == "literal" for input in expr.inputs()]), \
#             expr.inputs()
#         lower = expr.inputs()[0]._obj
#         upper = expr.inputs()[1]._obj
#         return configuration_space_module.UniformFloatHyperparameter(
#             label, lower, upper)
#
#     def read_loguniform(self, expr, label, condition=None):
#         assert len(expr.inputs()) == 2
#         assert all([input.name == "literal" for input in expr.inputs()]), \
#             expr.inputs()
#         lower = np.exp(expr.inputs()[0]._obj)
#         upper = np.exp(expr.inputs()[1]._obj)
#         return configuration_space_module.UniformFloatHyperparameter(
#             label, lower, upper, base=np.e)
#
#     def read_quniform(self, expr, label, condition=None):
#         # Or
#         # 0 quniform
#         # 1   Literal{2.50001}
#         # 2   Literal{8.5}
#         # 3   Literal{1}
#         assert len(expr.inputs()) == 3
#         assert all([input.name == "literal" for input in expr.inputs()]), \
#             expr.inputs()
#         lower = expr.inputs()[0]._obj
#         upper = expr.inputs()[1]._obj
#         q = expr.inputs()[2]._obj
#         if abs(q - 1.0) < 0.0001:
#             # Special case (see top of function)
#             # + avoid numerical issues when rounding param.upper
#             if abs(lower + 0.49999 - np.round(lower, 0)) < 0.0001 and\
#                 abs(upper - 0.5 - np.round(upper - 0.0001, 0)) < 0.0001 and \
#                 abs(q - 1.0) < 0.0001:
#                 lower = np.round(lower, 0)
#                 upper = np.round(upper - 0.0001, 0)
#             else:
#                 print "WARNING: %s has a q-value which suggests the parameter is an Integer," \
#                       "but the ranges could not be properly inferred."
#             return configuration_space_module.UniformIntegerHyperparameter(
#                 label, lower, upper)
#         else:
#             return configuration_space_module.UniformFloatHyperparameter(
#                 label, lower, upper, q=q)
#
#     def read_qloguniform(self, expr, label, condition=None):
#         assert len(expr.inputs()) == 3
#         assert all([input.name == "literal" for input in expr.inputs()]), \
#             expr.inputs()
#         lower = np.exp(expr.inputs()[0]._obj)
#         upper = np.exp(expr.inputs()[1]._obj)
#         q = expr.inputs()[2]._obj
#         if abs(q - 1.0) < 0.0001:
#             # Special case (see top of function)
#             # + avoid numerical issues when rounding param.upper
#             if abs(lower + 0.49999 - np.round(lower, 0)) < 0.0001 and\
#                 abs(upper - 0.5 - np.round(upper - 0.0001, 0)) < 0.0001 and \
#                 abs(q - 1.0) < 0.0001:
#                 lower = np.round(lower, 0)
#                 upper = np.round(upper, 0)
#             else:
#                 print "WARNING: %s has a q-value which suggests the parameter is an Integer," \
#                       "but the ranges could not be properly inferred."
#             return configuration_space_module.UniformIntegerHyperparameter(
#                 label, lower, upper)
#         else:
#             return configuration_space_module.UniformFloatHyperparameter(
#                 label, lower, upper, q=q, base=np.e)
#
#     def read_normal(self, expr, label, condition=None):
#         assert len(expr.inputs()) == 2
#         assert all([input.name == "literal" for input in expr.inputs()]), \
#             expr.inputs()
#         mu = expr.inputs()[0]._obj
#         sigma = expr.inputs()[1]._obj
#         return configuration_space_module.NormalFloatHyperparameter(
#             label, mu, sigma)
#
#     def read_lognormal(self, expr, label, condition=None):
#         # Returns a value drawn according to exp(normal(mu, sigma))
#         assert len(expr.inputs()) == 2
#         assert all([input.name == "literal" for input in expr.inputs()]), \
#             expr.inputs()
#         mu = expr.inputs()[0]._obj
#         sigma = expr.inputs()[1]._obj
#         return configuration_space_module.NormalFloatHyperparameter(
#             label, mu, sigma, base=np.e)
#
#     def read_qnormal(self, expr, label, condition=None):
#         assert len(expr.inputs()) == 3
#         assert all([input.name == "literal" for input in expr.inputs()]), \
#             expr.inputs()
#         mu = expr.inputs()[0]._obj
#         sigma = expr.inputs()[1]._obj
#         q = expr.inputs()[2]._obj
#         if abs(q - 1.0) < 0.0001:
#             return configuration_space_module.NormalIntegerHyperparameter(
#             label, mu, sigma)
#         else:
#             return configuration_space_module.NormalFloatHyperparameter(
#                 label, mu, sigma, q=q)
#
#     def read_qlognormal(self, expr, label, condition=None):
#         # Returns a value like round(exp(normal(mu, sigma)) / q) * q
#         assert len(expr.inputs()) == 3
#         assert all([input.name == "literal" for input in expr.inputs()]), \
#             expr.inputs()
#         mu = expr.inputs()[0]._obj
#         sigma = expr.inputs()[1]._obj
#         q = expr.inputs()[2]._obj
#         if abs(q - 1.0) < 0.0001:
#             return configuration_space_module.NormalIntegerHyperparameter(
#                 label, mu, sigma, base=np.e)
#         else:
#             return configuration_space_module.NormalFloatHyperparameter(
#                 label, mu, sigma, q=q, base=np.e)
#
#
# ################################################################################
# # Write functionality
# def write(configuration_space):
#     pyll_writer = PyllWriter()
#     return pyll_writer.write(configuration_space)
#
#
# class PyllWriter(object):
#     def __init__(self):
#         self.hyperparameters = {}
#
#     def add_hyperparameter(self, hyperparameter):
#         index = "param_%d" % len(self.hyperparameters)
#         self.hyperparameters[hyperparameter.name] = len(self.hyperparameters)
#         return index
#
#     def reset_hyperparameter_countr(self):
#         self.hyperparameters = {}
#
#     def write(self, configuration_space):
#         configuration_space = copy.deepcopy(configuration_space)
#         for key in configuration_space:
#             print configuration_space[key]
#
#         # Name conversions must happen here because the hyperparameter are
#         # later on referenced by this name, the values are converted later!!!
#         for hyperparameter in configuration_space.values():
#             if isinstance(hyperparameter, configuration_space_module
#                     .NumericalHyperparameter) and hyperparameter.base is not \
#                     None:
#                 if abs(hyperparameter.base - np.e) < 0.0000001:
#                     continue
#                 else:
#                     hyperparameter.name = self.convert_name(hyperparameter)
#
#         configuration_dag = configuration_space_module\
#             .create_dag_from_hyperparameters(configuration_space)
#
#         configuration_string = StringIO.StringIO()
#         configuration_string.write('from hyperopt import hp\n')
#         configuration_string.write('import hyperopt.pyll as pyll')
#         configuration_string.write('\n\n')
#
#         strings, hyperparameter_names = self.traverse_dag_depth_first(
#             configuration_dag)
#         for string in strings:
#             configuration_string.write(string)
#             configuration_string.write("\n")
#
#         configuration_string.write('\nspace = {')
#         configuration_string.write(', '
#             .join(['"%s": param_%s' % (name, self.hyperparameters[name])
#                    for name in hyperparameter_names]))
#         configuration_string.write('}\n')
#         configuration_string.seek(0)
#         return configuration_string.getvalue()
#
#     def traverse_dag_depth_first(self, dag):
#         hyperparameter_names = []
#         strings = []
#
#         for name in configuration_space_module.get_dag(dag):
#             hyperparameter = dag.node[name]['hyperparameter']
#             if hyperparameter.conditions == [[]]:
#                 hyperparameter_names.append(name)
#             children = dag[name]
#             _, string = self.write_hyperparameter(hyperparameter, children)
#             strings.append(string)
#
#         return strings, hyperparameter_names
#
#     def write_hyperparameter(self, hyperparameter, children):
#         # Which string generator to call
#         if isinstance(hyperparameter, configuration_space_module.NumericalHyperparameter):
#             generator_name = 'write_'
#             if isinstance(hyperparameter, configuration_space_module.UniformFloatHyperparameter) \
#                          and hyperparameter.q is not None and not (
#                         hyperparameter.q >= hyperparameter.lower or
#                         abs(hyperparameter.q / 2. - hyperparameter.lower) < 0.000001):
#                 raise ValueError()
#
#             if hyperparameter.base is None and hyperparameter.q is None:
#                 pass
#             elif hyperparameter.base is None and hyperparameter.q is not None:
#                 generator_name += 'q'
#             elif abs(hyperparameter.base - math.e) < 0.000001:
#                 if hyperparameter.q is not None:
#                     generator_name += 'q'
#                 generator_name += 'log'
#             else:
#                 pass
#
#             if children is not None and len(children) > 0:
#                 raise NotImplementedError()
#
#             if type(hyperparameter) in (configuration_space_module
#                                                 .NormalFloatHyperparameter,
#                                         configuration_space_module
#                                                 .NormalIntegerHyperparameter):
#                 generator_name += 'normal'
#             else:
#                 generator_name += 'uniform'
#
#             if hyperparameter.base is None or abs(hyperparameter.base - \
#                     math.e) < 0.000001:
#                 if isinstance(hyperparameter, \
#                     configuration_space_module.IntegerHyperparameter):
#                     generator_name += "_int"
#
#             name, string = getattr(self, generator_name)(hyperparameter)
#
#         elif isinstance(hyperparameter, configuration_space_module.CategoricalHyperparameter):
#                 name, string = self.write_choice(hyperparameter, children)
#
#         else:
#             raise NotImplementedError()
#
#         return name, string
#
#     def write_choice(self, parameter, children):
#         name = parameter.name
#         choices = dict()
#         for choice in parameter.choices:
#             choices[choice] = dict()
#
#         for key in children:
#             child = children[key]
#             operator = child['condition'][1]
#             if operator == "==":
#                 value = child['condition'][2]
#                 choices[value][key] = child
#             elif operator == "in":
#                 values = child['condition'][2].replace("{", "").replace("}", "")
#                 values = values.split(",")
#                 for value in values:
#                     choices[value][key] = child
#             else:
#                 raise NotImplementedError("You used an operator to specifiy "
#                                           "conditions which is not yet "
#                                           "implemented: %s" % operator)
#
#         index = "param_%d" % len(self.hyperparameters)
#         self.hyperparameters[name] = len(self.hyperparameters)
#         return_string = '%s = hp.choice("%s", [\n' % (index, name)
#         for choice in sorted(choices):
#             return_string += '    {'
#             return_string += '"%s": "%s", ' % (name, choice)
#             for key in sorted(choices[choice]):
#                 return_string += '"%s": param_%s, ' % \
#                                  (key, self.hyperparameters[key])
#             return_string += '},\n'
#         return_string += '    ])'
#
#         return name, return_string
#
#     def write_uniform(self, parameter):
#         name = parameter.name
#         # Special treatment of hyperparameters which potentially have a LOG
#         # and a Q in their name...
#         if parameter.q is not None or parameter.base is not None or \
#                 isinstance(parameter, configuration_space_module.IntegerHyperparameter):
#             if parameter.q is not None and abs(parameter.q - 1.0) < 0.00000001 or \
#                     isinstance(parameter, configuration_space_module.IntegerHyperparameter):
#                 parameter.lower = float(parameter.lower) - 0.49999
#                 parameter.upper = float(parameter.upper) + 0.5
#             elif parameter.q is not None:
#                 parameter.lower = float(parameter.lower) - (parameter.q / 2.0 - 0.0001)
#                 parameter.upper = float(parameter.upper) + (parameter.q / 2.0)
#
#         lower, upper = self.get_bounds_as_exponent(parameter)
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = hp.uniform("%s", %s, %s)' % \
#             (index, name, lower, upper)
#
#     def write_uniform_int(self, parameter):
#         name = parameter.name
#         # As like for all other uniform_int parameters, subtracting a number
#         # is fine if the parameter is optimized on log scale as
#         # a) it either is zero and thus illegal before subtraction
#         # b) it is > 1 and thus still legal after subtraction
#         parameter.lower = float(parameter.lower) - 0.49999
#         parameter.upper = float(parameter.upper) + 0.5
#         lower, upper = self.get_bounds_as_exponent(parameter)
#         q = 1.0
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = pyll.scope.int(hp.quniform("%s", %s, %s, %s))' \
#             % (index, name, lower, upper, q)
#
#     def write_quniform(self, parameter):
#         name = parameter.name
#         parameter.lower = float(parameter.lower) - (parameter.q / 2.0 - 0.0001)
#         parameter.upper = float(parameter.upper) + (parameter.q / 2.0)
#         lower, upper = self.get_bounds_as_exponent(parameter)
#         q = float(parameter.q)
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = hp.quniform("%s", %s, %s, %s)' % \
#             (index, name, lower, upper, q)
#
#     def write_quniform_int(self, parameter):
#         name = parameter.name
#         parameter.lower = float(parameter.lower) - 0.49999
#         parameter.upper = float(parameter.upper) + 0.5
#         lower, upper = self.get_bounds_as_exponent(parameter)
#         q = float(parameter.q) if parameter.q is not None else 1.0
#         assert abs(q - int(q)) < 0.000001
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = pyll.scope.int(hp.quniform("%s", %s, %s, %s))' \
#             % (index, name, lower, upper, q)
#
#     def write_loguniform(self, parameter):
#         name = parameter.name
#         lower, upper = self.get_bounds_as_exponent(parameter)
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = hp.loguniform("%s", %s, %s)' % \
#             (index, name, lower, upper)
#
#     def write_loguniform_int(self, parameter):
#         name = parameter.name
#         parameter.lower = float(parameter.lower) - 0.49999
#         parameter.upper = float(parameter.upper) + 0.5
#         lower, upper = self.get_bounds_as_exponent(parameter)
#         q = 1.0
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = pyll.scope.int(hp.qloguniform("%s", %s, %s, %s))' % \
#             (index, name, lower, upper, q)
#
#     def write_qloguniform(self, parameter):
#         name = parameter.name
#         parameter.lower = float(parameter.lower) - (parameter.q / 2.0 - 0.0001)
#         parameter.upper = float(parameter.upper) + (parameter.q / 2.0)
#         lower, upper = self.get_bounds_as_exponent(parameter)
#         q = float(parameter.q)
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = hp.qloguniform("%s", %s, %s, %s)' % \
#             (index, name, lower, upper, q)
#
#     def write_qloguniform_int(self, parameter):
#         name = parameter.name
#         parameter.lower = float(parameter.lower) - 0.49999
#         parameter.upper = float(parameter.upper) + 0.5
#         lower, upper = self.get_bounds_as_exponent(parameter)
#         q = float(parameter.q)
#         assert abs(q - int(q)) < 0.000001
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = pyll.scope.int(hp.qloguniform("%s", %s, %s, %s))' % \
#             (index, name, lower, upper, q)
#
#     def write_normal(self, parameter):
#         name = parameter.name
#         mu = float(parameter.mu)
#         sigma = float(parameter.sigma)
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = hp.normal("%s", %s, %s)' % (index, name, mu, sigma)
#
#     def write_normal_int(self, parameter):
#         name = parameter.name
#         mu = float(parameter.mu)
#         sigma = float(parameter.sigma)
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = pyll.scope.int(hp.qnormal("%s", %s, %s, 1.0))' % \
#                      (index, name, mu, sigma)
#
#     def write_qnormal(self, parameter):
#         name = parameter.name
#         mu = float(parameter.mu)
#         sigma = float(parameter.sigma)
#         q = float(parameter.q)
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = hp.qnormal("%s", %s, %s, %s)' % (index, name, mu, sigma, q)
#
#     def write_qnormal_int(self, parameter):
#         name = parameter.name
#         mu = float(parameter.mu)
#         sigma = float(parameter.sigma)
#         q = float(parameter.q)
#         assert abs(q - int(q)) < 0.000001
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = pyll.scope.int(hp.qnormal("%s", %s, %s, %s))' % \
#                      (index, name, mu, sigma, q)
#
#     def write_lognormal(self, parameter):
#         name = parameter.name
#         mu = float(parameter.mu)
#         sigma = float(parameter.sigma)
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = hp.lognormal("%s", %s, %s)' % (index, name, mu, sigma)
#
#     def write_lognormal_int(self, parameter):
#         name = parameter.name
#         mu = float(parameter.mu)
#         sigma = float(parameter.sigma)
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = pyll.scope.int(hp.qlognormal("%s", %s, %s, 1.0))' \
#                      % (index, name, mu, sigma)
#
#     def write_qlognormal(self, parameter):
#         name = parameter.name
#         mu = float(parameter.mu)
#         sigma = float(parameter.sigma)
#         q = float(parameter.q)
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = hp.qlognormal("%s", %s, %s, %s)' % \
#                      (index, name, mu, sigma, q)
#
#     def write_qlognormal_int(self, parameter):
#         name = parameter.name
#         mu = float(parameter.mu)
#         sigma = float(parameter.sigma)
#         q = float(parameter.q)
#         assert abs(q - int(q)) < 0.000001
#         index = self.add_hyperparameter(parameter)
#         return name, '%s = pyll.scope.int(hp.qlognormal("%s", %s, %s, %s))' % \
#                      (index, name, mu, sigma, q)
#
#     def convert_name(self, parameter):
#         """Add the LOG_ instruction to the name."""
#         base = parameter.base
#         q = parameter.q
#         name = parameter.name
#         if base is not None:
#             if q is not None:
#                 name = "Q%f_%s" % (q, name)
#             elif isinstance(parameter, configuration_space_module
#                                        .IntegerHyperparameter):
#                 name = "Q1_%s" % name
#             if abs(base - np.e) < 0.000001:
#                 return "LOG_%s" % name
#             elif abs(base - 10.0) < 0.000001:
#                 return "LOG10_%s" % name
#             elif abs(base - 2.0) < 0.000001:
#                 return "LOG2_%s" % name
#             elif abs(base - int(base)) < 0.000001:
#                 return "LOG%d_%s" % (int(base), name)
#             else:
#                 raise ValueError("Base Value %s not allowed" % str(base))
#         return name
#
#     def get_bounds_as_exponent(self, parameter):
#         """Return the exponent of lower and upper bound of a parameter.
#
#         Return the exponent of lower and upper bound of a parameter. If the
#         base of the parameter is None, return the lower and upper bound."""
#         base = parameter.base
#         lower = parameter.lower
#         upper = parameter.upper
#
#         if base is not None:
#             if parameter.lower <= 0:
#                 raise ValueError("Lower value for a log scale parameter is not "
#                              "allowed to be <= 0 (%f)" % parameter.lower)
#
#             if abs(base - np.e) < 0.000001:
#                 return np.log(lower), np.log(upper)
#             elif abs(base - 10.0) < 0.000001:
#                 return np.log10(lower), np.log10(upper)
#             elif abs(base - 2.0) < 0.000001:
#                 return np.log2(lower), np.log2(upper)
#             elif abs(base - int(base)) < 0.000001:
#                 lower = np.log10(lower) / np.log10(base)
#                 upper = np.log10(upper) / np.log10(base)
#                 return lower, upper
#             else:
#                 raise ValueError("Base Value %s not allowed" % str(base))
#
#         return lower, upper