ennbo 0.1.0__py3-none-any.whl → 0.1.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. enn/__init__.py +25 -13
  2. enn/benchmarks/__init__.py +3 -0
  3. enn/benchmarks/ackley.py +5 -0
  4. enn/benchmarks/ackley_class.py +17 -0
  5. enn/benchmarks/ackley_core.py +12 -0
  6. enn/benchmarks/double_ackley.py +24 -0
  7. enn/enn/candidates.py +14 -0
  8. enn/enn/conditional_posterior_draw_internals.py +15 -0
  9. enn/enn/draw_internals.py +15 -0
  10. enn/enn/enn.py +16 -229
  11. enn/enn/enn_class.py +423 -0
  12. enn/enn/enn_conditional.py +325 -0
  13. enn/enn/enn_fit.py +77 -76
  14. enn/enn/enn_hash.py +79 -0
  15. enn/enn/enn_index.py +92 -0
  16. enn/enn/enn_like_protocol.py +35 -0
  17. enn/enn/enn_normal.py +3 -3
  18. enn/enn/enn_params.py +3 -9
  19. enn/enn/enn_params_class.py +24 -0
  20. enn/enn/enn_util.py +79 -37
  21. enn/enn/neighbor_data.py +14 -0
  22. enn/enn/neighbors.py +14 -0
  23. enn/enn/posterior_flags.py +8 -0
  24. enn/enn/weighted_stats.py +14 -0
  25. enn/turbo/components/__init__.py +41 -0
  26. enn/turbo/components/acquisition.py +13 -0
  27. enn/turbo/components/acquisition_optimizer_protocol.py +19 -0
  28. enn/turbo/components/builder.py +22 -0
  29. enn/turbo/components/chebyshev_incumbent_selector.py +76 -0
  30. enn/turbo/components/enn_surrogate.py +115 -0
  31. enn/turbo/components/gp_surrogate.py +144 -0
  32. enn/turbo/components/hnr_acq_optimizer.py +83 -0
  33. enn/turbo/components/incumbent_selector.py +11 -0
  34. enn/turbo/components/incumbent_selector_protocol.py +16 -0
  35. enn/turbo/components/no_incumbent_selector.py +21 -0
  36. enn/turbo/components/no_surrogate.py +49 -0
  37. enn/turbo/components/pareto_acq_optimizer.py +49 -0
  38. enn/turbo/components/posterior_result.py +12 -0
  39. enn/turbo/components/protocols.py +13 -0
  40. enn/turbo/components/random_acq_optimizer.py +21 -0
  41. enn/turbo/components/scalar_incumbent_selector.py +39 -0
  42. enn/turbo/components/surrogate_protocol.py +32 -0
  43. enn/turbo/components/surrogate_result.py +12 -0
  44. enn/turbo/components/surrogates.py +5 -0
  45. enn/turbo/components/thompson_acq_optimizer.py +49 -0
  46. enn/turbo/components/trust_region_protocol.py +24 -0
  47. enn/turbo/components/ucb_acq_optimizer.py +49 -0
  48. enn/turbo/config/__init__.py +87 -0
  49. enn/turbo/config/acq_type.py +8 -0
  50. enn/turbo/config/acquisition.py +26 -0
  51. enn/turbo/config/base.py +4 -0
  52. enn/turbo/config/candidate_gen_config.py +49 -0
  53. enn/turbo/config/candidate_rv.py +7 -0
  54. enn/turbo/config/draw_acquisition_config.py +14 -0
  55. enn/turbo/config/enn_index_driver.py +6 -0
  56. enn/turbo/config/enn_surrogate_config.py +42 -0
  57. enn/turbo/config/enums.py +7 -0
  58. enn/turbo/config/factory.py +118 -0
  59. enn/turbo/config/gp_surrogate_config.py +14 -0
  60. enn/turbo/config/hnr_optimizer_config.py +7 -0
  61. enn/turbo/config/init_config.py +17 -0
  62. enn/turbo/config/init_strategies/__init__.py +9 -0
  63. enn/turbo/config/init_strategies/hybrid_init.py +23 -0
  64. enn/turbo/config/init_strategies/init_strategy.py +19 -0
  65. enn/turbo/config/init_strategies/lhd_only_init.py +24 -0
  66. enn/turbo/config/morbo_tr_config.py +82 -0
  67. enn/turbo/config/nds_optimizer_config.py +7 -0
  68. enn/turbo/config/no_surrogate_config.py +14 -0
  69. enn/turbo/config/no_tr_config.py +31 -0
  70. enn/turbo/config/optimizer_config.py +72 -0
  71. enn/turbo/config/pareto_acquisition_config.py +14 -0
  72. enn/turbo/config/raasp_driver.py +6 -0
  73. enn/turbo/config/raasp_optimizer_config.py +7 -0
  74. enn/turbo/config/random_acquisition_config.py +14 -0
  75. enn/turbo/config/rescalarize.py +7 -0
  76. enn/turbo/config/surrogate.py +12 -0
  77. enn/turbo/config/trust_region.py +34 -0
  78. enn/turbo/config/turbo_tr_config.py +71 -0
  79. enn/turbo/config/ucb_acquisition_config.py +14 -0
  80. enn/turbo/config/validation.py +45 -0
  81. enn/turbo/hypervolume.py +30 -0
  82. enn/turbo/impl_helpers.py +68 -0
  83. enn/turbo/morbo_trust_region.py +250 -0
  84. enn/turbo/no_trust_region.py +58 -0
  85. enn/turbo/optimizer.py +300 -0
  86. enn/turbo/optimizer_config.py +8 -0
  87. enn/turbo/proposal.py +46 -39
  88. enn/turbo/sampling.py +21 -0
  89. enn/turbo/strategies/__init__.py +9 -0
  90. enn/turbo/strategies/lhd_only_strategy.py +36 -0
  91. enn/turbo/strategies/optimization_strategy.py +19 -0
  92. enn/turbo/strategies/turbo_hybrid_strategy.py +124 -0
  93. enn/turbo/tr_helpers.py +202 -0
  94. enn/turbo/turbo_gp.py +9 -2
  95. enn/turbo/turbo_gp_base.py +0 -1
  96. enn/turbo/turbo_gp_fit.py +187 -0
  97. enn/turbo/turbo_gp_noisy.py +0 -1
  98. enn/turbo/turbo_optimizer_utils.py +98 -0
  99. enn/turbo/turbo_trust_region.py +129 -63
  100. enn/turbo/turbo_utils.py +144 -117
  101. enn/turbo/types/__init__.py +7 -0
  102. enn/turbo/types/appendable_array.py +85 -0
  103. enn/turbo/types/gp_data_prep.py +13 -0
  104. enn/turbo/types/gp_fit_result.py +11 -0
  105. enn/turbo/types/obs_lists.py +10 -0
  106. enn/turbo/types/prepare_ask_result.py +14 -0
  107. enn/turbo/types/tell_inputs.py +14 -0
  108. {ennbo-0.1.0.dist-info → ennbo-0.1.7.dist-info}/METADATA +22 -14
  109. ennbo-0.1.7.dist-info/RECORD +111 -0
  110. enn/enn/__init__.py +0 -4
  111. enn/turbo/__init__.py +0 -11
  112. enn/turbo/base_turbo_impl.py +0 -98
  113. enn/turbo/lhd_only_impl.py +0 -42
  114. enn/turbo/turbo_config.py +0 -28
  115. enn/turbo/turbo_enn_impl.py +0 -176
  116. enn/turbo/turbo_mode.py +0 -10
  117. enn/turbo/turbo_mode_impl.py +0 -67
  118. enn/turbo/turbo_one_impl.py +0 -163
  119. enn/turbo/turbo_optimizer.py +0 -337
  120. enn/turbo/turbo_zero_impl.py +0 -24
  121. ennbo-0.1.0.dist-info/RECORD +0 -27
  122. {ennbo-0.1.0.dist-info → ennbo-0.1.7.dist-info}/WHEEL +0 -0
  123. {ennbo-0.1.0.dist-info → ennbo-0.1.7.dist-info}/licenses/LICENSE +0 -0
