Skip to content

Commit

Permalink
add decreed continous variables (#546)
Browse files Browse the repository at this point in the history
* add decreed continous

* ruff

* update doc

* if cs installed

* add test

* designspace
  • Loading branch information
Paul-Saves authored Apr 15, 2024
1 parent b6bd190 commit 56ef7a9
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 34 deletions.
4 changes: 3 additions & 1 deletion doc/_src_docs/applications/Mixed_Hier_usage.rst

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion doc/_src_docs/applications/Mixed_Hier_usage.rstx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ hierarchical variables. We distinguish dimensional (or meta) variables which are
affect the dimension of the problem and decide if some other decreed variables are acting or non-acting.

Additionally, it is also possible to define value constraints that explicitly forbid two variables from having some
values simultaneously. This can be useful for modeling incompatibility relationships: for example, engines can't be
values simultaneously or for a continuous variable to be greater than another.
This can be useful for modeling incompatibility relationships: for example, engines can't be
installed on the back of the fuselage (vs on the wings) if a normal tail (vs T-tail) is selected. Note: this feature
is only available if ConfigSpace has been installed: `pip install smt[cs]`

Expand Down
2 changes: 1 addition & 1 deletion smt/surrogate_models/krg_based.py
Original file line number Diff line number Diff line change
Expand Up @@ -1668,7 +1668,7 @@ def _predict_variances(self, x: np.ndarray, is_acting=None) -> np.ndarray:
A = self.optimal_par["sigma2"]
B = 1.0 - (rt**2.0).sum(axis=0) + (u**2.0).sum(axis=0)
# machine precision: force to zero!
B[B<1e-12]=0
B[B < 1e-12] = 0
MSE = np.einsum("i,j -> ji", A, B)
# Mean Squared Error might be slightly negative depending on
# machine precision: force to zero!
Expand Down
78 changes: 47 additions & 31 deletions smt/utils/design_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
OrdinalHyperparameter,
UniformFloatHyperparameter,
UniformIntegerHyperparameter,
ForbiddenLessThanRelation,
)
from ConfigSpace.exceptions import ForbiddenValueError
from ConfigSpace.util import get_random_neighbor
Expand Down Expand Up @@ -897,49 +898,64 @@ def add_value_constraint(
# Get parameters
param1 = self._get_param(var1)
param2 = self._get_param(var2)
if not (isinstance(param1, UniformFloatHyperparameter)) and not (
isinstance(param2, UniformFloatHyperparameter)
):
# Add forbidden clauses
if isinstance(value1, Sequence):
clause1 = ForbiddenInClause(param1, value1)
else:
clause1 = ForbiddenEqualsClause(param1, value1)

# Add forbidden clauses
if isinstance(value1, Sequence):
clause1 = ForbiddenInClause(param1, value1)
else:
clause1 = ForbiddenEqualsClause(param1, value1)
if isinstance(value2, Sequence):
clause2 = ForbiddenInClause(param2, value2)
else:
clause2 = ForbiddenEqualsClause(param2, value2)

if isinstance(value2, Sequence):
clause2 = ForbiddenInClause(param2, value2)
constraint_clause = ForbiddenAndConjunction(clause1, clause2)
self._cs.add_forbidden_clause(constraint_clause)
else:
clause2 = ForbiddenEqualsClause(param2, value2)

constraint_clause = ForbiddenAndConjunction(clause1, clause2)
self._cs.add_forbidden_clause(constraint_clause)
if value1 in [">", "<"] and value2 in [">", "<"] and value1 != value2:
if value1 == "<":
constraint_clause = ForbiddenLessThanRelation(param1, param2)
self._cs.add_forbidden_clause(constraint_clause)
else:
constraint_clause = ForbiddenLessThanRelation(param2, param1)
self._cs.add_forbidden_clause(constraint_clause)
else :
raise ValueError("Bad definition of DesignSpace.")

## Fix to make constraints work correctly with either IntegerVariable or OrdinalVariable
## ConfigSpace is malfunctioning
# Get parameters
param1 = self._get_param2(var1)
param2 = self._get_param2(var2)
# Add forbidden clauses
if isinstance(value1, Sequence):
clause1 = ForbiddenInClause(param1, str(value1))
else:
clause1 = ForbiddenEqualsClause(param1, str(value1))
if not (isinstance(param1, UniformFloatHyperparameter)) and not (
isinstance(param2, UniformFloatHyperparameter)
):
if isinstance(value1, Sequence):
clause1 = ForbiddenInClause(param1, str(value1))
else:
clause1 = ForbiddenEqualsClause(param1, str(value1))

if isinstance(value2, Sequence):
try:
clause2 = ForbiddenInClause(
param2, list(np.atleast_1d(np.array(value2, dtype=str)))
)
except ValueError:
clause2 = ForbiddenInClause(
param2, list(np.atleast_1d(np.array(value2, dtype=float)))
)
else:
try:
clause2 = ForbiddenEqualsClause(param2, str(value2))
except ValueError:
clause2 = ForbiddenEqualsClause(param2, value2)
if isinstance(value2, Sequence):
try:
clause2 = ForbiddenInClause(
param2, list(np.atleast_1d(np.array(value2, dtype=str)))
)
except ValueError:
clause2 = ForbiddenInClause(
param2, list(np.atleast_1d(np.array(value2, dtype=float)))
)
else:
try:
clause2 = ForbiddenEqualsClause(param2, str(value2))
except ValueError:
clause2 = ForbiddenEqualsClause(param2, value2)

constraint_clause = ForbiddenAndConjunction(clause1, clause2)
self._cs_cate.add_forbidden_clause(constraint_clause)
constraint_clause = ForbiddenAndConjunction(clause1, clause2)
self._cs_cate.add_forbidden_clause(constraint_clause)

def _get_param(self, idx):
try:
Expand Down
46 changes: 46 additions & 0 deletions smt/utils/test/test_design_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,52 @@ def test_design_space_hierarchical_config_space(self):
assert len(seen_x) == 14
assert len(seen_is_acting) == 2

@unittest.skipIf(
not HAS_CONFIG_SPACE, "Hierarchy ConfigSpace dependency not installed"
)
def test_design_space_continuous(self):
ds = DesignSpace(
[
FloatVariable(0, 1), # x0
FloatVariable(0, 1), # x1
FloatVariable(0, 1), # x2
],
random_state=42,
)
ds.add_value_constraint(
var1=0, value1="<", var2=1, value2=">"
) # Prevent x0 < x1
ds.add_value_constraint(
var1=1, value1="<", var2=2, value2=">"
) # Prevent x0 < x1

# correct_get_acting
x_sampled, is_acting_sampled = ds.sample_valid_x(100, random_state=42)
self.assertTrue(np.min(x_sampled[:, 0] - x_sampled[:, 1]) > 0)
self.assertTrue(np.min(x_sampled[:, 1] - x_sampled[:, 2]) > 0)
ds = DesignSpace(
[
IntegerVariable(0, 2), # x0
FloatVariable(0, 2), # x1
IntegerVariable(0, 2), # x2
],
random_state=42,
)
ds.add_value_constraint(
var1=0, value1="<", var2=1, value2=">"
) # Prevent x0 < x1
ds.add_value_constraint(
var1=1, value1="<", var2=2, value2=">"
) # Prevent x0 < x1

# correct_get_acting
x_sampled, is_acting_sampled = ds.sample_valid_x(100, random_state=42)
self.assertTrue(np.min(x_sampled[:, 0] - x_sampled[:, 1]) > 0)
self.assertTrue(np.min(x_sampled[:, 1] - x_sampled[:, 2]) > 0)

@unittest.skipIf(
not HAS_CONFIG_SPACE, "Hierarchy ConfigSpace dependency not installed"
)
def test_check_conditionally_acting(self):
class WrongDesignSpace(DesignSpace):
def _is_conditionally_acting(self) -> np.ndarray:
Expand Down

0 comments on commit 56ef7a9

Please sign in to comment.