Reference/API

Optimization

robokit.opt.Var
Bases: abc.ABC
Members
property[source]
Var.tangent_dim: int
method[source]
Var.clone() -> Self
Returns
Self
method[source]
Var.view(
batch_size: int,
offset: int = 0
) -> Self

Elements offset to offset + batch_size, sharing storage.

Parameters
batch_sizeintrequired
offsetint= 0
Returns
Self
method[source]
Var.integrate(
velocity: ArrayLike,
out: Optional[Self] = None,
tangent_mask: Optional[wp.array] = None,
weight_decay: float = 0.0,
out_velocity: Optional[ArrayLike] = None
) -> Self

Move by velocity; only shortened steps write out_velocity, which may alias the input velocity.

Parameters
velocityArrayLikerequired
outOptional[Self]= None
tangent_maskOptional[wp.array]= None
weight_decayfloat= 0.0
out_velocityOptional[ArrayLike]= None
Returns
Self
method[source]
Var.difference(
other: Self,
out: Optional[ArrayLike] = None
) -> ArrayLike

(batch, tangent_dim) velocities that integrate moves each element by to reach other's.

Parameters
otherSelfrequired
outOptional[ArrayLike]= None
Returns
ArrayLike
method[source]
Var.invalidate()
property[source]
Var.batch_size: int
property[source]
Var.device: wp_device_type
method[source]
Var.gather(
indices: ArrayLike,
out: Optional[Self] = None
) -> Self
Parameters
indicesArrayLikerequired
outOptional[Self]= None
Returns
Self
method[source]
Var.accept(
accept_mask: ArrayLike,
proposed: Self
) -> Self

Accept proposed values and derived state where accept_mask[i] == 1; leave other rows unchanged.

Parameters
accept_maskArrayLikerequired
proposedSelfrequired
Returns
Self
robokit.opt.VarValues(
named_vars: Mapping[Enum, Var]
)
Bases: Var

Combine Vars into tangent-column blocks; preserve subclass step rules across copies and views.

Parameters
named_varsMapping[Enum, Var]required
Members
method[source]
VarValues.from_var(
var: Var,
key: Enum
) -> VarValues
Parameters
varVarrequired
keyEnumrequired
Returns
VarValues
method[source]
VarValues.get(
key: Enum
) -> Var
Parameters
keyEnumrequired
Returns
Var
method[source]
VarValues.values()
method[source]
VarValues.tangent_offset(
key: Enum
) -> int
Parameters
keyEnumrequired
Returns
int
property[source]
VarValues.tangent_dim: int
property[source]
VarValues.batch_size: int
property[source]
VarValues.device: wp_device_type
method[source]
VarValues.clone() -> VarValues
Returns
VarValues
method[source]
VarValues.view(
batch_size: int,
offset: int = 0
) -> VarValues
Parameters
batch_sizeintrequired
offsetint= 0
Returns
VarValues
method[source]
VarValues.integrate(
velocity: ArrayLike,
out: Optional[VarValues] = None,
tangent_mask: Optional[wp.array] = None,
weight_decay: float = 0.0,
out_velocity: Optional[ArrayLike] = None
) -> VarValues
Parameters
velocityArrayLikerequired
outOptional[VarValues]= None
tangent_maskOptional[wp.array]= None
weight_decayfloat= 0.0
out_velocityOptional[ArrayLike]= None
Returns
VarValues
method[source]
VarValues.difference(
other: VarValues,
out: Optional[ArrayLike] = None
) -> ArrayLike
Parameters
otherVarValuesrequired
outOptional[ArrayLike]= None
Returns
ArrayLike
method[source]
VarValues.gather(
indices: ArrayLike,
out: Optional[VarValues] = None
) -> VarValues
Parameters
indicesArrayLikerequired
outOptional[VarValues]= None
Returns
VarValues
method[source]
VarValues.accept(
accept_mask: ArrayLike,
proposed: VarValues
) -> VarValues
Parameters
accept_maskArrayLikerequired
proposedVarValuesrequired
Returns
VarValues
method[source]
VarValues.invalidate()
robokit.opt.VectorVar(
x: wp.array,
unit: float = 1.0,
max_step: float = 0.0
)
Bases: Var

A batched vector optimization variable on Euclidean space.

Example

python
>>> from enum import Enum
>>> from robokit.opt import VarValues
>>> class Key(Enum):
...     X = 0
>>> var = VectorVar(wp.array([[1.0, 2.0]], dtype=wp.float32))
>>> values = VarValues.from_var(var, Key.X)
>>> result = values.integrate(wp.array([[0.5, -1.0]], dtype=wp.float32))
>>> result.get(Key.X).x.numpy().tolist()
[[1.5, 1.0]]
>>> capped = VectorVar(wp.zeros((1, 2), dtype=wp.float32), unit=10.0, max_step=1.0)  # moves 10x, 1 at most
>>> capped.integrate(wp.array([[0.0625, 1.0]], dtype=wp.float32)).x.numpy().tolist()
[[0.625, 1.0]]
Parameters
xwp.arrayrequired
unitfloat= 1.0
max_stepfloat= 0.0
Members
methodinherited from Var[source]
VectorVar.invalidate()
property[source]
VectorVar.x: wp.array
property[source]
VectorVar.tangent_dim: int
property[source]
VectorVar.batch_size: int
property[source]
VectorVar.device: wp_device_type
method[source]
VectorVar.clone() -> VectorVar
Returns
VectorVar
method[source]
VectorVar.view(
batch_size: int,
offset: int = 0
) -> VectorVar
Parameters
batch_sizeintrequired
offsetint= 0
Returns
VectorVar
method[source]
VectorVar.integrate(
velocity: ArrayLike,
out: Optional[VectorVar] = None,
tangent_mask: Optional[wp.array] = None,
weight_decay: float = 0.0,
out_velocity: Optional[ArrayLike] = None
) -> VectorVar
Parameters
velocityArrayLikerequired
outOptional[VectorVar]= None
tangent_maskOptional[wp.array]= None
weight_decayfloat= 0.0
out_velocityOptional[ArrayLike]= None
Returns
VectorVar
method[source]
VectorVar.difference(
other: VectorVar,
out: Optional[ArrayLike] = None
) -> ArrayLike
Parameters
otherVectorVarrequired
outOptional[ArrayLike]= None
Returns
ArrayLike
method[source]
VectorVar.gather(
indices: ArrayLike,
out: Optional[VectorVar] = None
) -> VectorVar
Parameters
indicesArrayLikerequired
outOptional[VectorVar]= None
Returns
VectorVar
method[source]
VectorVar.accept(
accept_mask: ArrayLike,
proposed: VectorVar
) -> VectorVar
Parameters
accept_maskArrayLikerequired
proposedVectorVarrequired
Returns
VectorVar
robokit.opt.optimizer.Optimizer(
terms: Union[Task, Sequence[Task]],
config: Optional[OptimizerConfig] = None,
device: Optional[wp_device_type] = None,
active_dof_mask: Optional[wp.array] = None
)
Bases: abc.ABC

