Skip to content

Commit

Permalink
Speedup improvements for botorch and permutations
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikOrm committed Feb 9, 2024
1 parent 24ba45b commit a69322e
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 41 deletions.
2 changes: 1 addition & 1 deletion hypermapper/bo/local_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def get_parameter_neighbors(
# swap i and j (probably sloooow)
neighbor = configuration.clone()
permutation: List[int] = list(
parameter.get_permutation_value(configuration[parameter_idx])
parameter.get_permutation_value(int(configuration[parameter_idx].item()))
)
j_val = permutation[j]
permutation[j] = permutation[i]
Expand Down
1 change: 0 additions & 1 deletion hypermapper/bo/models/gpbotorch.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ def __init__(
batch_shape=torch.Size(),
noise_constraint=GreaterThan(
1e-4,
transform=None,
initial_value=noise_prior_mode,
),
)
Expand Down
76 changes: 38 additions & 38 deletions hypermapper/param/parameters.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import math
from abc import abstractmethod
from itertools import permutations
from typing import Any, List, Optional, Union, Tuple
Expand Down Expand Up @@ -750,27 +751,16 @@ def __init__(
if constraints is None:
constraints = []
self.n_elements = n_elements
self.permutation_values: List[tuple] = [
p for p in permutations([x for x in range(self.n_elements)])
]
self.string_values = [f"{tuple(p)}" for p in self.permutation_values]
self.values = torch.arange(len(self.permutation_values))
self.default_indices = None
if defaults:
self.default_indices = [
self.permutation_values.index(tuple(default)) for default in defaults
self.get_int_value(default) for default in defaults
]
Parameter.__init__(self, name, self.default_indices, constraints, dependencies)

self.size = self.get_size()
self.parametrization = parametrization.lower()
self.distribution = torch.ones(len(self.values)) / len(self.values)
self._val_indices = {
i.item(): i for i in self.values
} # from internal to index (which are the same for permutations)

self._permutation_indices = {
tuple(p): i for i, p in enumerate(self.permutation_values)
}
# self.distribution = torch.ones(self.get_size()) / self.get_size()

def parametrize(self, data: List[int]) -> Tuple[List[str], List[List[float]]]:
"""
Expand All @@ -787,7 +777,7 @@ def parametrize(self, data: List[int]) -> Tuple[List[str], List[List[float]]]:
[f"{self.name}_{i}" for i in range(self.n_elements)],
[
[
self.permutation_values[int(d)].index(i) / self.n_elements
self.get_permutation_value(int(d)).index(i) / self.n_elements
for i in range(self.n_elements)
]
for d in data
Expand All @@ -803,8 +793,8 @@ def parametrize(self, data: List[int]) -> Tuple[List[str], List[List[float]]]:
],
[
[
self.permutation_values[int(d)][i]
< self.permutation_values[int(d)][j]
self.get_permutation_value(int(d))[i]
< self.get_permutation_value(int(d))[j]
for i in range(self.n_elements)
for j in range(i + 1, self.n_elements)
]
Expand All @@ -821,7 +811,7 @@ def parametrize(self, data: List[int]) -> Tuple[List[str], List[List[float]]]:
],
[
[
self.permutation_values[int(d)][i] == j
self.get_permutation_value(int(d))[i] == j
for i in range(self.n_elements)
for j in range(self.n_elements)
]
Expand All @@ -831,8 +821,8 @@ def parametrize(self, data: List[int]) -> Tuple[List[str], List[List[float]]]:

elif self.parametrization == "naive":
return (
[f"{self.name}_{i}" for i in self.values],
[[int(int(d) == i) for i in self.values] for d in data],
[f"{self.name}_{i}" for i in range(self.get_size())],
[[int(int(d) == i) for i in range(self.get_size())] for d in data],
)

else:
Expand All @@ -849,8 +839,7 @@ def sample(self, size=1, uniform=False) -> torch.Tensor:
Returns:
- a random number.
"""
samples = np.random.choice(self.values, size=size)
samples = torch.tensor(samples)
samples = torch.randint(self.size, size=(size, ))
return samples

def pdf(self, x_idx: torch.Tensor) -> float:
Expand All @@ -864,11 +853,17 @@ def pdf(self, x_idx: torch.Tensor) -> float:
def get_defaults(self) -> List[int]:
return self.default_indices

def get_index(self, value: Any) -> int:
return self._val_indices[value]
def get_index(self, perm: Any) -> int:
perm = [x + 1 for x in perm]
n = len(perm)
encoded = 0
for i in range(n):
smaller_elements = sum(1 for j in range(i + 1, n) if perm[j] < perm[i])
encoded += smaller_elements * math.factorial(n - i - 1)
return encoded

def get_size(self) -> int:
return len(self.values)
return math.factorial(self.n_elements)

def get_discrete_size(self) -> int:
return self.get_size()
Expand All @@ -877,16 +872,21 @@ def get_discrete_values(self) -> List[int]:
return self.get_values()

def get_values(self) -> List[int]:
return self.values

def get_permutation_values(self) -> List[Tuple[int]]:
return self.permutation_values
raise Exception("PermutationParameter does not have a list of values.")

def get_int_value(self, permutation: Tuple[int]) -> int:
return self._permutation_indices[permutation]

def get_permutation_value(self, idx_value: int) -> Tuple[int]:
return self.permutation_values[int(idx_value)]
return self.get_index(permutation)

def get_permutation_value(self, idx: int) -> Tuple[int]:
perm = []
n = self.n_elements
nums = list(range(1, n + 1))
for i in range(n):
index = idx // math.factorial(n - i - 1)
perm.append(nums[index])
del nums[index]
idx %= math.factorial(n - i - 1)
return tuple([x - 1 for x in perm])

def string_to_int(self, string: str) -> int:
if string[0] == "(":
Expand Down Expand Up @@ -914,18 +914,18 @@ def convert(
- the converted value
"""
if from_type == "string":
intermediate_value = self.string_values.index(input_value)
intermediate_value = self.string_to_int(input_value)
elif from_type == "original":
intermediate_value = self.permutation_values.index(input_value)
intermediate_value = self.get_int_value(input_value)
elif from_type == "01":
intermediate_value = int(np.floor(input_value * self.get_size() * 0.999999))
intermediate_value = int(np.floor(input_value * self.get_size() * 0.99999))
else:
intermediate_value = int(input_value)

if to_type == "string":
return self.string_values[intermediate_value]
return self.int_to_string(intermediate_value)
if to_type == "original":
return self.permutation_values[intermediate_value]
return self.get_permutation_value(intermediate_value)
elif to_type == "01":
return intermediate_value / (self.get_size() - 1)
else:
Expand Down
2 changes: 1 addition & 1 deletion hypermapper/param/space.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ def run_configurations_with_black_box_function(
idx = 0
while idx < len(original_configurations):
configurations_to_run = original_configurations[
idx : idx + self.settings["batch_size"]
idx: idx + self.settings["batch_size"]
]
bbf_arguments = [
{name: value for name, value in zip(self.parameter_names, config)}
Expand Down

0 comments on commit a69322e

Please sign in to comment.