enn/__init__.py CHANGED
@@ -1,24 +1,36 @@
1
1
  from __future__ import annotations
2
-
3
- from .enn import EpistemicNearestNeighbors, enn_fit
4
-
5
- _LAZY_IMPORTS = ("TurboMode", "TurboOptimizer", "Turbo", "Telemetry")
6
-
7
-
8
- def _lazy_load(name: str):
9
- from . import turbo
10
-
11
- return getattr(turbo, name)
2
+ import importlib
3
+ from .enn.enn_class import EpistemicNearestNeighbors
4
+ from .enn.enn_fit import enn_fit
5
+
6
+ _LAZY_ATTRS: dict[str, tuple[str, str]] = {
7
+ "create_optimizer": (".turbo.optimizer", "create_optimizer"),
8
+ "Telemetry": (".turbo.turbo_utils", "Telemetry"),
9
+ "OptimizerConfig": (".turbo.optimizer_config", "OptimizerConfig"),
10
+ "turbo_one_config": (".turbo.optimizer_config", "turbo_one_config"),
11
+ "turbo_zero_config": (".turbo.optimizer_config", "turbo_zero_config"),
12
+ "turbo_enn_config": (".turbo.optimizer_config", "turbo_enn_config"),
13
+ "lhd_only_config": (".turbo.optimizer_config", "lhd_only_config"),
14
+ "TurboTRConfig": (".turbo.config.trust_region", "TurboTRConfig"),
15
+ "MorboTRConfig": (".turbo.config.trust_region", "MorboTRConfig"),
16
+ "NoTRConfig": (".turbo.config.trust_region", "NoTRConfig"),
17
+ "CandidateRV": (".turbo.optimizer_config", "CandidateRV"),
18
+ "InitStrategy": (".turbo.optimizer_config", "InitStrategy"),
19
+ "AcqType": (".turbo.optimizer_config", "AcqType"),
20
+ }
12
21
 