Warp optimizer lifecycle with shared Jacobian machinery for gradient optimizers and LM's CG step.

Solve sequence:

  1. reset: build once, slice to batch size, optionally capture a graph, then call _start.
  2. Repeat step max_iter times, replaying the iteration graph or running _step_body.

Subclass hooks:

  • _build(var): allocate all buffers at the first var's batch size (max_batch_size).
  • _BATCH_BUFFERS: identify batch-leading arrays and vars to slice per solve.
  • _start(var): initialize per-solve damping/history/schedules and starting cost.
  • _step_body(var): run one iteration in place using kernels only.
Parameters
termsUnion[Task, Sequence[Task]]required
configOptional[OptimizerConfig]= None
deviceOptional[wp_device_type]= None
active_dof_maskOptional[wp.array]= None
Members
attribute[source]
Optimizer.terms: Sequence[Task]
attribute[source]
Optimizer.config: OptimizerConfig
method[source]
Optimizer.solve(
var: VarValues,
iteration_callback: Optional[IterationCallback] = None
) -> Tuple[VarValues, wp.array]

Optimize var in place; returns it with the final cost per batch element.

Parameters
varVarValuesrequired
iteration_callbackOptional[IterationCallback]= None
Returns
Tuple[VarValues, wp.array]
method[source]
Optimizer.reset(
var: VarValues,
num_steps: Optional[int] = None
) -> None

Prepare to iterate from var; num_steps (default max_iter) steps follow and L-BFGS finishes the last.

Parameters
varVarValuesrequired
num_stepsOptional[int]= None
Returns
None
method[source]
Optimizer.step(
var: VarValues
) -> None

One iteration in place; an outer capture records kernels instead of replaying a graph.

Parameters
varVarValuesrequired
Returns
None
property[source]
Optimizer.is_all_sparse: bool
robokit.opt.optimizer.OptimizerConfig(
max_iter: int = 100,
early_stopping_interval: int = 1,
cost_tol: float = 1e-09,
cuda_graph_mode: Literal['none', 'iter', 'full'] = 'none'
)
Parameters
max_iterint= 100
early_stopping_intervalint= 1
cost_tolfloat= 1e-09
cuda_graph_modeLiteral['none', 'iter', 'full']= 'none'
Members
attribute[source]
OptimizerConfig.max_iter: int = 100

Iterations per solve.

attribute[source]
OptimizerConfig.early_stopping_interval: int = 1

Check the stop rule every this many iterations; 0 never stops early (each check syncs the host).

attribute[source]
OptimizerConfig.cost_tol: float = 1e-09

The solve stops once every batch element's cost is at most this.

attribute[source]
OptimizerConfig.cuda_graph_mode: Literal['none', 'iter', 'full'] = 'none'

Capture per width: "iter" replays one step; "full" replays the solve without callbacks or early stopping.

method[source]
OptimizerConfig.build(
terms: Sequence[Task],
device: wp_device_type,
active_dof_mask: Optional[wp.array] = None
) -> Optimizer

The optimizer a PopulationSolver steps with this config.

Parameters
termsSequence[Task]required
devicewp_device_typerequired
active_dof_maskOptional[wp.array]= None
Returns
Optimizer
robokit.opt.gd_optimizer.GDOptimizer
Bases: Optimizer

First-order optimizer supporting GD and Adam, with optional decoupled weight decay (AdamW).

Members
attributeinherited from Optimizer[source]
GDOptimizer.terms: Sequence[Task]
attributeinherited from Optimizer[source]
GDOptimizer.config: OptimizerConfig
methodinherited from Optimizer[source]
GDOptimizer.solve(
var: VarValues,
iteration_callback: Optional[IterationCallback] = None
) -> Tuple[VarValues, wp.array]

Optimize var in place; returns it with the final cost per batch element.

Parameters
varVarValuesrequired
iteration_callbackOptional[IterationCallback]= None
Returns
Tuple[VarValues, wp.array]
methodinherited from Optimizer[source]
GDOptimizer.reset(
var: VarValues,
num_steps: Optional[int] = None
) -> None

Prepare to iterate from var; num_steps (default max_iter) steps follow and L-BFGS finishes the last.

Parameters
varVarValuesrequired
num_stepsOptional[int]= None
Returns
None
propertyinherited from Optimizer[source]
GDOptimizer.is_all_sparse: bool
method[source]
GDOptimizer.step(
var: VarValues
) -> None
Parameters
varVarValuesrequired
Returns
None
robokit.opt.gd_optimizer.GDOptimizerConfig(
max_iter: int = 100,
early_stopping_interval: int = 1,
cost_tol: float = 1e-09,
cuda_graph_mode: Literal['none', 'iter', 'full'] = 'none',
learning_rate: float = 0.01,
lr_schedule: Optional[Callable[[int, float], float]] = None,
gradient_mode: Literal['analytic_jacobian', 'autodiff', 'analytic_gradient'] = 'analytic_jacobian',
optimizer_type: Literal['gd', 'adam'] = 'gd',
gradient_clip: float = math.inf,
beta1: float = 0.9,
beta2: float = 0.999,
epsilon: float = 1e-08,
weight_decay: float = 0.0
)
Bases: OptimizerConfig
Parameters
max_iterint= 100
early_stopping_intervalint= 1
cost_tolfloat= 1e-09
cuda_graph_modeLiteral['none', 'iter', 'full']= 'none'
learning_ratefloat= 0.01
lr_scheduleOptional[Callable[[int, float], float]]= None
gradient_modeLiteral['analytic_jacobian', 'autodiff', 'analytic_gradient']= 'analytic_jacobian'
optimizer_typeLiteral['gd', 'adam']= 'gd'
gradient_clipfloat= math.inf
beta1float= 0.9
beta2float= 0.999
epsilonfloat= 1e-08
weight_decayfloat= 0.0
Members
attributeinherited from OptimizerConfig[source]
GDOptimizerConfig.max_iter: int = 100

