pyglove 0.4.5.dev20240319__py3-none-any.whl → 0.4.5.dev202501140808__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.
- pyglove/core/__init__.py +54 -20
- pyglove/core/coding/__init__.py +42 -0
- pyglove/core/coding/errors.py +111 -0
- pyglove/core/coding/errors_test.py +98 -0
- pyglove/core/coding/execution.py +309 -0
- pyglove/core/coding/execution_test.py +333 -0
- pyglove/core/{object_utils/codegen.py → coding/function_generation.py} +10 -4
- pyglove/core/{object_utils/codegen_test.py → coding/function_generation_test.py} +5 -7
- pyglove/core/coding/parsing.py +153 -0
- pyglove/core/coding/parsing_test.py +150 -0
- pyglove/core/coding/permissions.py +100 -0
- pyglove/core/coding/permissions_test.py +93 -0
- pyglove/core/geno/base.py +54 -41
- pyglove/core/geno/base_test.py +2 -4
- pyglove/core/geno/categorical.py +37 -28
- pyglove/core/geno/custom.py +19 -16
- pyglove/core/geno/numerical.py +20 -17
- pyglove/core/geno/space.py +4 -5
- pyglove/core/hyper/base.py +6 -6
- pyglove/core/hyper/categorical.py +94 -55
- pyglove/core/hyper/custom.py +7 -7
- pyglove/core/hyper/custom_test.py +9 -10
- pyglove/core/hyper/derived.py +30 -22
- pyglove/core/hyper/derived_test.py +2 -4
- pyglove/core/hyper/dynamic_evaluation.py +5 -6
- pyglove/core/hyper/evolvable.py +57 -46
- pyglove/core/hyper/numerical.py +48 -24
- pyglove/core/hyper/numerical_test.py +9 -9
- pyglove/core/hyper/object_template.py +58 -46
- pyglove/core/io/__init__.py +1 -0
- pyglove/core/io/file_system.py +17 -7
- pyglove/core/io/file_system_test.py +2 -0
- pyglove/core/io/sequence.py +299 -0
- pyglove/core/io/sequence_test.py +124 -0
- pyglove/core/logging_test.py +0 -2
- pyglove/core/patching/object_factory.py +4 -4
- pyglove/core/patching/pattern_based.py +4 -4
- pyglove/core/patching/rule_based.py +17 -5
- pyglove/core/patching/rule_based_test.py +27 -4
- pyglove/core/symbolic/__init__.py +2 -7
- pyglove/core/symbolic/base.py +320 -183
- pyglove/core/symbolic/base_test.py +123 -19
- pyglove/core/symbolic/boilerplate.py +7 -13
- pyglove/core/symbolic/boilerplate_test.py +25 -23
- pyglove/core/symbolic/class_wrapper.py +48 -45
- pyglove/core/symbolic/class_wrapper_test.py +2 -2
- pyglove/core/symbolic/compounding.py +9 -15
- pyglove/core/symbolic/compounding_test.py +2 -4
- pyglove/core/symbolic/dict.py +154 -110
- pyglove/core/symbolic/dict_test.py +238 -130
- pyglove/core/symbolic/diff.py +199 -10
- pyglove/core/symbolic/diff_test.py +226 -0
- pyglove/core/symbolic/flags.py +1 -1
- pyglove/core/symbolic/functor.py +29 -26
- pyglove/core/symbolic/functor_test.py +102 -50
- pyglove/core/symbolic/inferred.py +2 -2
- pyglove/core/symbolic/list.py +81 -50
- pyglove/core/symbolic/list_test.py +119 -97
- pyglove/core/symbolic/object.py +225 -113
- pyglove/core/symbolic/object_test.py +320 -108
- pyglove/core/symbolic/origin.py +17 -14
- pyglove/core/symbolic/origin_test.py +4 -2
- pyglove/core/symbolic/pure_symbolic.py +4 -3
- pyglove/core/symbolic/ref.py +108 -21
- pyglove/core/symbolic/ref_test.py +93 -0
- pyglove/core/symbolic/symbolize_test.py +10 -2
- pyglove/core/tuning/local_backend.py +2 -2
- pyglove/core/tuning/protocols.py +3 -3
- pyglove/core/tuning/sample_test.py +3 -3
- pyglove/core/typing/__init__.py +14 -5
- pyglove/core/typing/annotation_conversion.py +43 -27
- pyglove/core/typing/annotation_conversion_test.py +23 -0
- pyglove/core/typing/callable_ext.py +241 -3
- pyglove/core/typing/callable_ext_test.py +255 -0
- pyglove/core/typing/callable_signature.py +510 -66
- pyglove/core/typing/callable_signature_test.py +619 -99
- pyglove/core/typing/class_schema.py +229 -154
- pyglove/core/typing/class_schema_test.py +149 -95
- pyglove/core/typing/custom_typing.py +5 -4
- pyglove/core/typing/inspect.py +63 -0
- pyglove/core/typing/inspect_test.py +39 -0
- pyglove/core/typing/key_specs.py +10 -11
- pyglove/core/typing/key_specs_test.py +7 -4
- pyglove/core/typing/type_conversion.py +4 -5
- pyglove/core/typing/type_conversion_test.py +12 -12
- pyglove/core/typing/typed_missing.py +6 -7
- pyglove/core/typing/typed_missing_test.py +7 -8
- pyglove/core/typing/value_specs.py +604 -362
- pyglove/core/typing/value_specs_test.py +328 -90
- pyglove/core/utils/__init__.py +164 -0
- pyglove/core/{object_utils → utils}/common_traits.py +3 -67
- pyglove/core/utils/common_traits_test.py +36 -0
- pyglove/core/{object_utils → utils}/docstr_utils.py +23 -0
- pyglove/core/{object_utils → utils}/docstr_utils_test.py +36 -4
- pyglove/core/{object_utils → utils}/error_utils.py +78 -9
- pyglove/core/{object_utils → utils}/error_utils_test.py +61 -5
- pyglove/core/utils/formatting.py +464 -0
- pyglove/core/utils/formatting_test.py +453 -0
- pyglove/core/{object_utils → utils}/hierarchical.py +23 -25
- pyglove/core/{object_utils → utils}/hierarchical_test.py +3 -5
- pyglove/core/{object_utils → utils}/json_conversion.py +177 -52
- pyglove/core/{object_utils → utils}/json_conversion_test.py +97 -16
- pyglove/core/{object_utils → utils}/missing.py +3 -3
- pyglove/core/{object_utils → utils}/missing_test.py +2 -4
- pyglove/core/utils/text_color.py +128 -0
- pyglove/core/utils/text_color_test.py +94 -0
- pyglove/core/{object_utils → utils}/thread_local_test.py +1 -3
- pyglove/core/utils/timing.py +236 -0
- pyglove/core/utils/timing_test.py +154 -0
- pyglove/core/{object_utils → utils}/value_location.py +275 -6
- pyglove/core/utils/value_location_test.py +707 -0
- pyglove/core/views/__init__.py +32 -0
- pyglove/core/views/base.py +804 -0
- pyglove/core/views/base_test.py +580 -0
- pyglove/core/views/html/__init__.py +27 -0
- pyglove/core/views/html/base.py +547 -0
- pyglove/core/views/html/base_test.py +830 -0
- pyglove/core/views/html/controls/__init__.py +35 -0
- pyglove/core/views/html/controls/base.py +275 -0
- pyglove/core/views/html/controls/label.py +207 -0
- pyglove/core/views/html/controls/label_test.py +157 -0
- pyglove/core/views/html/controls/progress_bar.py +183 -0
- pyglove/core/views/html/controls/progress_bar_test.py +97 -0
- pyglove/core/views/html/controls/tab.py +320 -0
- pyglove/core/views/html/controls/tab_test.py +87 -0
- pyglove/core/views/html/controls/tooltip.py +99 -0
- pyglove/core/views/html/controls/tooltip_test.py +99 -0
- pyglove/core/views/html/tree_view.py +1517 -0
- pyglove/core/views/html/tree_view_test.py +1461 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/METADATA +18 -4
- pyglove-0.4.5.dev202501140808.dist-info/RECORD +214 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/WHEEL +1 -1
- pyglove/core/object_utils/__init__.py +0 -154
- pyglove/core/object_utils/common_traits_test.py +0 -82
- pyglove/core/object_utils/formatting.py +0 -234
- pyglove/core/object_utils/formatting_test.py +0 -223
- pyglove/core/object_utils/value_location_test.py +0 -385
- pyglove/core/symbolic/schema_utils.py +0 -327
- pyglove/core/symbolic/schema_utils_test.py +0 -57
- pyglove/core/typing/class_schema_utils.py +0 -202
- pyglove/core/typing/class_schema_utils_test.py +0 -194
- pyglove-0.4.5.dev20240319.dist-info/RECORD +0 -185
- /pyglove/core/{object_utils → utils}/thread_local.py +0 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/top_level.txt +0 -0
pyglove/core/geno/numerical.py
CHANGED
@@ -17,10 +17,9 @@ import random
|
|
17
17
|
import types
|
18
18
|
from typing import Any, List, Optional, Union
|
19
19
|
|
20
|
-
from pyglove.core import object_utils
|
21
20
|
from pyglove.core import symbolic
|
22
21
|
from pyglove.core import typing as pg_typing
|
23
|
-
|
22
|
+
from pyglove.core import utils
|
24
23
|
from pyglove.core.geno.base import DecisionPoint
|
25
24
|
from pyglove.core.geno.base import DNA
|
26
25
|
|
@@ -164,25 +163,30 @@ class Float(DecisionPoint):
|
|
164
163
|
if not compact:
|
165
164
|
return super().format(compact, verbose, root_indent, **kwargs)
|
166
165
|
if show_id:
|
167
|
-
kvlist = [('id',
|
166
|
+
kvlist = [('id', str(self.id), '\'\'')]
|
168
167
|
else:
|
169
168
|
kvlist = []
|
170
|
-
details =
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
169
|
+
details = utils.kvlist_str(
|
170
|
+
kvlist
|
171
|
+
+ [
|
172
|
+
('name', self.name, None),
|
173
|
+
('min_value', self.min_value, None),
|
174
|
+
('max_value', self.max_value, None),
|
175
|
+
('scale', self.scale, None),
|
176
|
+
('hints', self.hints, None),
|
177
|
+
]
|
178
|
+
)
|
177
179
|
return f'{self.__class__.__name__}({details})'
|
178
180
|
|
179
181
|
|
180
|
-
def floatv(
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
182
|
+
def floatv(
|
183
|
+
min_value: float,
|
184
|
+
max_value: float,
|
185
|
+
scale: Optional[str] = None,
|
186
|
+
hints: Any = None,
|
187
|
+
location: utils.KeyPath = utils.KeyPath(),
|
188
|
+
name: Optional[str] = None,
|
189
|
+
) -> Float:
|
186
190
|
"""Returns a Float specification.
|
187
191
|
|
188
192
|
It creates the genotype for :func:`pyglove.floatv`.
|
@@ -226,4 +230,3 @@ def floatv(min_value: float,
|
|
226
230
|
"""
|
227
231
|
return Float(min_value, max_value, scale,
|
228
232
|
hints=hints, location=location, name=name)
|
229
|
-
|
pyglove/core/geno/space.py
CHANGED
@@ -18,10 +18,9 @@ import random
|
|
18
18
|
import types
|
19
19
|
from typing import List, Optional, Union
|
20
20
|
|
21
|
-
from pyglove.core import object_utils
|
22
21
|
from pyglove.core import symbolic
|
23
22
|
from pyglove.core import typing as pg_typing
|
24
|
-
|
23
|
+
from pyglove.core import utils
|
25
24
|
from pyglove.core.geno.base import DecisionPoint
|
26
25
|
from pyglove.core.geno.base import DNA
|
27
26
|
from pyglove.core.geno.base import DNASpec
|
@@ -138,7 +137,7 @@ class Space(DNASpec):
|
|
138
137
|
"""
|
139
138
|
return not self.elements
|
140
139
|
|
141
|
-
def validate(self, dna: DNA) -> None:
|
140
|
+
def validate(self, dna: DNA) -> None: # pytype: disable=signature-mismatch
|
142
141
|
"""Validate whether a DNA value conforms to this spec."""
|
143
142
|
if not self.elements and (dna.value is not None or dna.children):
|
144
143
|
raise ValueError(
|
@@ -231,8 +230,8 @@ class Space(DNASpec):
|
|
231
230
|
return sum([len(elem) for elem in self.elements])
|
232
231
|
|
233
232
|
def __getitem__(
|
234
|
-
self, index: Union[int, slice, str,
|
235
|
-
|
233
|
+
self, index: Union[int, slice, str, utils.KeyPath]
|
234
|
+
) -> Union[DecisionPoint, List[DecisionPoint]]:
|
236
235
|
"""Operator [] to return element by index or sub-DNASpec by name."""
|
237
236
|
if isinstance(index, (int, slice)):
|
238
237
|
return self.elements[index]
|
pyglove/core/hyper/base.py
CHANGED
@@ -18,9 +18,9 @@ import abc
|
|
18
18
|
from typing import Any, Callable, Optional
|
19
19
|
|
20
20
|
from pyglove.core import geno
|
21
|
-
from pyglove.core import object_utils
|
22
21
|
from pyglove.core import symbolic
|
23
22
|
from pyglove.core import typing as pg_typing
|
23
|
+
from pyglove.core import utils
|
24
24
|
|
25
25
|
|
26
26
|
class HyperValue(symbolic.NonDeterministic): # pytype: disable=ignored-metaclass
|
@@ -110,8 +110,7 @@ class HyperValue(symbolic.NonDeterministic): # pytype: disable=ignored-metaclas
|
|
110
110
|
"""
|
111
111
|
|
112
112
|
@abc.abstractmethod
|
113
|
-
def dna_spec(self,
|
114
|
-
location: Optional[object_utils.KeyPath] = None) -> geno.DNASpec:
|
113
|
+
def dna_spec(self, location: Optional[utils.KeyPath] = None) -> geno.DNASpec:
|
115
114
|
"""Get DNA spec of DNA that is decodable/encodable by this hyper value."""
|
116
115
|
|
117
116
|
|
@@ -186,12 +185,13 @@ def set_dynamic_evaluate_fn(
|
|
186
185
|
global _global_dynamic_evaluate_fn
|
187
186
|
if per_thread:
|
188
187
|
assert _global_dynamic_evaluate_fn is None, _global_dynamic_evaluate_fn
|
189
|
-
|
188
|
+
utils.thread_local_set(_TLS_KEY_DYNAMIC_EVALUATE_FN, fn)
|
190
189
|
else:
|
191
190
|
_global_dynamic_evaluate_fn = fn
|
192
191
|
|
193
192
|
|
194
193
|
def get_dynamic_evaluate_fn() -> Optional[Callable[[HyperValue], Any]]:
|
195
194
|
"""Gets current dynamic evaluate function."""
|
196
|
-
return
|
197
|
-
_TLS_KEY_DYNAMIC_EVALUATE_FN, _global_dynamic_evaluate_fn
|
195
|
+
return utils.thread_local_get(
|
196
|
+
_TLS_KEY_DYNAMIC_EVALUATE_FN, _global_dynamic_evaluate_fn
|
197
|
+
)
|
@@ -18,9 +18,9 @@ import typing
|
|
18
18
|
from typing import Any, Callable, Iterable, List, Optional, Tuple, Union
|
19
19
|
|
20
20
|
from pyglove.core import geno
|
21
|
-
from pyglove.core import object_utils
|
22
21
|
from pyglove.core import symbolic
|
23
22
|
from pyglove.core import typing as pg_typing
|
23
|
+
from pyglove.core import utils
|
24
24
|
from pyglove.core.hyper import base
|
25
25
|
from pyglove.core.hyper import object_template
|
26
26
|
|
@@ -85,7 +85,8 @@ class Choices(base.HyperPrimitive):
|
|
85
85
|
self._value_spec = None
|
86
86
|
|
87
87
|
def _update_children_paths(
|
88
|
-
self, old_path:
|
88
|
+
self, old_path: utils.KeyPath, new_path: utils.KeyPath
|
89
|
+
):
|
89
90
|
"""Customized logic to update children paths."""
|
90
91
|
super()._update_children_paths(old_path, new_path)
|
91
92
|
for t in self._candidate_templates:
|
@@ -104,19 +105,20 @@ class Choices(base.HyperPrimitive):
|
|
104
105
|
return False
|
105
106
|
return True
|
106
107
|
|
107
|
-
def dna_spec(self,
|
108
|
-
location: Optional[object_utils.KeyPath] = None) -> geno.Choices:
|
108
|
+
def dna_spec(self, location: Optional[utils.KeyPath] = None) -> geno.Choices:
|
109
109
|
"""Returns corresponding DNASpec."""
|
110
110
|
return geno.Choices(
|
111
111
|
num_choices=self.num_choices,
|
112
112
|
candidates=[ct.dna_spec() for ct in self._candidate_templates],
|
113
|
-
literal_values=[
|
114
|
-
|
113
|
+
literal_values=[
|
114
|
+
self._literal_value(c) for i, c in enumerate(self.candidates)
|
115
|
+
],
|
115
116
|
distinct=self.choices_distinct,
|
116
117
|
sorted=self.choices_sorted,
|
117
118
|
hints=self.hints,
|
118
119
|
name=self.name,
|
119
|
-
location=location or
|
120
|
+
location=location or utils.KeyPath(),
|
121
|
+
)
|
120
122
|
|
121
123
|
def _literal_value(
|
122
124
|
self, candidate: Any, max_len: int = 120) -> Union[int, float, str]:
|
@@ -124,10 +126,13 @@ class Choices(base.HyperPrimitive):
|
|
124
126
|
if isinstance(candidate, numbers.Number):
|
125
127
|
return candidate
|
126
128
|
|
127
|
-
literal =
|
128
|
-
|
129
|
-
|
130
|
-
|
129
|
+
literal = utils.format(
|
130
|
+
candidate,
|
131
|
+
compact=True,
|
132
|
+
hide_default_values=True,
|
133
|
+
hide_missing_values=True,
|
134
|
+
strip_object_id=True,
|
135
|
+
)
|
131
136
|
if len(literal) > max_len:
|
132
137
|
literal = literal[:max_len - 3] + '...'
|
133
138
|
return literal
|
@@ -139,52 +144,70 @@ class Choices(base.HyperPrimitive):
|
|
139
144
|
# Single choice.
|
140
145
|
if not isinstance(dna.value, int):
|
141
146
|
raise ValueError(
|
142
|
-
|
143
|
-
|
147
|
+
utils.message_on_path(
|
148
|
+
'Did you forget to specify values for conditional choices?\n'
|
144
149
|
f'Expect integer for {self.__class__.__name__}. '
|
145
|
-
f'Encountered: {dna!r}.',
|
150
|
+
f'Encountered: {dna!r}.',
|
151
|
+
self.sym_path,
|
152
|
+
)
|
153
|
+
)
|
146
154
|
if dna.value >= len(self.candidates):
|
147
155
|
raise ValueError(
|
148
|
-
|
156
|
+
utils.message_on_path(
|
149
157
|
f'Choice out of range. Value: {dna.value!r}, '
|
150
|
-
f'Candidates: {len(self.candidates)}.',
|
158
|
+
f'Candidates: {len(self.candidates)}.',
|
159
|
+
self.sym_path,
|
160
|
+
)
|
161
|
+
)
|
151
162
|
choices = [self._candidate_templates[dna.value].decode(
|
152
163
|
geno.DNA(None, dna.children))]
|
153
164
|
else:
|
154
165
|
# Multi choices.
|
155
166
|
if len(dna.children) != self.num_choices:
|
156
167
|
raise ValueError(
|
157
|
-
|
158
|
-
|
168
|
+
utils.message_on_path(
|
169
|
+
'Number of DNA child values does not match the number of '
|
159
170
|
f'choices. Child values: {dna.children!r}, '
|
160
|
-
f'Choices: {self.num_choices}.',
|
171
|
+
f'Choices: {self.num_choices}.',
|
172
|
+
self.sym_path,
|
173
|
+
)
|
174
|
+
)
|
161
175
|
if self.choices_distinct or self.choices_sorted:
|
162
176
|
sub_dna_values = [s.value for s in dna]
|
163
177
|
if (self.choices_distinct
|
164
178
|
and len(set(sub_dna_values)) != len(dna.children)):
|
165
179
|
raise ValueError(
|
166
|
-
|
167
|
-
|
168
|
-
f'Encountered: {sub_dna_values}.',
|
180
|
+
utils.message_on_path(
|
181
|
+
'DNA child values should be distinct. '
|
182
|
+
f'Encountered: {sub_dna_values}.',
|
183
|
+
self.sym_path,
|
184
|
+
)
|
185
|
+
)
|
169
186
|
if self.choices_sorted and sorted(sub_dna_values) != sub_dna_values:
|
170
187
|
raise ValueError(
|
171
|
-
|
172
|
-
|
173
|
-
f'Encountered: {sub_dna_values}.',
|
188
|
+
utils.message_on_path(
|
189
|
+
'DNA child values should be sorted. '
|
190
|
+
f'Encountered: {sub_dna_values}.',
|
191
|
+
self.sym_path,
|
192
|
+
)
|
193
|
+
)
|
174
194
|
choices = []
|
175
195
|
for i, sub_dna in enumerate(dna):
|
176
196
|
if not isinstance(sub_dna.value, int):
|
177
197
|
raise ValueError(
|
178
|
-
|
179
|
-
f'Choice value should be int. '
|
180
|
-
|
181
|
-
|
198
|
+
utils.message_on_path(
|
199
|
+
f'Choice value should be int. Encountered: {sub_dna.value}.',
|
200
|
+
utils.KeyPath(i, self.sym_path),
|
201
|
+
)
|
202
|
+
)
|
182
203
|
if sub_dna.value >= len(self.candidates):
|
183
204
|
raise ValueError(
|
184
|
-
|
205
|
+
utils.message_on_path(
|
185
206
|
f'Choice out of range. Value: {sub_dna.value}, '
|
186
207
|
f'Candidates: {len(self.candidates)}.',
|
187
|
-
|
208
|
+
utils.KeyPath(i, self.sym_path),
|
209
|
+
)
|
210
|
+
)
|
188
211
|
choices.append(self._candidate_templates[sub_dna.value].decode(
|
189
212
|
geno.DNA(None, sub_dna.children)))
|
190
213
|
return choices
|
@@ -240,15 +263,21 @@ class Choices(base.HyperPrimitive):
|
|
240
263
|
"""
|
241
264
|
if not isinstance(value, list):
|
242
265
|
raise ValueError(
|
243
|
-
|
244
|
-
|
245
|
-
f'Encountered: {value!r}.',
|
266
|
+
utils.message_on_path(
|
267
|
+
'Cannot encode value: value should be a list type. '
|
268
|
+
f'Encountered: {value!r}.',
|
269
|
+
self.sym_path,
|
270
|
+
)
|
271
|
+
)
|
246
272
|
choices = []
|
247
273
|
if self.num_choices is not None and len(value) != self.num_choices:
|
248
274
|
raise ValueError(
|
249
|
-
|
250
|
-
|
251
|
-
f'({self.num_choices}). Encountered: {value}.',
|
275
|
+
utils.message_on_path(
|
276
|
+
'Length of input list is different from the number of choices '
|
277
|
+
f'({self.num_choices}). Encountered: {value}.',
|
278
|
+
self.sym_path,
|
279
|
+
)
|
280
|
+
)
|
252
281
|
for v in value:
|
253
282
|
choice_id = None
|
254
283
|
child_dna = None
|
@@ -259,10 +288,12 @@ class Choices(base.HyperPrimitive):
|
|
259
288
|
break
|
260
289
|
if child_dna is None:
|
261
290
|
raise ValueError(
|
262
|
-
|
263
|
-
|
291
|
+
utils.message_on_path(
|
292
|
+
'Cannot encode value: no candidates matches with '
|
264
293
|
f'the value. Value: {v!r}, Candidates: {self.candidates}.',
|
265
|
-
self.sym_path
|
294
|
+
self.sym_path,
|
295
|
+
)
|
296
|
+
)
|
266
297
|
choices.append(geno.DNA(choice_id, [child_dna]))
|
267
298
|
return geno.DNA(None, choices)
|
268
299
|
|
@@ -313,12 +344,13 @@ class ManyOf(Choices):
|
|
313
344
|
|
314
345
|
def custom_apply(
|
315
346
|
self,
|
316
|
-
path:
|
347
|
+
path: utils.KeyPath,
|
317
348
|
value_spec: pg_typing.ValueSpec,
|
318
349
|
allow_partial: bool,
|
319
|
-
child_transform: Optional[
|
320
|
-
[
|
321
|
-
|
350
|
+
child_transform: Optional[
|
351
|
+
Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
|
352
|
+
] = None,
|
353
|
+
) -> Tuple[bool, 'Choices']:
|
322
354
|
"""Validate candidates during value_spec binding time."""
|
323
355
|
# Check if value_spec directly accepts `self`.
|
324
356
|
if value_spec.value_type and isinstance(self, value_spec.value_type):
|
@@ -329,10 +361,12 @@ class ManyOf(Choices):
|
|
329
361
|
dest_spec = value_spec
|
330
362
|
if not dest_spec.is_compatible(src_spec):
|
331
363
|
raise TypeError(
|
332
|
-
|
333
|
-
f'Cannot bind an incompatible value spec {dest_spec} '
|
334
|
-
f'to {self.__class__.__name__} with bound spec {src_spec}.',
|
335
|
-
path
|
364
|
+
utils.message_on_path(
|
365
|
+
f'Cannot bind an incompatible value spec {dest_spec!r} '
|
366
|
+
f'to {self.__class__.__name__} with bound spec {src_spec!r}.',
|
367
|
+
path,
|
368
|
+
)
|
369
|
+
)
|
336
370
|
return (False, self)
|
337
371
|
|
338
372
|
list_spec = typing.cast(
|
@@ -399,12 +433,13 @@ class OneOf(Choices):
|
|
399
433
|
|
400
434
|
def custom_apply(
|
401
435
|
self,
|
402
|
-
path:
|
436
|
+
path: utils.KeyPath,
|
403
437
|
value_spec: pg_typing.ValueSpec,
|
404
438
|
allow_partial: bool,
|
405
|
-
child_transform: Optional[
|
406
|
-
[
|
407
|
-
|
439
|
+
child_transform: Optional[
|
440
|
+
Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
|
441
|
+
] = None,
|
442
|
+
) -> Tuple[bool, 'OneOf']:
|
408
443
|
"""Validate candidates during value_spec binding time."""
|
409
444
|
# Check if value_spec directly accepts `self`.
|
410
445
|
if value_spec.value_type and isinstance(self, value_spec.value_type):
|
@@ -413,10 +448,13 @@ class OneOf(Choices):
|
|
413
448
|
if self._value_spec:
|
414
449
|
if not value_spec.is_compatible(self._value_spec):
|
415
450
|
raise TypeError(
|
416
|
-
|
417
|
-
f'Cannot bind an incompatible value spec {value_spec} '
|
451
|
+
utils.message_on_path(
|
452
|
+
f'Cannot bind an incompatible value spec {value_spec!r} '
|
418
453
|
f'to {self.__class__.__name__} with bound '
|
419
|
-
f'spec {self._value_spec}.',
|
454
|
+
f'spec {self._value_spec!r}.',
|
455
|
+
path,
|
456
|
+
)
|
457
|
+
)
|
420
458
|
return (False, self)
|
421
459
|
|
422
460
|
for i, c in enumerate(self.candidates):
|
@@ -427,6 +465,7 @@ class OneOf(Choices):
|
|
427
465
|
self._value_spec = value_spec
|
428
466
|
return (False, self)
|
429
467
|
|
468
|
+
|
430
469
|
#
|
431
470
|
# Helper methods for creating hyper values.
|
432
471
|
#
|
pyglove/core/hyper/custom.py
CHANGED
@@ -19,8 +19,8 @@ import types
|
|
19
19
|
from typing import Any, Callable, Optional, Tuple, Union
|
20
20
|
|
21
21
|
from pyglove.core import geno
|
22
|
-
from pyglove.core import object_utils
|
23
22
|
from pyglove.core import typing as pg_typing
|
23
|
+
from pyglove.core import utils
|
24
24
|
from pyglove.core.hyper import base
|
25
25
|
|
26
26
|
|
@@ -111,8 +111,7 @@ class CustomHyper(base.HyperPrimitive):
|
|
111
111
|
raise NotImplementedError(
|
112
112
|
f'\'custom_encode\' is not supported by {self.__class__.__name__!r}.')
|
113
113
|
|
114
|
-
def dna_spec(
|
115
|
-
self, location: Optional[object_utils.KeyPath] = None) -> geno.DNASpec:
|
114
|
+
def dna_spec(self, location: Optional[utils.KeyPath] = None) -> geno.DNASpec:
|
116
115
|
"""Always returns CustomDecisionPoint for CustomHyper."""
|
117
116
|
return geno.CustomDecisionPoint(
|
118
117
|
hyper_type=self.__class__.__name__,
|
@@ -147,12 +146,13 @@ class CustomHyper(base.HyperPrimitive):
|
|
147
146
|
|
148
147
|
def custom_apply(
|
149
148
|
self,
|
150
|
-
path:
|
149
|
+
path: utils.KeyPath,
|
151
150
|
value_spec: pg_typing.ValueSpec,
|
152
151
|
allow_partial: bool,
|
153
|
-
child_transform: Optional[
|
154
|
-
[
|
155
|
-
|
152
|
+
child_transform: Optional[
|
153
|
+
Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
|
154
|
+
] = None,
|
155
|
+
) -> Tuple[bool, 'CustomHyper']:
|
156
156
|
"""Validate candidates during value_spec binding time."""
|
157
157
|
del path, value_spec, allow_partial, child_transform
|
158
158
|
# Allow custom hyper to be assigned to any type.
|
@@ -11,15 +11,12 @@
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
|
-
"""Tests for pyglove.hyper.CustomHyper."""
|
15
|
-
|
16
14
|
import random
|
17
15
|
import unittest
|
18
16
|
|
19
17
|
from pyglove.core import geno
|
20
|
-
from pyglove.core import object_utils
|
21
18
|
from pyglove.core import symbolic
|
22
|
-
|
19
|
+
from pyglove.core import utils
|
23
20
|
from pyglove.core.hyper.categorical import oneof
|
24
21
|
from pyglove.core.hyper.custom import CustomHyper
|
25
22
|
from pyglove.core.hyper.iter import iterate
|
@@ -58,12 +55,14 @@ class CustomHyperTest(unittest.TestCase):
|
|
58
55
|
"""Test for CustomHyper."""
|
59
56
|
|
60
57
|
def test_dna_spec(self):
|
61
|
-
self.assertTrue(
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
58
|
+
self.assertTrue(
|
59
|
+
symbolic.eq(
|
60
|
+
IntSequence(hints='x').dna_spec('a'),
|
61
|
+
geno.CustomDecisionPoint(
|
62
|
+
hyper_type='IntSequence', location=utils.KeyPath('a'), hints='x'
|
63
|
+
),
|
64
|
+
)
|
65
|
+
)
|
67
66
|
|
68
67
|
def test_decode(self):
|
69
68
|
self.assertEqual(IntSequence().decode(geno.DNA('0,1,2')), [0, 1, 2])
|
pyglove/core/hyper/derived.py
CHANGED
@@ -17,16 +17,19 @@ import abc
|
|
17
17
|
import copy
|
18
18
|
from typing import Any, Callable, List, Optional, Tuple, Union
|
19
19
|
|
20
|
-
from pyglove.core import object_utils
|
21
20
|
from pyglove.core import symbolic
|
22
21
|
from pyglove.core import typing as pg_typing
|
22
|
+
from pyglove.core import utils
|
23
23
|
|
24
24
|
|
25
|
-
@symbolic.members([
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
@symbolic.members([(
|
26
|
+
'reference_paths',
|
27
|
+
pg_typing.List(pg_typing.Object(utils.KeyPath)),
|
28
|
+
(
|
29
|
+
'Paths of referenced values, which are relative paths searched from '
|
30
|
+
'current node to root.'
|
31
|
+
),
|
32
|
+
)])
|
30
33
|
class DerivedValue(symbolic.Object, pg_typing.CustomTyping):
|
31
34
|
"""Base class of value that references to other values in object tree."""
|
32
35
|
|
@@ -36,8 +39,10 @@ class DerivedValue(symbolic.Object, pg_typing.CustomTyping):
|
|
36
39
|
|
37
40
|
def resolve(
|
38
41
|
self, reference_path_or_paths: Optional[Union[str, List[str]]] = None
|
39
|
-
|
40
|
-
|
42
|
+
) -> Union[
|
43
|
+
Tuple[symbolic.Symbolic, utils.KeyPath],
|
44
|
+
List[Tuple[symbolic.Symbolic, utils.KeyPath]],
|
45
|
+
]:
|
41
46
|
"""Resolve reference paths based on the location of this node.
|
42
47
|
|
43
48
|
Args:
|
@@ -54,17 +59,17 @@ class DerivedValue(symbolic.Object, pg_typing.CustomTyping):
|
|
54
59
|
if reference_path_or_paths is None:
|
55
60
|
reference_paths = self.reference_paths
|
56
61
|
elif isinstance(reference_path_or_paths, str):
|
57
|
-
reference_paths = [
|
62
|
+
reference_paths = [utils.KeyPath.parse(reference_path_or_paths)]
|
58
63
|
single_input = True
|
59
|
-
elif isinstance(reference_path_or_paths,
|
64
|
+
elif isinstance(reference_path_or_paths, utils.KeyPath):
|
60
65
|
reference_paths = [reference_path_or_paths]
|
61
66
|
single_input = True
|
62
67
|
elif isinstance(reference_path_or_paths, list):
|
63
68
|
paths = []
|
64
69
|
for path in reference_path_or_paths:
|
65
70
|
if isinstance(path, str):
|
66
|
-
path =
|
67
|
-
elif not isinstance(path,
|
71
|
+
path = utils.KeyPath.parse(path)
|
72
|
+
elif not isinstance(path, utils.KeyPath):
|
68
73
|
raise ValueError('Argument \'reference_path_or_paths\' must be None, '
|
69
74
|
'a string, KeyPath object, a list of strings, or a '
|
70
75
|
'list of KeyPath objects.')
|
@@ -96,8 +101,7 @@ class DerivedValue(symbolic.Object, pg_typing.CustomTyping):
|
|
96
101
|
# Make sure referenced value does not have referenced value.
|
97
102
|
# NOTE(daiyip): We can support dependencies between derived values
|
98
103
|
# in future if needed.
|
99
|
-
if not
|
100
|
-
referenced_value, self._contains_not_derived_value):
|
104
|
+
if not utils.traverse(referenced_value, self._contains_not_derived_value):
|
101
105
|
raise ValueError(
|
102
106
|
f'Derived value (path={referenced_value.sym_path}) should not '
|
103
107
|
f'reference derived values. '
|
@@ -107,15 +111,18 @@ class DerivedValue(symbolic.Object, pg_typing.CustomTyping):
|
|
107
111
|
return self.derive(*referenced_values)
|
108
112
|
|
109
113
|
def _contains_not_derived_value(
|
110
|
-
self, path:
|
114
|
+
self, path: utils.KeyPath, value: Any
|
115
|
+
) -> bool:
|
111
116
|
"""Returns whether a value contains derived value."""
|
112
117
|
if isinstance(value, DerivedValue):
|
113
118
|
return False
|
114
119
|
elif isinstance(value, symbolic.Object):
|
115
120
|
for k, v in value.sym_items():
|
116
|
-
if not
|
117
|
-
v,
|
118
|
-
|
121
|
+
if not utils.traverse(
|
122
|
+
v,
|
123
|
+
self._contains_not_derived_value,
|
124
|
+
root_path=utils.KeyPath(k, path),
|
125
|
+
):
|
119
126
|
return False
|
120
127
|
return True
|
121
128
|
|
@@ -137,12 +144,13 @@ class ValueReference(DerivedValue):
|
|
137
144
|
|
138
145
|
def custom_apply(
|
139
146
|
self,
|
140
|
-
path:
|
147
|
+
path: utils.KeyPath,
|
141
148
|
value_spec: pg_typing.ValueSpec,
|
142
149
|
allow_partial: bool,
|
143
|
-
child_transform: Optional[
|
144
|
-
[
|
145
|
-
|
150
|
+
child_transform: Optional[
|
151
|
+
Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
|
152
|
+
] = None,
|
153
|
+
) -> Tuple[bool, 'DerivedValue']:
|
146
154
|
"""Implement pg_typing.CustomTyping interface."""
|
147
155
|
# TODO(daiyip): perform possible static analysis on referenced paths.
|
148
156
|
del path, value_spec, allow_partial, child_transform
|
@@ -11,13 +11,11 @@
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
|
-
"""Tests for pyglove.hyper.ValueReference."""
|
15
|
-
|
16
14
|
import unittest
|
17
15
|
|
18
|
-
from pyglove.core import object_utils
|
19
16
|
from pyglove.core import symbolic
|
20
17
|
from pyglove.core import typing as pg_typing
|
18
|
+
from pyglove.core import utils
|
21
19
|
from pyglove.core.hyper.derived import ValueReference
|
22
20
|
|
23
21
|
|
@@ -47,7 +45,7 @@ class ValueReferenceTest(unittest.TestCase):
|
|
47
45
|
self.assertEqual(sd.c[0].y.resolve(), [(sd.c[0], 'c[0].x[0].z')])
|
48
46
|
self.assertEqual(sd.c[1].y.resolve(), [(sd.c[1], 'c[1].x[0].z')])
|
49
47
|
# Resolve references from this point.
|
50
|
-
self.assertEqual(sd.c[0].y.resolve(
|
48
|
+
self.assertEqual(sd.c[0].y.resolve(utils.KeyPath(0)), (sd.c, 'c[0]'))
|
51
49
|
self.assertEqual(sd.c[0].y.resolve('[0]'), (sd.c, 'c[0]'))
|
52
50
|
self.assertEqual(
|
53
51
|
sd.c[0].y.resolve(['[0]', '[1]']), [(sd.c, 'c[0]'), (sd.c, 'c[1]')])
|
@@ -18,9 +18,9 @@ import types
|
|
18
18
|
from typing import Any, Callable, Dict, Iterator, List, Optional, Union
|
19
19
|
|
20
20
|
from pyglove.core import geno
|
21
|
-
from pyglove.core import object_utils
|
22
21
|
from pyglove.core import symbolic
|
23
22
|
from pyglove.core import typing as pg_typing
|
23
|
+
from pyglove.core import utils
|
24
24
|
from pyglove.core.hyper import base
|
25
25
|
from pyglove.core.hyper import categorical
|
26
26
|
from pyglove.core.hyper import custom
|
@@ -243,7 +243,7 @@ class DynamicEvaluationContext:
|
|
243
243
|
"""Registers a parameter with current context and return its first value."""
|
244
244
|
def _add_child_decision_point(c):
|
245
245
|
if isinstance(c, types.LambdaType):
|
246
|
-
s = pg_typing.
|
246
|
+
s = pg_typing.signature(c, auto_typing=False, auto_doc=False)
|
247
247
|
if not s.args and not s.has_wildcard_args:
|
248
248
|
sub_context = DynamicEvaluationContext(
|
249
249
|
where=self._where, per_thread=self._per_thread)
|
@@ -464,7 +464,7 @@ class DynamicEvaluationContext:
|
|
464
464
|
get_current_decision = self._decision_getter
|
465
465
|
def _apply_child(c):
|
466
466
|
if isinstance(c, types.LambdaType):
|
467
|
-
s = pg_typing.
|
467
|
+
s = pg_typing.signature(c, auto_typing=False, auto_doc=False)
|
468
468
|
if not s.args and not s.has_wildcard_args:
|
469
469
|
return c()
|
470
470
|
return c
|
@@ -520,10 +520,10 @@ class _DynamicEvaluationStack:
|
|
520
520
|
@property
|
521
521
|
def _local_stack(self):
|
522
522
|
"""Returns thread-local stack."""
|
523
|
-
stack =
|
523
|
+
stack = utils.thread_local_get(self._TLS_KEY, None)
|
524
524
|
if stack is None:
|
525
525
|
stack = []
|
526
|
-
|
526
|
+
utils.thread_local_set(self._TLS_KEY, stack)
|
527
527
|
return stack
|
528
528
|
|
529
529
|
def push(self, context: DynamicEvaluationContext):
|
@@ -585,4 +585,3 @@ def trace(
|
|
585
585
|
with context.collect():
|
586
586
|
fun()
|
587
587
|
return context
|
588
|
-
|