Options¶
Every configurable piece of Eden (the env, the scene and its entities, materials, sensors, managers and their terms, runners) is described by an options class: a Pydantic model that validates when you construct it and serializes into the run's saved config. This page documents the base classes; the pages in this section document each family.
Strict and dynamic options¶
ConfigurableOptions defaults to extra="forbid": a misspelled field raises at construction and names the fields
the class does accept. Containers that hold named children (SceneOptions holds entities, *ManagerOptions hold
terms, SensorsOptions and CamerasOptions hold sensors and cameras) subclass DynamicConfigurableOptions, which
allows extra fields; each extra is one child, keyed by its name. Genesis' own options classes (gs.morphs.*,
gs.sensors.*) keep Genesis' strict validation.
Copy an options object with clone() (or copy.deepcopy), not with Pydantic's model_copy(deep=True); and use
updated(**changes) to get a validated copy with some fields changed. lock() freezes an options tree and
unlock() thaws it. The task configuration guide covers serialization.
ConfigurableOptions: base class for all Eden options.
:class:ConfigurableOptions extends the Genesis Options Pydantic model with Eden
conventions: lock()/unlock() to freeze/unfreeze a config, clone() for a
lock-aware deep copy, and a best-effort __deepcopy__ that tolerates unpicklable
objects (e.g. USD stages).
Copying. clone() is the canonical way to duplicate an options object — prefer
it (or bare copy.deepcopy) over pydantic's model_copy(deep=True), because only
the former routes through this class's __deepcopy__ and therefore inherits the
unpicklable-safe fallback (a value that can't be deep-copied is shared by reference
rather than raising). __deepcopy__ is faithful: the clone mirrors the source's
lock state. clone(unlock=True) is the explicit "copy so I can edit it" path — it
returns a mutable clone regardless of the source, leaving the source untouched.
updated(**fields) is the copy-with-changes path: a deep copy with those fields replaced,
whose names are validated like construction (so is model_copy(update=...)).
Locking. Locking is tree-scoped and downward-propagating: lock()/unlock()
apply to the node they are called on and recurse into all nested options (through
declared fields, extras, and any list/tuple/dict of options). They never walk upward,
so a child can lock/unlock its own subtree without affecting its parent — reaching a
mixed state (e.g. a locked config with one locally-unlocked child) is intentional and
supported; the __setattr__ guard keys off each node's own _locked. Both calls
are idempotent per node (a no-op if the node is already in the target state), so a
redundant parent lock() does not clobber a child that was toggled locally. The lock is not part of an
options object's value: a locked object == the same values unlocked.
The default is extra="forbid" so every fixed-schema options class (EnvOptions,
entity/material/camera options, rsl_rl configs, ...) rejects misspelled fields loudly.
Classes that legitimately carry dynamically-named children (scene entities, manager
terms, observation terms, cameras, sensors, splat PCDs) or receive framework-injected
.configure() params (name / params) as extras subclass
:class:DynamicConfigurableOptions instead, which restores extra="allow".
Attributes¶
Classes¶
ConfigurableOptions
¶
Bases: Options
Base class for configurable options.
Use with ConfigurableMixin.
Source code in eden/options/options.py
Attributes¶
keys
cached
property
¶
Mimic dict.keys(): declared field names + dynamic extras, without triggering value serialization.
Methods:¶
lock
¶
Freeze this options node against further mutation, recursing into all children.
Tree-scoped and downward-propagating: applies to this node and every nested options object (declared fields, extras, and options inside list/tuple/dict). It never walks upward, so locking a child does not lock its parent. Idempotent (a no-op if already locked). See the module docstring for the full lock model.
Source code in eden/options/options.py
unlock
¶
Re-enable modification of this options node, recursing into all children.
Exact mirror of :meth:lock (same downward propagation, no upward walk, same
idempotency), so a child can unlock its own subtree without affecting its
parent. _locked is exempt from the __setattr__ guard, so this clears it
even on a currently-locked object.
Source code in eden/options/options.py
clone
¶
clone(unlock: bool = False) -> ConfigurableOptions
Return a deep copy of this options object — the canonical way to duplicate options.
Prefer this (or bare copy.deepcopy) over model_copy(deep=True): only these
route through :meth:__deepcopy__ and thus tolerate unpicklable values (shared by
reference instead of raising). By default the clone mirrors this object's lock
state (a copy of a locked config stays locked). Pass unlock=True for a mutable
clone regardless of the source — the explicit "copy so I can edit it" path, so
re-enabling mutation is visible at the call site rather than a silent side effect
of copying. The source is never modified.
Source code in eden/options/options.py
updated
¶
Return a deep copy with the given fields replaced — the way to override inherited options.
base.track_linear_velocity.updated(weight=0.0) reads as what it does, and a misspelled
name raises on that line (see :meth:model_copy, which this builds on: deep=True so the
copy shares nothing with the inherited default, keywords so the names are not buried in a dict).
The copy is always unlocked, even when this object is locked. A variant is made to be
edited further, and the call already says "copy with changes" at the call site — the same
visibility clone(unlock=True) asks for. There is no unlock= switch because every
keyword here is a field name. model_copy stays faithful and mirrors the lock state; the
source is never modified.
| PARAMETER | DESCRIPTION |
|---|---|
**updates
|
Field values to change on the copy. Unknown names raise
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Self
|
The updated, unlocked deep copy. |
Source code in eden/options/options.py
dict
¶
dict(include_extra: bool = False)
Source code in eden/options/options.py
DynamicConfigurableOptions
¶
Bases: ConfigurableOptions
ConfigurableOptions variant that keeps extra="allow".
Only the classes that genuinely need it subclass this:
- Dynamic named children — holders that store their children as extra fields:
SceneOptions(entities),ManagerOptions(terms),ObservationGroupOptions(obs terms),CamerasOptions/SensorsOptions, and the splat holdersSplatOptions/NyxSplatOptions(which collect namedPCDOptionschildren). - Framework-injected params — the
*TermOptionsand other.configure()targets, whose per-term parameters land in__pydantic_extra__.
Open extras are NOT unvalidated: manager term options subclass
:class:~eden.options.managers.base.ManagerTermOptions, which validates extras against
the registered term's parameter names at construction time whenever name resolves in
the kind's term registry (RewardTermOptions(name="upright", wieght=2.0) raises), and
ConfigurableMixin.__init__ re-checks at build time. Non-term .configure() targets
(extension options) rely on the .configure()-path name validation only.
Source code in eden/options/options.py
Functions:¶
Pages¶
| Page | Module | Contents |
|---|---|---|
| Environment | eden.options.envs |
EnvOptions (simulation, solver, batching), CudaGraphOptions |
| Entities | eden.options.entities |
entity, primitive, terrain, grouped and composite options, SceneOptions |
| Materials | eden.options.materials |
rigid, kinematic, MPM, SPH, PBD, FEM and Mochi materials |
| Surfaces | eden.options.surfaces |
surface appearance presets |
| Scenes | eden.options.scenes |
scene presets |
| Robots | eden.options.robots |
robot presets and composite rigs |
| Actuators | eden.options.actuators |
actuator specs and motor presets |
| Sensors and cameras | eden.options.sensors, eden.options.camera |
sensor and camera options |
| Splats and renderers | eden.options.splat, eden.options.renderer |
Gaussian splats, point clouds, ray tracer, batch renderer |
| Managers | eden.options.managers |
manager and term options |
| Learning | eden.options.learning |
rsl_rl and rl_games runner options |
Dataset options are on the datasets page and extension options on each extension page.
Resolving options classes by name¶
eden.options.catalog answers "which options class does this name mean?" for the eden view and eden snap
commands and for robots: ROBOT_REGISTRY maps a robot key to its class, and the curated MANIFEST describes assets
semantically (category, placement, tags).
Resolving entity Options classes by name — two layers over one "module:Class" pointer.
Eden assets (eden.options.objects.*, eden.options.robots.*) know how to load a mesh
(their EntityOptions carry up/front/scale etc.) but the question "which Options
class does this name mean?" is answered here, two ways, both lazily importing only the one
module they need:
-
Auto-scan index (:func:
find_entity_options/ :func:entity_options_index) — exhaustive. A filesystem walk of the asset packages, cached on disk and keyed by a source fingerprint, maps every class name (RoboLabYcbBanana) and family-registry key (robolab/ycb/banana) to its"module:Class"pointer. This backs theeden view/eden snapCLI, where a warm lookup imports only the single defining module. -
Curated manifest (:data:
MANIFEST) — a hand-curated semantic subset. An asset is curated by decorating its Options class::@MANIFEST.curate("sofa", "furniture/seating", tags=("seating",), description="a couch") @REPLICA_CAD_OBJECT_REGISTRY.register(name="frl_apartment_sofa") class FRLApartmentSofa(ReplicaCADObjectOptions): ...
What's curated is semantic only (no EntityOptions field is re-declared): category
(/-delimited taxonomy path), placement (floor | surface | wall | ceiling),
support_surface (place-on target), container (place-in target), tags,
description. Physical pose stays on the Options class (up/front/scale);
AssetSpec.movable is derived at curation time from the constructed Options
(not is_fixed_base), never hand-curated, so it can't drift from the class.
Variant families (one Options class serving many mesh variants, e.g. Objaverse) curate each
variant with the direct-call form and make_kwargs — the variant selector baked into
:meth:AssetSpec.make (explicit make() overrides still win)::
MANIFEST.curate("apple_3", "food/fruit", make_kwargs={"variant": 3})(ObjaverseApple)
Population is never eager — so importing Eden stays cheap no matter how many assets exist:
a @curate decorator registers its asset only when that family module is imported, and
re-import is idempotent (a true name collision raises); :meth:AssetManifest.build imports the
known families on demand; :meth:ensure is the build-if-empty entry point for consumers.
Both layers share one "module:Class" spelling and one lazy resolver (:func:_resolve):
AssetSpec stores options_path (so :meth:AssetManifest.summary rows are JSON-ready and
:meth:make imports only the one class it constructs), and the auto-scan index stores the same.
from eden.options.catalog import MANIFEST, find_entity_options
MANIFEST.ensure() # populate (imports asset families)
MANIFEST.facets() # what can I query? axes → valid values
MANIFEST.names(category="furniture/seating") # drill down → ['armchair','chair','sofa']
MANIFEST.search("mug") # free text → ['coffee_cup', ...]
opts = MANIFEST.make("sofa", default_root_pos=(0, 0, 0)) # lazily imports + constructs
cls = find_entity_options("RoboLabYcbBanana") # exhaustive auto-scan lookup
Attributes¶
ROBOT_REGISTRY
module-attribute
¶
ROBOT_REGISTRY = RobotRegistry('robot', populated_by=('eden.options.robots',))
Classes¶
RobotRegistry
¶
Bases: Registry
Friendly name -> RobotOptions subclass lookup for robots.
A plain :class:~eden.utils.registry.Registry that only adds a type guard: every
entry must be a :class:~eden.options.entities.RobotOptions subclass. The check
lives in :meth:_do_register, the single chokepoint both the decorator and the
direct-call registration forms funnel through, so the inherited register() API is
extended (not disabled — unlike :class:AssetManifest, which forbids it).
Robots register themselves in (or alongside) their option modules::
@ROBOT_REGISTRY.register(name="g1")
class G1Dofs29(RobotOptions): ...
ROBOT_REGISTRY.register(FrankaEmikaPanda) # direct-call form
Keys default to to_snake_case(cls.__name__); pass name= for a cleaner alias.
The exhaustive :func:find_entity_options index discovers this registry too and
surfaces its entries as robot/<key> (plus a bare <key> when unambiguous).
Source code in eden/utils/registry.py
AssetManifest
¶
Bases: Registry
Tree-structured registry of :class:AssetSpec, keyed by friendly name.
Not populated on import: call :meth:ensure (or :meth:build) first. :meth:make
imports the chosen asset's class lazily.
Source code in eden/utils/registry.py
Methods:¶
curate
¶
curate(name: str, category: str, *, placement: Placement = 'floor', support_surface: bool = False, container: bool = False, tags=(), description: str = '', make_kwargs: dict | None = None)
Curate the decorated EntityOptions subclass as an asset.
Usable as a stacked decorator, or in direct-call form for variant families
(one class, many curated names): curate("apple_3", ..., make_kwargs={"variant": 3})(cls).
Source code in eden/options/catalog.py
register
¶
Disable the inherited register entry point.
The base :meth:Registry.register would store a raw class where an :class:AssetSpec
is expected, silently corrupting every query method. Curate assets (with their semantic
metadata) via :meth:curate instead.
Source code in eden/options/catalog.py
make
¶
make(name: str, **overrides) -> EntityOptions
build
¶
build(modules: tuple[str, ...] | None = None) -> AssetManifest
Import the asset families so their @curate decorators populate the manifest.
Source code in eden/options/catalog.py
ensure
¶
ensure(modules: tuple[str, ...] | None = None) -> AssetManifest
Populate from the asset families if empty (idempotent); the consumer entry point.
names
¶
names(*, category: str | None = None, placement: str | None = None, tag: str | None = None, support_surface: bool | None = None, container: bool | None = None, movable: bool | None = None) -> list[str]
Asset names matching every given filter. Filter axes/values come from :meth:facets.
Source code in eden/options/catalog.py
search
¶
Find asset names by free text.
Case-insensitive substring match over each asset's name, category path, tags, and
description — the loosest entry point for an agent resolving task language
("a mug", "trash") to catalog names. An empty query raises (use :meth:names or
:meth:summary to list the whole catalog).
Source code in eden/options/catalog.py
facets
¶
Describe the available queries: each filter axis :meth:names accepts → its present values.
Source code in eden/options/catalog.py
summary
¶
Flat per-asset records (JSON-ready; handy to dump into an agent's context).
AssetSpec
dataclass
¶
AssetSpec(name: str, options_path: str, category: str = 'object', placement: Placement = 'floor', support_surface: bool = False, container: bool = False, tags: tuple[str, ...] = tuple(), description: str = '', make_kwargs: dict = dict(), movable: bool = True)
Semantic metadata for one curated asset (the physical pose lives on the Options class).
Attributes¶
Methods:¶
make
¶
make(**overrides) -> EntityOptions
Construct the asset's Options (lazily importing its class; canonical pose from it).
Curated make_kwargs are applied first; explicit overrides win.
Source code in eden/options/catalog.py
Functions:¶
find_entity_options
¶
find_entity_options(name: str) -> type[BaseEntityOptions]
Resolve an Options class by class name (RoboLabYcbBanana) or registry
key (robolab/ycb/banana). Warm lookups import only the defining module.
Source code in eden/options/catalog.py
entity_options_index
¶
Name -> "module:Class" map of all discoverable entity Options.