Iterations per solve.

attributeinherited from OptimizerConfig[source]
GDOptimizerConfig.early_stopping_interval: int = 1

Check the stop rule every this many iterations; 0 never stops early (each check syncs the host).

attributeinherited from OptimizerConfig[source]
GDOptimizerConfig.cost_tol: float = 1e-09

The solve stops once every batch element's cost is at most this.

attributeinherited from OptimizerConfig[source]
GDOptimizerConfig.cuda_graph_mode: Literal['none', 'iter', 'full'] = 'none'

Capture per width: "iter" replays one step; "full" replays the solve without callbacks or early stopping.

attribute[source]
GDOptimizerConfig.learning_rate: float = 0.01

Step size.

attribute[source]
GDOptimizerConfig.lr_schedule: Optional[Callable[[int, float], float]] = None

Learning rate per iteration from (zero-based iteration, learning_rate); None keeps it constant.

attribute[source]
GDOptimizerConfig.gradient_mode: Literal['analytic_jacobian', 'autodiff', 'analytic_gradient'] = 'analytic_jacobian'

analytic_jacobian: terms fill a shared sparse/dense J, then one Jᵀr reduction; autodiff: tape gradients; analytic_gradient: term kernels accumulate cost and gradient directly.

attribute[source]
GDOptimizerConfig.optimizer_type: Literal['gd', 'adam'] = 'gd'

Update rule: plain gradient descent or Adam.

attribute[source]
GDOptimizerConfig.gradient_clip: float = math.inf

Clamp each gradient entry to ±this before the update, so a stiff cost cannot overshoot.

attribute[source]
GDOptimizerConfig.beta1: float = 0.9

Adam's first-moment decay.

attribute[source]
GDOptimizerConfig.beta2: float = 0.999

Adam's second-moment decay.

attribute[source]
GDOptimizerConfig.epsilon: float = 1e-08

Adam's denominator stabilizer.

attribute[source]
GDOptimizerConfig.weight_decay: float = 0.0

Decoupled decay: each step also moves x by -learning_rate weight_decay x (AdamW under Adam).

method[source]
GDOptimizerConfig.build(
terms: Sequence[Task],
device: wp_device_type,
active_dof_mask: Optional[wp.array] = None
) -> GDOptimizer
Parameters
termsSequence[Task]required
devicewp_device_typerequired
active_dof_maskOptional[wp.array]= None
Returns
GDOptimizer
robokit.opt.gd_optimizer.AnnealedGDOptimizer
Bases: GDOptimizer

GD with RMSProp proposals and Metropolis acceptance, supporting every base-class gradient mode.

Acceptance outcomes:

  • Accept: keep the proposed point and gradient.
  • Reject: restore the previous point and gradient, continuing the chain there.
Members
attributeinherited from Optimizer[source]
AnnealedGDOptimizer.terms: Sequence[Task]
attributeinherited from Optimizer[source]
AnnealedGDOptimizer.config: OptimizerConfig
methodinherited from Optimizer[source]
AnnealedGDOptimizer.solve(
var: VarValues,
iteration_callback: Optional[IterationCallback] = None
) -> Tuple[VarValues, wp.array]

Optimize var in place; returns it with the final cost per batch element.

Parameters
varVarValuesrequired
iteration_callbackOptional[IterationCallback]= None
Returns
Tuple[VarValues, wp.array]
methodinherited from Optimizer[source]
AnnealedGDOptimizer.reset(
var: VarValues,
num_steps: Optional[int] = None
) -> None

Prepare to iterate from var; num_steps (default max_iter) steps follow and L-BFGS finishes the last.

Parameters
varVarValuesrequired
num_stepsOptional[int]= None
Returns
None
propertyinherited from Optimizer[source]
AnnealedGDOptimizer.is_all_sparse: bool
method[source]
AnnealedGDOptimizer.step(
var: VarValues
) -> None
Parameters
varVarValuesrequired
Returns
None
robokit.opt.gd_optimizer.AnnealedGDConfig(
max_iter: int = 100,
early_stopping_interval: int = 1,
cost_tol: float = 1e-09,
cuda_graph_mode: Literal['none', 'iter', 'full'] = 'none',
learning_rate: float = 0.01,
gradient_mode: Literal['analytic_jacobian', 'autodiff', 'analytic_gradient'] = 'analytic_jacobian',
mu: float = 0.98,
stepsize_period: int = 50,
starting_temperature: float = 18.0,
temperature_decay: float = 0.95,
annealing_period: int = 30,
grad_eps: float = 1e-06,
seed: int = 0
)
Bases: OptimizerConfig

learning_rate and the Metropolis temperature both decay by temperature_decay, on their own periods.

Parameters
max_iterint= 100
early_stopping_intervalint= 1
cost_tolfloat= 1e-09
cuda_graph_modeLiteral['none', 'iter', 'full']= 'none'
learning_ratefloat= 0.01
gradient_modeLiteral['analytic_jacobian', 'autodiff', 'analytic_gradient']= 'analytic_jacobian'
mufloat= 0.98
stepsize_periodint= 50
starting_temperaturefloat= 18.0
temperature_decayfloat= 0.95
annealing_periodint= 30
grad_epsfloat= 1e-06
seedint= 0
Members
attributeinherited from OptimizerConfig[source]
AnnealedGDConfig.max_iter: int = 100

Iterations per solve.

attributeinherited from OptimizerConfig[source]
AnnealedGDConfig.early_stopping_interval: int = 1

Check the stop rule every this many iterations; 0 never stops early (each check syncs the host).

attributeinherited from OptimizerConfig[source]
AnnealedGDConfig.cost_tol: float = 1e-09

The solve stops once every batch element's cost is at most this.

