designspace.Representation#
- class designspace.Representation(source: Space, target: Space, decode: Callable[[dict[str, Any]], dict[str, Any]], encoded: tuple[str, ...] = (), excluded_by_prop: tuple[str, ...] = (), opaque_conditions: tuple[str, ...] = (), opaque_constraints: tuple[Constraint, ...] = (), dropped_defaults: tuple[str, ...] = (), dropped_anchors: tuple[str, ...] = (), encode: Callable[[dict[str, Any]], dict[str, Any]] | None = None, measure_preserving: bool = False)#
Bases:
objectA Space → Space morphism carrying a value-level decode/encode pair. Two tiers construct one: derived, from Space.represent(*rules), and supplied, from this constructor called directly. A derived representation is a supplied one; both compose through then and are checked through check().
source is the phenotype and target the genotype, an ordinary Space, so a solver asks it the same questions it would ask any space. Never enters the IR, to_json, or the fingerprint preimage; target serializes as an ordinary Space in its own right.
- target#
The genotype space, the one a solver works in. An ordinary Space, so all the usual introspection applies to it.
- Type:
- decode#
Genotype configuration to phenotype configuration. Total: every configuration valid for target decodes to one valid for source.
- excluded_by_prop#
Paths left alone because a .repeat() count or a .prop() reads them.
- opaque_conditions#
Conditions carried across as opaque callables rather than rewritten structurally.
- opaque_constraints#
Constraints carried across the same way.
- Type:
tuple[Constraint, …]
- encode#
Phenotype to genotype, when the morphism is invertible. Raises if it is not.
- measure_preserving#
Whether every applied encoding declared that it preserves the declared measure. Never assumed: an encoding that says nothing counts as False.
- Type:
- then(other: Representation) Representation#
Compose self (source → target) with other (target → its own target), producing a single morphism from self.source all the way to other.target. Requires other.source to fingerprint-equal self.target (a TypeError otherwise, which is misuse rather than resolution). decode composes right-to-left (self.decode(other.decode(g)), other first, since it is closer to the composed target); encode the reverse (other.encode(self.encode(x))), and only when both sides are invertible.
- Parameters:
other (Representation) – A morphism whose source is this one’s target.
- Returns:
The composite, from self.source to other.target.
- Return type:
- Raises:
TypeError – If other.source does not fingerprint-equal self.target.
Examples
The identity of composition is a representation onto the same space, so composing with one changes nothing observable:
>>> s = ds.space(ds.param("depth").integer(1, 8)) >>> rep = s.represent() >>> identity = ds.Representation( ... source=rep.target, target=rep.target, decode=lambda g: g ... ) >>> composed = rep.then(identity) >>> composed.source.fingerprint() == s.fingerprint() True >>> composed.decode({"depth": 0.5}) == rep.decode({"depth": 0.5}) True
Composing morphisms that do not meet is refused:
>>> other = ds.space(ds.param("width").integer(1, 8)) >>> rep.then(other.represent()) Traceback (most recent call last): ... TypeError: then(): other.source does not fingerprint-equal self.target ...
- check(n: int = 200, seed: int | Generator | None = None) RepresentationCheck#
Sample n draws of target, decode each, and assert the conformance laws a Representation owes regardless of tier: decode totality (source.validate(decode(g)).param_errors == ()), feasibility agreement (target.is_feasible(g) == source.is_feasible(decode(g))), and, when invertible, the one-directional round-trip decode(encode(x)) == x for x = decode(g). Never raises on a law violation: the suite as a tool, since a supplied morphism has no other way to be shown sound. Structural laws (path/arity) are guaranteed by construction for the derived tier and asserted directly in the conformance suite, since a supplied morphism has no such law to check, so check() does not re-derive them here.
Failures dedupe by (law, detail), accumulating a count rather than one row per draw.
- Parameters:
n (int) – How many genotype draws to check.
seed (int | numpy.random.Generator | None) – Seed or generator, for a reproducible check.
- Returns:
With .ok and the deduplicated .failures.
- Return type:
Examples
>>> s = ds.space( ... ds.param("lr").real(1e-4, 1e-1).log_scale(), ... ds.param("depth").integer(1, 8), ... ) >>> report = s.represent().check(n=50, seed=0) >>> report.ok, report.failures (True, ())