pygx.hyper¶
Hyper objects: representing template-based object space.
hyper
¶
Hyper objects: representing template-based object space.
In PyGX, an object space is represented by a hyper object, which is a
symbolic object with parts placeheld by hyper primitives
(pg.hyper.HyperPrimitive). Through hyper objects, object templates
(pg.hyper.ObjectTemplate) can be obtained to generate objects
based on program genomes (pg.DNA).
classDiagram
HyperValue <|-- ObjectTemplate
HyperValue <|-- HyperPrimitive
HyperPrimitive <|-- Choices
Choices <|-- OneOf
Choices <|-- ManyOf
HyperPrimitive <|-- Float
HyperPrimitive <|-- CustomHyper
CustomHyper <|-- Evolvable
Hyper values map 1:1 to genotypes as the following:
HyperPrimitive
¶
HyperPrimitive(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: Object, HyperValue
Base class for hyper primitives.
A hyper primitive is a pure symbolic object which represents an object
generation rule. It corresponds to a decision point
(pygx.geno.DecisionPoint) in the algorithm's view.
Child classes:
Source code in pygx/symbolic/_object.py
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 | |
HyperValue
¶
Bases: NonDeterministic
Base class for a hyper value.
Hyper value represents a space of objects, which is essential for programmatically generating objects. It can encode a concrete object into a DNA, or decode a DNA into a concrete object.
DNA is a nestable numeric interface we use to generate objects (see pg.geno).
Each position in the DNA represents either the index of a choice, or a value
itself is numeric. There could be multiple choices standing side-by-side,
representing knobs on different parts of an object, or choices being chained,
forming conditional choice spaces, which can be described by a tree structure.
Hyper values form a tree as the following:
classDiagram
ObjectTemplate --> HyperPrimitive : elements (1..*)
HyperPrimitive <|-- OneOf_ManyOf
HyperPrimitive <|-- Float
HyperPrimitive <|-- CustomHyper
OneOf_ManyOf --> ObjectTemplate : candidates (1..*)
Source code in pygx/hyper/_base.py
set_dna
¶
set_dna(dna: DNA) -> None
Use this DNA to generate value.
NOTE(daiyip): self.dna is only used in __call_. Thus 'set_dna' can be called multiple times to generate different values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dna
|
DNA
|
DNA to use to decode the value. |
required |
Source code in pygx/hyper/_base.py
encode
abstractmethod
¶
encode(value: Any) -> DNA
Encode a value into a DNA.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
A value that conforms to the hyper value definition. |
required |
Returns:
| Type | Description |
|---|---|
DNA
|
DNA for the value. |
dna_spec
abstractmethod
¶
Choices
¶
Choices(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: HyperPrimitive
Categorical choices from a list of candidates.
Example:
# A single categorical choice:
v = pg.oneof([1, 2, 3])
# A multiple categorical choice as a list:
vs = pg.manyof(2, [1, 2, 3])
# A hierarchical categorical choice:
v2 = pg.oneof([
'foo',
'bar',
pg.manyof(2, [1, 2, 3])
])
See also:
Source code in pygx/symbolic/_object.py
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 | |
on_sym_bound
¶
On members are bound.
Source code in pygx/hyper/_categorical.py
dna_spec
¶
Returns corresponding DNASpec.
Source code in pygx/hyper/_categorical.py
encode
¶
encode(value: list[Any]) -> DNA
Encode a list of values into DNA.
Each value in the list is matched against the candidates from left to right; the DNA records the index of the first matching candidate, plus child decisions when the candidate contains nested hyper values.
Example:
# A multiple choice encodes to the list of chosen indices.
c = pg.manyof(2, ['a', 'b', 'c'])
assert c.encode(['a', 'c']) == pg.DNA([0, 2])
# A nested hyper value contributes child decisions.
c = pg.oneof(['foo', pg.Dict(x=pg.oneof([1, 2]))])
assert c.encode('foo') == pg.DNA(0)
assert c.encode(pg.Dict(x=2)) == pg.DNA((1, 1))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
list[Any]
|
A list of values that can match choice candidates. |
required |
Returns:
| Type | Description |
|---|---|
DNA
|
Encoded DNA. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in pygx/hyper/_categorical.py
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | |
ManyOf
¶
ManyOf(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: Choices
N Choose K.
Example:
# Chooses 2 distinct candidates.
v = pg.manyof(2, [1, 2, 3])
# Chooses 2 non-distinct candidates.
v = pg.manyof(2, [1, 2, 3], distinct=False)
# Chooses 2 distinct candidates sorted by their indices.
v = pg.manyof(2, [1, 2, 3], sorted=True)
# Permutates the candidates.
v = pg.permutate([1, 2, 3])
# A hierarchical categorical choice:
v2 = pg.manyof(2, [
'foo',
'bar',
pg.oneof([1, 2, 3])
])
See also:
pygx.manyofpygx.permutatepygx.hyper.Choicespygx.hyper.OneOfpygx.hyper.Floatpygx.hyper.CustomHyper
Source code in pygx/symbolic/_object.py
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 | |
custom_apply
¶
custom_apply(
path: KeyPath,
value_spec: ValueSpec,
allow_partial: bool,
child_transform: None | Callable[[KeyPath, Field, Any], Any] = None,
) -> tuple[bool, Choices]
Validate candidates during value_spec binding time.
Source code in pygx/hyper/_categorical.py
OneOf
¶
OneOf(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: Choices
N Choose 1.
Example:
# A single categorical choice:
v = pg.oneof([1, 2, 3])
# A hierarchical categorical choice:
v2 = pg.oneof([
'foo',
'bar',
pg.oneof([1, 2, 3])
])
See also:
Source code in pygx/symbolic/_object.py
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 | |
on_sym_bound
¶
encode
¶
encode(value: Any) -> DNA
Encode a value into a DNA.
Source code in pygx/hyper/_categorical.py
custom_apply
¶
custom_apply(
path: KeyPath,
value_spec: ValueSpec,
allow_partial: bool,
child_transform: None | Callable[[KeyPath, Field, Any], Any] = None,
) -> tuple[bool, OneOf]
Validate candidates during value_spec binding time.
Source code in pygx/hyper/_categorical.py
CustomHyper
¶
CustomHyper(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: HyperPrimitive
User-defined hyper primitive.
User-defined hyper primitive is useful when users want to have full control on the semantics and genome encoding of the search space. For example, the decision points are of variable length, which is not yet supported by built-in hyper primitives.
Using a user-defined hyper primitive is simple. The user should:
1) Subclass CustomHyper and implement the
custom_decode method.
It's optional to implement the
custom_encode method, which
is only necessary when the user wants to encode a material object
into a DNA.
2) Introduce a DNAGenerator that can generate DNA for the
pygx.geno.CustomDecisionPoint.
For example, the following code tries to find an optimal sub-sequence of an integer sequence by their sums:
import random
class IntSequence(pg.hyper.CustomHyper):
def custom_decode(self, dna):
return [int(v) for v in dna.value.split(',') if v != '']
class SubSequence(pg.algo.evolution.Mutator):
def mutate(self, dna):
genome = dna.value
items = genome.split(',')
start = random.randint(0, len(items))
end = random.randint(start, len(items))
new_genome = ','.join(items[start:end])
return pg.DNA(new_genome, spec=dna.spec)
@pg.geno.dna_generator
def initial_population(dna_spec):
yield pg.DNA('12,-34,56,-2,100,98', spec=dna_spec)
algo = pg.algo.evolution.Evolution(
(pg.algo.evolution.selectors.Random(10)
>> pg.algo.evolution.selectors.Top(1)
>> SubSequence()),
population_init=initial_population(),
population_update=pg.algo.evolution.selectors.Last(20))
best_reward, best_example = None, None
for int_seq, feedback in pg.sample(IntSequence(), algo, num_examples=100):
reward = sum(int_seq)
if best_reward is None or best_reward < reward:
best_reward, best_example = reward, int_seq
feedback(reward)
print(best_reward, best_example)
Please note that user-defined hyper value can be used together with PyGX's built-in hyper primitives, for example:
Therefore it's also a mechanism to extend PyGX's search space definitions.
Source code in pygx/symbolic/_object.py
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 | |
dna_spec
¶
Always returns CustomDecisionPoint for CustomHyper.
Source code in pygx/hyper/_custom.py
first_dna
¶
first_dna() -> DNA
Returns the first DNA of current sub-space.
Returns:
| Type | Description |
|---|---|
DNA
|
A string-valued DNA. |
Source code in pygx/hyper/_custom.py
next_dna
¶
Override to support sweeping via pg.geno.Sweeping.
random_dna
¶
random_dna(
random_generator: ModuleType | Random | None = None,
previous_dna: DNA | None = None,
) -> DNA
Override to support random sampling via pg.random_dna.
Source code in pygx/hyper/_custom.py
custom_apply
¶
custom_apply(
path: KeyPath,
value_spec: ValueSpec,
allow_partial: bool,
child_transform: None | Callable[[KeyPath, Field, Any], Any] = None,
) -> tuple[bool, CustomHyper]
Validate candidates during value_spec binding time.
Source code in pygx/hyper/_custom.py
DerivedValue
¶
DerivedValue(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: Object, CustomTyping
Base class for values that reference other values in an object tree.
Source code in pygx/symbolic/_object.py
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 | |
derive
abstractmethod
¶
resolve
¶
resolve(
reference_path_or_paths: str | list[str] | None = None,
) -> tuple[Symbolic, KeyPath] | list[tuple[Symbolic, KeyPath]]
Resolve reference paths based on the location of this node.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reference_path_or_paths
|
str | list[str] | None
|
(Optional) a string or KeyPath as a reference path or a list of strings or KeyPath objects as a list of reference paths. If this argument is not provided, prebound reference paths of this object will be used. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[Symbolic, KeyPath] | list[tuple[Symbolic, KeyPath]]
|
A tuple (or list of tuple) of (resolved parent, resolved full path) |
Source code in pygx/hyper/_derived.py
ValueReference
¶
Bases: DerivedValue
Class that represents a value referencing another value.
Source code in pygx/hyper/_derived.py
on_sym_bound
¶
derive
¶
Derive value by return a copy of the referenced value.
Source code in pygx/hyper/_derived.py
custom_apply
¶
custom_apply(
path: KeyPath,
value_spec: ValueSpec,
allow_partial: bool,
child_transform: None | Callable[[KeyPath, Field, Any], Any] = None,
) -> tuple[bool, DerivedValue]
Implement pg_typing.CustomTyping interface.
Source code in pygx/hyper/_derived.py
DynamicEvaluationContext
¶
DynamicEvaluationContext(
where: Callable[[HyperPrimitive], bool] | None = None,
require_hyper_name: bool = False,
per_thread: bool = True,
dna_spec: DNASpec | None = None,
)
Context for dynamic evaluation of hyper primitives.
Example:
# Define a function that implicitly declares a search space.
def foo():
return pg.oneof(range(-10, 10)) ** 2 + pg.oneof(range(-10, 10)) ** 2
# Define the search space by running `foo` once.
search_space = pg.hyper.DynamicEvaluationContext()
with search_space.collect():
_ = foo()
# Create a search algorithm.
search_algorithm = pg.algo.evolution.regularized_evolution(
pg.algo.evolution.mutators.Uniform(),
population_size=32, tournament_size=16)
# Define the feedback loop.
best_foo, best_reward = None, None
for example, feedback in pg.sample(
search_space, search_algorithm, num_examples=100):
# Calling `example` returns a context manager under which `foo` is
# connected with current search algorithm decisions.
with example():
reward = foo()
feedback(reward)
if best_reward is None or best_reward < reward:
best_foo, best_reward = example, reward
Create a dynamic evaluation context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
where
|
Callable[[HyperPrimitive], bool] | None
|
A callable object that decide whether a hyper primitive should be
included when being instantiated under |
None
|
require_hyper_name
|
bool
|
If True, all hyper primitives (e.g. pg.oneof) must
come with a |
False
|
per_thread
|
bool
|
If True, the context manager will be applied to current thread only. Otherwise, it will be applied on current process. |
True
|
dna_spec
|
DNASpec | None
|
External provided search space. If None, the dynamic evaluation
context can be used to create new search space via the |
None
|
Source code in pygx/hyper/_dynamic_evaluation.py
per_thread
property
¶
Returns True if current context collects/applies decisions per thread.
is_external
property
¶
Returns True if the search space is defined by an external DNASpec.
hyper_dict
property
¶
hyper_dict: Dict | None
Returns collected hyper primitives as a dict.
None if current context is controlled by an external DNASpec.
collect
¶
collect() -> Iterator[Dict]
A context manager for collecting hyper primitives within this context.
Within the scope, each hyper primitive is registered as a decision point in the search space and evaluates to its first value.
Example:
context = pg.hyper.DynamicEvaluationContext()
with context.collect():
x = pg.oneof([1, 2, 3]) + pg.oneof([4, 5, 6])
# Prints 5 (1 + 4): each `pg.oneof` evaluated to its first
# candidate. Meanwhile 2 decision points were registered in the
# search space represented by the context.
print(x)
Yields:
| Type | Description |
|---|---|
Dict
|
The hyper dict representing the search space. |
Source code in pygx/hyper/_dynamic_evaluation.py
add_decision_point
¶
add_decision_point(hyper_primitive: HyperPrimitive)
Registers a parameter with current context and return its first value.
Source code in pygx/hyper/_dynamic_evaluation.py
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | |
apply
¶
apply(decisions: DNA | list[int | float | str]) -> Iterator[None]
Context manager for applying decisions.
Within the scope, each hyper primitive evaluates to the value chosen by the corresponding decision, instead of returning a symbolic object.
Example:
def fun():
return pg.oneof([1, 2, 3]) + pg.oneof([4, 5, 6])
context = pg.hyper.DynamicEvaluationContext()
with context.collect():
fun()
with context.apply([0, 1]):
# Prints 6 (1 + 5).
print(fun())
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
decisions
|
DNA | list[int | float | str]
|
A DNA or a list of numbers or strings as decisions for current search space. |
required |
Yields:
| Type | Description |
|---|---|
None
|
None |
Source code in pygx/hyper/_dynamic_evaluation.py
evaluate
¶
evaluate(hyper_primitive: HyperPrimitive)
Evaluates a hyper primitive based on current decisions.
Source code in pygx/hyper/_dynamic_evaluation.py
Evolvable
¶
Evolvable(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: CustomHyper
Hyper primitive for evolving an arbitrary symbolic object.
Source code in pygx/symbolic/_object.py
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 | |
mutation_points_and_weights
¶
mutation_points_and_weights(
value: Symbolic,
) -> tuple[list[MutationPoint], list[float]]
Returns mutation points with weights for a symbolic tree.
Source code in pygx/hyper/_evolvable.py
random_dna
¶
random_dna(
random_generator: ModuleType | Random | None = None,
previous_dna: DNA | None = None,
) -> DNA
Generates a random DNA.
Source code in pygx/hyper/_evolvable.py
mutate
¶
Returns the next value for a symbolic value.
Source code in pygx/hyper/_evolvable.py
MutationPoint
dataclass
¶
MutationPoint(
mutation_type: MutationType,
location: KeyPath,
old_value: Any,
parent: Symbolic | None,
)
Information about a candidate mutation point in a symbolic tree.
Attributes:
| Name | Type | Description |
|---|---|---|
mutation_type |
MutationType
|
The type of the mutation. |
location |
KeyPath
|
The location where the mutation will take place. |
old_value |
Any
|
The value of the mutation point before mutation. |
parent |
Symbolic | None
|
The parent node of the mutation point. |
MutationType
¶
Bases: str, Enum
Mutation type.
Float
¶
Float(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: HyperPrimitive
A continuous value within a range.
Example:
See also:
Source code in pygx/symbolic/_object.py
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 | |
on_sym_bound
¶
Constructor.
Source code in pygx/hyper/_numerical.py
dna_spec
¶
Returns corresponding DNASpec.
Source code in pygx/hyper/_numerical.py
encode
¶
encode(value: float) -> DNA
Encode a float value into a DNA.
Source code in pygx/hyper/_numerical.py
custom_apply
¶
custom_apply(
path: KeyPath,
value_spec: ValueSpec,
allow_partial: bool = False,
child_transform: None | Callable[[KeyPath, Field, Any], Any] = None,
) -> tuple[bool, Float]
Validate candidates during value_spec binding time.
Source code in pygx/hyper/_numerical.py
ObjectTemplate
¶
ObjectTemplate(
value: Any,
compute_derived: bool = False,
where: Callable[[HyperPrimitive], bool] | None = None,
)
Bases: HyperValue, Formattable
Object template that encodes and decodes symbolic values.
An object template can be created from a hyper value, which is a symbolic value with some parts placeheld by hyper primitives.
Example:
x = pg.Dict(
a=0,
b=pg.oneof(['foo', 'bar']),
c=pg.manyof(2, [1, 2, 3]),
d=pg.floatv(0.1, 0.5),
e=pg.oneof([
pg.Dict(f=pg.oneof([True, False])),
pg.Dict(g=pg.manyof(2, [4, 5, 6], distinct=False)),
]),
)
t = pg.template(x)
In this example, the root template has 4 child hyper primitives associated with keys 'b', 'c', 'd' and 'e', while the hyper primitive 'e' has child decision points under keys 'f' and 'g' - one per candidate - creating a conditional search space.
The DNA shape is determined by the definition of the template, described
by its geno.DNASpec (accessible via t.dna_spec()), which in this
case renders as:
Space({
0 = 'b': Choices(num_choices=1, [
(0): 'foo'
(1): 'bar'
])
1 = 'c': Choices(num_choices=2, [
(0): 1
(1): 2
(2): 3
])
2 = 'd': Float(min_value=0.1, max_value=0.5)
3 = 'e': Choices(num_choices=1, [
(0): Space({
0 = 'f': Choices(num_choices=1, [
(0): True
(1): False
])
})
(1): Space({
0 = 'g': Choices(num_choices=2, [
(0): 4
(1): 5
(2): 6
], distinct=False)
})
])
})
A DNA of this spec can then be decoded into a concrete value:
assert t.decode(pg.DNA([0, [0, 2], 0.1, (0, 0)])) == pg.Dict(
a=0, b='foo', c=[1, 3], d=0.1, e=pg.Dict(f=True))
A template can also work on only a subset of the hyper primitives from
the input value through the where function. This is useful to
partition a search space into parts for separate optimization. Note
that filtering by path requires the enclosing object to track topology
(pg.Dict and pg.List do; pg.Object subclasses need topo=True):
class A(pg.Object, topo=True):
a: int
b: int
t = pg.hyper.ObjectTemplate(
A(a=pg.oneof([1, 2]), b=pg.oneof([3, 4])),
where=lambda e: e.topo_path == 'a')
assert len(t.dna_spec().decision_points) == 1
# 'b' stays a hyper primitive in the decoded value.
assert t.decode(pg.DNA(0)) == A(a=1, b=pg.oneof([3, 4]))
Constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
Value (maybe) annotated with generators to use as template. |
required |
compute_derived
|
bool
|
Whether to compute derived value at this level. We only want to compute derived value at root level since reference path may go out of scope of a non-root ObjectTemplate. |
False
|
where
|
Callable[[HyperPrimitive], bool] | None
|
Function to filter hyper primitives. If None, all hyper primitives
from |
None
|
Source code in pygx/hyper/_object_template.py
hyper_primitives
property
¶
hyper_primitives: list[tuple[str, HyperValue]]
Returns hyper primitives in tuple (relative path, hyper primitive).
dna_spec
¶
Return DNA spec (geno.Space) from this template.
Source code in pygx/hyper/_object_template.py
encode
¶
encode(value: Any) -> DNA
Encode a value into a DNA.
Example:
# DNA of a constant template:
template = pg.hyper.ObjectTemplate({'a': 0})
assert template.encode({'a': 0}) == pg.DNA(None)
# Raises: Unmatched value between template and input.
template.encode({'a': 1})
# DNA of a template containing only one pg.oneof.
template = pg.hyper.ObjectTemplate({'a': pg.oneof([1, 2])})
assert template.encode({'a': 1}) == pg.DNA(0)
# DNA of a template containing only one pg.floatv.
template = pg.hyper.ObjectTemplate({'a': pg.floatv(0.1, 1.0)})
assert template.encode({'a': 0.5}) == pg.DNA(0.5)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
Value to encode. |
required |
Returns:
| Type | Description |
|---|---|
DNA
|
Encoded DNA. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in pygx/hyper/_object_template.py
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 | |
try_encode
¶
try_encode(value: Any) -> tuple[bool, DNA | None]
Try to encode a value without raising an exception.
Returns:
| Type | Description |
|---|---|
tuple[bool, DNA | None]
|
A tuple of (success, DNA). DNA is None when encoding fails. |
Source code in pygx/hyper/_object_template.py
format
¶
Format this object.
Source code in pygx/hyper/_object_template.py
custom_apply
¶
custom_apply(
path: KeyPath,
value_spec: ValueSpec,
allow_partial: bool,
child_transform: None | Callable[[KeyPath, Field, Any], Any] = None,
) -> tuple[bool, ObjectTemplate]
Validate candidates during value_spec binding time.
Source code in pygx/hyper/_object_template.py
manyof
¶
manyof(
k: int,
candidates: Iterable[Any],
distinct: bool = True,
sorted: bool = False,
*,
name: str | None = None,
hints: Any | None = None,
**kwargs: Any
) -> Any
N choose K.
Example:
class A(pg.Object):
x: int
# Chooses 2 distinct candidates.
v = pg.manyof(2, [1, 2, 3])
# Chooses 2 non-distinct candidates.
v = pg.manyof(2, [1, 2, 3], distinct=False)
# Chooses 2 distinct candidates sorted by their indices.
v = pg.manyof(2, [1, 2, 3], sorted=True)
# A complex type as candidate.
v1 = pg.manyof(2, ['a', {'x': 1}, A(x=1)])
# A hierarchical categorical choice:
v2 = pg.manyof(2, [
'foo',
'bar',
A(x=pg.oneof([1, 2, 3]))
])
Note
Under symbolic mode (the default), pg.manyof returns a
pg.hyper.ManyOf object. Under dynamic
evaluation mode, i.e. within the context of
pg.hyper.DynamicEvaluationContext.collect or
pg.hyper.DynamicEvaluationContext.apply, it evaluates to a
concrete list of candidate values.
To use conditional search space in dynamic evaluation mode, the
candidate should be wrapped with a lambda function, which is not
necessary under symbolic mode. For example:
See also:
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
k
|
int
|
Number of choices to make. Should be no larger than the length of
|
required |
candidates
|
Iterable[Any]
|
Candidates to select from. Items of candidate can be any type, therefore it can have nested hyper primitives, which forms a hierarchical search space. |
required |
distinct
|
bool
|
If True, each choice needs to be unique. |
True
|
sorted
|
bool
|
If True, choices are sorted by their indices in the candidates. |
False
|
name
|
str | None
|
A name that identifies the decision point in the search space.
Instances created with the same name refer to the same decision point
(see |
None
|
hints
|
Any | None
|
An optional value which acts as a hint for the controller. |
None
|
**kwargs
|
Any
|
Keyword arguments for backward compatibility.
|
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
In symbolic mode, this function returns a |
Any
|
In dynamic evaluation mode, this function returns a list of items in |
Any
|
|
Any
|
under a |
Any
|
the list of selected candidates; |
Any
|
under a |
Any
|
the first valid combination from the |
Any
|
```python |
Any
|
Evaluates to [0, 1, 2] under
|
Any
|
pg.manyof(3, range(5)) |
Any
|
Evaluates to [0, 0, 0] under
|
Any
|
pg.manyof(3, range(5), distinct=False) |
Any
|
``` |
Source code in pygx/hyper/_categorical.py
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 | |
oneof
¶
N choose 1.
Example:
class A(pg.Object):
x: int
# A single categorical choice:
v = pg.oneof([1, 2, 3])
# A complex type as candidate.
v1 = pg.oneof(['a', {'x': 1}, A(x=1)])
# A hierarchical categorical choice:
v2 = pg.oneof([
'foo',
'bar',
A(x=pg.oneof([1, 2, 3]))
])
Decision points at different call sites are distinct by default. Passing
the same name merges them into ONE decision point: the search space has
a single decision under that name, and every call evaluates to the same
value. Primitives sharing a name must have identical definitions,
otherwise an error is raised.
def fn():
# Both calls carry the name 'x', so they refer to the same decision
# point in the search space.
return pg.oneof([1, 2, 3], name='x') + pg.oneof([1, 2, 3], name='x')
context = pg.hyper.trace(fn)
assert len(context.dna_spec.decision_points) == 1
with context.apply([2]):
assert fn() == 3 + 3
See also:
Note
Under symbolic mode (the default), pg.oneof returns a
pg.hyper.OneOf object. Under dynamic evaluation
mode, i.e. within the context of
pg.hyper.DynamicEvaluationContext.collect or
pg.hyper.DynamicEvaluationContext.apply, it evaluates to a
concrete candidate value.
To use conditional search space in dynamic evaluation mode, the
candidate should be wrapped with a lambda function, which is not
necessary under symbolic mode. For example:
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
candidates
|
Iterable[Any]
|
Candidates to select from. Items of candidate can be any type, therefore it can have nested hyper primitives, which forms a hierarchical search space. |
required |
name
|
str | None
|
A name that identifies the decision point in the search space.
Instances created with the same name refer to the same decision point
(see above) - this is needed when the code that instantiates the
hyper primitive may be called multiple times under a
|
None
|
hints
|
Any | None
|
An optional value which acts as a hint for the controller. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
In symbolic mode, this function returns a |
Any
|
In dynamic evaluation mode, this function returns one of the items in |
Any
|
|
Any
|
under a |
Any
|
the selected candidate; |
Any
|
under a |
Any
|
the first candidate. |
Source code in pygx/hyper/_categorical.py
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 | |
permutate
¶
Permutation of candidates.
Example:
class A(pg.Object):
x: int
# Permutates the candidates.
v = pg.permutate([1, 2, 3])
# A complex type as candidate.
v1 = pg.permutate(['a', {'x': 1}, A(x=1)])
# A hierarchical categorical choice:
v2 = pg.permutate([
'foo',
'bar',
A(x=pg.oneof([1, 2, 3]))
])
Note
Under symbolic mode (the default), pg.permutate returns a
pg.hyper.ManyOf object. Under dynamic
evaluation mode, i.e. within the context of
pg.hyper.DynamicEvaluationContext.collect or
pg.hyper.DynamicEvaluationContext.apply, it evaluates to a
concrete permutation of the candidate values.
To use conditional search space in dynamic evaluation mode, the
candidate should be wrapped with a lambda function, which is not
necessary under symbolic mode. For example:
See also:
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
candidates
|
Iterable[Any]
|
Candidates to select from. Items of candidate can be any type, therefore it can have nested hyper primitives, which forms a hierarchical search space. |
required |
name
|
str | None
|
A name that identifies the decision point in the search space.
Instances created with the same name refer to the same decision point
(see |
None
|
hints
|
Any | None
|
An optional value which acts as a hint for the controller. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
In symbolic mode, this function returns a |
Any
|
In dynamic evaluation mode, this function returns a permutation of |
Any
|
|
Any
|
under a |
Any
|
a permutation of candidates based on controller decisions; |
Any
|
under a |
Any
|
the first valid permutation. For example: |
Any
|
```python |
Any
|
Evaluates to [0, 1, 2, 3, 4] under
|
Any
|
pg.permutate(range(5), name='numbers') |
Any
|
``` |
Source code in pygx/hyper/_categorical.py
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 | |
reference
¶
reference(reference_path: str) -> ValueReference
Creates a reference to the value at a relative path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reference_path
|
str
|
Relative path of the referenced value, searched from the parent of the reference towards the root of the object tree. |
required |
Returns:
| Type | Description |
|---|---|
ValueReference
|
A |
ValueReference
|
to a copy of the referenced value. |
Source code in pygx/hyper/_derived.py
dynamic_evaluate
¶
dynamic_evaluate(
evaluate_fn: Callable[[HyperValue], Any] | None,
yield_value: Any = None,
exit_fn: Callable[[], None] | None = None,
per_thread: bool = True,
) -> Iterator[Any]
Eagerly evaluate hyper primitives within current scope.
Within the scope, creating a hyper primitive (e.g. pg.oneof) does not
return the symbolic object; instead evaluate_fn is called with it and
its result is returned.
Example:
def evaluate_fn(x: pg.hyper.HyperPrimitive):
if isinstance(x, pg.hyper.OneOf):
return x.candidates[0]
raise NotImplementedError()
with pg.hyper.dynamic_evaluate(evaluate_fn):
assert pg.oneof([0, 1, 2]) == 0
Please see
apply
as an example for using this method.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
evaluate_fn
|
Callable[[HyperValue], Any] | None
|
A callable object that evaluates a hyper value (e.g. oneof, manyof, floatv) into a concrete value. |
required |
yield_value
|
Any
|
Value to yield return. |
None
|
exit_fn
|
Callable[[], None] | None
|
A callable object to be called when exiting the context scope. |
None
|
per_thread
|
bool
|
If True, the context manager will be applied to current thread only. Otherwise, it will be applied on current process. |
True
|
Yields:
| Type | Description |
|---|---|
Any
|
|
Source code in pygx/hyper/_dynamic_evaluation.py
trace
¶
trace(
fun: Callable[[], Any],
*,
where: Callable[[HyperPrimitive], bool] | None = None,
require_hyper_name: bool = False,
per_thread: bool = True
) -> DynamicEvaluationContext
Trace the hyper primitives called within a function by executing it.
Example:
import pygx as pg
def fn():
return pg.oneof([1, 2, 3]) + pg.floatv(0.0, 1.0)
context = pg.hyper.trace(fn)
with context.apply([0, 0.5]):
assert fn() == 1.5
See more examples in
DynamicEvaluationContext.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fun
|
Callable[[], Any]
|
Function in which the search space is defined. |
required |
where
|
Callable[[HyperPrimitive], bool] | None
|
A callable object that decide whether a hyper primitive should be
included when being instantiated under |
None
|
require_hyper_name
|
bool
|
If True, all hyper primitives defined in this scope will need to carry their names, which is usually a good idea when the function that instantiates the hyper primtives need to be called multiple times. |
False
|
per_thread
|
bool
|
If True, the context manager will be applied to current thread only. Otherwise, it will be applied on current process. |
True
|
Returns:
| Type | Description |
|---|---|
DynamicEvaluationContext
|
A |
Source code in pygx/hyper/_dynamic_evaluation.py
evolve
¶
evolve(
initial_value: Symbolic,
node_transform: Callable[[KeyPath, Any, Symbolic], Any],
*,
weights: (
None | Callable[[MutationType, KeyPath, Any, Symbolic], float]
) = None,
name: str | None = None,
hints: Any | None = None
) -> Evolvable
An evolvable symbolic value.
Example:
import random
import pygx as pg
class Foo(pg.Object):
x: int
y: int
class Bar(pg.Object):
a: int
b: int
# Defines possible transitions.
def node_transform(location, value, parent):
if isinstance(value, Foo):
return Bar(a=value.x, b=value.y)
if location.key == 'x':
return random.choice([1, 2, 3])
if location.key == 'y':
return random.choice([3, 4, 5])
return value
v = pg.evolve(Foo(x=1, y=3), node_transform)
See also:
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
initial_value
|
Symbolic
|
The initial value to evolve. |
required |
node_transform
|
Callable[[KeyPath, Any, Symbolic], Any]
|
A callable object that takes information of the value to
operate (e.g. location, old value, parent node) and returns a new value as
a replacement for the node. Such information allows users to not only
access the mutation node, but the entire symbolic tree if needed, allowing
complex mutation rules to be written with ease - for example - check
adjacent nodes while modifying a list element. This function is designed
to take care of both node replacements and node insertions. When insertion
happens, the old value for the location will be |
required |
weights
|
None | Callable[[MutationType, KeyPath, Any, Symbolic], float]
|
An optional callable object that returns the unnormalized (i.e. the probabilities don't have to sum to 1.0) mutation probabilities for all the nodes in the symbolic tree, based on (mutation type, location, old value, parent node). If None, all the locations and mutation types will be sampled uniformly. |
None
|
name
|
str | None
|
An optional name of the decision point. |
None
|
hints
|
Any | None
|
An optional hints for the decision point. |
None
|
Returns:
| Type | Description |
|---|---|
Evolvable
|
A |
Source code in pygx/hyper/_evolvable.py
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | |
iterate
¶
iterate(
hyper_value: Any,
num_examples: int | None = None,
algorithm: DNAGenerator | None = None,
where: Callable[[HyperPrimitive], bool] | None = None,
force_feedback: bool = False,
) -> Iterator[Any]
Iterate a hyper value based on an algorithm.
Example:
hyper_dict = pg.Dict(x=pg.oneof([1, 2, 3]), y=pg.oneof(['a', 'b']))
# Get all examples from the hyper_dict.
assert list(pg.iter(hyper_dict)) == [
pg.Dict(x=1, y='a'),
pg.Dict(x=1, y='b'),
pg.Dict(x=2, y='a'),
pg.Dict(x=2, y='b'),
pg.Dict(x=3, y='a'),
pg.Dict(x=3, y='b'),
]
# Get the first two examples.
assert list(pg.iter(hyper_dict, 2)) == [
pg.Dict(x=1, y='a'),
pg.Dict(x=1, y='b'),
]
# Random sample examples, which is equivalent to `pg.random_sample`.
list(pg.iter(hyper_dict, 2, pg.geno.Random()))
# Iterate examples with feedback loop.
algo = pg.algo.evolution.regularized_evolution(
pg.algo.evolution.mutators.Uniform())
for d, feedback in pg.iter(hyper_dict, 10, algo):
feedback(d.x)
# Only materialize selected parts.
assert list(
pg.iter(hyper_dict, where=lambda x: len(x.candidates) == 2)) == [
pg.Dict(x=pg.oneof([1, 2, 3]), y='a'),
pg.Dict(x=pg.oneof([1, 2, 3]), y='b'),
]
pg.iter differs from pg.sample in that it's designed
for simple in-process iteration, which is handy for quickly generating
examples from algorithms without maintaining trial states. On the contrary,
pg.sample is designed for distributed sampling, with parallel workers and
failover handling.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hyper_value
|
Any
|
A hyper value that represents a space of instances, or a
|
required |
num_examples
|
int | None
|
An optional integer as the max number of examples to
yield. If None, iteration ends only when the algorithm is exhausted
(e.g. |
None
|
algorithm
|
DNAGenerator | None
|
An optional DNA generator. If None, Sweeping will be used, which iterates examples in order. |
None
|
where
|
Callable[[HyperPrimitive], bool] | None
|
Function to filter hyper primitives. If None, all hyper primitives
from |
None
|
force_feedback
|
bool
|
If True, always return the Feedback object together
with the example, this is useful when the user want to pass different
DNAGenerators to |
False
|
Yields:
| Type | Description |
|---|---|
Any
|
A tuple of (example, feedback_fn) if the algorithm needs a feedback or |
Any
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
when |
Source code in pygx/hyper/_iter.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | |
random_sample
¶
random_sample(
value: Any,
num_examples: int | None = None,
where: Callable[[HyperPrimitive], bool] | None = None,
seed: int | None = None,
) -> Iterator[Any]
Returns an iterator of random sampled examples.
Example:
hyper_dict = pg.Dict(x=pg.oneof(range(3)), y=pg.floatv(0.0, 1.0))
# Generate one random example from the hyper_dict.
d = next(pg.random_sample(hyper_dict))
# Generate 5 random examples with random seed.
ds = list(pg.random_sample(hyper_dict, 5, seed=1))
# Generate 3 random examples of `x` with `y` intact.
ds = list(pg.random_sample(hyper_dict, 3,
where=lambda x: isinstance(x, pg.hyper.OneOf)))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
A (maybe) hyper value. |
required |
num_examples
|
int | None
|
An optional integer as number of examples to yield. If None, the returned iterator iterates forever. |
None
|
where
|
Callable[[HyperPrimitive], bool] | None
|
Function to filter hyper primitives. If None, all hyper primitives in
|
None
|
seed
|
int | None
|
An optional integer as random seed. |
None
|
Returns:
| Type | Description |
|---|---|
Iterator[Any]
|
Iterator of random examples. |
Source code in pygx/hyper/_iter.py
floatv
¶
floatv(
min_value: float,
max_value: float,
scale: str | None = None,
*,
name: str | None = None,
hints: Any | None = None
) -> Any
A continuous value within a range.
Example:
See also:
Note
Under symbolic mode (the default), pg.floatv returns a
pg.hyper.Float object. Under dynamic evaluation
mode, i.e. within the context of
pg.hyper.DynamicEvaluationContext.collect or
pg.hyper.DynamicEvaluationContext.apply, it evaluates to a
concrete float value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
min_value
|
float
|
Minimum acceptable value (inclusive). |
required |
max_value
|
float
|
Maximum acceptable value (inclusive). |
required |
scale
|
str | None
|
An optional string as the scale of the range. Supported values
are None, 'linear', 'log', and 'rlog'.
If None, the feasible space is unscaled.
If |
None
|
name
|
str | None
|
A name that identifies the decision point in the search space.
Instances created with the same name refer to the same decision point
(see |
None
|
hints
|
Any | None
|
An optional value which acts as a hint for the controller. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
In symbolic mode, this function returns a |
Any
|
In dynamic evaluation mode, this function returns a float value that is |
Any
|
no less than the |
Any
|
under a |
Any
|
the chosen float value from the controller decisions; |
Any
|
under a |
Any
|
|
Source code in pygx/hyper/_numerical.py
dna_spec
¶
dna_spec(
value: Any, where: Callable[[HyperPrimitive], bool] | None = None
) -> DNASpec
Returns the DNASpec from a (maybe) hyper value.
Example:
hyper = pg.Dict(x=pg.oneof([1, 2, 3]), y=pg.oneof(['a', 'b']))
spec = pg.dna_spec(hyper)
assert spec.space_size == 6
assert len(spec.decision_points) == 2
print(spec.decision_points)
# Select a partial space with `where` argument.
spec = pg.dna_spec(hyper, where=lambda x: len(x.candidates) == 2)
assert spec.space_size == 2
assert len(spec.decision_points) == 1
See also:
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
A (maybe) hyper value. |
required |
where
|
Callable[[HyperPrimitive], bool] | None
|
Function to filter hyper primitives. If None, all hyper
primitives from |
None
|
Returns:
| Type | Description |
|---|---|
DNASpec
|
A DNASpec object, which represents the search space from algorithm's view. |
Source code in pygx/hyper/_object_template.py
materialize
¶
materialize(
value: Any,
parameters: DNA | dict[str, Any],
use_literal_values: bool = False,
where: Callable[[HyperPrimitive], bool] | None = None,
) -> Any
Materialize a (maybe) hyper value using a DNA or parameter dict.
Example:
hyper_dict = pg.Dict(x=pg.oneof(['a', 'b']), y=pg.floatv(0.0, 1.0))
# Materialize using DNA.
assert pg.materialize(
hyper_dict, pg.DNA([0, 0.5])) == pg.Dict(x='a', y=0.5)
# Materialize using key value pairs.
# See `pg.DNA.from_dict` for more details.
assert pg.materialize(
hyper_dict, {'x': 0, 'y': 0.5}) == pg.Dict(x='a', y=0.5)
# Partially materialize.
v = pg.materialize(
hyper_dict, pg.DNA(0), where=lambda x: isinstance(x, pg.hyper.OneOf))
assert v == pg.Dict(x='a', y=pg.floatv(0.0, 1.0))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
A (maybe) hyper value |
required |
parameters
|
DNA | dict[str, Any]
|
A DNA object, or a dict from each decision point's ID (key
path) or name to its decision. A decision can be an integer as the
candidate index for |
required |
use_literal_values
|
bool
|
If True, integer decisions in |
False
|
where
|
Callable[[HyperPrimitive], bool] | None
|
Function to filter hyper primitives. If None, all hyper
primitives from |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
A materialized value. |
Raises:
| Type | Description |
|---|---|
TypeError
|
if parameters is not a DNA or dict. |
ValueError
|
if parameters cannot be decoded. |
Source code in pygx/hyper/_object_template.py
template
¶
template(
value: Any, where: Callable[[HyperPrimitive], bool] | None = None
) -> ObjectTemplate
Creates an object template from the input.
Example:
d = pg.Dict(x=pg.oneof(['a', 'b', 'c']), y=pg.manyof(2, range(4)))
t = pg.template(d)
assert t.encode(pg.Dict(x='a', y=[0, 1])) == pg.DNA([0, [0, 1]])
assert t.decode(pg.DNA([0, [0, 1]])) == pg.Dict(x='a', y=[0, 1])
With a where filter, only the matching hyper primitives take part in
encoding/decoding; the rest stay in place:
t = pg.template(d, where=lambda x: isinstance(x, pg.hyper.ManyOf))
assert t.encode(
pg.Dict(x=pg.oneof(['a', 'b', 'c']), y=[0, 1])) == pg.DNA([0, 1])
assert t.decode(pg.DNA([0, 1])) == pg.Dict(
x=pg.oneof(['a', 'b', 'c']), y=[0, 1])
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
A value based on which the template is created. |
required |
where
|
Callable[[HyperPrimitive], bool] | None
|
Function to filter hyper values. If None, all hyper primitives
from |
None
|
Returns:
| Type | Description |
|---|---|
ObjectTemplate
|
A template object. |
Source code in pygx/hyper/_object_template.py
options: show_root_heading: false show_root_toc_entry: false members_order: source heading_level: 2