attributeinherited from OptimizerConfig[source]
AnnealedGDConfig.cuda_graph_mode: Literal['none', 'iter', 'full'] = 'none'

Capture per width: "iter" replays one step; "full" replays the solve without callbacks or early stopping.

methodinherited from OptimizerConfig[source]
AnnealedGDConfig.build(
terms: Sequence[Task],
device: wp_device_type,
active_dof_mask: Optional[wp.array] = None
) -> Optimizer

The optimizer a PopulationSolver steps with this config.

Parameters
termsSequence[Task]required
devicewp_device_typerequired
active_dof_maskOptional[wp.array]= None
Returns
Optimizer
attribute[source]
AnnealedGDConfig.learning_rate: float = 0.01

Initial RMSProp step size.

attribute[source]
AnnealedGDConfig.gradient_mode: Literal['analytic_jacobian', 'autodiff', 'analytic_gradient'] = 'analytic_jacobian'

How the gradient is computed, as in GDOptimizerConfig.

attribute[source]
AnnealedGDConfig.mu: float = 0.98

RMSProp's weight on the newest squared gradient.

attribute[source]
AnnealedGDConfig.stepsize_period: int = 50

Iterations between step-size decays.

attribute[source]
AnnealedGDConfig.starting_temperature: float = 18.0

Initial Metropolis temperature.

attribute[source]
AnnealedGDConfig.temperature_decay: float = 0.95

Factor applied to the step size and the temperature at each of their periods.

attribute[source]
AnnealedGDConfig.annealing_period: int = 30

Iterations between temperature decays.

attribute[source]
AnnealedGDConfig.grad_eps: float = 1e-06

RMSProp's denominator stabilizer.

attribute[source]
AnnealedGDConfig.seed: int = 0

Seed of the Metropolis acceptance draws.

robokit.opt.lbfgs_optimizer.LBFGSOptimizer
Bases: Optimizer
Members
attributeinherited from Optimizer[source]
LBFGSOptimizer.terms: Sequence[Task]
attributeinherited from Optimizer[source]
LBFGSOptimizer.config: OptimizerConfig
methodinherited from Optimizer[source]
LBFGSOptimizer.solve(
var: VarValues,
iteration_callback: Optional[IterationCallback] = None
) -> Tuple[VarValues, wp.array]

Optimize var in place; returns it with the final cost per batch element.

Parameters
varVarValuesrequired
iteration_callbackOptional[IterationCallback]= None
Returns
Tuple[VarValues, wp.array]
methodinherited from Optimizer[source]
LBFGSOptimizer.reset(
var: VarValues,
num_steps: Optional[int] = None
) -> None

Prepare to iterate from var; num_steps (default max_iter) steps follow and L-BFGS finishes the last.

Parameters
varVarValuesrequired
num_stepsOptional[int]= None
Returns
None
methodinherited from Optimizer[source]
LBFGSOptimizer.step(
var: VarValues
) -> None

One iteration in place; an outer capture records kernels instead of replaying a graph.

Parameters
varVarValuesrequired
Returns
None
propertyinherited from Optimizer[source]
LBFGSOptimizer.is_all_sparse: bool
robokit.opt.lbfgs_optimizer.LBFGSOptimizerConfig(
max_iter: int = 100,
early_stopping_interval: int = 0,
cost_tol: float = 1e-09,
cuda_graph_mode: Literal['none', 'iter', 'full'] = 'none',
history_len: int = 10,
h0_scale: float = 1.0,
line_search_alphas: tuple = (1e-05, 0.0001, 0.001, 0.01, 0.1, 0.5, 1.0),
wolfe_c1: float = 0.0001,
wolfe_c2: float = 0.9,
line_search: Literal['armijo', 'approx_wolfe'] = 'armijo',
normalize_direction: bool = False,
gradient_mode: Literal['analytic_jacobian', 'autodiff', 'analytic_gradient'] = 'analytic_jacobian',
fold_line_search_gradient: bool = False
)
Bases: OptimizerConfig
Parameters
max_iterint= 100
early_stopping_intervalint= 0
cost_tolfloat= 1e-09
cuda_graph_modeLiteral['none', 'iter', 'full']= 'none'
history_lenint= 10
h0_scalefloat= 1.0
line_search_alphastuple= (1e-05, 0.0001, 0.001, 0.01, 0.1, 0.5, 1.0)
wolfe_c1float= 0.0001
wolfe_c2float= 0.9
line_searchLiteral['armijo', 'approx_wolfe']= 'armijo'
normalize_directionbool= False
gradient_modeLiteral['analytic_jacobian', 'autodiff', 'analytic_gradient']= 'analytic_jacobian'
fold_line_search_gradientbool= False
Members
attributeinherited from OptimizerConfig[source]
LBFGSOptimizerConfig.max_iter: int = 100

Iterations per solve.

attributeinherited from OptimizerConfig[source]
LBFGSOptimizerConfig.cost_tol: float = 1e-09

The solve stops once every batch element's cost is at most this.

attributeinherited from OptimizerConfig[source]
LBFGSOptimizerConfig.cuda_graph_mode: Literal['none', 'iter', 'full'] = 'none'

Capture per width: "iter" replays one step; "full" replays the solve without callbacks or early stopping.

attribute[source]
LBFGSOptimizerConfig.early_stopping_interval: int = 0

Off by default: without host checks, a folded line search defers each step into the next direction launch.

attribute[source]
LBFGSOptimizerConfig.history_len: int = 10

Curvature pairs kept for the two-loop recursion.

attribute[source]
LBFGSOptimizerConfig.h0_scale: float = 1.0

Initial inverse-Hessian scale when there is no history or normalize_direction is on (else Barzilai-Borwein).

attribute[source]
LBFGSOptimizerConfig.line_search_alphas: tuple = (1e-05, 0.0001, 0.001, 0.01, 0.1, 0.5, 1.0)

Trial step sizes, evaluated in parallel.

attribute[source]
LBFGSOptimizerConfig.wolfe_c1: float = 0.0001

Sufficient-decrease constant.

attribute[source]
LBFGSOptimizerConfig.wolfe_c2: float = 0.9

Curvature constant (approx_wolfe).

