#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
from typing import Dict
from ConfigSpace import __version__
from ConfigSpace.configuration_space import ConfigurationSpace
from ConfigSpace.hyperparameters import (
Hyperparameter,
CategoricalHyperparameter,
UniformIntegerHyperparameter,
UniformFloatHyperparameter,
NormalIntegerHyperparameter,
NormalFloatHyperparameter,
OrdinalHyperparameter,
Constant,
UnParametrizedHyperparameter,
BetaFloatHyperparameter,
BetaIntegerHyperparameter
)
from ConfigSpace.conditions import (
AbstractCondition,
EqualsCondition,
NotEqualsCondition,
InCondition,
AndConjunction,
OrConjunction,
GreaterThanCondition,
LessThanCondition,
)
from ConfigSpace.forbidden import (
ForbiddenEqualsClause,
ForbiddenAndConjunction,
ForbiddenInClause,
AbstractForbiddenComponent,
ForbiddenRelation,
ForbiddenLessThanRelation,
ForbiddenEqualsRelation,
ForbiddenGreaterThanRelation,
)
JSON_FORMAT_VERSION = 0.4
################################################################################
# Builder for hyperparameters
def _build_constant(param: Constant) -> Dict:
return {
'name': param.name,
'type': 'constant',
'value': param.value,
}
def _build_unparametrized_hyperparameter(param: UnParametrizedHyperparameter) -> Dict:
return {
'name': param.name,
'type': 'unparametrized',
'value': param.value,
}
def _build_uniform_float(param: UniformFloatHyperparameter) -> Dict:
return {
'name': param.name,
'type': 'uniform_float',
'log': param.log,
'lower': param.lower,
'upper': param.upper,
'default': param.default_value
}
def _build_normal_float(param: NormalFloatHyperparameter) -> Dict:
return {
'name': param.name,
'type': 'normal_float',
'log': param.log,
'mu': param.mu,
'sigma': param.sigma,
'default': param.default_value
}
def _build_beta_float(param: BetaFloatHyperparameter) -> Dict:
return {
'name': param.name,
'type': 'beta_float',
'log': param.log,
'alpha': param.alpha,
'beta': param.beta,
'default': param.default_value
}
def _build_uniform_int(param: UniformIntegerHyperparameter) -> Dict:
return {
'name': param.name,
'type': 'uniform_int',
'log': param.log,
'lower': param.lower,
'upper': param.upper,
'default': param.default_value
}
def _build_normal_int(param: NormalIntegerHyperparameter) -> Dict:
return {
'name': param.name,
'type': 'normal_int',
'log': param.log,
'mu': param.mu,
'sigma': param.sigma,
'default': param.default_value
}
def _build_beta_int(param: BetaIntegerHyperparameter) -> Dict:
return {
'name': param.name,
'type': 'beta_int',
'log': param.log,
'alpha': param.alpha,
'beta': param.beta,
'default': param.default_value
}
def _build_categorical(param: CategoricalHyperparameter) -> Dict:
return {
'name': param.name,
'type': 'categorical',
'choices': param.choices,
'default': param.default_value,
'weights': param.weights,
}
def _build_ordinal(param: OrdinalHyperparameter) -> Dict:
return {
'name': param.name,
'type': 'ordinal',
'sequence': param.sequence,
'default': param.default_value
}
################################################################################
# Builder for Conditions
def _build_condition(condition: AbstractCondition) -> Dict:
if isinstance(condition, AndConjunction):
return _build_and_conjunction(condition)
elif isinstance(condition, OrConjunction):
return _build_or_conjunction(condition)
elif isinstance(condition, InCondition):
return _build_in_condition(condition)
elif isinstance(condition, EqualsCondition):
return _build_equals_condition(condition)
elif isinstance(condition, NotEqualsCondition):
return _build_not_equals_condition(condition)
elif isinstance(condition, GreaterThanCondition):
return _build_greater_than_condition(condition)
elif isinstance(condition, LessThanCondition):
return _build_less_than_condition(condition)
else:
raise TypeError(condition)
def _build_and_conjunction(conjunction: AndConjunction) -> Dict:
child = conjunction.get_descendant_literal_conditions()[0].child.name
cond_list = list()
for component in conjunction.components:
cond_list.append(_build_condition(component))
return {
'child': child,
'type': 'AND',
'conditions': cond_list,
}
def _build_or_conjunction(conjunction: OrConjunction) -> Dict:
child = conjunction.get_descendant_literal_conditions()[0].child.name
cond_list = list()
for component in conjunction.components:
cond_list.append(_build_condition(component))
return {
'child': child,
'type': 'OR',
'conditions': cond_list,
}
def _build_in_condition(condition: InCondition) -> Dict:
child = condition.child.name
parent = condition.parent.name
values = list(condition.values)
return {
'child': child,
'parent': parent,
'type': 'IN',
'values': values,
}
def _build_equals_condition(condition: EqualsCondition) -> Dict:
child = condition.child.name
parent = condition.parent.name
value = condition.value
return {
'child': child,
'parent': parent,
'type': 'EQ',
'value': value,
}
def _build_not_equals_condition(condition: NotEqualsCondition) -> Dict:
child = condition.child.name
parent = condition.parent.name
value = condition.value
return {
'child': child,
'parent': parent,
'type': 'NEQ',
'value': value,
}
def _build_greater_than_condition(condition: GreaterThanCondition) -> Dict:
child = condition.child.name
parent = condition.parent.name
value = condition.value
return {
'child': child,
'parent': parent,
'type': 'GT',
'value': value,
}
def _build_less_than_condition(condition: LessThanCondition) -> Dict:
child = condition.child.name
parent = condition.parent.name
value = condition.value
return {
'child': child,
'parent': parent,
'type': 'LT',
'value': value,
}
################################################################################
# Builder for forbidden
def _build_forbidden(clause) -> Dict:
if isinstance(clause, ForbiddenEqualsClause):
return _build_forbidden_equals_clause(clause)
elif isinstance(clause, ForbiddenInClause):
return _build_forbidden_in_clause(clause)
elif isinstance(clause, ForbiddenAndConjunction):
return _build_forbidden_and_conjunction(clause)
elif isinstance(clause, ForbiddenRelation):
return _build_forbidden_relation(clause)
else:
raise TypeError(clause)
def _build_forbidden_equals_clause(clause: ForbiddenEqualsClause) -> Dict:
return {
'name': clause.hyperparameter.name,
'type': 'EQUALS',
'value': clause.value,
}
def _build_forbidden_in_clause(clause: ForbiddenInClause) -> Dict:
return {
'name': clause.hyperparameter.name,
'type': 'IN',
# The values are a set, but a set cannot be serialized to json
'values': list(clause.values),
}
def _build_forbidden_and_conjunction(clause: ForbiddenAndConjunction) -> Dict:
return {
'name': clause.get_descendant_literal_clauses()[0].hyperparameter.name,
'type': 'AND',
'clauses': [
_build_forbidden(component) for component in clause.components
],
}
def _build_forbidden_relation(clause: ForbiddenRelation) -> Dict:
if isinstance(clause, ForbiddenLessThanRelation):
lambda_ = 'LESS'
elif isinstance(clause, ForbiddenEqualsRelation):
lambda_ = 'EQUALS'
elif isinstance(clause, ForbiddenGreaterThanRelation):
lambda_ = 'GREATER'
else:
raise ValueError("Unknown relation '%s'" % type(clause))
return {
'left': clause.left.name,
'right': clause.right.name,
'type': 'RELATION',
'lambda': lambda_
}
################################################################################
[docs]def write(configuration_space, indent=2):
"""
Create a string representation of a
:class:`~ConfigSpace.configuration_space.ConfigurationSpace` in json format.
This string can be written to file.
Example:
..code:: python
from ConfigSpace import ConfigurationSpace
import ConfigSpace.hyperparameters as CSH
from ConfigSpace.read_and_write import json
cs = ConfigurationSpace()
cs.add_hyperparameter(CSH.CategoricalHyperparameter('a', choices=[1, 2, 3]))
with open('configspace.json', 'w') as f:
f.write(json.write(cs))
Parameters
----------
configuration_space : :class:`~ConfigSpace.configuration_space.ConfigurationSpace`
a configuration space, which should be written to file.
indent : int
number of whitespaces to use as indent
Returns
-------
str
String representation of the configuration space,
which will be written to file
"""
if not isinstance(configuration_space, ConfigurationSpace):
raise TypeError("pcs_parser.write expects an instance of %s, "
"you provided '%s'" % (ConfigurationSpace,
type(configuration_space)))
hyperparameters = []
conditions = []
forbiddens = []
for hyperparameter in configuration_space.get_hyperparameters():
if isinstance(hyperparameter, Constant):
hyperparameters.append(_build_constant(hyperparameter))
elif isinstance(hyperparameter, UnParametrizedHyperparameter):
hyperparameters.append(
_build_unparametrized_hyperparameter(hyperparameter)
)
elif isinstance(hyperparameter, UniformFloatHyperparameter):
hyperparameters.append(_build_uniform_float(hyperparameter))
elif isinstance(hyperparameter, NormalFloatHyperparameter):
hyperparameters.append(_build_normal_float(hyperparameter))
elif isinstance(hyperparameter, BetaFloatHyperparameter):
hyperparameters.append(_build_beta_float(hyperparameter))
elif isinstance(hyperparameter, UniformIntegerHyperparameter):
hyperparameters.append(_build_uniform_int(hyperparameter))
elif isinstance(hyperparameter, NormalIntegerHyperparameter):
hyperparameters.append(_build_normal_int(hyperparameter))
elif isinstance(hyperparameter, BetaIntegerHyperparameter):
hyperparameters.append(_build_beta_int(hyperparameter))
elif isinstance(hyperparameter, CategoricalHyperparameter):
hyperparameters.append(_build_categorical(hyperparameter))
elif isinstance(hyperparameter, OrdinalHyperparameter):
hyperparameters.append(_build_ordinal(hyperparameter))
else:
raise TypeError(
"Unknown type: %s (%s)" % (
type(hyperparameter), hyperparameter,
)
)
for condition in configuration_space.get_conditions():
conditions.append(_build_condition(condition))
for forbidden_clause in configuration_space.get_forbiddens():
forbiddens.append(_build_forbidden(forbidden_clause))
rval = {}
if configuration_space.name is not None:
rval['name'] = configuration_space.name
rval['hyperparameters'] = hyperparameters
rval['conditions'] = conditions
rval['forbiddens'] = forbiddens
rval['python_module_version'] = __version__
rval['json_format_version'] = JSON_FORMAT_VERSION
return json.dumps(rval, indent=indent)
################################################################################
[docs]def read(jason_string):
"""
Create a configuration space definition from a json string.
Example
-------
.. testsetup:: json_test
from ConfigSpace import ConfigurationSpace
import ConfigSpace.hyperparameters as CSH
from ConfigSpace.read_and_write import json
cs = ConfigurationSpace()
cs.add_hyperparameter(CSH.CategoricalHyperparameter('a', choices=[1, 2, 3]))
with open('configspace.json', 'w') as f:
f.write(json.write(cs))
.. doctest:: json_test
>>> from ConfigSpace.read_and_write import json
>>> with open('configspace.json', 'r') as f:
... jason_string = f.read()
... config = json.read(jason_string)
Parameters
----------
jason_string : str
A json string representing a configuration space definition
Returns
-------
:class:`~ConfigSpace.configuration_space.ConfigurationSpace`
The deserialized ConfigurationSpace object
"""
jason = json.loads(jason_string)
if 'name' in jason:
configuration_space = ConfigurationSpace(name=jason['name'])
else:
configuration_space = ConfigurationSpace()
for hyperparameter in jason['hyperparameters']:
configuration_space.add_hyperparameter(_construct_hyperparameter(
hyperparameter,
))
for condition in jason['conditions']:
configuration_space.add_condition(_construct_condition(
condition, configuration_space,
))
for forbidden in jason['forbiddens']:
configuration_space.add_forbidden_clause(_construct_forbidden(
forbidden, configuration_space,
))
return configuration_space
def _construct_hyperparameter(hyperparameter: Dict) -> Hyperparameter:
hp_type = hyperparameter['type']
name = hyperparameter['name']
if hp_type == 'constant':
return Constant(
name=name,
value=hyperparameter['value'],
)
elif hp_type == 'unparametrized':
return UnParametrizedHyperparameter(
name=name,
value=hyperparameter['value'],
)
elif hp_type == 'uniform_float':
return UniformFloatHyperparameter(
name=name,
log=hyperparameter['log'],
lower=hyperparameter['lower'],
upper=hyperparameter['upper'],
default_value=hyperparameter['default'],
)
elif hp_type == 'normal_float':
return NormalFloatHyperparameter(
name=name,
log=hyperparameter['log'],
mu=hyperparameter['mu'],
sigma=hyperparameter['sigma'],
default_value=hyperparameter['default'],
)
elif hp_type == 'uniform_int':
return UniformIntegerHyperparameter(
name=name,
log=hyperparameter['log'],
lower=hyperparameter['lower'],
upper=hyperparameter['upper'],
default_value=hyperparameter['default'],
)
elif hp_type == 'normal_int':
return NormalIntegerHyperparameter(
name=name,
log=hyperparameter['log'],
lower=hyperparameter['lower'],
upper=hyperparameter['upper'],
default_value=hyperparameter['default'],
)
elif hp_type == 'categorical':
return CategoricalHyperparameter(
name=name,
choices=hyperparameter['choices'],
default_value=hyperparameter['default'],
weights=hyperparameter.get('weights'),
)
elif hp_type == 'ordinal':
return OrdinalHyperparameter(
name=name,
sequence=hyperparameter['sequence'],
default_value=hyperparameter['default'],
)
else:
raise ValueError(hp_type)
def _construct_condition(
condition: Dict,
cs: ConfigurationSpace,
) -> AbstractCondition:
condition_type = condition['type']
if condition_type == 'AND':
return _construct_and_condition(condition, cs)
elif condition_type == 'OR':
return _construct_or_condition(condition, cs)
elif condition_type == 'IN':
return _construct_in_condition(condition, cs)
elif condition_type == 'EQ':
return _construct_eq_condition(condition, cs)
elif condition_type == 'NEQ':
return _construct_neq_condition(condition, cs)
elif condition_type == 'GT':
return _construct_gt_condition(condition, cs)
elif condition_type == 'LT':
return _construct_lt_condition(condition, cs)
else:
raise ValueError(condition_type)
def _construct_and_condition(
condition: Dict,
cs: ConfigurationSpace,
) -> AndConjunction:
conditions = [
_construct_condition(cond, cs) for cond in condition['conditions']
]
return AndConjunction(*conditions)
def _construct_or_condition(
condition: Dict,
cs: ConfigurationSpace,
) -> OrConjunction:
conditions = [_construct_condition(cond, cs) for cond in condition['conditions']]
return OrConjunction(*conditions)
def _construct_in_condition(
condition: Dict,
cs: ConfigurationSpace,
) -> InCondition:
return InCondition(
child=cs.get_hyperparameter(condition['child']),
parent=cs.get_hyperparameter(condition['parent']),
values=condition['values'],
)
def _construct_eq_condition(
condition: Dict,
cs: ConfigurationSpace,
) -> EqualsCondition:
return EqualsCondition(
child=cs.get_hyperparameter(condition['child']),
parent=cs.get_hyperparameter(condition['parent']),
value=condition['value'],
)
def _construct_neq_condition(
condition: Dict,
cs: ConfigurationSpace,
) -> NotEqualsCondition:
return NotEqualsCondition(
child=cs.get_hyperparameter(condition['child']),
parent=cs.get_hyperparameter(condition['parent']),
value=condition['value'],
)
def _construct_gt_condition(
condition: Dict,
cs: ConfigurationSpace,
) -> GreaterThanCondition:
return GreaterThanCondition(
child=cs.get_hyperparameter(condition['child']),
parent=cs.get_hyperparameter(condition['parent']),
value=condition['value'],
)
def _construct_lt_condition(
condition: Dict,
cs: ConfigurationSpace,
) -> LessThanCondition:
return LessThanCondition(
child=cs.get_hyperparameter(condition['child']),
parent=cs.get_hyperparameter(condition['parent']),
value=condition['value'],
)
def _construct_forbidden(
clause: Dict,
cs: ConfigurationSpace,
) -> AbstractForbiddenComponent:
forbidden_type = clause['type']
if forbidden_type == 'EQUALS':
return _construct_forbidden_equals(clause, cs)
elif forbidden_type == 'IN':
return _construct_forbidden_in(clause, cs)
elif forbidden_type == 'AND':
return _construct_forbidden_and(clause, cs)
elif forbidden_type == 'RELATION':
return _construct_forbidden_equals(clause, cs)
else:
return ValueError(forbidden_type)
def _construct_forbidden_equals(
clause: Dict,
cs: ConfigurationSpace,
) -> ForbiddenEqualsClause:
return ForbiddenEqualsClause(
hyperparameter=cs.get_hyperparameter(clause['name']),
value=clause['value']
)
def _construct_forbidden_in(
clause: Dict,
cs: ConfigurationSpace,
) -> ForbiddenEqualsClause:
return ForbiddenInClause(
hyperparameter=cs.get_hyperparameter(clause['name']),
values=clause['values']
)
def _construct_forbidden_and(
clause: Dict,
cs: ConfigurationSpace,
) -> ForbiddenAndConjunction:
clauses = [_construct_forbidden(cl, cs) for cl in clause['clauses']]
return ForbiddenAndConjunction(*clauses)
def _construct_forbidden_relation(
clause: Dict,
cs: ConfigurationSpace,
) -> ForbiddenRelation:
left = cs.get_hyperparameter(clause['left'])
right = cs.get_hyperparameter(clause['right'])
if clause['lambda'] == "LESS":
return ForbiddenLessThanRelation(left, right)
elif clause['lambda'] == "EQUALS":
return ForbiddenEqualsRelation(left, right)
elif clause['lambda'] == "GREATER":
return ForbiddenGreaterThanRelation(left, right)
else:
raise ValueError("Unknown relation '%s'" % clause['lambda'])