designspace.Encoding#
- class designspace.Encoding(*args, **kwargs)#
Bases:
ProtocolThe genotype for one param.
Required:
target(self, param: ParamDef) -> ParamDef, the genotype ParamDef at param’s own path. Returning any other path is a resolution error.
decode(self, param: ParamDef, value: Any) -> Any, genotype value to phenotype value; must be total over target’s domain. Repair inside decode when the phenotype domain carries an invariant the genotype cannot express, or choose a genotype that cannot represent an invalid value.
Optional capabilities (checked via hasattr, not part of this Protocol’s static shape):
encode(self, param: ParamDef, value: Any) -> Any, phenotype to genotype; present iff this one param direction is invertible.
decode_expr(self, param: ParamDef) -> Expr | None, decode as an expression, for structural (leaf-substitution) transport; None opts this param out of structural transport for conditions/ constraints that reference it (opaque transport, or rewrite, covers it instead).
prop_expr(self, param: ParamDef, name: str) -> Expr | None, a phenotype property (.prop(name)) as a genotype expression; the repair that lets a .prop()-read param be encoded at all. Absent, or returning None for a property something still reads, represent() raises rather than encoding it.
rewrite(self, param: ParamDef, node: Expr) -> Expr | None, per-node structural rewrite where leaf substitution cannot reach (a one-vs- rest categorical bridge turning algo == “adam” into a pairwise comparison between two of its own coordinates); tried before leaf substitution at each node touching this param.
measure_preserving(self) -> bool, declared and never assumed. Absence means “not asserted” rather than “false”; silence implies neither. _build.py treats absence as False for the Representation.measure_preserving conjunction.
Examples
An encoding re-expressing an integer parameter as a real coordinate, so a continuous solver can propose values for it. decode rounds, which is what makes it total: every real in range decodes to a legal integer.
>>> import dataclasses >>> class RoundedInteger: ... def target(self, param): ... return dataclasses.replace( ... param, ... type_kind="real", ... domain=ds.RealDomain(float(param.domain.lo), float(param.domain.hi)), ... default=None, ... chart=None, ... ) ... ... def decode(self, param, value): ... return int(round(value)) ... ... def encode(self, param, value): ... return float(value)
A rule decides which parameters it applies to:
>>> def rule(param): ... return RoundedInteger() if param.type_kind == "integer" else None >>> s = ds.space(ds.param("depth").integer(1, 8)) >>> rep = s.represent(rule) >>> rep.target.params["depth"].type_kind 'real' >>> rep.decode({"depth": 4.4}) {'depth': 4} >>> rep.check(n=50, seed=0).ok True
- target(param: ParamDef) ParamDef#
The genotype parameter replacing param.
Must keep param’s own path, since a different path is a resolution error, but may change everything else: kind, domain, prior.
- decode(param: ParamDef, value: Any) Any#
Turn a genotype value back into a phenotype value.
Must be total over the target’s domain: every value a solver can produce has to decode to something valid. Where the phenotype carries an invariant the genotype cannot express, either repair it here or choose a genotype that cannot represent a violation. those are the two honest options, and failing on some inputs is not one of them.
- Parameters:
param (ParamDef) – The phenotype parameter being decoded to.
value (Any) – A value from the target parameter’s domain.
- Returns:
A valid phenotype value.
- Return type:
Any