attribute[source]
LBFGSOptimizerConfig.normalize_direction: bool = False

Normalize directions so trial steps are lengths; H0 then uses h0_scale instead of Barzilai-Borwein.

attribute[source]
LBFGSOptimizerConfig.gradient_mode: Literal['analytic_jacobian', 'autodiff', 'analytic_gradient'] = 'analytic_jacobian'

analytic_jacobian: terms fill a shared sparse/dense J, then one Jᵀr reduction; autodiff: tape gradients; analytic_gradient: term kernels accumulate cost and gradient directly.

attribute[source]
LBFGSOptimizerConfig.fold_line_search_gradient: bool = False

Fold trial gradients to skip a recompute; needs analytic_gradient or all-sparse analytic_jacobian terms.

method[source]
LBFGSOptimizerConfig.build(
terms: Sequence[Task],
device: wp_device_type,
active_dof_mask: Optional[wp.array] = None
) -> LBFGSOptimizer

Runs without a per-iteration graph: each step branches on the host iteration count.

Parameters
termsSequence[Task]required
devicewp_device_typerequired
active_dof_maskOptional[wp.array]= None
Returns
LBFGSOptimizer
robokit.opt.lm_optimizer.LMOptimizer
Bases: Optimizer

Levenberg-Marquardt with Cholesky or conjugate gradients.

Solver paths:

  • Cholesky/default Jacobian terms: fused fast path.
  • Cholesky/other terms: accumulate JᵀJ, then factor in one tile or 16×16 tiles within the sparse-pattern band.
  • CG: reuse sparse/dense Jacobians without forming JᵀJ.
Members
attributeinherited from Optimizer[source]
LMOptimizer.terms: Sequence[Task]
attributeinherited from Optimizer[source]
LMOptimizer.config: OptimizerConfig
methodinherited from Optimizer[source]
LMOptimizer.solve(
var: VarValues,
iteration_callback: Optional[IterationCallback] = None
) -> Tuple[VarValues, wp.array]

Optimize var in place; returns it with the final cost per batch element.

Parameters
varVarValuesrequired
iteration_callbackOptional[IterationCallback]= None
Returns
Tuple[VarValues, wp.array]
methodinherited from Optimizer[source]
LMOptimizer.reset(
var: VarValues,
num_steps: Optional[int] = None
) -> None

Prepare to iterate from var; num_steps (default max_iter) steps follow and L-BFGS finishes the last.

Parameters
varVarValuesrequired
num_stepsOptional[int]= None
Returns
None
methodinherited from Optimizer[source]
LMOptimizer.step(
var: VarValues
) -> None

One iteration in place; an outer capture records kernels instead of replaying a graph.

Parameters
varVarValuesrequired
Returns
None
propertyinherited from Optimizer[source]
LMOptimizer.is_all_sparse: bool
robokit.opt.lm_optimizer.LMOptimizerConfig(
max_iter: int = 100,
early_stopping_interval: int = 1,
cost_tol: float = 1e-09,
cuda_graph_mode: Literal['none', 'iter', 'full'] = 'none',
lm_lambda: float = 0.001,
lambda_factor: float = 2.0,
lambda_min: float = 1e-05,
lambda_max: float = 10000000000.0,
rho_min: float = 0.001,
gain_ratio_epsilon: float = 1e-08,
linear_solver: Literal['cholesky', 'cg', 'torch_cholesky'] = 'cholesky',
cg_max_iter: Optional[int] = None,
cg_tol: float = 1e-06,
cg_check_interval: int = 4
)
Bases: OptimizerConfig
Parameters
max_iterint= 100
early_stopping_intervalint= 1
cost_tolfloat= 1e-09
cuda_graph_modeLiteral['none', 'iter', 'full']= 'none'
lm_lambdafloat= 0.001
lambda_factorfloat= 2.0
lambda_minfloat= 1e-05
lambda_maxfloat= 10000000000.0
rho_minfloat= 0.001
gain_ratio_epsilonfloat= 1e-08
linear_solverLiteral['cholesky', 'cg', 'torch_cholesky']= 'cholesky'
cg_max_iterOptional[int]= None
cg_tolfloat= 1e-06
cg_check_intervalint= 4
Members
attributeinherited from OptimizerConfig[source]
LMOptimizerConfig.max_iter: int = 100

Iterations per solve.

attributeinherited from OptimizerConfig[source]
LMOptimizerConfig.early_stopping_interval: int = 1

Check the stop rule every this many iterations; 0 never stops early (each check syncs the host).

attributeinherited from OptimizerConfig[source]
LMOptimizerConfig.cost_tol: float = 1e-09

The solve stops once every batch element's cost is at most this.

attributeinherited from OptimizerConfig[source]
LMOptimizerConfig.cuda_graph_mode: Literal['none', 'iter', 'full'] = 'none'

Capture per width: "iter" replays one step; "full" replays the solve without callbacks or early stopping.

attribute[source]
LMOptimizerConfig.lm_lambda: float = 0.001

Initial damping.

attribute[source]
LMOptimizerConfig.lambda_factor: float = 2.0

Damping divides by this on accept and multiplies on reject.

attribute[source]
LMOptimizerConfig.lambda_min: float = 1e-05

Smallest damping.

attribute[source]
LMOptimizerConfig.lambda_max: float = 10000000000.0

Largest damping.

attribute[source]
LMOptimizerConfig.rho_min: float = 0.001

Minimum gain ratio rho to accept a step.

attribute[source]
LMOptimizerConfig.gain_ratio_epsilon: float = 1e-08

Stabilizer in the rho denominator; 0 uses the exact ratio.

attribute[source]
LMOptimizerConfig.linear_solver: Literal['cholesky', 'cg', 'torch_cholesky'] = 'cholesky'

Native Cholesky, matrix-free CG, or optional Torch float64 Cholesky for large dense systems.

attribute[source]
LMOptimizerConfig.cg_max_iter: Optional[int] = None