13
22
 
14
23
  def __getattr__(name: str):
15
- if name in _LAZY_IMPORTS:
16
- return _lazy_load(name)
24
+ spec = _LAZY_ATTRS.get(name)
25
+ if spec is not None:
26
+ module_name, attr_name = spec
27
+ module = importlib.import_module(module_name, __package__)
28
+ return getattr(module, attr_name)
17
29
  raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
18
30
 
19
31
 
20
32
  __all__: list[str] = [
21
33
  "EpistemicNearestNeighbors",
22
34
  "enn_fit",
23
- *_LAZY_IMPORTS,
35
+ *_LAZY_ATTRS.keys(),
24
36
  ]
@@ -0,0 +1,3 @@
1
+ from .ackley import Ackley, DoubleAckley
2
+
3
+ __all__ = ["Ackley", "DoubleAckley"]
@@ -0,0 +1,5 @@
1
+ from .ackley_class import Ackley
2
+ from .ackley_core import ackley_core
3
+ from .double_ackley import DoubleAckley
4
+
5
+ __all__ = ["Ackley", "DoubleAckley", "ackley_core"]
@@ -0,0 +1,17 @@
1
+ import numpy as np
2
+ from numpy.random import Generator
3
+ from .ackley_core import ackley_core
4
+
5
+
6
+ class Ackley:
7
+ def __init__(self, noise: float, rng: Generator):
8
+ self.noise = noise
9
+ self.rng = rng
10
+ self.bounds = [-32.768, 32.768]
11
+
12
+ def __call__(self, x: np.ndarray) -> np.ndarray:
13
+ x = np.asarray(x, dtype=float)
14
+ if x.ndim == 1:
15
+ x = x[None, :]
16
+ y = -ackley_core(x) + self.noise * self.rng.normal(size=(x.shape[0],))
17
+ return y if y.ndim > 0 else float(y)
@@ -0,0 +1,12 @@
1
+ import numpy as np
2
+
3
+
4
+ def ackley_core(
5
+ x: np.ndarray, a: float = 20.0, b: float = 0.2, c: float = 2 * np.pi
6
+ ) -> np.ndarray:
7
+ if x.ndim == 1:
8
+ x = x[None, :]
9
+ x = x - 1
10
+ term1 = -a * np.exp(-b * np.sqrt((x**2).mean(axis=1)))
11
+ term2 = -np.exp(np.cos(c * x).mean(axis=1))
12
+ return term1 + term2 + a + np.e
@@ -0,0 +1,24 @@
1
+ import numpy as np
2
+ from numpy.random import Generator
3
+ from .ackley_core import ackley_core
4
+
5
+
6
+ class DoubleAckley:
7
+ def __init__(self, noise: float, rng: Generator):
8
+ self.noise = noise
9
+ self.rng = rng
10
+ self.bounds = [-32.768, 32.768]
11
+
12
+ def __call__(self, x: np.ndarray) -> np.ndarray:
13
+ x = np.asarray(x, dtype=float)
14
+ if x.ndim == 1:
15
+ x = x[None, :]
16
+ n, d = x.shape
17
+ if d % 2 != 0:
18
+ raise ValueError("num_dim must be even for DoubleAckley")
19
+ mid = d // 2
20
+ x1 = x[:, :mid]
21
+ x2 = x[:, mid:]
22
+ y1 = -ackley_core(x1) + self.noise * self.rng.normal(size=n)
23
+ y2 = -ackley_core(x2) + self.noise * self.rng.normal(size=n)
24
+ return np.stack([y1, y2], axis=1)
enn/enn/candidates.py ADDED
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ import numpy as np
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class Candidates:
11
+ dist2: np.ndarray
12
+ ids: np.ndarray
13
+ y: np.ndarray
14
+ yvar: np.ndarray | None
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ import numpy as np
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class ConditionalPosteriorDrawInternals:
11
+ idx: np.ndarray
12
+ w_normalized: np.ndarray
13
+ l2: np.ndarray
14
+ mu: np.ndarray
15
+ se: np.ndarray
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ import numpy as np
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class DrawInternals:
11
+ idx: np.ndarray
12
+ w_normalized: np.ndarray
13
+ l2: np.ndarray
14
+ mu: np.ndarray
15
+ se: np.ndarray
enn/enn/enn.py CHANGED
@@ -1,229 +1,16 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING, Any
4
-
5
- if TYPE_CHECKING:
6
- import numpy as np
7
-
8
- from .enn_normal import ENNNormal
9
- from .enn_params import ENNParams
10
-
11
-
12
- class EpistemicNearestNeighbors:
13
- def __init__(
14
- self,
15
- train_x: np.ndarray | Any,
16
- train_y: np.ndarray | Any,
17
- train_yvar: np.ndarray | Any | None = None,
18
- ) -> None:
19
- import numpy as np
20
-
21
- if train_x.ndim != 2:
22
- raise ValueError(train_x.shape)
23
- if train_y.ndim != 2:
24
- raise ValueError(train_y.shape)
25
- if train_x.shape[0] != train_y.shape[0]:
26
- raise ValueError((train_x.shape, train_y.shape))
27
- if train_yvar is not None:
28
- if train_yvar.ndim != 2:
29
- raise ValueError(train_yvar.shape)
30
- if train_y.shape != train_yvar.shape:
31
- raise ValueError((train_y.shape, train_yvar.shape))
32
- self._train_x = np.asarray(train_x, dtype=float)
33
- self._train_y = np.asarray(train_y, dtype=float)
34
- self._train_yvar = (
35
- np.asarray(train_yvar, dtype=float) if train_yvar is not None else None
36
- )
37
- self._num_obs, self._num_dim = self._train_x.shape
38
- _, self._num_metrics = self._train_y.shape
39
- self._eps_var = 1e-9
40
- if len(self._train_y) < 2:
41
- self._y_scale = np.ones(shape=(1, self._num_metrics), dtype=float)
42
- else:
43
- self._y_scale = np.std(self._train_y, axis=0, keepdims=True).astype(float)
44
-
45
- self._index: Any | None = None
46
- self._build_index()
47
-
48
- @property
49
- def train_x(self) -> np.ndarray:
50
- return self._train_x
51
-
52
- @property
53
- def train_y(self) -> np.ndarray:
54
- return self._train_y
55
-
56
- @property
57
- def train_yvar(self) -> np.ndarray | None:
58
- return self._train_yvar
59
-
60
- @property
61
- def num_outputs(self) -> int:
62
- return self._num_metrics
63
-
64
- def __len__(self) -> int:
65
- return self._num_obs
66
-
67
- def _build_index(self) -> None:
68
- import faiss
69
- import numpy as np
70
-
71
- if self._num_obs == 0:
72
- return
73
- x_f32 = self._train_x.astype(np.float32, copy=False)
74
- index = faiss.IndexFlatL2(self._num_dim)
75
- index.add(x_f32)
76
- self._index = index
77
-
78
- def posterior(
79
- self,
80
- x: np.ndarray | Any,
81
- *,
82
- params: ENNParams,
83
- exclude_nearest: bool = False,
84
- observation_noise: bool = False,
85
- ) -> ENNNormal:
86
- from .enn_normal import ENNNormal
87
-
88
- post_batch = self.batch_posterior(
89
- x,
90
- [params],
91
- exclude_nearest=exclude_nearest,
92
- observation_noise=observation_noise,
93
- )
94
- mu = post_batch.mu[0]
95
- se = post_batch.se[0]
96
- return ENNNormal(mu, se)
97
-
98
- def batch_posterior(
99
- self,
100
- x: np.ndarray | Any,
101
- paramss: list[ENNParams],
102
- *,
103
- exclude_nearest: bool = False,
104
- observation_noise: bool = False,
105
- ) -> ENNNormal:
106
- import numpy as np
107
-
108
- from .enn_normal import ENNNormal
109
-
110
- if x.ndim != 2:
111
- raise ValueError(x.shape)
112
- if x.shape[1] != self._num_dim:
113
- raise ValueError(x.shape)
114
- if len(paramss) == 0:
115
- raise ValueError("paramss must be non-empty")
116
- batch_size = x.shape[0]
117
- num_params = len(paramss)
118
- if len(self) == 0:
119
- mu = np.zeros((num_params, batch_size, self._num_metrics), dtype=float)
120
- se = np.ones((num_params, batch_size, self._num_metrics), dtype=float)
121
- return ENNNormal(mu, se)
122
- max_k = max(params.k for params in paramss)
123
- if exclude_nearest:
124
- if len(self) <= 1:
125
- raise ValueError(len(self))
126
- search_k = int(min(max_k + 1, len(self)))
127
- else:
128
- search_k = int(min(max_k, len(self)))
129
- x_f32 = x.astype(np.float32, copy=False)
130
- if self._index is None:
131
- raise RuntimeError("index is not initialized")
132
- dist2s_full, idx_full = self._index.search(x_f32, search_k)
133
- dist2s_full = dist2s_full.astype(float)
134
- idx_full = idx_full.astype(int)
135
- if exclude_nearest:
136
- dist2s_full = dist2s_full[:, 1:]
137
- idx_full = idx_full[:, 1:]
138
- mu_all = np.zeros((num_params, batch_size, self._num_metrics), dtype=float)
139
- se_all = np.zeros((num_params, batch_size, self._num_metrics), dtype=float)
140
- available_k = search_k - 1 if exclude_nearest else search_k
141
- for i, params in enumerate(paramss):
142
- k = min(params.k, available_k)
143
- if k > dist2s_full.shape[1]:
144
- raise RuntimeError(
145
- f"k={k} exceeds available columns={dist2s_full.shape[1]}"
146
- )
147
- if k == 0:
148
- mu_all[i] = np.zeros((batch_size, self._num_metrics), dtype=float)
149
- se_all[i] = np.ones((batch_size, self._num_metrics), dtype=float)
150
- continue
151
- dist2s = dist2s_full[:, :k]
152
- idx = idx_full[:, :k]
153
- y_neighbors = self._train_y[idx]
154
-
155
- dist2s_expanded = dist2s[..., np.newaxis]
156
- var_component = (
157
- params.ale_homoscedastic_scale + params.epi_var_scale * dist2s_expanded
158
- )
159
- if self._train_yvar is not None:
160
- yvar_neighbors = self._train_yvar[idx] / self._y_scale**2
161
- var_component = var_component + yvar_neighbors
162
- else:
163
- yvar_neighbors = None
164
-
165
- w = 1.0 / (self._eps_var + var_component)
166
- norm = np.sum(w, axis=1)
167
- mu_all[i] = np.sum(w * y_neighbors, axis=1) / norm
168
- epistemic_var = 1.0 / norm
169
- vvar = epistemic_var
170
- if observation_noise:
171
- vvar = vvar + params.ale_homoscedastic_scale
172
- if yvar_neighbors is not None:
173
- ale_heteroscedastic = np.sum(w * yvar_neighbors, axis=1) / norm
174
- vvar = vvar + ale_heteroscedastic
175
- vvar = np.maximum(vvar, self._eps_var)
176
- se_all[i] = np.sqrt(vvar) * self._y_scale
177
- return ENNNormal(mu_all, se_all)
178
-
179
- def neighbors(
180
- self,
181
- x: np.ndarray | Any,
182
- k: int,
183
- *,
184
- exclude_nearest: bool = False,
185
- ) -> list[tuple[np.ndarray, np.ndarray]]:
186
- import numpy as np
187
-
188
- x = np.asarray(x, dtype=float)
189
- if x.ndim == 1:
190
- x = x[np.newaxis, :]
191
- if x.ndim != 2:
192
- raise ValueError(f"x must be 1D or 2D, got shape {x.shape}")
193
- if x.shape[0] != 1:
194
- raise ValueError(f"x must be a single point, got shape {x.shape}")
195
- if x.shape[1] != self._num_dim:
196
- raise ValueError(
197
- f"x must have {self._num_dim} dimensions, got {x.shape[1]}"
198
- )
199
- if k < 0:
200
- raise ValueError(f"k must be non-negative, got {k}")
201
- if len(self) == 0:
202
- return []
203
- if exclude_nearest:
204
- if len(self) <= 1:
205
- raise ValueError(
206
- f"exclude_nearest=True requires at least 2 observations, got {len(self)}"
207
- )
208
- search_k = int(min(k + 1, len(self)))
209
- else:
210
- search_k = int(min(k, len(self)))
211
- if search_k == 0:
212
- return []
213
- x_f32 = x.astype(np.float32, copy=False)
214
- if self._index is None:
215
- raise RuntimeError("index is not initialized")
216
- dist2s_full, idx_full = self._index.search(x_f32, search_k)
217
- dist2s_full = dist2s_full.astype(float)
218
- idx_full = idx_full.astype(int)
219
- if exclude_nearest:
220
- dist2s_full = dist2s_full[:, 1:]
221
- idx_full = idx_full[:, 1:]
222
- actual_k = min(k, len(idx_full[0]))
223
- idx = idx_full[0, :actual_k]
224
- result = []
225
- for i in idx:
226
- x_neighbor = self._train_x[i].copy()
227
- y_neighbor = self._train_y[i].copy()
228
- result.append((x_neighbor, y_neighbor))
229
- return result
1
+ from .draw_internals import DrawInternals
2
+ from .neighbor_data import NeighborData
3
+ from .weighted_stats import WeightedStats
4
+
5
+ _DrawInternals = DrawInternals
6
+ _NeighborData = NeighborData
7
+ _WeightedStats = WeightedStats
8
+
9
+ __all__ = [
10
+ "DrawInternals",
11
+ "NeighborData",
12
+ "WeightedStats",
13
+ "_DrawInternals",
14
+ "_NeighborData",
15
+ "_WeightedStats",
16
+ ]