Operand validation#
What each surface checks before a kernel runs, and where. The rules are per
operand class, not per method: every input of every algorithm belongs to
one of the classes below (a CPU test, test/test_operand_validation_table.py,
asserts this against the ABI table so a new algorithm cannot add an unlisted
operand), and each surface applies the class rule at one place.
The rule set#
Operand class |
Rule |
Members |
Not checked |
|---|---|---|---|
|
|
|
finite values, joint limits, unit quaternion on a floating base |
|
|
|
frame consistency |
|
|
|
finite values |
|
|
|
positive semi-definiteness of weights |
|
|
|
— |
|
16 numbers (a 4x4 column-major SE(3) transform) or none; the target
joint index must be in |
|
orthonormality of the rotation block |
|
Python floats / ints ( |
— |
finiteness, |
A wrong num_joints vs num_vel width on a floating base (an
nv-wide qd / u, the mjx and Pinocchio habit) is refused by name on
the numpy and JAX surfaces (num_vel footgun message) and by the generic
last-dimension rule on torch.
Where each surface enforces the rules#
Surface |
Coercion |
Shape / batch |
Error type |
|---|---|---|---|
numpy |
|
|
|
torch |
none: a CPU tensor, a non-contiguous tensor or a wrong dtype is refused (the op never copies an input) |
|
|
JAX |
|
Python: 2D or 1D-under- |
|
The native checks are the contract; the Python checks exist to give the
friendlier message first. One asymmetry: an empty batch is refused on
numpy and torch (batch must be >= 1), but on JAX it returns an empty
result — XLA elides a zero-sized custom call, so no handler runs. The
malformed-input test module (test/python_wrappers/test_operand_validation.py)
drives the configuration and force classes through all three surfaces,
including the native JAX handler with the Python checks bypassed, plus the
plant, tool and runtime-offset classes on the surfaces that carry them; the
CPU table test proves the classification is complete, not that every class is
exercised on every surface.