CG iterations per step; None: max(4, min(64, tangent_dim // 4)).

attribute[source]
LMOptimizerConfig.cg_tol: float = 1e-06

Squared CG residual that stops early; checked on the host, so only without CUDA graphs.

attribute[source]
LMOptimizerConfig.cg_check_interval: int = 4

CG iterations between those host checks.

method[source]
LMOptimizerConfig.build(
terms: Sequence[Task],
device: wp_device_type,
active_dof_mask: Optional[wp.array] = None
) -> LMOptimizer

CG and Torch Cholesky use eager steps; the native backend retains CUDA graph capture.

Parameters
termsSequence[Task]required
devicewp_device_typerequired
active_dof_maskOptional[wp.array]= None
Returns
LMOptimizer

LMOptimizerConfig(linear_solver="cholesky"), the default, solves each step exactly. Small systems factor in one tile; larger ones use the band implied by SparseTask sparsity patterns. linear_solver="cg" iterates matrix-free on whichever Jacobian the terms provide, sparse or dense, and never forms the normal matrix.

robokit.opt.population_solver.PopulationSolver(
terms: Union[List[ResidualTask], List[List[ResidualTask]]],
config: PopulationSolverConfig,
device: wp_device_type,
score_terms: Optional[Union[ResidualTask, List[Optional[ResidualTask]]]] = None,
active_dof_mask: Optional[wp.array] = None
)

Refine batch * num_candidates[0] VarValues, reusing one var, allocation, and graph per population width.

Each scheduled round:

  1. sample: move candidates using prior scores, then draw around them.
  2. optimize: move each candidate toward lower cost.
  3. score: evaluate the terms or score term.
  4. select: keep each problem's best num_candidates[k + 1] candidates.
Parameters
termsUnion[List[ResidualTask], List[List[ResidualTask]]]required
configPopulationSolverConfigrequired
devicewp_device_typerequired
score_termsOptional[Union[ResidualTask, List[Optional[ResidualTask]]]]= None
active_dof_maskOptional[wp.array]= None
Members
attribute[source]
PopulationSolver.device: wp_device_type
attribute[source]
PopulationSolver.terms: List[List[ResidualTask]]
attribute[source]
PopulationSolver.score_terms: List[Optional[ResidualTask]]
method[source]
PopulationSolver.setup(
var: VarValues
)
Parameters
varVarValuesrequired
property[source]
PopulationSolver.winner_indices: wp.array

(batch, 1) row of each instance's winner: the first ranked index.

method[source]
PopulationSolver.solve(
var: VarValues,
iteration_callback: Optional[IterationCallback] = None
) -> Tuple[VarValues, wp.array]

Run the schedule on widest var; return each instance's best candidate and cost.

Parameters
varVarValuesrequired
iteration_callbackOptional[IterationCallback]= None
Returns
Tuple[VarValues, wp.array]
method[source]
PopulationSolver.compute_costs(
var: VarValues
) -> Dict[str, wp.array]

Sum final-segment term costs by cost_name, including duplicate names such as ConfigurationTask.

Parameters
varVarValuesrequired
Returns
Dict[str, wp.array]
robokit.opt.population_solver.PopulationSolverConfig(
stages: List[Stage] = (lambda: [Stage(iters=10)])(),
samplers: Sequence[SamplerConfig] = tuple(),
cuda_graph_mode: Literal['none', 'full', 'iter'] = 'iter',
use_early_stopping: bool = True,
rank_final: bool = False,
min_improvement: float = 0.0,
patience: int = 2
)

What a round does, and when: stages run in order, iterations counted across them.

Parameters
stagesList[Stage]= (lambda: [Stage(iters=10)])()
samplersSequence[SamplerConfig]= tuple()
cuda_graph_modeLiteral['none', 'full', 'iter']= 'iter'
use_early_stoppingbool= True
rank_finalbool= False
min_improvementfloat= 0.0
patienceint= 2
Members
attribute[source]
PopulationSolverConfig.stages: List[Stage] = field(default_factory=lambda: [Stage(iters=10)])

The schedule, e.g. [Stage(6, 64, LMOptimizerConfig(lm_lambda=10.0)), Stage(10, 4), Stage(0, 1)]: 64 candidates for 6 iterations, the best 4 for 10 more, then the best one.

attribute[source]
PopulationSolverConfig.samplers: Sequence[SamplerConfig] = field(default_factory=tuple)

Built during setup, then applied in order where the stages sample.

At each sampling iteration:

  1. Sample the population before optimizing.
  2. Restart the optimizer on that population.
  3. At the last iteration, sample after the final score and end on the moved candidates.
attribute[source]
PopulationSolverConfig.cuda_graph_mode: Literal['none', 'full', 'iter'] = 'iter'

"iter" replays one optimizer step per width; "full" the whole solve (no callbacks or early stopping).

attribute[source]
PopulationSolverConfig.use_early_stopping: bool = True

Let a segment end at its optimizer's stop rule (OptimizerConfig.early_stopping_interval); not under "full".

attribute[source]
PopulationSolverConfig.rank_final: bool = False

Score only the final population and return it best first; earlier selects keep the optimization cost.

attribute[source]
PopulationSolverConfig.min_improvement: float = 0.0

If positive, a drawing sampler's solve ends once each instance's cheapest draw beats its first one (the center) by less than this, patience scores in a row; the last sample still moves the candidates. Reads scores on the host.

attribute[source]
PopulationSolverConfig.patience: int = 2
property[source]
PopulationSolverConfig.num_draws: int

The widest the population gets per candidate: the drawing sampler's draw count, or 1.

property[source]
PopulationSolverConfig.num_iters: int

Iterations of the whole schedule.

property[source]
PopulationSolverConfig.select_at: List[int]

Iterations (1-based, after the update) where a stage ends and the next one's width is kept.

property[source]
PopulationSolverConfig.num_candidates: List[int]

Population width per instance in each stage.

property[source]
PopulationSolverConfig.optimizer: Optional[List[Optional[OptimizerConfig]]]

Each stage's gradient update, or None if no stage sets one.

property[source]
PopulationSolverConfig.sample_at: List[int]

Zero-based sampling iterations; empty: the default (see Stage.sample_every).

robokit.opt.population_solver.Stage(
iters: int = 0,
num_candidates: Optional[int] = None,
optimizer: Optional[OptimizerConfig] = None,
sample_every: int = 0
)

A run of iters iterations at one population width. Entering a stage keeps each instance's best num_candidates (the first stage's is the input width); unset fields keep the previous stage's.

Parameters
itersint= 0
num_candidatesOptional[int]= None
optimizerOptional[OptimizerConfig]= None
sample_everyint= 0
Members
attribute[source]
Stage.iters: int = 0

Iterations of this stage.

attribute[source]
Stage.num_candidates: Optional[int] = None

Population width per instance; None: the previous stage's, or 1.

attribute[source]
Stage.optimizer: Optional[OptimizerConfig] = None

Gradient update; None: the previous stage's. With none in any stage: drawing samplers (MPPI/ES) update by sampling, other solvers by dense LM.

attribute[source]
Stage.sample_every: int = 0

Draw with the samplers every this many iterations from the stage's start; 0 in every stage: a drawing sampler draws before every score and once after the last, others at each select.

robokit.opt.sampler.SamplerConfig(
var_key: Enum = VarKey.ROBOT
)

Sampler settings; owners supply variables, population width, device and masks at build time.

Parameters
var_keyEnum= VarKey.ROBOT
Members
attribute[source]
SamplerConfig.var_key: Enum = VarKey.ROBOT
method[source]
SamplerConfig.build(
var: VarValues,
num_candidates: int,
active_dof_mask: Optional[wp.array] = None
) -> Sampler

Construct an independent runtime sampler for these variables.

Parameters
varVarValuesrequired
num_candidatesintrequired
active_dof_maskOptional[wp.array]= None
Returns
Sampler
robokit.opt.sampler.RobertsSamplerConfig(
var_key: Enum = VarKey.ROBOT,
scale: Union[float, Sequence[float]] = 0.2,
seed: Optional[int] = None,
keep_first: bool = False
)
Bases: SamplerConfig

Low-discrepancy initialization settings; helpers may scale the spread by joint ranges.

Example

python
>>> config = RobertsSamplerConfig(scale=0.5, keep_first=True)
>>> config.scale, config.keep_first, config.num_draws
(0.5, True, 1)
Parameters
var_keyEnum= VarKey.ROBOT
scaleUnion[float, Sequence[float]]= 0.2
seedOptional[int]= None
keep_firstbool= False
Members
attributeinherited from SamplerConfig[source]
RobertsSamplerConfig.var_key: Enum = VarKey.ROBOT
attribute[source]
RobertsSamplerConfig.scale: Union[float, Sequence[float]] = 0.2
attribute[source]
RobertsSamplerConfig.seed: Optional[int] = None
attribute[source]
RobertsSamplerConfig.keep_first: bool = False
method[source]
RobertsSamplerConfig.build(
var: VarValues,
num_candidates: int,
active_dof_mask: Optional[wp.array] = None
) -> Sampler
Parameters
varVarValuesrequired
num_candidatesintrequired
active_dof_maskOptional[wp.array]= None
Returns
Sampler
robokit.opt.sampler.GaussianSamplerConfig(
var_key: Enum = VarKey.ROBOT,
num_draws: int = 64,
weighting: Weighting = 'softmax',
init_std: float = 0.1,
min_std: float = 0.01,
temperature: float = 0.1,
gamma: float = 0.9,
learning_rate: float = 0.1,
epsilon: float = 1e-06,
seed: int = 0,
sample_mode: Literal['mean', 'best'] = 'mean',
elite_ratio: float = 0.0,
mean_elites: bool = False,
noise_decay: float = 1.0,
wide_ratio: float = 0.0,
wide_scale: float = 1.0
)
Bases: SamplerConfig

Gaussian draw/update settings, independent of runtime population buffers.

Example

python
>>> from robokit.opt.var_values import VectorVar
>>> class Key(Enum):
...     X = 0
>>> var = VarValues({Key.X: VectorVar(wp.zeros((1, 2), dtype=wp.float32, device="cpu"))})
>>> config = GaussianSamplerConfig(var_key=Key.X, num_draws=8)
>>> first, second = config.build(var, 1), config.build(var, 1)
>>> first is second
False
>>> first.noise.shape
(8, 2)
Parameters
var_keyEnum= VarKey.ROBOT
num_drawsint= 64
weightingWeighting= 'softmax'
init_stdfloat= 0.1
min_stdfloat= 0.01
temperaturefloat= 0.1
gammafloat= 0.9
learning_ratefloat= 0.1
epsilonfloat= 1e-06
seedint= 0
sample_modeLiteral['mean', 'best']= 'mean'
elite_ratiofloat= 0.0
mean_elitesbool= False
noise_decayfloat= 1.0
wide_ratiofloat= 0.0
wide_scalefloat= 1.0
Members
attributeinherited from SamplerConfig[source]
GaussianSamplerConfig.var_key: Enum = VarKey.ROBOT
attribute[source]
GaussianSamplerConfig.num_draws: int = 64

Candidates drawn around each center, the center itself first.

attribute[source]
GaussianSamplerConfig.weighting: Weighting = 'softmax'

How scored draws become weights: MPPI's softmax, or evolution strategies' fitness shaping.

attribute[source]
GaussianSamplerConfig.init_std: float = 0.1

Noise standard deviation at the start of a solve.

attribute[source]
GaussianSamplerConfig.min_std: float = 0.01

Floor the softmax refit keeps the standard deviation above.

attribute[source]
GaussianSamplerConfig.temperature: float = 0.1

Softmax temperature; lower follows the cheapest draws more closely.

attribute[source]
GaussianSamplerConfig.gamma: float = 0.9

Share of the weighted spread the softmax refit moves the standard deviation toward.

attribute[source]
GaussianSamplerConfig.learning_rate: float = 0.1

Fitness-shaping step size.

attribute[source]
GaussianSamplerConfig.epsilon: float = 1e-06

Guard against division by a zero spread.

attribute[source]
GaussianSamplerConfig.seed: int = 0

Noise seed; the same seed replays the same draws.

attribute[source]
GaussianSamplerConfig.sample_mode: Literal['mean', 'best'] = 'mean'

Move each center to its draws' weighted mean, or onto its cheapest draw.

attribute[source]
GaussianSamplerConfig.elite_ratio: float = 0.0

If positive, the softmax temperature is each center's cost spread over this best share of its draws (at least two) instead of temperature, and best_draws keeps its cheapest draw.

attribute[source]
GaussianSamplerConfig.mean_elites: bool = False

With elite_ratio, move each center to the weighted mean of that share alone.

attribute[source]
GaussianSamplerConfig.noise_decay: float = 1.0

Each scored round scales std by this before the refit, never below min_std.

attribute[source]
GaussianSamplerConfig.wide_ratio: float = 0.0

Share of a solve's first-round draws (its last ones) drawn at wide_scale times std, to explore.

attribute[source]
GaussianSamplerConfig.wide_scale: float = 1.0
method[source]
GaussianSamplerConfig.build(
var: VarValues,
num_candidates: int,
active_dof_mask: Optional[wp.array] = None
) -> Sampler
Parameters
varVarValuesrequired
num_candidatesintrequired
active_dof_maskOptional[wp.array]= None
Returns
Sampler
robokit.opt.sampler.Sampler
Bases: Protocol

Draws new candidates around each candidate of one named variable.

Members
attribute[source]
Sampler.var_key: Enum
attribute[source]
Sampler.num_draws: int

Candidates drawn around each one; 1 draws in place.

method[source]
Sampler.reset() -> None

Reopen the distribution at the start of a solve.

Returns
None
method[source]
Sampler.sample(
var: VarValues,
scores: Optional[wp.array]
) -> VarValues

Move candidates by scores; return var or sampler-owned draws for scoring.

Parameters
varVarValuesrequired
scoresOptional[wp.array]required
Returns
VarValues
robokit.opt.sampler.RobertsSampler(
num_candidates: int,
scale: Sequence[float],
seed: Optional[int] = None,
keep_first: bool = False,
device: wp_device_type = 'cpu',
var_key: Enum = VarKey.ROBOT
)

Seed candidates once with a Roberts low-discrepancy sequence, covering a box more evenly than random draws.

Sampling options:

  • scale: half-width per tangent column; joint columns usually use joint range.
  • sampler.scale.assign(...): change the device-side scale between solves.
  • keep_first: leave candidate zero at the seed by assigning it zero noise.

Example

python
>>> sampler = RobertsSampler(num_candidates=4, scale=[0.5, 0.5], device="cpu")
>>> tangent = wp.zeros((8, 2), dtype=wp.float32, device="cpu")
>>> sampler.fill(tangent)
>>> tangent.numpy().shape
(8, 2)
>>> bool(np.abs(tangent.numpy()).max() <= 0.25)
True
Parameters
num_candidatesintrequired
scaleSequence[float]required
seedOptional[int]= None
keep_firstbool= False
devicewp_device_type= 'cpu'
var_keyEnum= VarKey.ROBOT
Members
method[source]
RobertsSampler.reset() -> None

A Roberts spread is fixed.

Returns
None
method[source]
RobertsSampler.sample(
var: VarValues,
scores: Optional[wp.array]
) -> VarValues

A Roberts spread is fixed, so scores tell it nothing: spread var in place.

Parameters
varVarValuesrequired
scoresOptional[wp.array]required
Returns
VarValues
method[source]
RobertsSampler.fill(
out_tangent: wp.array,
iteration: int = 0
) -> None
Parameters
out_tangentwp.arrayrequired
iterationint= 0
Returns
None
robokit.opt.sampler.GaussianSampler(
num_draws: int,
tangent_dim: int,
instances: int,
weighting: Weighting = 'softmax',
init_std: float = 0.1,
min_std: float = 0.01,
temperature: float = 0.1,
gamma: float = 0.9,
learning_rate: float = 0.1,
epsilon: float = 1e-06,
seed: int = 0,
sample_mode: Literal['mean', 'best'] = 'mean',
elite_ratio: float = 0.0,
mean_elites: bool = False,
noise_decay: float = 1.0,
wide_ratio: float = 0.0,
wide_scale: float = 1.0,
active_dof_mask: Optional[wp.array] = None,
device: wp_device_type = 'cpu',
var_key: Enum = VarKey.ROBOT
)

Fit a sampling distribution from costs, as in MPPI and evolution strategies.

Each round:

  1. Move centers using scored draws and refit std.
  2. Draw num_draws std-scaled normals per center; draw zero has no noise and preserves the center.

Center updates:

  • sample_mode=mean: cost-weighted mean of noise.
  • sample_mode=best: cheapest draw's current position, including any optimizer movement.

Cost weighting:

  • softmax: Boltzmann weights over negative cost at temperature.
  • fitness_shaping: standardized costs scaled by learning_rate.

Capacity/reset controls:

  • instances: maximum center population; narrower solves use prefixes.
  • std_reset and temperatures: caller-adjustable per-instance settings; reset restores std from std_reset.
  • lower and upper: optional (instances, tangent_dim) bounds on a vector variable's drawn values; each draw is clamped to its center's and its noise follows.
Parameters
num_drawsintrequired
tangent_dimintrequired
instancesintrequired
weightingWeighting= 'softmax'
init_stdfloat= 0.1
min_stdfloat= 0.01
temperaturefloat= 0.1
gammafloat= 0.9
learning_ratefloat= 0.1
epsilonfloat= 1e-06
seedint= 0
sample_modeLiteral['mean', 'best']= 'mean'
elite_ratiofloat= 0.0
mean_elitesbool= False
noise_decayfloat= 1.0
wide_ratiofloat= 0.0
wide_scalefloat= 1.0
active_dof_maskOptional[wp.array]= None
devicewp_device_type= 'cpu'
var_keyEnum= VarKey.ROBOT
Members
attribute[source]
GaussianSampler.lower: Optional[wp.array]
attribute[source]
GaussianSampler.upper: Optional[wp.array]
attribute[source]
GaussianSampler.best_draws: Optional[VarValues]
attribute[source]
GaussianSampler.draws: Optional[VarValues]
method[source]
GaussianSampler.reset() -> None

Reopen the spread to std_reset and replay the same noise sequence.

Returns
None
method[source]
GaussianSampler.sample(
var: VarValues,
scores: Optional[wp.array]
) -> VarValues
Parameters
varVarValuesrequired
scoresOptional[wp.array]required
Returns
VarValues
method[source]
GaussianSampler.fill(
out_tangent: wp.array,
iteration: int = 0
) -> None
Parameters
out_tangentwp.arrayrequired
iterationint= 0
Returns
None