Add Objectives Or Constraints¶
Objective and constraint config classes declare a typed class-level kind.
Registries are keyed by members of extensible enum bases.
from dataclasses import dataclass
import numpy as np
from retarget import ObjectiveKind
from retarget.optimization import (
ObjectiveConfig,
ObjectiveContribution,
TermContext,
objective_terms,
)
class LabObjective(ObjectiveKind):
ENERGY = "energy"
class EnergyObjectiveConfig(ObjectiveConfig):
kind = LabObjective.ENERGY
@objective_terms.register(LabObjective.ENERGY)
@dataclass(frozen=True)
class EnergyObjective:
kind: LabObjective = LabObjective.ENERGY
config_type: type[EnergyObjectiveConfig] = EnergyObjectiveConfig
def describe(self) -> str:
return "Penalize high-energy joint motion."
def build(
self,
context: TermContext,
config: EnergyObjectiveConfig,
) -> tuple[ObjectiveContribution, ...]:
return (
ObjectiveContribution(
matrix=np.eye(context.dof),
target=np.zeros(context.dof),
),
)
Compose typed reusable profiles:
from retarget import Constraint
from retarget.optimization import (
NominalTrackingObjectiveConfig,
OptimizationProfile,
SmoothnessObjectiveConfig,
)
profile = (
OptimizationProfile.defaults(name="low_smoothness")
.with_objective(SmoothnessObjectiveConfig(weight=0.05))
.with_objective(NominalTrackingObjectiveConfig(joints=nominal_joints))
.without_constraint(Constraint.FOOT_STICKING)
)
Collision constraints require explicit typed selections:
from retarget import NonPenetrationSource
from retarget.optimization import GeometryPair, NonPenetrationConstraintConfig
clearance = NonPenetrationConstraintConfig(
sources=(NonPenetrationSource.GEOMETRY,),
geometry_pairs=(
GeometryPair(
first=LabGeometry.LEFT_FOOT,
second=LabSceneGeometry.GROUND,
),
),
)
There is no backend-selected all-pairs fallback. Missing links, subjects, or geometry pairs fail before optimization.
Declarative configs may use kind = "energy" only after importing the module
that registers LabObjective.ENERGY. Config preflight resolves the string to
that concrete enum member and rejects unknown registrations.
Solver extensions follow the same pattern with SolverKind:
from retarget import SolverKind
from retarget.optimization import SolverSpec, solver_factories
class LabSolver(SolverKind):
CUSTOM = "lab_solver"
@solver_factories.register(LabSolver.CUSTOM)
def make_solver(spec: SolverSpec) -> object:
return MySolver(verbose=spec.verbose)