diff --git a/.github/workflows/verify-gpu-proof.yml b/.github/workflows/verify-gpu-proof.yml index 1614fdac..477bb428 100644 --- a/.github/workflows/verify-gpu-proof.yml +++ b/.github/workflows/verify-gpu-proof.yml @@ -73,13 +73,15 @@ jobs: - name: Install GRiD (CPU deps only) # Packaging unit tests import the build hooks outside pip's isolated # build environment, so their setuptools dependency must be explicit. - # pytest-gpu-proof requires CPython 3.11 or newer; on 3.10 the two - # plugin-scope test modules importorskip it and everything else runs. + # The pinned plugin supports every interpreter in this matrix; receipt + # tooling and scope regressions must also execute on Python 3.10. run: | pip install -e . && pip install pytest matplotlib pyyaml "setuptools>=77" - if python -c 'import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)'; then - pip install "$(grep -o '^pytest-gpu-proof==[0-9.]*' install/requirements-dev.txt)" - fi + pip install "$(grep -o '^pytest-gpu-proof==[0-9.]*' install/requirements-dev.txt)" + # The Pinocchio anchors of the CPU lane (test_manifold_conventions, + # test_urdf_oracle) import pinocchio; the same load-bearing pins as + # install/requirements-dev.txt and the RBDReference peer suite. + pip install "pin<4" cmeel-eigen "cmeel-urdfdom<5" # The BROAD CPU set (audit W00, 2026-09-19): every test/test_*.py + the # benchmark-spec guards + the CPU-only bindings modules. The former six- diff --git a/CHANGELOG.md b/CHANGELOG.md index 7223bb8d..d80db1c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,144 @@ repository. Everything below describes the packaged A2R-Lab releases. ## [Unreleased] +## [0.7.0] — 2026-10-31 + +Consumers rebuild their prepared artifacts (schema 2), declare pybind modules +with `python_extension=True` and pass `CollisionMargins` where a scalar margin +went before; the migration steps are on the documentation's *Migrating to 0.7* +page, which this changelog does not repeat. + +- Make the spherizer's warnings errors on the generated-from-mesh collision + routes (`collision=True`, `collision_native=True`, `grid-generate -c`, + `grid-spherize`): a mesh whose voxel fill failed and would be replaced by a + bounding-box cover, or a model with no spheres, stops generation because + either silently changes the clearance rows a consumer bakes. + `strict_collision=False` on `build_artifact`, `--no-strict-collision` on the + two command lines, and `strict=False` on `spherize_urdf` accept them with the + warning. The opt-out is part of the artifact's input key. +- Rename `ConsumerArtifact.descriptor` to `model_descriptor`, the name the + robot handle uses for the same metadata. +- Keep the double-precision cells of the suites added in this release in the + fp64 alpha lane (`make test-fp64`), outside the signed receipt, the policy + the fp64 tier has had since 0.5.3; the receipt keeps a float cell of every + configuration those suites cover. +- Add the skew revolute mimic fixture `revolute_mimic_fixed_tool` with + closed-form mechanics checks of the numpy oracle and the generated CUDA + inverse dynamics, forward dynamics, ABA, mass matrix and its inverse. +- Compile the CUDA equivalence runners with `-std=c++17`, the header's + documented standard; the generated constexpr record accessors use switch + bodies, which the runners' former `-std=c++11` flag rejected. +- Update GLASS to `6a82320`, which declares no `__restrict__` on its own + interfaces and carries a regression for the selected-operand miscompile + (nvcc 11.4 through 13.2 dropped the stores of an inlined scalar joint + transform writer whose output a restricted gemm operand selected). The scalar + joint transform writer is inlined again; no call frame in consumer suffixes. +- Add thread-local full-configuration and independent-scalar joint transforms, + plus compact and dense native configuration-difference Jacobians derived from + the model descriptor's coordinate partition. Document buffer ownership, + runtime-table limits, parameter lifetime and defect composition. +- Repair admitted fixed-spherical Euler/semi-implicit step second derivatives + in the CPU reference and generated CUDA. Stabilize small-angle reference + Rodrigues coefficients. Clarify that `plant_step_hessian` differentiates the + moving-basis gradient: it is not a fixed-chart or shooting-defect Hessian. +- Add checked generated cost-menu views, explicit full-shared/deep-spill + fixed-base plant-Hessian views with matched evaluators, gradient initialization + extents, and exact target EE inner scratch queries. Keep allocation, collective + synchronization, invocation ownership and actual enclosing-kernel resource + admission explicit. Correct the plant-Hessian launch-size query's missing + runtime-transform reserve using the same resolved layout as its kernel. + Numerical arithmetic and tier selection are unchanged. +- Add CPU model inspection and generated host/device coordinate/frame metadata + with explicit independent ownership, xyzw conventions and typed canonical + identity. A single-body-pose frame composer requires no FK pass or shared + scratch and refuses nominal runtime-transform offsets. Wrapper metadata is + derived from the same descriptor. Consumer artifact schema 2 validates the + descriptor/header/binary association on build, reuse and source-free prepared + load; older prepared artifacts require rebuilding. Descriptor identity is + not complete dynamics or artifact identity. +- Omit zero-weight reduced projections in floating-base constant-joint CRBA + while retaining composite inertia and transport through constant ancestors. + Add FP32/FP64 floating and fixed-base descendant dynamics tests against + independently locked models, including first and second derivatives. +- Preserve coordinate ownership in homogeneous derivative tables and EE + gradient/Hessian metadata when constant joints are retained. Correct empty + coordinate generation in integrator/plant arena binders. Add zero-DoF pose + Hessian and Euler plant-gradient device canary tests plus nonempty controls. + Preserve non-unit mimic scaling when an EE Jacobian column has just one + contribution, including after a constant column is removed. +- Exclude constant-joint reduced rows/columns in fixed-base mimic CRBA, preserving + composite inertia and ancestor transport. This removes a zero-size mass-matrix + access exposed by FP64 memcheck through the Euler plant-gradient path. +- Omit constant-joint local columns and zero-weight projection reads in generated + inverse-dynamics derivatives, second-order folds, frame Jacobians/Jdot and + Coriolis metadata. Retain inherited body motion and force transmission. Add + independent locked-model, poisoned-branch and empty-buffer CUDA regressions; + these do not establish general zero-DoF wrapper or timing support. +- Match plant-step, gradient and Hessian C ABI gravity/timestep scalars to the + selected precision, including MuJoCo entry points. FP64 callers previously + passed doubles to float arguments; add signature and numerical regressions. +- Size the full-shared plant-gradient arena independently of the tier-selected + integrator allocation. Reject oversized plant-gradient calls before launching; + retain the tiered integrator-gradient path. Share spherical scratch dimensions + and test compiled carve bounds, canaries and device admission. Fix template + commas in generated debug-layout assertions. +- Correct constant-mimic coordinate evaluation: fixed-leader chains keep their + composed offset without reading an unrelated q slot. Match block and + thread/warp FK emission and omit zero local motion/projection contributions in + the reference while retaining body motion and transmitted forces. Add + direct/chained, empty-coordinate and FP32/FP64 CUDA regressions. +- Do not read placeholder velocity/acceleration slots or project zero local + contributions in generated inverse dynamics for constant joints. Add a + branched FP32/FP64 poison regression that preserves genuine NaN inputs. +- Update GLASS first to `9a24f37` for cooperative handoff reloads under + restricted callers (the release pin is `6a82320`, above). Add embedded FP32/FP64 solve, eigenvalue-clamp and pivoted-inverse + regressions with poisoned scratch and thread-count checks. Update + pytest-gpu-proof to 0.5.0, enable receipt-tooling CPU checks on Python 3.10, + and test compiler-metadata merge refusal and legacy-field compatibility. +- Capture consumer translation-unit dependencies with `nvcc -MM` and hash the + selected non-system header contents into artifact and per-unit reuse keys. + Build lookups rediscover include selection, including newly shadowing headers; + prepared loads remain compiler-free. Explicit headers supplement discovery, + while omitted system development headers remain a documented environment + assumption. No stat-based hashing shortcut or kernel change is introduced. +- Project fixed-base skew-axis mimic forces and mass matrices through every + nonzero motion-subspace component instead of a cardinal-axis index. Independent + slider mechanics now check ID/FD/ABA/M/Minv, with first derivatives on cardinal + slider/mimic cases. This is not a claim of general skew-mimic derivative support. +- Add an executable native/Python consumer migration example and process-death + publication recovery tests. Document the non-transactional crash boundary and + the tested GCC/CUDA scope separately from Clang flag recognition. +- Add independent environment and self clearances to collision feasibility checks + through `CollisionMargins` and the validating host factory + `make_collision_margins`. Scalar calls still apply one margin to both; explicit + per-side zero overrides are supported. The trailing self toggle, raw distances, + normals and cost derivatives retain their meanings. +- Isolate consumer-library symbols by default. Native C entry points now require + explicit default visibility; pybind initialization exports remain visible. + Consumer compile and link share a recorded host-compiler selection, with + GCC-only GNU-unique suppression. GRiD's wrapper export policy is unchanged. +- Add `python_extension=True` to consumer builds. Interpreter ABI identity guards + pointer, content and object reuse and prepared loads; native artifacts remain + independent of Python. Builds verify the declaration against exported Python + initialization symbols before publication. +- Repair incomplete wrapper and consumer cache entries under a per-content + publication lock. Private staging, object, pointer and identity temporary files + distinguish concurrent threads as well as processes. Publication I/O failures propagate + instead of being mistaken for a completed concurrent build. Malformed identity + records are cache misses; permission failures remain errors. +- Include resolved source paths and suffixes in consumer reuse identity (including + path-sensitive `__FILE__` expansions), and use + distinct output paths for same-stem sources such as `kernel.cu` and + `kernel.cpp`. Collision requests fingerprint resolved mesh paths and bytes + before reusing a cached artifact. +- Fix generation for all-prismatic robots with fixed tool targets, initialize + mimic coordinates on no-trigonometry transform paths, and use typed block + loads on those paths. One-DoF second-order contraction uses an untiled width + of one instead of failing to select a tile. +- Floor a warm compile job's memory prediction at the largest measured peak + among variants of the same robot. A relink measurement cannot reduce that + floor; first-job admission and the default for unmeasured jobs are unchanged. + ## [0.6.0] — 2026-10-24 - Margin-aware collision checking (HJCD-IK ask G7). `grid_collision::config_free` on the block, @@ -470,7 +608,8 @@ update on top of that, organised here by theme: - Closed kinematic loops are unsupported. - No Windows, macOS, Jetson or precompiled-robot wheels. -[Unreleased]: https://github.com/A2R-Lab/GRiD/compare/v0.6.0...HEAD +[Unreleased]: https://github.com/A2R-Lab/GRiD/compare/v0.7.0...HEAD +[0.7.0]: https://github.com/A2R-Lab/GRiD/compare/v0.6.0...v0.7.0 [0.6.0]: https://github.com/A2R-Lab/GRiD/compare/v0.5.5...v0.6.0 [0.5.5]: https://github.com/A2R-Lab/GRiD/compare/v0.5.4...v0.5.5 [0.5.4]: https://github.com/A2R-Lab/GRiD/compare/v0.5.3...v0.5.4 diff --git a/CLAUDE.md b/CLAUDE.md index 1c4618da..ba5adfe4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -73,7 +73,7 @@ to the harness's real skip strings by `test/test_suite_floor.py`. GPU test outco test fingerprints (red when correctness inputs change without a refreshed receipt). Main requires a receipt whose collection EQUALS `test/gpu-proof-tests.txt` (policy `required_test_manifest`); diagnostic smoke receipts do not replace that evidence. A release needs one fresh full run -verified under the RELEASE policy. pytest-gpu-proof is pinned at 0.4.1 (`install/requirements-dev.txt`, CI installs +verified under the RELEASE policy. pytest-gpu-proof is pinned at 0.5.0 (`install/requirements-dev.txt`, CI installs the same pin): the driver takes its BASE carry verdicts from the tool's `plan_carry` (ancestry, receipt and per-shard dirty flags, narrow fingerprint) and only demotes on top (oracle pin, recorded failures, wrapper fingerprint schema, cuda byte-neutrality); `gpu-proof merge --verify-inputs` authenticates every shard receipt; the merged receipt is diff --git a/README.md b/README.md index 1e6161b3..e3181c57 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ The full documentation is the website, : installation [How do I…?](https://a2r-lab.org/GRiD/docs/how_do_i.html) index, concepts, API reference, benchmarks and validation. This README points to it. -Version 0.5.1 is on [PyPI](https://pypi.org/project/grid-rbd/); 0.5.0 was the first packaged release. GRiD is alpha +Version 0.7.0 is on [PyPI](https://pypi.org/project/grid-rbd/); 0.5.0 was the first packaged release. GRiD is alpha software and APIs may change between minor versions (see the [changelog](https://github.com/A2R-Lab/GRiD/blob/main/CHANGELOG.md)). The GPU test receipt and the release timing ran on Linux x86-64 with CPython 3.12, CUDA toolkit 13.2 and one RTX 5090 (sm_120); CPython 3.10, 3.11 and 3.13 wheels are built and @@ -45,7 +45,7 @@ pip install "grid-rbd[jax]" # or "grid-rbd[torch]", "grid-rbd[all]" ``` ```python import numpy as np, grid_rbd -robot = grid_rbd.load_robot("grid_assets/robots/iiwa14.urdf", +robot = grid_rbd.load_robot("robot.urdf", # any URDF; samples: grid_assets/robots/ in the repository algorithm_list=["forward_dynamics"], enable_mujoco_kernels=False) q = np.zeros((8, robot.nq), dtype=np.float32) qd, tau = (np.zeros((8, robot.nv), dtype=np.float32) for _ in range(2)) @@ -100,7 +100,7 @@ A Docker image with grid-rbd, `jax[cuda12]`, a cu128 torch wheel and the generat |-----------|------| | `grid_codegen/` | the code-generation engine: emits `grid.cuh` and the checked-in generated binding regions, driven by the `abi_specs.py` table | | `bindings/` | the `grid-rbd` Python package (NumPy/JAX/torch handles over a cached per-robot `.so`); see [`bindings/README.md`](https://github.com/A2R-Lab/GRiD/blob/main/bindings/README.md) | -| `grid_assets/` | ten sample URDFs (`robots/`) + tuned per-GPU launch configs (`launch_configs/`) | +| `grid_assets/` | ten sample robots and three test fixtures (`robots/`) + tuned per-GPU launch configs (`launch_configs/`) | | `test/` | pytest suites + the split-suite/receipt machinery | | `examples/` | `notebooks/` (Python tour), `codegen/`, `cuda/`; see [`examples/README.md`](https://github.com/A2R-Lab/GRiD/blob/main/examples/README.md) | | `docs/` | the Sphinx site (`source/`), the cover page (`landing/`), `agent_debugging_guide.md`, `design_history/` | diff --git a/RELEASING.md b/RELEASING.md index e84bc7f9..5e1a808c 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -146,6 +146,22 @@ touches `gpu-proof.json`. Commit the receipt with its header-key ledger. A release does not wait for secondary receipts; they are documentation of the other architectures, recorded when those machines are available. +Jetson resource budget for a secondary receipt (Orin, 64 GB, twelve cores). The +10-24 attempt rebooted the box out of memory inside the compile pool (guide +7.z63), so launch with the budget stated, not the defaults: `GRID_SPLIT_COMPILE_JOBS=2` +(two full wrapper builds fit with room to spare; the default of five does not), +`GRID_SPLIT_SHARD_JOBS=1` (one GPU shard at a time, the default), and +`GRID_RBD_BUILD_JOBS=4` per wrapper build (the default is `min(8, cores, +MemAvailable / 3 GB)`, which with two concurrent humanoid builds at 12 to 15 GB +each exceeds the box). Disk preflight before launch: the pass writes wrapper +objects to `~/.cache/grid-rbd` and runner executables to `.grid_build_cache/cuda` +in the checkout (37 GB and 47 GB respectively on the 5090 box after a month of +runs); check `df -h` on both volumes, clear both caches if the volume is under +30 GB free, and do not start at the 6 GB free the 10-24 launch had. Set +`GRID_SPLIT_STALL_SECS=7200` so a long humanoid compile is not mistaken for a +hang, and `GRID_CUDA_ARCH=87` only if a test's architecture probe reports +otherwise. Everything else follows the shell path above unchanged. + Inspect final package provenance and verify the exact frozen artifacts before the TestPyPI rehearsal and the production upload. Package ownership, publisher setup, tagging, pushes and uploads need maintainer approval; the artifact workflow @@ -219,7 +235,7 @@ pull access or GPU runtime correctness. Before announcing the container: visibility in the package settings. Do not work around failed anonymous access with a personal token and then call the public release verified. 2. On an exclusively available GPU host with Docker and NVIDIA Container Toolkit, - run `sudo bash tools/check_container_image.sh 0.5.3` (substitute the released + run `sudo bash tools/check_container_image.sh 0.7.0` (substitute the released version). Omit sudo if Docker is already accessible. No host packages, drivers, group permissions or registry settings are changed by this checker. It uses an empty Docker CLI configuration for the anonymous pull, resolves diff --git a/bindings/README.md b/bindings/README.md index 259cd661..a85a7b2f 100644 --- a/bindings/README.md +++ b/bindings/README.md @@ -161,8 +161,8 @@ The full reference for the rest of the surface lives in the ## Install -`grid_rbd` ships inside the `grid-rbd` distribution on PyPI (0.5.1: Linux -x86-64 wheels and an sdist, CPython 3.10–3.13) together with the code +`grid_rbd` ships inside the `grid-rbd` distribution on PyPI (Linux +x86-64 wheels and an sdist, CPython 3.10 to 3.13) together with the code generator and the `grid-generate` / `grid-spherize` CLIs. Each GPU backend is an opt-in extra on top of the numpy base; pick the row for the wrapper surface you need: @@ -254,6 +254,14 @@ the collision options (`collision`, `collision_res`, `collision_native`, `spheri GRiD never binds the consumer's symbols; load the artifact with `ctypes` or your own binding. See the agent integration guide for the full description. +Consumer build lookups use `nvcc -MM` to rediscover and hash selected non-system +headers, including transitive headers and newly shadowing include-path matches. +`headers=[...]` adds explicit file inputs rather than replacing discovery. +Unchanged build lookups and cached `plan_only` checks preprocess but do not +compile. Prepared `load_artifact` remains compiler-free. System development +headers omitted by `-MM` require an immutable build environment or explicit +header declarations; compiler version alone is not a complete environment hash. + Registration failures (a failed device allocation, a bad toolkit) raise a Python exception naming the failed operation; the generated initializers never `exit()` or reset the CUDA context inside the interpreter (they use the @@ -264,7 +272,7 @@ nvcc/ptxas output can be inspected for warnings. A first use announces itself: a one-line `WARNING` on the `grid_rbd` logger (printed to stderr with no logging setup) says which robot is being compiled, how many units on how many cores, roughly how long (1–3 min for an arm or -quadruped, ~10 min for a humanoid) and where it is cached; a second line reports +quadruped, 6 to 10 min for a humanoid) and where it is cached; a second line reports the elapsed time. `GRID_RBD_QUIET=1` silences it. `get_robot(name, urdf_path=...)` compiles and registers on a *miss* (a hit ignores the URDF), so a script can be written as "get or build". `python -m grid_rbd.gc_cache` reports store entries diff --git a/bindings/examples/AGENT_INTEGRATION_GUIDE.md b/bindings/examples/AGENT_INTEGRATION_GUIDE.md index 2d10c965..77e0b3b2 100644 --- a/bindings/examples/AGENT_INTEGRATION_GUIDE.md +++ b/bindings/examples/AGENT_INTEGRATION_GUIDE.md @@ -1,6 +1,6 @@ # Integrating GRiD via `grid_rbd`: a guide for agents and users -*Last reviewed 2026-10-14 against main `f4f72e7` (grid-rbd 0.5.1). The API surface below (entry points, `get_robot` compile-on-miss, cache layout and `gc_cache`, build knobs, method shapes) was re-checked against `bindings/grid_rbd` and `docs/source/user_guide/tutorials/python_wrappers.rst`. The measured numbers quoted inline (the 2026-10-01 host-copy timings, the iiwa14 FFI block-size figure, the CUDA-graph submit costs) were not re-measured. If the code and this guide disagree, trust the code and fix the guide.* +*Last reviewed 2026-10-14 against main `f4f72e7` (grid-rbd 0.5.1); method shapes, integrator names, precision tiers and the batch-overlay API re-checked 2026-10-31 against 0.7.0. The API surface below (entry points, `get_robot` compile-on-miss, cache layout and `gc_cache`, build knobs, method shapes) was re-checked against `bindings/grid_rbd` and `docs/source/user_guide/tutorials/python_wrappers.rst`. The measured numbers quoted inline (the 2026-10-01 host-copy timings, the iiwa14 FFI block-size figure, the CUDA-graph submit costs) were not re-measured. If the code and this guide disagree, trust the code and fix the guide.* This guide covers how to call GRiD's GPU rigid-body dynamics from Python effectively. The governing rule is to place your data on the GPU once and keep it @@ -110,24 +110,97 @@ art = grid_rbd.build_artifact( include_dirs=["csrc"], defines={"HJCD_BATCH": 64}, algorithm_list=["end_effector_pose"], # the codegen keywords of register_robot collision=True, collision_res=[0.10, 0.06], # or spherized_urdf=, collision_native=True + # the spherizer's warnings are errors; strict_collision=False accepts them emit_plant=None) # the generator's rule: a kinematics header carries no plant block lib = ctypes.CDLL(str(art.path)) # art.header is the grid.cuh it was compiled against ``` The identity covers the URDF, the codegen options and the codegen tree, the sources and -headers by content, the defines and flags, the toolchain, the CUDA architecture and the -package version. A second call with the same inputs is a pointer hit (no generation, no -`nvcc`); a changed define or source re-keys the artifact, and a unit whose exact inputs were +headers by content, resolved source paths (including `__FILE__` expansions), +the defines and flags, the toolchain, the CUDA architecture and the +package version. Collision requests also fingerprint the resolved collision mesh paths +and bytes before consulting a cached pointer. Consumer builds run `nvcc -MM` with +their effective compile flags and record the selected non-system dependencies +and their content hashes per translation unit in `meta.json`. Discovery runs on +build lookups too: changing a transitive header, upgrading pybind headers in +place or adding an earlier include-path match invalidates affected reuse. +Explicit `headers` supplement this discovery for additional file inputs. +System development headers omitted by `-MM` remain an environment assumption; +compiler identity does not fingerprint every toolkit/host package file. Use an +immutable build environment, or explicitly list relevant omitted headers. +Dependency output must name readable files. Literal backslashes in POSIX filenames +are not supported: nvcc rewrites them as slashes in its report. Use ordinary path +separators and names; spaces, `#` and `$` are tested. An unreadable dependency is +an error, never a reason to accept an old cached artifact. +Keep all inputs +immutable throughout a build. Detected input changes abort publication, but these checks +are not a filesystem snapshot and cannot detect a concurrent edit restored between reads. +A second call with the same inputs is a pointer hit (no generation or compilation, +but dependency preprocessing still runs); a changed define or source re-keys the artifact, and a unit whose exact inputs were compiled before links from the per-consumer object cache. The artifact, its `grid.cuh`, -`meta.json`, `build_inputs.json` and `build.log` are published atomically under +`meta.json`, `build_inputs.json` and `build.log` are staged together and published under `consumers//store//`, and the consumer's manifest binds the name under a lock. `grid_rbd.load_artifact("hjcd", "panda")` returns a prepared artifact without a toolchain, refusing one recorded for another device or key schema; `build_artifact(..., plan_only=True)` reports what a build would use and whether it is -already there; `grid_rbd.list_artifacts("hjcd")` lists the manifest. The gate is +already there. Checking a cached plan performs dependency preprocessing; a cold +plan does not generate or compile. `grid_rbd.list_artifacts("hjcd")` lists the manifest. The gate is `test/python_wrappers/test_consumer_artifact.py`. -Ship the cache dir (`grid_rbd.default_cache_dir()`, default `~/.cache/grid-rbd/` or -`$GRID_RBD_CACHE_DIR`) and every later run starts in well under a second. +Keep the cache dir (`grid_rbd.default_cache_dir()`, default `~/.cache/grid-rbd/` or +`$GRID_RBD_CACHE_DIR`) to reuse a compatible prepared build without compiling again. +Cold loading time depends on the filesystem, CUDA context and consuming application. +Incomplete entries are rebuilt without clearing reusable object files. Concurrent +publishers coordinate per content key, and private staging directories distinguish +threads as well as processes. Replacing a damaged or forced entry can briefly make +that path unavailable to a concurrent prepared reader; it does not expose a partly +written new directory. `force_rebuild` replaces the store entry; it does not +unload or replace a shared library already loaded by the application. Restart that +process to adopt a rebuilt library at the same path. + +For a Python extension (including pybind11), pass `python_extension=True` before +the first build. GRiD records the interpreter implementation, major/minor version, +SOABI, extension suffix and debug/free-threaded ABI settings in pointer, content +and object identity. Prepared loading checks that ABI without requiring a compiler. +Native libraries keep the default `False` and do not depend on Python's ABI. +The caller still supplies matching Python/pybind headers and flags; this is not +an automatic extension builder or an `abi3` compatibility promise. Consumer builds +require `nm` to verify the declaration against exported `PyInit_*` symbols before +publication. Prepared entries without an ABI declaration must be rebuilt once; +do not relabel an old entry manually. + +Consumer modules compile with hidden symbol visibility, including inline +functions. GCC builds also disable GNU-unique statics so one robot's template +state cannot bind to another module's state. Export intended C entry points +explicitly, for example +`extern "C" __attribute__((visibility("default"))) int num_pos();`. +pybind11 exports its `PyInit_*` entry point itself. This policy applies to +consumer units only; the wrapper's C ABI and framework exports are unchanged. + +The consumer build resolves nvcc's host selection from `-ccbin` / +`--compiler-bindir`, `NVCC_CCBIN` and prepend/append flags, records the selected +compiler's path, bytes and version, and pins that compiler for compilation and +linking. GCC and Clang are recognized separately; Clang does not receive the +GCC-only GNU-unique option. An explicitly conflicting link compiler is rejected. +The current native/CUDA integration gates exercise Linux with GCC; recognition +of Clang flags alone is not evidence of a validated Clang/CUDA configuration. +Expand nvcc option files into the explicit flag lists rather than passing +`--options-file`, `-optf` or `@file`; hidden compiler selections/dependencies in +those files are not supported by this consumer build path. + +For an executable migration example, run `consumer_artifact_migration.py` from +this directory with `--urdf /absolute/robot.urdf --work /tmp/new-probe --cuda-arch 120` +(select your actual architecture). It requires pybind11 as a consumer build +dependency, builds explicit native exports and a declared Python extension, +then checks pointer reuse and prepared loading. It is a build/load contract +example, not a numerical or performance benchmark. + +Publication is recoverable, not a durable filesystem transaction. An abrupt +process death after displacing an entry can leave a `.replaced_*` backup and a +missing entry; the next build repairs the entry and retains reusable objects. +Death after installing the new entry can leave a complete winner and its backup. +The OS releases the writer lock on exit. Backups are not automatically purged; +do not delete shared entries or assume concurrent readers see a snapshot. Normal +rename errors attempt rollback, but process death cannot execute that rollback. - `max_batch_size` is a compile-time cap. Calls with `batch ≤ max_batch` run in one launch; larger batches must be chunked by the caller. Bake in the largest batch you will run. @@ -210,7 +283,7 @@ t = torch.utils.dlpack.from_dlpack(jax_array) # JAX → torch j = jax.dlpack.from_dlpack(torch_tensor.contiguous()) # torch → JAX ``` -## Methods (all batched on axis 0; jax/torch are fp32) +## Methods (all batched on axis 0; fp32 is the validated tier on every surface) `B` = batch, `NJ` = `num_joints` (= `nq`), `NV` = `num_vel` (tangent space). On a fixed base, `NV == NJ` and all shapes coincide; on a floating base, `NJ > NV` (the free-flyer adds a quat slot). @@ -219,9 +292,9 @@ Dynamics and kinematics: | Method | Returns | Notes | |--------|---------|-------| -| `inverse_dynamics(q,qd,qdd=None)` | `(B,NJ)` | alias `rnea`; `qdd=None` gives the bias `c=h−g`, nonzero adds `M·qdd` | -| `forward_dynamics(q,qd,u)` | `(B,NJ)` | alias `fd` | -| `aba(q,qd,u)` | `(B,NJ)` | | +| `inverse_dynamics(q,qd,qdd=None)` | `(B,NV)` | alias `rnea`; `qdd=None` gives the bias `c=h−g`, nonzero adds `M·qdd` | +| `forward_dynamics(q,qd,u)` | `(B,NV)` | alias `fd` | +| `aba(q,qd,u)` | `(B,NV)` | | | `crba(q)` | `(B,NV,NV)` | | | `minv(q)` | `(B,NV,NV)` | | | `end_effector_pose(q)`/`_gradient`/`_hessian` | `(B,6·num_ees[,NV[,NV]])` | | @@ -238,8 +311,8 @@ Trajectory optimization (`grid_plant`): | Method | Returns | Notes | |--------|---------|-------| -| `integrator(q,qd,u,dt)` / `integrator_gradient` | | `integrator_type=` is `euler` (default) / `semi_implicit_euler` / `midpoint` / `rk3` / `rk4` | -| `plant_step(x,u,dt)` | `(B,NX)` | state `x=[q;qd]` (`NX=2·NV`) | +| `integrator(q,qd,u,dt)` / `integrator_gradient` | | `integrator_type=` is `euler` (default) / `semi_implicit_euler` / `midpoint` / `rk4` / `trapezoidal` / `constant_acceleration` | +| `plant_step(x,u,dt)` | `(B,NX)` | state `x=[q;qd]` (`NX=NJ+NV`; `2·NV` on a fixed base) | | `plant_step_gradient(x,u,dt)` | `(B,2·NV,3·NV)` | `[A\|B]` | Cost terms return `(value, grad, hess)`: `quadratic_state_cost(x,x_des,Q)`, @@ -271,9 +344,9 @@ Properties: `num_joints`, `num_vel`, `num_ees`, `num_bodies`, `floating_base`, ` `output_convention`, plus the aliases `nq` / `nv` / `nb` (configuration width / tangent width / body count). -The jax/torch surfaces are strictly fp32 (compute and I/O). The numpy -handle defaults to fp32 too but supports a true fp64 tier via `dtype="float64"` (separate `.so`) -(the fp32 compute path is the validated one). +All three surfaces default to fp32, the validated tier. `dtype="float64"` builds a +separate `.so` and the handle's arrays, gravity and `dt` follow it; fp64 is the alpha tier +outside the signed receipt. ## Gradients are analytic @@ -371,8 +444,8 @@ fastest at 128 threads but the FFI path is fastest at ~768: the same kernel, ~1. Per-algo introspection: `handle.kernel_max_threads(key)` returns the real compiled `__launch_bounds__` ceiling for that algo's baked-tier kernel (keys = the autotune short names: id, fd, minv, id_du, fd_du, ee_pose, idsva_so, ...; -1 = unknown/absent). -Batch-regime switching: `set_threads_for_n`/`apply_batch_overlay`/`get_batch_switch` -apply per-algo small-batch thread overrides from the config's `ffi_bases_by_n` block. +Batch-regime switching: `apply_batch_overlay` +applies per-algo small-batch thread overrides from the config's `ffi_bases_by_n` block. ## Fusing GRiD into your own CUDA kernel diff --git a/bindings/examples/consumer_artifact_migration.py b/bindings/examples/consumer_artifact_migration.py new file mode 100644 index 00000000..8f88df73 --- /dev/null +++ b/bindings/examples/consumer_artifact_migration.py @@ -0,0 +1,116 @@ +"""Build, reuse and load a native C library and a declared Python extension. + +Run with an installed candidate, nvcc, a supported GCC host and pybind11: + python consumer_artifact_migration.py --urdf robot.urdf --work /tmp/new-probe --cuda-arch 120 + +The work directory must be new. Inputs stay at stable paths for both builds; +the example does not modify a shared cache or pin a consumer dependency. +This tests the build/load contract, not GPU numerical behavior or performance. +The last phase re-runs in a fresh process that cannot see the URDF or nvcc and +loads both artifacts through the prepared path with the expected descriptor +identity, which is how a deployed application without a toolchain should start. +""" +from __future__ import annotations + +import argparse +import ctypes +import importlib.util +import json +from pathlib import Path +import sysconfig + +import os +import shutil +import subprocess +import sys + +import pybind11 +from grid_rbd import build_artifact, describe_model, load_artifact + +PREPARED_PHASE = "GRID_MIGRATION_EXAMPLE_PREPARED" + + +def prepared_phase(): + """Fresh process: no URDF, no compiler on PATH, prepared load with the expected identity.""" + spec = json.loads(os.environ[PREPARED_PHASE]) + assert shutil.which("nvcc") is None, "the prepared phase must not see a compiler" + assert not Path(spec["urdf"]).exists() or spec["urdf_hidden"], "the prepared phase must not need the URDF" + native = load_artifact("migration", "native", cache_dir=spec["cache_dir"], cuda_arch=spec["cuda_arch"], + expected_descriptor_id=spec["descriptor_id"]) + python = load_artifact("migration", "python", cache_dir=spec["cache_dir"], cuda_arch=spec["cuda_arch"], + expected_descriptor_id=spec["descriptor_id"]) + assert not native.compiled and not python.compiled + assert native.model_descriptor["descriptor_id"] == python.model_descriptor["descriptor_id"] == spec["descriptor_id"] + lib = ctypes.CDLL(str(native.path)) + lib.consumer_num_pos.restype = ctypes.c_int + module_spec = importlib.util.spec_from_file_location("grid_migration_probe", str(python.path)) + module = importlib.util.module_from_spec(module_spec) + module_spec.loader.exec_module(module) + assert lib.consumer_num_pos() == module.num_pos() == spec["num_pos"] + print(json.dumps({"prepared_phase": "ok", "num_pos": module.num_pos()}, sort_keys=True)) + + +def main(): + if PREPARED_PHASE in os.environ: + return prepared_phase() + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--urdf", type=Path, required=True) + parser.add_argument("--work", type=Path, required=True) + parser.add_argument("--cuda-arch", type=int, required=True) + args = parser.parse_args() + work = args.work.resolve() + work.mkdir(parents=True, exist_ok=False) + native = work / "native.cu" + native.write_text('#include "grid.cuh"\n' + 'extern "C" __attribute__((visibility("default")))\n' + 'int consumer_num_pos() { return grid::NUM_POS; }\n') + extension = work / "python.cu" + extension.write_text('#include \n#include "grid.cuh"\n' + 'PYBIND11_MODULE(grid_migration_probe, m) {\n' + ' m.def("num_pos", []() { return grid::NUM_POS; });\n}\n') + model = describe_model(args.urdf) + common = dict(cache_dir=work / "cache", cuda_arch=args.cuda_arch, + algorithm_list=["end_effector_pose"], enable_mujoco_kernels=False, + expected_descriptor_id=model["descriptor_id"]) + requests = { + "native": dict(sources=[native]), + "python": dict(sources=[extension], python_extension=True, + include_dirs=[pybind11.get_include(), sysconfig.get_path("include")], + artifact_name="grid_migration_probe" + sysconfig.get_config_var("EXT_SUFFIX")), + } + records = {} + for name, options in requests.items(): + first = build_artifact("migration", name, args.urdf, **common, **options) + again = build_artifact("migration", name, args.urdf, **common, **options) + prepared = load_artifact("migration", name, cache_dir=common["cache_dir"], cuda_arch=args.cuda_arch) + assert first.compiled and not again.compiled and not prepared.compiled + assert first.content_key == again.content_key == prepared.content_key + records[name] = {"path": str(prepared.path), "content_key": prepared.content_key, + "python_extension": prepared.meta["python_extension"]} + lib = ctypes.CDLL(records["native"]["path"]) + lib.consumer_num_pos.argtypes = [] + lib.consumer_num_pos.restype = ctypes.c_int + spec = importlib.util.spec_from_file_location("grid_migration_probe", records["python"]["path"]) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + assert lib.consumer_num_pos() == module.num_pos() > 0 + + # Deployment check: a fresh interpreter with no compiler on PATH and a URDF path that + # does not exist loads both artifacts through the prepared path and gets the same value. + toolchain_free = os.pathsep.join(p for p in os.environ.get("PATH", "").split(os.pathsep) + if not (Path(p) / "nvcc").exists()) + spec = {"cache_dir": str(common["cache_dir"]), "cuda_arch": args.cuda_arch, + "descriptor_id": model["descriptor_id"], "num_pos": module.num_pos(), + "urdf": str(work / "missing.urdf"), "urdf_hidden": True} + env = {**os.environ, "PATH": toolchain_free, PREPARED_PHASE: json.dumps(spec)} + result = subprocess.run([sys.executable, "-I", __file__], env=env, cwd=work, + capture_output=True, text=True) + if result.returncode != 0: + raise SystemExit("prepared phase failed:\n" + result.stdout + result.stderr) + prepared = json.loads(result.stdout.strip().splitlines()[-1]) + print(json.dumps({"num_pos": module.num_pos(), "artifacts": records, + "prepared_phase": prepared["prepared_phase"]}, sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/bindings/grid_rbd/__init__.py b/bindings/grid_rbd/__init__.py index dee438ff..de7a8dc3 100644 --- a/bindings/grid_rbd/__init__.py +++ b/bindings/grid_rbd/__init__.py @@ -45,7 +45,9 @@ write_build_inputs, ) from ._compile import compile_sources, generate_sources +from ._store import entry_metadata, publish_entry, staging_dir from ._handle import RobotHandle, SecondOrderID, SecondOrderFD +from ._model import describe_model _log = logging.getLogger("grid_rbd") @@ -303,8 +305,8 @@ def register_robot( ``"torch"``/``None`` (what the torch/jax registrars pass — jax's baked defaults already ARE its tuned config). Runtime-only. runtime_inertia : bool, optional - Build the robot with a runtime-mutable inertia table (D.4 / Phase 5, - numpy backend only). Default False ⇒ the per-link spatial inertia is + Build the robot with a runtime-mutable inertia table shared by the + NumPy, JAX and PyTorch surfaces of the loaded module. Default False ⇒ the per-link spatial inertia is baked into the .so (byte-identical to a plain build, same cache key). When True, the codegen emits a ``d_inertia_params`` table + on-device 6x6 rebuild + a host mutator, and the handle gains @@ -519,7 +521,7 @@ def _build_notice_start(name: str, meta: dict, gen_dir: Path, cache_dir: Path) - from ._compile import _build_jobs nv = int(meta.get("num_vel", meta.get("NUM_VEL", 0)) or 0) units = len(list(gen_dir.glob("wrapper_*.cu"))) - size = "about 10 min for a humanoid" if nv >= 20 else "typically 1–3 min for an arm or quadruped" + size = "6 to 10 min for a humanoid" if nv >= 20 else "typically 1–3 min for an arm or quadruped" _notice(f"grid_rbd: compiling the dynamics of {name!r} ({nv} velocity DOF) — a one-time nvcc build " f"of {units} units on up to {_build_jobs()} cores, {size}; the result is cached under " f"{cache_dir} for every later run (progress: robot.build.log in the cache entry).") @@ -719,7 +721,6 @@ def warm_robot( code_options["launch_config"] = {k: dict(v) for k, v in sorted(_lc.items())} except Exception: pass # un-resolvable config -> conservative fallback bakes; key stays urdf-derived - import json import shutil as _shutil # Two-stage content-addressed store (2026-08-19, see _cache.py): the input @@ -737,7 +738,7 @@ def warm_robot( entry_dir = store_dir(cache_dir, mapped) if mapped else None stale = None if entry_dir is not None: - stale = ([] if ((entry_dir / "robot.so").exists() and (entry_dir / "meta.json").exists()) + stale = ([] if entry_metadata(entry_dir, "robot.so") is not None else ["entry incomplete (robot.so or meta.json missing)"]) stale = stale or stale_hit_reasons(entry_dir, identity) return { @@ -759,15 +760,14 @@ def warm_robot( if mapped: entry_dir = store_dir(cache_dir, mapped) so_path = entry_dir / "robot.so" - meta_path = entry_dir / "meta.json" # Audit W05: a pointer is honoured only when the entry is whole AND # its recorded build identity equals ours — never hand back a .so # built by another toolchain / GLASS content / key schema. - stale = ([] if (so_path.exists() and meta_path.exists()) + meta = entry_metadata(entry_dir, "robot.so") + stale = ([] if meta is not None else ["entry incomplete (robot.so or meta.json missing)"]) stale = stale or stale_hit_reasons(entry_dir, identity) if not stale: - meta = json.loads(meta_path.read_text()) manifest_register(cache_dir, name, mapped, meta) return mapped, so_path, meta _log.info("grid_rbd: cache pointer for %s rejected (%s); regenerating", @@ -775,9 +775,7 @@ def warm_robot( # Generate sources into a private staging dir, then content-address them. _sweep_dead_staging_dirs(cache_dir / "store") - gen_dir = cache_dir / "store" / f".gen_{os.getpid()}_{input_key[:8]}" - _shutil.rmtree(gen_dir, ignore_errors=True) - gen_dir.mkdir(parents=True, exist_ok=True) + gen_dir = staging_dir(cache_dir / "store", input_key) try: # generate_sources takes a Path. For an inline URDF, persist the string # in the staging dir (published with the entry for re-runs/debugging). @@ -787,14 +785,17 @@ def warm_robot( gen_urdf_path = gen_dir / "robot.urdf" gen_urdf_path.write_text(urdf_string) meta = generate_sources(gen_urdf_path, code_options, gen_dir) + if gen_urdf_path.read_bytes() != urdf_bytes: + raise RuntimeError("URDF changed during generation; retry with immutable inputs") content_key = compute_content_key( gen_dir, code_options, cuda_arch, max_batch_size) entry_dir = store_dir(cache_dir, content_key) so_path = entry_dir / "robot.so" - if so_path.exists() and not force_rebuild: + cached_meta = entry_metadata(entry_dir, "robot.so") + if cached_meta is not None and not force_rebuild: # Identical build already in the store (e.g. a codegen edit whose # emitted bytes are unchanged) — generation-only reuse, no nvcc. - meta = json.loads((entry_dir / "meta.json").read_text()) + meta = cached_meta if read_build_inputs(entry_dir) is None: write_build_inputs(entry_dir, identity) # entry predates W05 else: @@ -811,16 +812,11 @@ def warm_robot( torch_op_key="k" + content_key[:12], object_cache_dir=cache_dir / "objects", reuse_objects=not force_rebuild) + if gen_urdf_path.read_bytes() != urdf_bytes: + raise RuntimeError("URDF changed during compilation; no artifact published") _build_notice_end(name, time.monotonic() - t_build, cache_dir) write_build_inputs(gen_dir, identity) # published with the entry - if force_rebuild and entry_dir.exists(): - _shutil.rmtree(entry_dir) - try: - os.replace(gen_dir, entry_dir) # atomic publish - except OSError: - # A concurrent builder published the same content first — - # theirs is complete (rename is the last step); use it. - meta = json.loads((entry_dir / "meta.json").read_text()) + meta = publish_entry(gen_dir, entry_dir, "robot.so", force=force_rebuild) finally: _shutil.rmtree(gen_dir, ignore_errors=True) # no-op when published @@ -1102,6 +1098,7 @@ def load_robot( ConsumerArtifactError, build_artifact, list_artifacts, load_artifact) __all__ = [ + "describe_model", "gc_cache", "build_artifact", "load_artifact", diff --git a/bindings/grid_rbd/_artifacts.py b/bindings/grid_rbd/_artifacts.py index 4b009bda..653ccce6 100644 --- a/bindings/grid_rbd/_artifacts.py +++ b/bindings/grid_rbd/_artifacts.py @@ -33,8 +33,11 @@ import re import shutil import subprocess +import sys +import sysconfig import threading import time +import xml.etree.ElementTree as ET from dataclasses import dataclass from pathlib import Path from typing import Any, Sequence @@ -45,15 +48,20 @@ keymap_record, package_version, read_build_inputs, stale_hit_reasons, write_build_inputs) from ._compile import _NVCC_DEFAULT_FLAGS, _build_jobs, find_nvcc, generate_grid_cuh +from ._store import entry_metadata, publish_entry, publish_object, staging_dir +from ._consumer_toolchain import resolve_consumer_toolchain +from ._consumer_dependencies import discover_dependencies +from ._model_artifact import (check_expected_id, file_digest, record_model_association, + validate_model_association) _log = logging.getLogger("grid_rbd") -ARTIFACT_SCHEMA = 1 +ARTIFACT_SCHEMA = 2 _NAME_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]*$") -# What a load must match: the key schema and the device the artifact was compiled for. The +# What a load must match: key schema, device, and the ABI for Python extensions. The # toolchain, GLASS content and the codegen tree are provenance, recorded but not required to # load a prepared artifact on a box that never had them. -LOAD_IDENTITY_KEYS = ("key_schema", "artifact_schema", "cuda_arch") +LOAD_IDENTITY_KEYS = ("key_schema", "artifact_schema", "cuda_arch", "python_abi") _SOURCE_SUFFIXES = (".cu", ".cpp", ".cc", ".cxx") @@ -82,6 +90,14 @@ class ConsumerArtifact: meta: dict[str, Any] compiled: bool # nvcc ran in this call (False: reused or loaded) + @property + def model_descriptor(self): + """Validated coordinate metadata, not complete dynamics/artifact identity.""" + try: + return validate_model_association(self.store_dir, self.meta, self.path.name) + except ValueError as exc: + raise ArtifactIncompatibleError(str(exc)) from exc + # ─── paths ────────────────────────────────────────────────────────────────── def consumer_root(cache_dir: Path, consumer: str) -> Path: @@ -96,7 +112,11 @@ def _check_name(value: str, what: str) -> str: def _artifact_module_hash() -> str: try: - return hashlib.sha256(Path(__file__).read_bytes()).hexdigest() + h = hashlib.sha256(Path(__file__).read_bytes()) + h.update(Path(__file__).with_name("_consumer_toolchain.py").read_bytes()) + h.update(Path(__file__).with_name("_consumer_dependencies.py").read_bytes()) + h.update(Path(__file__).with_name("_model_artifact.py").read_bytes()) + return h.hexdigest() except OSError: return "" @@ -134,8 +154,45 @@ def artifact_identity(cuda_arch: int) -> dict[str, Any]: return identity -def _file_digest(path: Path) -> str: - return hashlib.sha256(path.read_bytes()).hexdigest() +def _python_abi_identity() -> dict[str, Any]: + """Interpreter ABI for declared extensions, without querying a compiler.""" + return { + "implementation": sys.implementation.name, + "version": list(sys.version_info[:2]), + "cache_tag": sys.implementation.cache_tag, + "soabi": sysconfig.get_config_var("SOABI"), + "ext_suffix": sysconfig.get_config_var("EXT_SUFFIX"), + "debug": bool(sysconfig.get_config_var("Py_DEBUG")), + "free_threaded": bool(sysconfig.get_config_var("Py_GIL_DISABLED")), + } + + +def _validate_python_exports(path: Path, python_extension: bool) -> None: + """Validate the pre-lookup declaration; inspecting a binary alone is too late + to protect reuse. Consumer builds require nm, prepared loads do not.""" + result = subprocess.run(["nm", "-D", "--defined-only", str(path)], + capture_output=True, text=True, check=True) + has_pyinit = any(line.split() and line.split()[-1].startswith("PyInit_") + for line in result.stdout.splitlines()) + if has_pyinit != python_extension: + raise ValueError("python_extension declaration disagrees with exported PyInit_* symbols; " + "pass python_extension=True for a Python extension and False for a native library") + + +def _collision_mesh_identity(urdf_path: Path) -> list[dict[str, str]]: + """Resolve with the spherizer's rules before considering a fast cache hit.""" + from grid_codegen.algorithms._spherize import _resolve_mesh_path + dependencies = [] + for link in ET.parse(urdf_path).getroot().findall("link"): + for mesh in link.findall("collision/geometry/mesh"): + filename = mesh.get("filename", "") + resolved = _resolve_mesh_path(filename, str(urdf_path.parent)) + if resolved is None: + raise ValueError(f"collision mesh {filename!r} for link {link.get('name')!r} " + "could not be resolved; provide the asset locally") + path = Path(resolved).resolve() + dependencies.append({"uri": filename, "path": str(path), "sha256": file_digest(path)}) + return dependencies def _resolve_files(values: Sequence[str | Path], what: str, suffixes: tuple[str, ...] | None) -> list[Path]: @@ -169,7 +226,8 @@ def _normalize_defines(defines: Sequence[str] | dict[str, Any] | None) -> list[s def _code_options(urdf_path: Path, cuda_arch: int, *, floating_base, ee_joint_names, dtype, algorithm_list, enable_mujoco_kernels, cost_frames, contact_frames, use_joint_dynamics, friction_model, friction_width, runtime_inertia, runtime_transform, runtime_joint_dynamics, emit_plant, - collision, collision_res, collision_native, spherized_urdf, launch_config_profile) -> dict[str, Any]: + collision, collision_res, collision_native, spherized_urdf, strict_collision, + launch_config_profile) -> dict[str, Any]: if dtype not in ("float32", "float64"): raise ValueError(f"dtype must be 'float32' or 'float64', got {dtype!r}") opts: dict[str, Any] = { @@ -223,7 +281,7 @@ def _code_options(urdf_path: Path, cuda_arch: int, *, floating_base, ee_joint_na if not sp.is_file(): raise FileNotFoundError(f"spherized_urdf not found: {sp}") opts["spherized_urdf"] = str(sp) - opts["spherized_urdf_sha256"] = _file_digest(sp) # the key follows the model's content + opts["spherized_urdf_sha256"] = file_digest(sp) # the key follows the model's content elif collision_native: opts["collision_native"] = True opts["collision_res"] = float(collision_res) if not isinstance(collision_res, (list, tuple)) \ @@ -232,6 +290,13 @@ def _code_options(urdf_path: Path, cuda_arch: int, *, floating_base, ee_joint_na opts["collision"] = True opts["collision_res"] = float(collision_res) if not isinstance(collision_res, (list, tuple)) \ else [float(r) for r in collision_res] + if collision or collision_native: + opts["collision_meshes"] = _collision_mesh_identity(urdf_path) + if not strict_collision: + opts["strict_collision"] = False + elif not strict_collision: + raise ValueError("strict_collision=False applies to the spherizer (collision=True or " + "collision_native=True); a supplied spherized_urdf is validated, not spherized") if launch_config_profile is not None: opts["launch_config_profile"] = str(launch_config_profile) # The baked per-algorithm launch configuration is not derivable from the URDF bytes; fold @@ -260,7 +325,7 @@ def _input_key(urdf_bytes: bytes, code_options: dict[str, Any], sources: list[Pa h.update(urdf_bytes) h.update(canonical_options(code_options).encode()) for p in sources: - h.update(b"source:" + p.name.encode() + b"\0" + p.read_bytes() + b"\0") + h.update(b"source:" + str(p).encode() + b"\0" + p.read_bytes() + b"\0") for p in headers: h.update(b"header:" + p.name.encode() + b"\0" + p.read_bytes() + b"\0") h.update(("include_dirs=" + ";".join(str(d) for d in include_dirs)).encode()) @@ -277,35 +342,51 @@ def _toolchain_tag() -> str: f"glass={_cache._glass_content_hash()};key_schema={_KEY_SCHEMA};artifact_schema={ARTIFACT_SCHEMA}") +def _hash_build_context(h, python_abi, toolchain, dependencies) -> None: + """The declarations both build keys open with: the Python ABI, the consumer toolchain + identity and the dependency capture, each canonically serialized.""" + h.update(("python_abi=" + canonical_options(python_abi)).encode()) + h.update(("consumer_toolchain=" + canonical_options(toolchain)).encode()) + h.update(("dependencies=" + canonical_options(dependencies)).encode()) + + +def _hash_compile_flags(h, compile_cmd: list[str], gen_dir: Path) -> None: + """Every nvcc argument except the staging directory's own paths, whose content is hashed.""" + src = str(gen_dir) + for a in compile_cmd[1:]: + if a == f"-I{src}" or a.startswith(src + "/"): + continue + h.update(a.encode() + b"\0") + + def _content_key(gen_dir: Path, sources: list[Path], headers: list[Path], compile_cmd: list[str], - link_flags: list[str], artifact_name: str) -> str: + link_flags: list[str], artifact_name: str, *, python_abi, toolchain, + dependencies) -> str: """Exactly what nvcc sees: the generated header, the static collision header, the consumer's sources and headers, the flag list (the staging dir's own -I removed; its content is hashed), the link flags and the toolchain.""" h = hashlib.sha256() + _hash_build_context(h, python_abi, toolchain, dependencies) h.update((gen_dir / "grid.cuh").read_bytes()) h.update(_collision_include_dir().joinpath("grid_collision_geometry.cuh").read_bytes()) for p in sources: - h.update(b"source:" + p.name.encode() + b"\0" + p.read_bytes() + b"\0") + h.update(b"source:" + str(p).encode() + b"\0" + p.read_bytes() + b"\0") for p in headers: h.update(b"header:" + p.name.encode() + b"\0" + p.read_bytes() + b"\0") - src = str(gen_dir) - for a in compile_cmd[1:]: - if a == f"-I{src}" or a.startswith(src + "/"): - continue - h.update(a.encode() + b"\0") + _hash_compile_flags(h, compile_cmd, gen_dir) h.update(("link_flags=" + ";".join(link_flags) + ";artifact_name=" + artifact_name).encode()) h.update(_toolchain_tag().encode()) return h.hexdigest() -def _unit_object_key(unit: Path, gen_dir: Path, headers: list[Path], compile_cmd: list[str]) -> str: +def _unit_object_key(unit: Path, gen_dir: Path, headers: list[Path], compile_cmd: list[str], *, + python_abi, toolchain, dependencies) -> str: h = hashlib.sha256() - src = str(gen_dir) - for a in compile_cmd[1:]: - if a == f"-I{src}" or a.startswith(src + "/"): - continue - h.update(a.encode() + b"\0") + _hash_build_context(h, python_abi, toolchain, dependencies) + # nvcc receives this exact resolved path: suffix selects the language and + # __FILE__ exposes the whole path, not merely its basename. + h.update(str(unit).encode() + b"\0") + _hash_compile_flags(h, compile_cmd, gen_dir) h.update((gen_dir / "grid.cuh").read_bytes()); h.update(b"\0") h.update(_collision_include_dir().joinpath("grid_collision_geometry.cuh").read_bytes()); h.update(b"\0") for p in headers: @@ -317,11 +398,14 @@ def _unit_object_key(unit: Path, gen_dir: Path, headers: list[Path], compile_cmd # ─── the compile half ───────────────────────────────────────────────────────── def _compile_command(gen_dir: Path, cuda_arch: int, include_dirs: list[Path], defines: list[str], - extra_flags: list[str]) -> list[str]: + toolchain) -> list[str]: + """The nvcc command of every consumer unit; the toolchain carries the consumer's extra + flags, the host compiler selection and the symbol isolation flags.""" nvcc = find_nvcc() archs = effective_cuda_archs(cuda_arch) gencode_archs = archs if archs is not None else [cuda_arch] - cmd = [nvcc] + list(_NVCC_DEFAULT_FLAGS) + [f"-gencode=arch=compute_{a},code=sm_{a}" for a in gencode_archs] + cmd = [nvcc] + toolchain.prepend + list(_NVCC_DEFAULT_FLAGS) + cmd += [f"-gencode=arch=compute_{a},code=sm_{a}" for a in gencode_archs] cmd.append(f"-I{gen_dir}") cmd.append(f"-I{_collision_include_dir()}") for d in _glass_include_dirs(): @@ -330,14 +414,17 @@ def _compile_command(gen_dir: Path, cuda_arch: int, include_dirs: list[Path], de cmd.append(f"-I{d}") for d in defines: cmd.append(f"-D{d}") - cmd.extend(extra_flags) + cmd.extend(toolchain.compile_flags()) return cmd def _build(gen_dir: Path, sources: list[Path], headers: list[Path], compile_cmd: list[str], link_flags: list[str], - out_so: Path, object_cache_dir: Path, reuse_objects: bool) -> dict[str, Any]: + out_so: Path, object_cache_dir: Path, reuse_objects: bool, *, validate_unit, + python_abi, toolchain, dependencies) -> dict[str, Any]: """Compile each consumer source with -c (from the object cache when its key exists), then - host-link the objects into `out_so`. Returns a record of what ran.""" + host-link the objects into `out_so`. Returns a record of what ran. `validate_unit(unit)` + runs before a unit is compiled and before its object is published; it raises when the + build inputs or that unit's dependencies changed.""" nvcc = compile_cmd[0] jobs = _build_jobs() build_dir = gen_dir / "build" @@ -351,26 +438,30 @@ def _build(gen_dir: Path, sources: list[Path], headers: list[Path], compile_cmd: record: dict[str, Any] = {"units": {}} def one(unit: Path) -> tuple[Path, int, float, str]: - key = _unit_object_key(unit, gen_dir, headers, compile_cmd) - cached = object_cache_dir / f"{unit.stem}-{key}.o" + validate_unit(unit) + key = _unit_object_key(unit, gen_dir, headers, compile_cmd, python_abi=python_abi, + toolchain=toolchain.identity, dependencies=dependencies["units"][str(unit)]) + cached = object_cache_dir / f"{unit.name}-{key}.o" if reuse_objects and cached.is_file(): os.utime(cached) with lock, log_path.open("a") as log: log.write(f"[{unit.stem}] cached {cached.name}\n") record["units"][unit.name] = {"object": cached.name, "compiled": False} return cached, 0, 0.0, "" - obj = build_dir / (unit.stem + ".o") - ulog = build_dir / (unit.stem + ".log") + obj = build_dir / (unit.name + ".o") + ulog = build_dir / (unit.name + ".log") t0 = time.monotonic() with ulog.open("w") as f: - r = subprocess.run(compile_cmd + ["-c", str(unit), "-o", str(obj)], stdout=f, stderr=subprocess.STDOUT) + r = subprocess.run(compile_cmd + ["-c", str(unit), "-o", str(obj)], stdout=f, + stderr=subprocess.STDOUT, env=toolchain.environment()) secs = time.monotonic() - t0 with lock, log_path.open("a") as log: log.write(f"[{unit.stem}] rc={r.returncode} {secs:.1f}s\n") if r.returncode == 0: - tmp = cached.with_name(cached.name + f".{os.getpid()}.tmp") - shutil.move(str(obj), str(tmp)) - os.replace(tmp, cached) + # Reject changed inputs before exposing an object under its old key, + # not merely before publishing the final linked artifact. + validate_unit(unit) + publish_object(obj, cached) obj = cached record["units"][unit.name] = {"object": cached.name, "compiled": True, "seconds": round(secs, 1)} return obj, r.returncode, secs, ulog.read_text() @@ -382,12 +473,12 @@ def one(unit: Path) -> tuple[Path, int, float, str]: obj, rc, out = failed[0] raise RuntimeError(f"nvcc exit {rc} compiling the consumer unit {obj.stem}:\n{out}") objs = [str(obj) for obj, _, _, _ in results] - link = [nvcc, "--shared", "-o", str(out_so)] + objs + link_flags + link = toolchain.link_command(nvcc, out_so, objs) t0 = time.monotonic() with log_path.open("a") as log: log.write("\n$ " + " ".join(link) + "\n") log.flush() - r = subprocess.run(link, stdout=log, stderr=subprocess.STDOUT) + r = subprocess.run(link, stdout=log, stderr=subprocess.STDOUT, env=toolchain.environment()) log.write(f"[link] rc={r.returncode} {time.monotonic() - t0:.1f}s\n") if r.returncode != 0: raise RuntimeError(f"nvcc exit {r.returncode} linking the consumer artifact; build log:\n" + log_path.read_text()) @@ -397,6 +488,25 @@ def one(unit: Path) -> tuple[Path, int, float, str]: # ─── manifest (one per consumer, under a lock) ─────────────────────────────── +def _schema_ok(record: dict[str, Any]) -> bool: + """The record declares this artifact schema as an int (not a bool or a string).""" + return type(record.get('artifact_schema')) is int and record['artifact_schema'] == ARTIFACT_SCHEMA + + +def _abi_disagreement(meta: dict[str, Any], recorded: dict[str, Any] | None, + python_extension: bool, python_abi) -> str | None: + """Why the metadata or the identity record does not declare the requested extension + ABI, or None when both agree with it.""" + if (meta.get("python_extension") is not python_extension or + "python_abi" not in meta or meta["python_abi"] != python_abi): + return "python_abi: metadata declaration disagrees with requested artifact" + if recorded is not None and "python_abi" not in recorded: + return "python_abi: no extension declaration recorded" + if recorded is not None and recorded["python_abi"] != python_abi: + return "python_abi: identity record disagrees with requested artifact" + return None + + def _manifest_path(root: Path) -> Path: return root / "manifest.json" @@ -436,6 +546,8 @@ def build_artifact(consumer: str, name: str, urdf_path: str | Path, *, extra_flags: Sequence[str] = (), link_flags: Sequence[str] = (), artifact_name: str = "artifact.so", + python_extension: bool = False, + expected_descriptor_id: str | None = None, cache_dir: str | Path | None = None, cuda_arch: int | None = None, force_rebuild: bool = False, @@ -458,6 +570,7 @@ def build_artifact(consumer: str, name: str, urdf_path: str | Path, *, collision_res: float | Sequence[float] = 0.05, collision_native: bool = False, spherized_urdf: str | Path | None = None, + strict_collision: bool = True, launch_config_profile: str | None = None) -> ConsumerArtifact | dict[str, Any]: """Build (or reuse) the consumer's artifact for one robot and return where it is. @@ -465,28 +578,52 @@ def build_artifact(consumer: str, name: str, urdf_path: str | Path, *, `name` the artifact within it (one per robot and configuration). `sources` are the consumer's translation units, each compiled with `-c` against the generated `grid.cuh` (plus the static collision header and the GLASS include directories) and linked with - `--shared` into `artifact_name`; `headers` are the consumer's own header files, folded - into the identity by content (an include directory is folded by path only, so list the - headers whose edits must re-key the build); `defines` are `NAME` or `NAME=VALUE`; + `--shared` into `artifact_name`. Non-system dependencies are discovered with nvcc + before reuse/build and hashed by path and contents; `headers` supplements that + discovery with explicitly declared dependencies. System headers excluded by -MM + are an environment assumption, not covered by compiler identity alone. + `defines` are `NAME` or `NAME=VALUE`; `extra_flags` go to every unit compile, `link_flags` to the link. + Declare `python_extension=True` for Python/pybind extensions before cache lookup. + Their interpreter ABI participates in pointer, content and object identity and is + checked on prepared loads. Native C libraries (the default) are Python-independent. + Builds check the declaration against exported PyInit_* symbols using `nm`. + `expected_descriptor_id` optionally checks prior CPU model inspection on + cold and reused builds. Schema-2 artifacts bind validated coordinate + metadata to the generated header and binary; older artifacts need rebuilding. The codegen keywords (`floating_base`, `ee_joint_names`, `dtype`, `algorithm_list`, `enable_mujoco_kernels`, `cost_frames`, `contact_frames`, the joint-dynamics and runtime knobs) mean what they mean on `register_robot`; `emit_plant` is the generator's plant-block switch (None: the generator's rule), and `collision`, `collision_res`, `collision_native` and `spherized_urdf` select the generated `grid_collision` namespace (a sphere model is - validated against the URDF). `launch_config_profile` (default: the bindings' "ffi" - profile) names the baked launch-configuration profile. - - `plan_only=True` reports what a build would use and whether it is already there, without - generating or compiling. `force_rebuild=True` recompiles every unit.""" + validated against the URDF). `strict_collision` (default True) makes the spherizer's + warnings errors on the `collision` and `collision_native` routes: a mesh whose voxel + fill failed and would be replaced by a bounding-box cover, or a model with no spheres, + stops the build instead of silently changing the clearance rows; False accepts them + with the warning. `launch_config_profile` (default: the bindings' "ffi" profile) names + the baked launch-configuration profile. + + `plan_only=True` reports what a build would use without generating or compiling; + validating an existing entry still invokes dependency preprocessing. Cold plans + cannot know dependencies until a generated header exists. `force_rebuild=True` + recompiles every unit. Prepared `load_artifact` requires neither source nor compiler.""" _check_name(consumer, "consumer") _check_name(name, "name") _check_name(artifact_name, "artifact_name") + if not isinstance(python_extension, bool): + raise TypeError("python_extension must be a bool") + check_expected_id(expected_descriptor_id) urdf_p = Path(urdf_path).expanduser().resolve() if not urdf_p.is_file(): raise FileNotFoundError(f"URDF file not found: {urdf_p}") urdf_bytes = urdf_p.read_bytes() + if expected_descriptor_id is not None: + from ._model import describe_model + actual = describe_model(urdf_p, floating_base=floating_base, + runtime_transform=runtime_transform)['descriptor_id'] + if actual != expected_descriptor_id: + raise ValueError('expected_descriptor_id does not match requested model') srcs = _resolve_files(list(sources), "source", _SOURCE_SUFFIXES) if not srcs: raise ValueError("sources must name at least one translation unit") @@ -512,15 +649,53 @@ def build_artifact(consumer: str, name: str, urdf_path: str | Path, *, friction_width=friction_width, runtime_inertia=runtime_inertia, runtime_transform=runtime_transform, runtime_joint_dynamics=runtime_joint_dynamics, emit_plant=emit_plant, collision=collision, collision_res=collision_res, collision_native=collision_native, spherized_urdf=spherized_urdf, - launch_config_profile=launch_config_profile) + strict_collision=strict_collision, launch_config_profile=launch_config_profile) identity = artifact_identity(cuda_arch) + toolchain = resolve_consumer_toolchain(xflags, lflags) + identity["host_cxx"] = toolchain.identity + python_abi = _python_abi_identity() if python_extension else None + identity["python_abi"] = python_abi input_key = _input_key(urdf_bytes, code_options, srcs, hdrs, incs, defs, xflags, lflags, artifact_name, identity) store = root / "store" + def check_inputs_unchanged(): + toolchain.environment() + current_options = dict(code_options) + if collision or collision_native: + current_options["collision_meshes"] = _collision_mesh_identity(urdf_p) + if spherized_urdf: + current_options["spherized_urdf_sha256"] = file_digest(Path(code_options["spherized_urdf"])) + current_key = _input_key(urdf_p.read_bytes(), current_options, srcs, hdrs, incs, + defs, xflags, lflags, artifact_name, identity) + if current_key != input_key: + raise RuntimeError("consumer build inputs changed during generation or compilation; " + "no artifact published; retry with immutable inputs") + def _entry_state(entry_dir: Path) -> list[str]: - if not ((entry_dir / artifact_name).exists() and (entry_dir / "meta.json").exists()): + meta = entry_metadata(entry_dir, artifact_name) + if meta is None: return ["entry incomplete (artifact or meta.json missing)"] - return stale_hit_reasons(entry_dir, identity) + if not _schema_ok(meta): + return ['artifact_schema: incompatible metadata; rebuild artifact'] + try: + validate_model_association(entry_dir, meta, artifact_name, expected_descriptor_id) + except ValueError as exc: + return [str(exc)] + recorded = read_build_inputs(entry_dir) + if recorded is not None and not _schema_ok(recorded): + return ['artifact_schema: incompatible identity record; rebuild artifact'] + disagreement = _abi_disagreement(meta, recorded, python_extension, python_abi) + if disagreement: + return [disagreement] + stale = stale_hit_reasons(entry_dir, identity) + if stale: + return stale + command = _compile_command(entry_dir, cuda_arch, incs, defs, toolchain) + current = discover_dependencies(entry_dir, srcs, command, toolchain) + check_inputs_unchanged() + if meta.get("dependencies") != current: + return ["dependencies: selected files or contents changed (or no capture recorded)"] + return [] if plan_only: mapped = keymap_lookup(root, input_key) @@ -533,7 +708,7 @@ def _entry_state(entry_dir: Path) -> list[str]: "content_key": mapped if (mapped and stale == []) else None, "cached": bool(mapped) and stale == [], "stale_reasons": stale, "store_dir": str(entry_dir) if (mapped and stale == []) else None, - "would": "load" if (mapped and stale == []) else "generate+compile (the content key decides whether nvcc runs)", + "would": "load" if (mapped and stale == []) else "generate+compile (the content key decides whether compilation is needed)", } if not force_rebuild: @@ -543,6 +718,7 @@ def _entry_state(entry_dir: Path) -> list[str]: stale = _entry_state(entry_dir) if not stale: meta = json.loads((entry_dir / "meta.json").read_text()) + validate_model_association(entry_dir, meta, artifact_name, expected_descriptor_id) _manifest_register(root, name, mapped, meta) return ConsumerArtifact(consumer, name, entry_dir / artifact_name, entry_dir / "grid.cuh", entry_dir, mapped, input_key, cuda_arch, meta, False) @@ -550,47 +726,88 @@ def _entry_state(entry_dir: Path) -> list[str]: store.mkdir(parents=True, exist_ok=True) _sweep_dead_staging_dirs(store) - gen_dir = store / f".gen_{os.getpid()}_{input_key[:8]}" - shutil.rmtree(gen_dir, ignore_errors=True) - gen_dir.mkdir(parents=True, exist_ok=True) + gen_dir = staging_dir(store, input_key) compiled = False try: generate_grid_cuh(urdf_p, code_options, gen_dir / "grid.cuh") - compile_cmd = _compile_command(gen_dir, cuda_arch, incs, defs, xflags) - content_key = _content_key(gen_dir, srcs, hdrs, compile_cmd, lflags, artifact_name) + check_inputs_unchanged() + compile_cmd = _compile_command(gen_dir, cuda_arch, incs, defs, toolchain) + dependencies = discover_dependencies(gen_dir, srcs, compile_cmd, toolchain) + + def check_build_inputs(): + check_inputs_unchanged() + if discover_dependencies(gen_dir, srcs, compile_cmd, toolchain) != dependencies: + raise RuntimeError("consumer dependencies changed during build; no artifact published; " + "retry with immutable inputs") + + def check_unit_inputs(unit): + # Check the unit being exposed, not every unrelated unit around each + # compile. Whole-build checks still bracket final publication. + check_inputs_unchanged() + current = discover_dependencies(gen_dir, [unit], compile_cmd, toolchain) + if current["units"][str(unit)] != dependencies["units"][str(unit)]: + raise RuntimeError("consumer dependencies changed during unit compilation; " + "retry with immutable inputs") + + check_build_inputs() + content_key = _content_key(gen_dir, srcs, hdrs, compile_cmd, lflags, artifact_name, + python_abi=python_abi, toolchain=toolchain.identity, + dependencies=dependencies) entry_dir = store / content_key - if (entry_dir / artifact_name).exists() and (entry_dir / "meta.json").exists() and not force_rebuild: - meta = json.loads((entry_dir / "meta.json").read_text()) + cached_meta = entry_metadata(entry_dir, artifact_name) + recorded = read_build_inputs(entry_dir) if cached_meta is not None else None + # A sidecar can be damaged independently of the content-addressed binary. + # Do not return a hit that load_artifact would reject, or relabel an + # incompatible declaration. Rebuild and replace under the publication lock. + replace = force_rebuild + if cached_meta is not None: + replace = replace or _abi_disagreement(cached_meta, recorded, python_extension, python_abi) is not None + replace = replace or cached_meta.get("dependencies") != dependencies + replace = replace or not _schema_ok(cached_meta) or (recorded is not None and ( + not _schema_ok(recorded) or recorded.get('key_schema') != _KEY_SCHEMA)) + try: + validate_model_association(entry_dir, cached_meta, artifact_name, expected_descriptor_id) + except ValueError: + replace = True + if cached_meta is not None and not replace: + check_build_inputs() + meta = cached_meta if read_build_inputs(entry_dir) is None: write_build_inputs(entry_dir, identity) else: _log.info("grid_rbd: building consumer artifact %s/%s (%d unit(s))", consumer, name, len(srcs)) t0 = time.monotonic() record = _build(gen_dir, srcs, hdrs, compile_cmd, lflags, gen_dir / artifact_name, - root / "objects", reuse_objects=not force_rebuild) + root / "objects", reuse_objects=not force_rebuild, validate_unit=check_unit_inputs, + python_abi=python_abi, toolchain=toolchain, dependencies=dependencies) + _validate_python_exports(gen_dir / artifact_name, python_extension) + check_build_inputs() compiled = record["compiled_units"] > 0 meta = { "consumer": consumer, "name": name, "artifact": artifact_name, "urdf": str(urdf_p), "urdf_sha256": hashlib.sha256(urdf_bytes).hexdigest(), - "sources": {p.name: _file_digest(p) for p in srcs}, - "headers": {p.name: _file_digest(p) for p in hdrs}, + "sources": {p.name: file_digest(p) for p in srcs}, + "source_paths": [str(p) for p in srcs], + "headers": {p.name: file_digest(p) for p in hdrs}, "include_dirs": [str(d) for d in incs], "defines": defs, "extra_flags": xflags, "link_flags": lflags, + "python_extension": python_extension, "python_abi": python_abi, + "consumer_toolchain": toolchain.identity, + "dependencies": dependencies, "code_options": code_options, "cuda_arch": int(cuda_arch), "grid_rbd": package_version(), "artifact_schema": ARTIFACT_SCHEMA, "build_seconds": round(time.monotonic() - t0, 1), "build": record, + "model_association": record_model_association(gen_dir, artifact_name), } archs = effective_cuda_archs(cuda_arch) if archs is not None: meta["cuda_archs"] = archs (gen_dir / "meta.json").write_text(json.dumps(meta, indent=2, sort_keys=True)) write_build_inputs(gen_dir, identity) - if force_rebuild and entry_dir.exists(): - shutil.rmtree(entry_dir) - try: - os.replace(gen_dir, entry_dir) - except OSError: - meta = json.loads((entry_dir / "meta.json").read_text()) + check_build_inputs() + meta = publish_entry(gen_dir, entry_dir, artifact_name, force=replace) + check_inputs_unchanged() + validate_model_association(entry_dir, meta, artifact_name, expected_descriptor_id) finally: shutil.rmtree(gen_dir, ignore_errors=True) keymap_record(root, input_key, content_key) @@ -600,12 +817,14 @@ def _entry_state(entry_dir: Path) -> list[str]: def load_artifact(consumer: str, name: str, cache_dir: str | Path | None = None, - cuda_arch: int | None = None) -> ConsumerArtifact: + cuda_arch: int | None = None, *, expected_descriptor_id: str | None = None) -> ConsumerArtifact: """The prepared artifact `name` of `consumer`, without a toolchain: the manifest names it, the entry must be whole and recorded for this device (or one of a fat build's devices) and - key schema. With no detectable GPU and no `cuda_arch`, the recorded device is trusted.""" + key schema. Declared Python extensions must match this interpreter's ABI. + With no detectable GPU and no `cuda_arch`, the recorded device is trusted.""" _check_name(consumer, "consumer") _check_name(name, "name") + check_expected_id(expected_descriptor_id) cache_root = Path(cache_dir).expanduser() if cache_dir else default_cache_dir() root = consumer_root(cache_root, consumer) entry = _load_manifest(root)["artifacts"].get(name) if _manifest_path(root).exists() else None @@ -613,7 +832,8 @@ def load_artifact(consumer: str, name: str, cache_dir: str | Path | None = None, raise ArtifactNotFoundError(f"no artifact {name!r} for consumer {consumer!r} under {cache_root}") entry_dir = root / "store" / entry["content_key"] artifact_name = entry.get("artifact", "artifact.so") - if not (entry_dir / artifact_name).exists() or not (entry_dir / "meta.json").exists(): + meta = entry_metadata(entry_dir, artifact_name) + if meta is None: raise ArtifactIncompatibleError(f"artifact {name!r} of {consumer!r} is incomplete at {entry_dir}") recorded = read_build_inputs(entry_dir) if recorded is None: @@ -621,15 +841,30 @@ def load_artifact(consumer: str, name: str, cache_dir: str | Path | None = None, if cuda_arch is None: cuda_arch = detect_cuda_arch() reasons = [] + if "python_abi" not in recorded: + reasons.append("python_abi: no extension declaration recorded; rebuild the artifact") + else: + abi = recorded["python_abi"] + if (abi is not None and not isinstance(abi, dict)) or ( + meta.get("python_extension") is not (abi is not None)) or ( + "python_abi" not in meta or meta["python_abi"] != abi): + reasons.append("python_abi: metadata and identity declarations disagree; rebuild the artifact") + elif abi is not None and abi != _python_abi_identity(): + reasons.append(f"python_abi: recorded {abi!r} != current {_python_abi_identity()!r}") if recorded.get("key_schema") != _KEY_SCHEMA: reasons.append(f"key_schema: recorded {recorded.get('key_schema')!r} != current {_KEY_SCHEMA!r}") - if recorded.get("artifact_schema") != ARTIFACT_SCHEMA: + if not _schema_ok(recorded): reasons.append(f"artifact_schema: recorded {recorded.get('artifact_schema')!r} != current {ARTIFACT_SCHEMA!r}") + if not _schema_ok(meta): + reasons.append('artifact_schema: metadata is incompatible; rebuild artifact') if cuda_arch and not _arch_loadable(recorded, int(cuda_arch)): reasons.append(f"cuda_arch: recorded {recorded.get('cuda_arch')!r} (archs {recorded.get('cuda_archs')!r}) != current {cuda_arch!r}") if reasons: raise ArtifactIncompatibleError(f"artifact {name!r} of {consumer!r} cannot be loaded here: " + "; ".join(reasons)) - meta = json.loads((entry_dir / "meta.json").read_text()) + try: + validate_model_association(entry_dir, meta, artifact_name, expected_descriptor_id) + except ValueError as exc: + raise ArtifactIncompatibleError(str(exc)) from exc return ConsumerArtifact(consumer, name, entry_dir / artifact_name, entry_dir / "grid.cuh", entry_dir, entry["content_key"], None, int(recorded.get("cuda_arch", cuda_arch or 0)), meta, False) diff --git a/bindings/grid_rbd/_cache.py b/bindings/grid_rbd/_cache.py index 9769c2b4..e210c38c 100644 --- a/bindings/grid_rbd/_cache.py +++ b/bindings/grid_rbd/_cache.py @@ -66,6 +66,8 @@ from pathlib import Path from typing import Any +from ._store import atomic_write_text + try: import platformdirs _DEFAULT_DIR = Path(platformdirs.user_cache_dir("grid-rbd")) @@ -202,13 +204,14 @@ def _codegen_source_hash() -> str: # flags here silently reuses a stale .so (this is how an enable_idsva_so_world_frame # default change went unbuilt). Hash it explicitly; keep the rest of the binding # package out so runtime-only file churn doesn't force needless rebuilds. - compile_py = Path(__file__).resolve().parent / "_compile.py" - try: - h.update(b"bindings/grid_rbd/_compile.py") - h.update(compile_py.read_bytes()) - found = True - except OSError: - pass + for name in ("_compile.py", "_model.py"): + compile_py = Path(__file__).resolve().parent / name + try: + h.update(("bindings/grid_rbd/" + name).encode()) + h.update(compile_py.read_bytes()) + found = True + except OSError: + pass return h.hexdigest() if found else "" @@ -312,9 +315,13 @@ def store_dir(cache_dir: Path, cache_key: str) -> Path: def _compile_source_hash() -> str: - """sha256 of _compile.py — the compile-flag logic is a compile input.""" + """Compile and publication helpers participate in the fast-path identity.""" try: - return hashlib.sha256((Path(__file__).parent / "_compile.py").read_bytes()).hexdigest() + h = hashlib.sha256() + for name in ("_compile.py", "_store.py", "_model.py"): + h.update(name.encode() + b"\0") + h.update((Path(__file__).parent / name).read_bytes()) + return h.hexdigest() except FileNotFoundError: return "" @@ -518,16 +525,15 @@ def build_identity(cuda_arch: int) -> dict[str, Any]: # the identity existed (no sidecar) or by a process whose identity did not # include everything (older key schema). def write_build_inputs(entry_dir: Path, identity: dict[str, Any]) -> None: - tmp = entry_dir / f".{BUILD_INPUTS_FILE}.{os.getpid()}.tmp" - tmp.write_text(json.dumps(identity, indent=2, sort_keys=True)) - tmp.replace(entry_dir / BUILD_INPUTS_FILE) + atomic_write_text(entry_dir / BUILD_INPUTS_FILE, json.dumps(identity, indent=2, sort_keys=True)) def read_build_inputs(entry_dir: Path) -> dict[str, Any] | None: try: - return json.loads((entry_dir / BUILD_INPUTS_FILE).read_text()) - except (OSError, ValueError): + recorded = json.loads((entry_dir / BUILD_INPUTS_FILE).read_text()) + except (FileNotFoundError, json.JSONDecodeError, UnicodeDecodeError): return None + return recorded if isinstance(recorded, dict) else None def stale_hit_reasons(entry_dir: Path, identity: dict[str, Any]) -> list[str]: @@ -624,16 +630,14 @@ def keymap_lookup(cache_dir: Path, input_key: str) -> str | None: path = cache_dir / "bykey" / input_key try: return path.read_text().strip() or None - except OSError: + except FileNotFoundError: return None def keymap_record(cache_dir: Path, input_key: str, content_key: str) -> None: bykey = cache_dir / "bykey" bykey.mkdir(parents=True, exist_ok=True) - tmp = bykey / f".{input_key}.{os.getpid()}.tmp" - tmp.write_text(content_key + "\n") - tmp.replace(bykey / input_key) # atomic; last-writer-wins with same value + atomic_write_text(bykey / input_key, content_key + "\n") def load_manifest(cache_dir: Path) -> dict[str, Any]: diff --git a/bindings/grid_rbd/_compile.py b/bindings/grid_rbd/_compile.py index ce1e4b69..8f627c79 100644 --- a/bindings/grid_rbd/_compile.py +++ b/bindings/grid_rbd/_compile.py @@ -30,6 +30,7 @@ import sys from pathlib import Path from typing import Any +from ._store import publish_object # Package logger. Quiet by default (no handler) — the host app opts in via @@ -132,14 +133,9 @@ def generate_grid_cuh(urdf_path: Path, options: dict[str, Any], out_path: Path) if _p not in sys.path: sys.path.insert(0, _p) - from URDFParser import URDFParser from grid_codegen import GRiDCodeGenerator - - parser = URDFParser() - robot = parser.parse( - str(urdf_path), - floating_base=options.get("floating_base", False), - ) + from ._model import parse_model + robot = parse_model(urdf_path, floating_base=options.get("floating_base", False)) file_namespace = options.get("file_namespace", "grid") debug_mode = options.get("debug_mode", 0) @@ -307,8 +303,10 @@ def generate_grid_cuh(urdf_path: Path, options: dict[str, Any], out_path: Path) # names a sphere model that must pass validate_sphere_model against the URDF, and # `collision_native` uses the URDF's native primitives as capsule rows. The caller # folds the sphere model's bytes into the options (build_artifact does), so the key - # follows the model's content. register_robot never sets these. + # follows the model's content. register_robot never sets these. `strict_collision` + # is recorded only when a consumer opts out of the spherizer's errors. collision_spec = None + strict_collision = bool(options.get("strict_collision", True)) if options.get("spherized_urdf"): from grid_codegen.algorithms._collision import resolve_spherized_urdf, build_sphere_tiers path = resolve_spherized_urdf(str(options["spherized_urdf"]), str(urdf_path)) @@ -318,12 +316,12 @@ def generate_grid_cuh(urdf_path: Path, options: dict[str, Any], out_path: Path) from grid_codegen.algorithms._collision import native_collision_spec_from_urdf res = options.get("collision_res", 0.05) res = list(res)[-1] if isinstance(res, (list, tuple)) else float(res) - collision_spec = native_collision_spec_from_urdf(robot, str(urdf_path), res) + collision_spec = native_collision_spec_from_urdf(robot, str(urdf_path), res, strict=strict_collision) elif options.get("collision"): from grid_codegen.algorithms._collision import multi_tier_collision_spec_from_urdf res = options.get("collision_res", 0.05) res = list(res) if isinstance(res, (list, tuple)) else [float(res)] - collision_spec = multi_tier_collision_spec_from_urdf(robot, str(urdf_path), res) + collision_spec = multi_tier_collision_spec_from_urdf(robot, str(urdf_path), res, strict=strict_collision) with contextlib.redirect_stdout(io.StringIO()): cg.gen_all_code( output_path=str(out_path), @@ -361,33 +359,17 @@ def generate_grid_cuh(urdf_path: Path, options: dict[str, Any], out_path: Path) # joint_names (index == joint id) + leaf_jids let the handle's runtime-target # list API resolve ee_joint_names -> jids (mirrors RBDReference # select_end_effector_joints) without a robot model on the Python side. - import math - - def _jsafe(v): - # ±inf is not valid JSON; serialize unbounded / unspecified limits as null. - if v is None: - return None - try: - return None if math.isinf(float(v)) else float(v) - except (TypeError, ValueError): - return None - - joint_names = [] - # per-joint limits (index == jid), surfaced on the handle as metadata only - # (not consumed by any kernel). pos = [lower, upper], vel/effort = scalar. - joint_pos_limits = [] - joint_vel_limits = [] - joint_effort_limits = [] - for jid in range(robot.get_num_joints()): - joint = robot.get_joint_by_id(jid) - joint_names.append(joint.get_name() if joint is not None else "") - lim = robot.get_joint_limits_by_id(jid) or [] - joint_pos_limits.append([_jsafe(lim[0]), _jsafe(lim[1])] if len(lim) == 2 else None) - joint_vel_limits.append(_jsafe(robot.get_velocity_limit_by_id(jid))) - joint_effort_limits.append(_jsafe(robot.get_effort_limit_by_id(jid))) + descriptor = cg.model_descriptor + if descriptor is None: + raise RuntimeError('bindings generation requires a quaternion model descriptor') + (out_path.parent/'model_descriptor.json').write_text( + json.dumps(descriptor, ensure_ascii=False, allow_nan=False, indent=2)) + joints = descriptor['joints'] meta = { - "num_joints": robot.get_num_pos(), - "num_vel": robot.get_num_vel(), + "model_descriptor": descriptor, + "num_joints": descriptor['nq'], # historical name means position width + "num_vel": descriptor['nv'], + "num_bodies": descriptor['nb'], # A single named fixed target exposes ONE EE (the named flange) — mirror the # codegen's GRID_RBD_NUM_EES so meta matches the .so's grid_rbd_num_ees(). # "" / "all" keep the all-leaf count. @@ -398,11 +380,11 @@ def _jsafe(v): # handle locate grid_assets/launch_configs//.json for the E6 profile overlay. "launch_config_robot": launch_config_robot, "launch_config_gpu": launch_config_gpu, # the profile actually baked (W15) - "joint_names": joint_names, + "joint_names": [j['name'] for j in joints], "leaf_jids": [int(j) for j in robot.get_leaf_nodes()], - "joint_pos_limits": joint_pos_limits, - "joint_vel_limits": joint_vel_limits, - "joint_effort_limits": joint_effort_limits, + "joint_pos_limits": [j['limits']['position'] for j in joints], + "joint_vel_limits": [j['limits']['velocity'] for j in joints], + "joint_effort_limits": [j['limits']['effort'] for j in joints], # A2 (N2.2, 2026-09-08): what this .so actually contains. algorithm_list # is the REQUESTED build list (the _DEFAULT full profile when the caller # passed None); generated_algorithms is the POST-dep-expansion emit set @@ -411,7 +393,8 @@ def _jsafe(v): "generated_algorithms": sorted(cg.generated_algorithms), "enable_mujoco_kernels": bool(enable_mujoco_kernels), } - meta["configuration_layout"] = configuration_layout_from_robot(robot) + meta["configuration_layout"] = [tuple(b[k] for k in ('kind', 'q_start', 'v_start', 'nq', 'nv')) + for b in descriptor['configuration_layout']] # Multi-contact f_ext: echo the registered contact frames (registration # order == the contact_fext f_c column order) so the handle can validate # shapes and report frames without reopening the URDF. @@ -873,9 +856,7 @@ def one(unit: Path) -> tuple[Path, int, float, str]: log.write(f"[{unit.stem}] rc={r.returncode} {secs:.1f}s\n") if r.returncode == 0 and cached is not None: # publish atomically (another build may be producing the same key) - tmp = cached.with_name(cached.name + f".{os.getpid()}.tmp") - shutil.move(str(obj), str(tmp)) - os.replace(tmp, cached) + publish_object(obj, cached) obj = cached return obj, r.returncode, secs, ulog.read_text() diff --git a/bindings/grid_rbd/_consumer_dependencies.py b/bindings/grid_rbd/_consumer_dependencies.py new file mode 100644 index 00000000..0bce1125 --- /dev/null +++ b/bindings/grid_rbd/_consumer_dependencies.py @@ -0,0 +1,105 @@ +"""Non-system CUDA dependencies for consumer builds, never prepared loads. + +Every lookup rediscovers selection, including new earlier include-path matches. +System development headers excluded by nvcc -MM remain an environment assumption. +Inputs must stay immutable during discovery/build; this is not a filesystem snapshot. +""" +from __future__ import annotations + +import hashlib +from pathlib import Path +import re +import subprocess + +DEPENDENCY_SCHEMA = 1 +_TARGET = "grid_dependency" + + +def parse_dependencies(output: str) -> list[Path]: + """Parse nvcc's Make-like rule; it escapes spaces but leaves # and $ literal. + + This is compiler output, not a Make program. Never expand shell/Make syntax. + """ + text = re.sub(r"\\\r?\n", " ", output).strip() + target = re.match(re.escape(_TARGET) + r"[ \t]*:", text) + if target is None: + raise RuntimeError("consumer dependency output lacks the requested Make target") + text = text[target.end():] + tokens, word = [], [] + i = 0 + while i < len(text): + ch = text[i] + if ch == "\\": + i += 1 + if i == len(text): + raise RuntimeError("consumer dependency output has a dangling escape") + if text[i].isspace(): + word.append(text[i]) + else: + word.extend(("\\", text[i])) + elif ch in "\r\n": + raise RuntimeError("consumer dependency output has multiple rules") + elif ch.isspace(): + if word: + tokens.append("".join(word)); word = [] + else: + word.append(ch) + i += 1 + if word: + tokens.append("".join(word)) + if not tokens: + raise RuntimeError("consumer dependency output has no files") + # Preserve compiler spelling: two symlink aliases to identical bytes can + # expand __FILE__ differently. Record the resolved target separately below. + return sorted({Path(token).absolute() for token in tokens}) + + +def discover_dependencies(gen_dir, sources, compile_cmd, toolchain=None): + """Return per-unit path/digest records with generated paths relocation-neutral.""" + # The common compile command contains semantic flags, not phase/output options. + # Refuse overrides rather than silently preprocess a different compilation. + forbidden = {"-M", "-MM", "-MD", "-MMD", "-MP", "-MF", "-MT", "-MQ", + "-o", "-c", "-E", "--compile", "--preprocess", "--shared", "--lib", + "--ptx", "--cubin", "--fatbin", "--run", "--output-file", + "--dependency-output", "--dependency-target-name", + "--generate-dependencies", "--generate-nonsystem-dependencies", + "--generate-dependencies-with-compile", + "--generate-nonsystem-dependencies-with-compile"} + for flag in compile_cmd[1:]: + if flag.split("=", 1)[0] in forbidden or any( + flag.startswith(prefix) and flag != prefix for prefix in ("-MF", "-MT", "-MQ")): + raise ValueError(f"consumer dependency discovery does not accept phase/output option {flag!r}") + generated = Path(gen_dir).resolve() + units = {} + for source in sources: + source = Path(source).resolve() + command = [*compile_cmd, "-MM", "-MT", _TARGET, str(source)] + try: + result = subprocess.run(command, capture_output=True, text=True, timeout=120, + env=toolchain.environment() if toolchain else None) + except (OSError, subprocess.TimeoutExpired) as exc: + raise RuntimeError(f"consumer dependency discovery failed for {source}: {exc}") from exc + if result.returncode: + raise RuntimeError(f"consumer dependency discovery failed for {source} (nvcc exit " + f"{result.returncode}):\n{result.stdout}\n{result.stderr}") + paths = parse_dependencies(result.stdout) + if source not in paths: + raise RuntimeError(f"consumer dependency output omits source {source}") + def path_name(path): + try: + return "@generated/" + path.relative_to(generated).as_posix() + except ValueError: + return str(path) + + rows = [] + for path in paths: + try: + digest = hashlib.sha256(path.read_bytes()).hexdigest() + except OSError as exc: + raise RuntimeError(f"consumer dependency cannot be read: {path}; " + "nvcc dependency output must name readable files " + "(literal backslashes in POSIX filenames are not preserved by nvcc)") from exc + rows.append({"path": path_name(path), "resolved_path": path_name(path.resolve()), + "sha256": digest}) + units[str(source)] = sorted(rows, key=lambda row: row["path"]) + return {"schema": DEPENDENCY_SCHEMA, "mode": "nvcc-MM", "units": units} diff --git a/bindings/grid_rbd/_consumer_toolchain.py b/bindings/grid_rbd/_consumer_toolchain.py new file mode 100644 index 00000000..dd2e34fd --- /dev/null +++ b/bindings/grid_rbd/_consumer_toolchain.py @@ -0,0 +1,108 @@ +"""Resolve one explicit host compiler and consumer-only symbol policy. + +nvcc environment flags are flattened once, then removed from child environments +so compile and link use the same recorded selection. Wrapper builds do not use +this module. +""" +from __future__ import annotations + +from dataclasses import dataclass +import hashlib +import os +from pathlib import Path +import shlex +import shutil +import subprocess + + +_ENV_KEYS = ("NVCC_PREPEND_FLAGS", "NVCC_APPEND_FLAGS", "NVCC_CCBIN") + + +def _host_selection(flags): + """Remove nvcc host selection arguments, retaining their last explicit value.""" + remaining, selected = [], None + iterator = iter(flags) + for flag in iterator: + if flag in ("--options-file", "-optf") or flag.startswith(("--options-file=", "-optf=", "@")): + raise ValueError("consumer builds require explicit flags, not nvcc options files; " + "expand the file into extra_flags/link_flags for recorded identity") + if flag in ("-ccbin", "--compiler-bindir"): + selected = next(iterator, None) + if not selected or selected.startswith("-"): + raise ValueError(f"{flag} requires a host compiler path") + elif flag.startswith(("-ccbin=", "--compiler-bindir=")): + selected = flag.split("=", 1)[1] + if not selected: + raise ValueError("empty host compiler selection") + else: + remaining.append(flag) + return remaining, selected + + +def _resolve_host(selected): + selected = selected or "g++" + if Path(selected).is_dir(): + selected = str(Path(selected) / "g++") + path = shutil.which(selected) + if path is None: + raise RuntimeError(f"consumer host C++ compiler not found: {selected}") + # Preserve the driver spelling: clang++ and clang may be symlinks to the + # same binary but select different link behavior from argv[0]. + return str(Path(path).absolute()) + + +@dataclass(frozen=True) +class ConsumerToolchain: + host: str + prepend: list[str] + append: list[str] + extra: list[str] + link: list[str] + isolation: list[str] + identity: dict + env_flags: dict + + def environment(self): + env = dict(os.environ) + if {key: env.get(key, "") for key in _ENV_KEYS} != self.env_flags: + raise RuntimeError("nvcc environment changed during consumer build") + for key in _ENV_KEYS: + env.pop(key, None) + return env + + def compile_flags(self): + return self.extra + self.append + [f"-ccbin={self.host}"] + self.isolation + + def link_command(self, nvcc, out, objects): + return ([nvcc] + self.prepend + ["--shared", "-o", str(out)] + objects + + self.link + self.append + [f"-ccbin={self.host}"]) + + +def resolve_consumer_toolchain(extra_flags, link_flags): + env = {key: os.environ.get(key, "") for key in _ENV_KEYS} + pre, pre_host = _host_selection(shlex.split(env["NVCC_PREPEND_FLAGS"])) + post, post_host = _host_selection(shlex.split(env["NVCC_APPEND_FLAGS"])) + extra, extra_host = _host_selection(extra_flags) + link, link_host = _host_selection(link_flags) + host = _resolve_host(post_host or extra_host or pre_host or env["NVCC_CCBIN"]) + explicit_link_host = post_host or link_host + if explicit_link_host and _resolve_host(explicit_link_host) != host: + raise ValueError("consumer compile and link must select the same host compiler") + macros = subprocess.run([host, "-dM", "-E", "-x", "c++", "-"], input="", + capture_output=True, text=True, check=True, timeout=30).stdout + if "#define __clang__ " in macros: + family = "clang" + elif "#define __GNUC__ " in macros: + family = "gcc" + else: + raise RuntimeError("consumer symbol isolation supports GCC and Clang host compilers only") + flags = "-fvisibility=hidden,-fvisibility-inlines-hidden" + if family == "gcc": + flags += ",-fno-gnu-unique" + version = subprocess.run([host, "--version"], capture_output=True, text=True, + check=True, timeout=30).stdout + identity = {"host": host, "host_sha256": hashlib.sha256(Path(host).read_bytes()).hexdigest(), + "host_version": version, "family": family, "nvcc_environment": env, + "isolation": flags} + return ConsumerToolchain(host, pre, post, extra, link, + ["--compiler-options=" + flags], identity, env) diff --git a/bindings/grid_rbd/_handle.py b/bindings/grid_rbd/_handle.py index 85cc8000..a103de79 100644 --- a/bindings/grid_rbd/_handle.py +++ b/bindings/grid_rbd/_handle.py @@ -336,9 +336,24 @@ def dtype(self) -> str: def floating_base(self) -> bool: return bool(self._meta.get("floating_base", False)) + @property + def model_descriptor(self): + """Nominal GRiD-native coordinates, irrespective of output_convention. + + Returns an independent copy. Runtime origin updates do not mutate this + baked descriptor; runtime_transform marks those placements as nominal. + """ + from copy import deepcopy + from grid_codegen.model_descriptor import validate_descriptor + descriptor = self._meta.get('model_descriptor') + if descriptor is None: + raise RuntimeError('cached robot predates the model descriptor; rebuild it') + return deepcopy(validate_descriptor(descriptor)) + @property def configuration_layout(self): - """Independent joint blocks mapping public positions to tangent slots.""" + """Configuration layout: one coordinate block per independent joint, + mapping public positions to tangent slots.""" from ._configuration import configuration_layout_from_meta return configuration_layout_from_meta(self._meta, self.num_joints, self.num_vel) @@ -462,8 +477,12 @@ def meta(self): @property def joint_names(self): - """Joint names, index == joint id (the input-vector ordering). ``None`` - on a .so registered before names were persisted.""" + """Names indexed by effective joint ID, not by q/v coordinate slot. + + Mimic joints own no inputs; spherical/floating joints own multiple + entries. Use model_descriptor/configuration_layout for coordinate + ownership. ``None`` on an artifact predating persisted names. + """ names = self._meta.get("joint_names") return list(names) if names else None @@ -1870,16 +1889,22 @@ def plant_step_hessian(self, x, u, dt, *, integrator_type: str = "euler", gravit x is (B, NUM_POS + NUM_VEL); u is (B, NUM_VEL). Returns the s_d2AB surface of shape (B, 2*NV, 3*NV, 3*NV): - H[b, o, a, b'] = d^2 x_{k+1}[o] / dz[a] dz[b'], z = [dq; dqd; du] + H[batch, o, a, c] = d G(perturb(z, epsilon*e_a))[o,c] / d epsilon - with output rows split as [position-tangent (NV); velocity (NV)]. + with z = [dq; dv; du] and output rows [position-tangent; velocity]. + Each G uses its own input tangent basis and predicted-output anchor. + On manifold coordinates this moving-gradient sensitivity can be + asymmetric: it is NOT the fixed-chart Hessian or a shooting-defect + Hessian. Symmetrization does not supply the missing transport terms. + See the public integration-conventions reference for the precise maps. Matches ``RBDReference.plant_step_hessian``. Pass-through to grid::integrator_hessian_device (composes fdsva_so + dt-scaled assembly). Scope: euler / semi_implicit_euler on fixed and floating bases (multi-stage step Hessians are not generated; those codes return the rc=3 error). - Floating-base and multi-stage RK are deferred (the C-ABI returns rc=3 / - raises for any other ``integrator_type``). + Fixed-base spherical coordinates use their SO(3) retract as well. + Other integrators return rc=3; this NumPy method does not imply a + JAX/Torch second-derivative handle method or general manifold support. """ x = np.ascontiguousarray(x, dtype=self._dt) u = np.ascontiguousarray(u, dtype=self._dt) diff --git a/bindings/grid_rbd/_model.py b/bindings/grid_rbd/_model.py new file mode 100644 index 00000000..00ebc62e --- /dev/null +++ b/bindings/grid_rbd/_model.py @@ -0,0 +1,38 @@ +"""CPU-only model inspection shared with the generation path.""" +import logging +from pathlib import Path + +_log = logging.getLogger('grid_rbd') + + +def parse_model(urdf_path, *, floating_base=False): + from URDFParser import URDFParser + + class LibraryParser(URDFParser): + def print_joint_order(self): + # Do not redirect process-global stdout around concurrent parses. + _log.info('grid_rbd: joint order: %s (nq=%d, nv=%d)', + ', '.join(j.name for j in self.robot.get_joints_ordered_by_id()), + self.robot.get_num_pos(), self.robot.get_num_vel()) + + if type(floating_base) is not bool: + raise TypeError('floating_base must be bool') + return LibraryParser().parse(str(Path(urdf_path)), floating_base=floating_base) + + +def describe_model(urdf_path, *, floating_base=False, runtime_transform=False, schema_version=1): + """Return schema-1 coordinate/frame metadata without CUDA or compilation. + + The descriptor ID covers coordinate geometry, ownership and names, not + inertia, actuation, code-generation options or artifact identity. Placements + are nominal when runtime_transform=True. Unknown schema versions and options + are errors, never silently ignored. Zero-coordinate metadata is valid but + does not promise every GPU operation supports a zero-coordinate model. + """ + from grid_codegen.model_descriptor import SCHEMA_VERSION, descriptor_from_robot + if type(schema_version) is not int or schema_version != SCHEMA_VERSION: + raise ValueError('unsupported model descriptor schema version') + if type(runtime_transform) is not bool: + raise TypeError('runtime_transform must be bool') + return descriptor_from_robot(parse_model(urdf_path, floating_base=floating_base), + runtime_transform=runtime_transform) diff --git a/bindings/grid_rbd/_model_artifact.py b/bindings/grid_rbd/_model_artifact.py new file mode 100644 index 00000000..c5de28fb --- /dev/null +++ b/bindings/grid_rbd/_model_artifact.py @@ -0,0 +1,75 @@ +"""Descriptor/header/binary consistency for prepared consumer artifacts. + +This detects stale or accidentally swapped files, not adversarial modifications +to a whole store. No parser, compiler, source lookup or binary loading is used. +""" +import hashlib +import json +import re + +from grid_codegen.model_descriptor import canonical_bytes, validate_descriptor + + +def check_expected_id(value): + if value is not None and (type(value) is not str or not re.fullmatch('[0-9a-f]{64}', value)): + raise ValueError('expected_descriptor_id must be a lowercase SHA256 hex string') + + +def file_digest(path): + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def _read_descriptor(entry): + return validate_descriptor(json.loads((entry/'model_descriptor.json').read_text())) + + +def _header_string(header, name): + # Read only the emitter's finite literal grammar, never evaluate C++ or + # invoke a generator on a prepared load. Escapes are three octal digits. + pattern = (r'^\s*inline constexpr char ' + name + + r'\[\] = "((?:[^"\\\n]|\\[0-7]{3})*)";[ \t]*$') + values = re.findall(pattern, header, re.MULTILINE) + if len(values) != 1: + raise ValueError('missing or ambiguous generated model literal: ' + name) + raw, result, i = values[0], bytearray(), 0 + while i < len(raw): + if raw[i] == '\\': + result.append(int(raw[i+1:i+4], 8)) + i += 4 + else: + result.extend(raw[i].encode('ascii')) + i += 1 + return result.decode('utf-8') + + +def _association_snapshot(entry, artifact_name): + descriptor=_read_descriptor(entry) + header_bytes=(entry/'grid.cuh').read_bytes() + header=header_bytes.decode('utf-8') + embedded=validate_descriptor(json.loads(_header_string(header, 'descriptor_json'))) + if (canonical_bytes(embedded) != canonical_bytes(descriptor) or + _header_string(header, 'descriptor_id') != descriptor['descriptor_id']): + raise ValueError('model descriptor disagrees with generated header') + association=dict(schema=1,descriptor_id=descriptor['descriptor_id'], + header_sha256=hashlib.sha256(header_bytes).hexdigest(), + artifact_sha256=file_digest(entry/artifact_name)) + return descriptor, association + + +def record_model_association(entry, artifact_name): + return _association_snapshot(entry, artifact_name)[1] + + +def validate_model_association(entry, meta, artifact_name, expected=None): + """Return validated descriptor or raise ValueError with rebuild guidance.""" + check_expected_id(expected) + try: + descriptor, actual=_association_snapshot(entry, artifact_name) + recorded=meta.get('model_association') + if canonical_bytes(recorded) != canonical_bytes(actual): + raise ValueError('descriptor/header/binary association mismatch') + if expected is not None and descriptor['descriptor_id'] != expected: + raise ValueError('expected_descriptor_id does not match artifact') + return descriptor + except (OSError,ValueError,TypeError,KeyError) as exc: + raise ValueError(f'model descriptor association invalid; rebuild artifact: {exc}') from exc diff --git a/bindings/grid_rbd/_store.py b/bindings/grid_rbd/_store.py new file mode 100644 index 00000000..ad05533f --- /dev/null +++ b/bindings/grid_rbd/_store.py @@ -0,0 +1,97 @@ +"""Shared publication rules for wrapper and consumer content-addressed entries.""" +from contextlib import contextmanager +import fcntl +import json +import os +from pathlib import Path +import shutil +import tempfile +import uuid + + +def staging_dir(store: Path, input_key: str) -> Path: + store.mkdir(parents=True, exist_ok=True) + return Path(tempfile.mkdtemp(prefix=f".gen_{os.getpid()}_{input_key[:8]}_", dir=store)) + + +def atomic_write_text(path: Path, text: str) -> None: + """Publish a small sidecar with a private temporary, including across threads.""" + fd, name = tempfile.mkstemp(prefix=f".{path.name}.", suffix=".tmp", dir=path.parent) + temporary = Path(name) + try: + with os.fdopen(fd, "w", encoding="utf-8") as stream: + stream.write(text) + temporary.replace(path) + finally: + temporary.unlink(missing_ok=True) + + +def entry_metadata(entry: Path, artifact: str) -> dict | None: + """A readable manifest and all runtime files are required for reuse. + + Missing/corrupt entries are cache misses. Permission and other I/O errors + are not misses and must reach the caller. + """ + try: + for name in (artifact, "grid.cuh"): + if not (entry / name).is_file() or (entry / name).stat().st_size == 0: + return None + meta = json.loads((entry / "meta.json").read_text()) + except (FileNotFoundError, json.JSONDecodeError, UnicodeDecodeError): + return None + return meta if isinstance(meta, dict) else None + + +@contextmanager +def entry_lock(entry: Path): + # Lock inode lives outside the replaceable entry and is never unlinked. + locks = entry.parent / ".locks" + locks.mkdir(parents=True, exist_ok=True) + with (locks / (entry.name + ".lock")).open("a+") as lock: + fcntl.flock(lock.fileno(), fcntl.LOCK_EX) + try: + yield + finally: + fcntl.flock(lock.fileno(), fcntl.LOCK_UN) + + +def publish_entry(staged: Path, entry: Path, artifact: str, *, force: bool = False) -> dict: + """Adopt a complete concurrent winner, or replace an incomplete entry. + + Only the owner of the per-content lock may displace an entry. Keep the + previous directory until publication succeeds, restoring it on failure. + Do not interpret arbitrary rename errors as a successful concurrent build. + """ + meta = entry_metadata(staged, artifact) + if meta is None: + raise RuntimeError(f"cannot publish incomplete artifact: {staged}") + with entry_lock(entry): + winner = entry_metadata(entry, artifact) + if winner is not None and not force: + return winner + backup = entry.with_name(f".replaced_{entry.name}_{uuid.uuid4().hex}") + displaced = False + try: + if entry.exists(): + os.replace(entry, backup) + displaced = True + os.replace(staged, entry) + except OSError: + if displaced: + os.replace(backup, entry) + raise + if displaced: + shutil.rmtree(backup) + return meta + + +def publish_object(obj: Path, cached: Path) -> None: + """Unique temporary even for threads in the same process and object key.""" + fd, name = tempfile.mkstemp(prefix=cached.name + ".", suffix=".tmp", dir=cached.parent) + os.close(fd) + tmp = Path(name) + try: + shutil.move(str(obj), str(tmp)) + os.replace(tmp, cached) + finally: + tmp.unlink(missing_ok=True) diff --git a/bindings/grid_rbd/_surface_common.py b/bindings/grid_rbd/_surface_common.py index 2027b682..d1426e6f 100644 --- a/bindings/grid_rbd/_surface_common.py +++ b/bindings/grid_rbd/_surface_common.py @@ -168,6 +168,11 @@ class BaseDelegateMixin: ``RobotHandle`` (``self._base``): tool welding, runtime parameter tables, thread-count control, metadata, and the shared output-convention contract.""" + @property + def model_descriptor(self): + """Baked GRiD-native coordinate/frame metadata, independent of IO adapter.""" + return self._base.model_descriptor + @property def output_convention(self) -> str: """Default IO convention for this handle: ``"pinocchio"`` or ``"mujoco"``. diff --git a/bindings/grid_rbd/wrapper_template.cu b/bindings/grid_rbd/wrapper_template.cu index e3050863..7e2bd92d 100644 --- a/bindings/grid_rbd/wrapper_template.cu +++ b/bindings/grid_rbd/wrapper_template.cu @@ -3221,7 +3221,7 @@ static void launch_plant_step(GridCtx *ctx, int batch, T gravity, T dt) { } extern "C" int grid_plant_step(long long ctx_id, - const T* x, const T* u, T* x_kp1, int batch, float gravity, float dt, int it) { + const T* x, const T* u, T* x_kp1, int batch, T gravity, T dt, int it) { GRID_RBD_CTX_OR_RETURN(ctx_id); if (batch < 1) return 1; if (batch > kMaxBatch) return 2; @@ -3255,7 +3255,7 @@ static void launch_plant_step_mujoco(GridCtx *ctx, int batch, T gravity, T dt) { nx, grid::NUM_VEL, g_robot, gravity, dt, batch); } extern "C" int grid_plant_step_mujoco(long long ctx_id, - const T* x, const T* u, T* x_kp1, int batch, float gravity, float dt, int it) { + const T* x, const T* u, T* x_kp1, int batch, T gravity, T dt, int it) { GRID_RBD_CTX_OR_RETURN(ctx_id); if (batch < 1) return 1; if (batch > kMaxBatch) return 2; @@ -3516,7 +3516,7 @@ static void launch_plant_step_gradient(GridCtx *ctx, int batch, T gravity, T dt) dim3 thr = grid_clamp_threads_for(grid_plant::plant_step_gradient_kernel, grid_rbd_launch_threads_n(g_ctx, batch)); // the dAB output band + fdsva-grad scratch arena exceeds the 48 KB static-smem // default -> raise the per-kernel dynamic-smem cap (else cudaErrorInvalidValue/rc=201). - const size_t smem = grid::INTEGRATOR_DU_DYNAMIC_SHARED_MEM_BYTES(); + const size_t smem = grid_plant::PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES(); cudaFuncSetAttribute(grid_plant::plant_step_gradient_kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, (int)smem); grid_plant::plant_step_gradient_kernel<< kMaxBatch) return 2; + if (cudaError_t err = grid_plant::plant_step_gradient_check_shared_memory(); err != cudaSuccess) return 200 + (int)err; if (plant_alloc(g_ctx)) return 4; const int nx = grid::NUM_POS + grid::NUM_VEL; const int nv = grid::NUM_VEL; @@ -3555,7 +3556,7 @@ static void launch_plant_step_gradient_mujoco(GridCtx *ctx, int batch, T gravity dim3 grid_dim = grid_rbd_grid_for(g_ctx, batch); dim3 thr = grid_clamp_threads_for(grid_plant::plant_step_gradient_kernel, grid_rbd_launch_threads_n(g_ctx, batch)); // raise the per-kernel dynamic-smem cap (arena > 48 KB; else rc=201). - const size_t smem = grid::INTEGRATOR_DU_DYNAMIC_SHARED_MEM_BYTES(); + const size_t smem = grid_plant::PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES(); cudaFuncSetAttribute(grid_plant::plant_step_gradient_kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, (int)smem); grid_plant::plant_step_gradient_kernel<< kMaxBatch) return 2; + if (cudaError_t err = grid_plant::plant_step_gradient_check_shared_memory(); err != cudaSuccess) return 200 + (int)err; if (plant_alloc(g_ctx)) return 4; const int nx = grid::NUM_POS + grid::NUM_VEL; const int nv = grid::NUM_VEL; @@ -3613,7 +3615,7 @@ static void launch_plant_step_hessian(GridCtx *ctx, int batch, T gravity, T dt) } extern "C" int grid_plant_step_hessian(long long ctx_id, - const T* x, const T* u, T* d2AB, int batch, float gravity, float dt, int it) { + const T* x, const T* u, T* d2AB, int batch, T gravity, T dt, int it) { GRID_RBD_CTX_OR_RETURN(ctx_id); if (batch < 1) return 1; if (batch > kMaxBatch) return 2; @@ -3667,7 +3669,7 @@ static void launch_plant_step_hessian_mujoco(GridCtx *ctx, int batch, T gravity, g_plant.d_d2AB, g_plant.d_d2AB_workspace, g_plant.d_in_a, g_plant.d_in_b, nx, nv, g_robot, gravity, dt, batch); } extern "C" int grid_plant_step_hessian_mujoco(long long ctx_id, - const T* x, const T* u, T* d2AB, int batch, float gravity, float dt, int it) { + const T* x, const T* u, T* d2AB, int batch, T gravity, T dt, int it) { GRID_RBD_CTX_OR_RETURN(ctx_id); if (batch < 1) return 1; if (batch > kMaxBatch) return 2; @@ -6246,7 +6248,7 @@ static void launch_plant_step_gradient_jax(GridCtx *ctx, cudaStream_t stream, in const int nx = grid::NUM_POS + grid::NUM_VEL; const int nv = grid::NUM_VEL; dim3 grid_dim = grid_rbd_grid_for(g_ctx, batch); - const size_t smem = grid::INTEGRATOR_DU_DYNAMIC_SHARED_MEM_BYTES(); + const size_t smem = grid_plant::PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES(); cudaFuncSetAttribute(grid_plant::plant_step_gradient_kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, (int)smem); dim3 thr = grid_clamp_threads_for(grid_plant::plant_step_gradient_kernel, grid_rbd_launch_threads_n(g_ctx, batch)); grid_plant::plant_step_gradient_kernel<<() != cudaSuccess) return ffi::Error::Internal("plant_step_gradient full-shared arena exceeds device capacity; use integrator_gradient"); auto dims = x.dimensions(); if (dims.size() != 2 || (int)dims[1] != nx) return ffi::Error::InvalidArgument("plant_step_gradient: x must be 2D (B, NX)"); @@ -7653,7 +7656,7 @@ static void torch_launch_plant_step_gradient(GridCtx *ctx, cudaStream_t stream, GRID_RBD_CTX_LOCALS(ctx); const int nx = grid::NUM_POS + grid::NUM_VEL, nv = grid::NUM_VEL; dim3 grid_dim = grid_rbd_grid_for(g_ctx, batch); - const size_t smem = grid::INTEGRATOR_DU_DYNAMIC_SHARED_MEM_BYTES(); + const size_t smem = grid_plant::PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES(); cudaFuncSetAttribute(grid_plant::plant_step_gradient_kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, (int)smem); dim3 thr = grid_clamp_threads_for(grid_plant::plant_step_gradient_kernel, grid_rbd_launch_threads_n(g_ctx, batch)); grid_plant::plant_step_gradient_kernel<<() == cudaSuccess, "plant_step_gradient full-shared arena exceeds device capacity; use integrator_gradient"); grid_torch_check_n(x, "plant_step_gradient: x", nx); grid_torch_check_n(u, "plant_step_gradient: u", nv); int batch = grid_torch_batch(x); diff --git a/docs/agent_debugging_guide.md b/docs/agent_debugging_guide.md index 5667abac..5db9c8ba 100644 --- a/docs/agent_debugging_guide.md +++ b/docs/agent_debugging_guide.md @@ -3268,3 +3268,529 @@ Symptom: the last shard of the Orin receipt failed both cells of `test_cuda_mjx_ Diagnosis: the test read `GRID_CUDA_ARCH` with a default of 120 and passed `-arch sm_120` to nvcc, while the sixty-one other compile sites go through `detect_cuda_arch`, which probes the first GPU and lets the variable override it. The test dates from July and had only ever run on the 5090; the sm_87 table row had no earlier receipt to catch it. Fix: the test probes like the others. For the v0.5.4 receipt, whose tree cannot change, the shard is resumed with `GRID_CUDA_ARCH=87` in the environment, which is the override the test documents and gives the harness sites the same answer as the probe. Audit before any new architecture row: `grep -n '"-arch"' test/cuda_equivalents/test_*.py test/python_wrappers/test_*.py | grep -v 'sm_{arch}\|cuda_arch()'` must come back empty. + +### 7.z63 Compile-pool peak ledger: a same-robot warm variant measured after its sibling is a near-free relink, and its tiny peak admits it concurrently next time + +The Orin v0.6.0 receipt (10-24) died six minutes in and the box hard-rebooted +with no shutdown record. The pool had admitted warm00 (iiwa14, mb=8), warm01 +(go2), warm08 (iiwa14, mb=64) and warm20 (iiwa14, mb=256) together because the +ledger predicted the two extra iiwa14 variants at under 1 GiB. Those numbers +came from the v0.5.4 run, where the pool happened to start them after warm00 +had finished: the object cache made each a 15 s relink at 0.7 GiB, status +BUILT, so `report_pool` recorded them as real builds. On a cold cache the same +three iiwa14 specs are three full 14 GiB compiles, plus go2, about 56 GiB on a +61 GB Jetson with 2 GB of swap; the 10 GiB MemAvailable floor is checked only +at admission. Symptoms: `[pool] + ...` lines whose `avail` falls by tens of +GiB per minute, a log that stops mid-pool, `last -x reboot` with no matching +shutdown, and `~/GRiD/test/.split_suite/compile_rss.json` entries for +`warm:,mb=NN` keys far below their `warm:,mb=8` sibling. +Rule: a warm job's peak is a property of the robot family and the cache state, +not of the key, so a warm prediction must not fall below the largest recorded +peak of any key for the same robot. Until that lands in `compile_sched`, launch +Jetson receipts with `GRID_SPLIT_COMPILE_JOBS=2` (two full compiles fit with +30 GiB to spare). Cause confirmation needs the user: `sudo journalctl -b -1` +grep for oom or panic; the agent cannot read the journal. + +### 7.z64 All-prismatic fixed tools reach previously hidden no-trig paths + +HJCD G13 reproduces with a single prismatic joint and a fixed tool, each with +nonzero origin rotation and translation. The homogeneous transform list mixes +symbolic movable transforms with NumPy fixed transforms. Calling `.atoms` on +every matrix crashes when no earlier revolute transform short-circuits the trig +scan. Normalize each matrix to a symbolic matrix for the scan only. + +Do not stop at removing the exception. The no-trig XI loader still emitted an +undeclared cooperative group `tgrp`, and passed element counts to a byte-copy +API. Use typed block loads. Mimic coordinate substitution reads folded scratch +even when all joints are prismatic, so both XI and homogeneous loaders need +the mimic fold on their no-trig paths. A one-DoF full-profile generation also +exposed an empty contraction-tile candidate set; the untiled width of one is +valid. CPU FK/Jacobian checks for the skew slider match an independent transform. +The direct CUDA gate passes pose/Jacobian, thread/warp FK, and thread-count +invariance for the slider and its negative-multiplier, nonzero-offset mimic +variant at 1/32/64/128 threads. A runner's target is a body index: +`NUM_JOINTS` is the reduced coordinate count here, so the last articulated +frame is `NUM_BODIES - 1`, not `NUM_JOINTS - 1`. Request the gradient explicitly +and use the named fixed-target entry points when checking the tool frame. + +### 7.z65 A content key does not make incomplete-entry repair race safe + +Both wrapper and consumer stores could rebuild after a missing binary, then +interpret a failed directory rename as evidence that another builder had +published a complete winner. The old incomplete directory could instead win +again. Metadata loss could also fail on a content hit before any repair. +Use shared completeness checks and a per-content lock whose inode is outside +the replaceable entry. Keep the old entry until publication succeeds, restore +it after a failed replacement, and propagate I/O errors. Use unique staging +and object temporary names even within one process. A process ID alone does +not distinguish concurrent threads. The same rule applies after the artifact +publish: `bykey` pointers and legacy identity-sidecar backfills also need private +temporaries. Barrier tests reproduced their rename races. Identity readers must +validate the JSON object type and must not turn permission errors into cache +misses; otherwise repair can hide a real access failure. + +Consumer object names must retain the source suffix: `kernel.cu` and +`kernel.cpp` are distinct units even when their stems or bytes coincide. +Collision mesh bytes and resolution paths are request inputs, not just the +URDF text. Validate detected input changes before publishing objects as well +as the final artifact; rejecting only the final publication can leave a bad +object cached under an earlier key. Caller inputs must remain immutable: +before/after comparisons cannot rule out edits restored between the reads. + +### 7.z66 Consumer extensions need ABI identity before lookup and private statics + +A generated header and unchanged consumer source do not imply a reusable Python +extension. The interpreter ABI must enter the input pointer, content and per-unit +object keys before lookup. Checking only at prepared load leaves incompatible +content/object reuse open. `python_extension=True` declares the build up front; +the exported `PyInit_*` symbols validate the declaration before publication. +Native C artifacts do not need a Python ABI dependency. Prepared loads require +the recorded declaration but not a compiler. The focused fixture separates its +pybind `.cpp` unit from its generated-header `.cu` unit: including CUDA device +code in a `.cpp` unit makes nvcc select the wrong source language. + +Consumer-only hidden visibility and GCC's `-fno-gnu-unique` keep function-local +template state private across robot modules. Explicit C exports and pybind's +initialization export remain visible. Do not copy this policy blindly to the +wrapper, whose dlsym/framework exports have their own contract. The consumer +test loads two robots in both orders in fresh processes, checks distinct state +addresses and independent writes, and inspects dynamic symbols. + +Resolve the actual nvcc host compiler, not whichever `c++` happens to be first +on PATH. Compile and link must use the same selected driver. Preserve its driver +spelling (a `clang++` symlink can select different link behavior from `clang`), +record its binary and version, and distinguish GCC from Clang before selecting +GNU-only flags. Flatten nvcc prepend/append flags once so the compiler choice +and flags used by child processes agree with the recorded identity. + +The full CPU oracle also exposed a naming assumption in the new mimic fixture: +URDF joints and links may share a name. Pinocchio frame lookup must request the +BODY frame explicitly, not search every frame type. Keep the inertia/FK +comparisons and their tolerances unchanged. + +Both pointer and content hits must check agreement between ABI metadata and the +identity sidecar. Checking only prepared loads permits a fast build lookup to +return an artifact its own loader refuses. Fault injection into each file is +needed; a sidecar-only test does not cover damaged metadata. + +Consumer paths are compile inputs too: nvcc receives an absolute source path and +`__FILE__` exposes it. Same basename, suffix and bytes in two directories must not +alias in pointer, content or unit-object keys. The compiled regression exports +`__FILE__` and checks both actual locations, not just key inequality. + +### 7.z67 Separate feasibility margins must reach every tier and surface + +Resolve a common margin and optional environment/self overrides once on the host. +Zero is an explicit override, never an inheritance sentinel. Pass the effective +pair to block, warp and native-capsule feasibility checks; the same per-side +margin must reach broad and fine tiers. Self inflation belongs to one member of +the pair, not both. Keep distance, normal and cost paths independent. + +Replacing a templated scalar parameter with a value object can break inferred +template calls even if the object has a converting constructor. Put that argument +in a non-deduced context, and compile tests for inferred as well as explicit T. +Keep the trailing boolean in place so an existing self-toggle call is not silently +reinterpreted as another scalar. Test the consumer's environment-only plus empty-env +self calls against the combined call, and use independent raw clearances and +exactly representable boundary cases rather than relying on mutual agreement alone. + +### 7.z68 Compose joint properties in the projection, not only the forward pass + +A skew-axis joint and a mimic joint can each work alone while their combination +fails. The mimic RNEA force extraction still selected a single signed S row; +fixed-base mimic CRBA did the same for IC*S and both diagonal/ancestor S^T*f +projections. Use the full motion column, scaled by the mimic multiplier at each +reduced-coordinate projection. Preserve the existing cardinal emission rather +than changing its arithmetic during a targeted repair. + +The all-prismatic two-body fixture gives an independent check: a mass-1 slider +and mass-.7 follower with absolute displacement q/2+offset have generalized mass +1+.7/4, not 1+.7/2 or 1+.7. Gravity projects with 1+.7/2. Test mass, inverse mass, +inverse/forward dynamics and ABA at rest and nonzero state/input, with thread +invariance. This fixture's zero q/qd derivatives do not prove nonzero curvature, +floating-base skew-mimic support or general skew-mimic gradients. + +### 7.z69 Process death is not a caught publication exception + +Fault injection that raises OSError exercises rollback, not abrupt termination. +Spawn a writer and exit it at each rename boundary while it holds the entry +lock, then recover from a separate process with bounded waits. The lock inode +must remain stable and the OS must release its ownership. A crash after moving +the previous entry may leave a missing entry and a `.replaced_*` backup; a crash +after installation leaves a complete winner and possibly a backup. Retry must +repair/adopt correctly without deleting reusable objects. This is recovery, +not durable atomic replacement or reader snapshot isolation. Do not purge +backups or shared caches automatically as part of a regression test. + +### 7.z70 Header selection is a build input, not just a directory name + +Hashing a caller's source and explicit header list misses unlisted transitive +headers and a new header earlier on the include path. Discover dependencies +with the actual compiler and effective flags on every consumer build lookup, +then hash selected paths and bytes per unit. A stored list alone cannot detect +a previously absent earlier match. Generated staging paths must be normalized +so a fresh generation directory does not defeat content reuse. +Retain the compiler-selected spelling as well as the resolved target: a new +symlink alias can change a header's `__FILE__` while its bytes stay identical. + +Parse the compiler's Make format, including whitespace before the target colon, +escaped spaces and line continuations. nvcc leaves `#` and `$` literal, so do not +apply Make variable/comment evaluation or dollar unescaping. Its POSIX backslash +filename rewriting can yield an unreadable dependency; report that failure rather +than guessing or accepting an old artifact. Fail closed on preprocessing failure; +never return the old artifact as a fallback. Recheck before publishing objects +and the completed entry while inputs are immutable. This does not create a +filesystem snapshot. Prepared loading is a separate compiler-free operation. +Check each unit's own dependencies around its compilation and all dependencies +before artifact publication; rescanning every unit around every other unit makes +the number of preprocessor invocations quadratic without protecting that unit's +object any better. No stat-based digest reuse is needed for this reduction. +`-MM` deliberately omits system headers: compiler identity is not evidence that +all development packages stayed unchanged. Record that assumption explicitly. + +### 7.z71 Cooperative barriers do not cancel restricted-pointer load reuse + +GLASS fix `e537364` reloads values published by other threads through narrowly +volatile cooperative loads. An inlined restricted caller can otherwise reuse a +thread's earlier value across a barrier. The barrier remains necessary; volatile +does not grant arbitrary pointer aliasing or make an unsynchronized read safe. +The single-thread barrier specialization retains ordinary loads. + +GRiD embeds the affected triangular solve, pivoted inverse and adaptive symmetric +eigensolve bodies. Test the generated header, not only the standalone dependency: +`test_cuda_glass_handoff.py` uses disjoint restricted inputs/arena/outputs, two +independent blocks, poisoned/reused shared and global scratch, FP32/FP64 independent +NumPy references and 1/17/32/64/128/256 threads. The existing robot PSD-clamp gate +checks the enclosing cost path. This does not certify other consumer algorithms +or pre-Blackwell hardware. A compiler-version field in a receipt describes PATH +nvcc; it must agree with the compiler selected by the actual build. + +### 7.z72 Constant mimic joints have no input coordinate + +A mimic chain ending at a fixed joint has a constant local coordinate, not a +scaled read from dense slot zero. Resolve its effective multiplier to zero and +retain the composed offset. Return/emit the offset before indexing q: zero times +an invalid read is still invalid, and zero times NaN is still NaN. The ordinary +transform fold and thread/warp FK must use the same rule; the latter previously +looked up target -1 directly. + +The body still transmits ancestor motion and inertia. Omit only its zero local +derivative contribution, not the whole body traversal. A retained dense scatter +placeholder is not coordinate ownership. Test fixed leaders both before and +after the follower, chains, poisoned/empty q, and an independent rigidly locked +model. A constant-only FK pass does not establish zero-DoF support for every +dynamics routine; audit velocity reads and reduced-output writes separately. + +### 7.z73 A tier name does not imply a full-shared layout + +The plant-gradient kernel and binder kept every band in shared memory, but +advertised the integrator's tier-selected reservation. G1 FP32 carved 124256 +bytes from an advertised 84128. A debug inequality using that same wrong sizer +does not repair the allocation. Give the full-shared surface its own exact +sizer and reject unsupported device admission; retain the separately validated +tiered integrator path. Composed callers must budget their own additional storage. + +Use the common integrator scratch definitions rather than copying older +``nv+floating`` pose dimensions or a fixed 36-element spherical derivative slab. +Exercise the actual emitted carve with field-end writes, canaries and sanitizer, +and test both fitting and rejected public calls. Enabling debug layout also +exposed ``assert(expr())``: wrap the whole expression in parentheses so +the preprocessor does not treat the template comma as a second macro argument. + +### 7.z74 C ABI scalar arguments must match the selected precision + +The six handwritten plant step/gradient/Hessian entry points declared gravity +and timestep as float, while the CAbi function pointer passed CT. FP32 tests +therefore passed and FP64 calls corrupted the scalar values without a linker +error. Match these parameters to T, just like their typed array arguments and +the generated launchers. Include the MuJoCo twins in the signature regression. + +Test an actual FP64 binding against both the typed integrator and the reference; +a source-only declaration test cannot establish runtime agreement. The failing +iiwa gradient had reference agreement near 1e-14 through the integrator, but plant +errors near 1e17 through the mismatched C boundary. Keep this distinct from an +arena sizing defect or numerical tolerance issue. + +### 7.z75 A constant body's placeholder slot is not a derivative column + +A zero mimic multiplier removes local coordinate ownership, not the body. Skip +its local velocity reads, derivative columns and reduced force/tensor projection +before evaluating the expression. Multiplying an unrelated NaN or an invalid +empty-buffer read by zero is not safe. Preserve inherited motion, derivatives +and force transmission: removing the complete body traversal would lose the +inertial contribution of a rigid attachment. + +Apply this rule to metadata job lists as well as scalar expressions. Frame +Jacobian and Coriolis columns exclude constant bodies; second-order folds cover +only active internal tensor cells. An empty fold has no jobs but still needs a +legal inert initializer in generated device storage. Topology allocation and +loading must use the same body-based query for mimic models, not a reduced +coordinate count that may be zero. + +The constant derivative regressions compare with an independently locked robot, +use NaNs in an unrelated branch without sanitizing the owning branch, and pass +actual null q/v/a pointers when their lengths are zero. Output canaries and +1/17/32/64/256-thread checks complement memcheck and synccheck. Composed inners +need their own caller kernel for this thread matrix; a generated kernel's +explicit launch bound is not permission to launch it with more threads. + +Keep the support claim bounded. These checks exercise ID gradients, frame +Jacobians/Jdot, body/world ID second derivatives, Coriolis and frame-velocity +cost pieces. They do not certify every zero-DoF dynamics wrapper, floating +constant-joint derivative combination or timing kernel. Generated zero-trip +timing expressions can still contain division by zero; do not suppress warnings +or infer support from a different inner's successful compilation. + +The broader generation audit exposed the same error in homogeneous derivative +ownership and EE gradient/Hessian job lists. A retained constant joint must not +overwrite a real q owner's entry (including the floating quaternion block) or +create a Hessian pair. The empty pose-Hessian device test caught a real output +canary overwrite before this correction. Follow generation tests with actual +device calls: successful header emission alone did not detect that write. +The zero-DoF Euler plant-gradient call is separately checked at its emitted +32-thread maximum; this is not a blanket claim about all integrator modes or +Python empty-batch/empty-coordinate handling. + +Do not stop at unchanged canaries: FP64 memcheck found an invalid shared read +through Minv/CRBA even though the zero-DoF plant-gradient numerical test passed. +CRBA emitted `s_M[0] += 0 * ...` into a zero-sized reduced matrix. Skip the +constant body's mass projection after propagating composite inertia. When an +active body's ancestor is constant, keep the spatial transport through that +ancestor but omit its reduced matrix entry. Use a nonempty locked-model control +as well as the empty call; a global early return would not test this distinction. + +Filtering jobs can change which emission path a surviving column takes. The +pose-Jacobian disjoint-column path writes an unscaled axis; a lone non-unit mimic +job must still take the weighted path. A constant intermediary followed by a +cross-branch mimic with multiplier -0.5 reproduced a yaw derivative of +1 before +this correction. Test the survivor's scale, not just removal of the zero job. + +The floating CRBA fold follows the same ownership rule: accumulate every body's +composite inertia, but omit a constant body's reduced projection. A constant +ancestor still transports an active descendant's force to earlier active joints +and the floating base. The bounded dynamics audit covers both precisions, +nontrivial base quaternion/velocity/acceleration and five thread counts against +independently locked models for CRBA, ID gradients and body/world second order. + +When composing these tests, obey the selected entry point's contract. A kernel's +arena-byte query does not size a differently carved device wrapper. The optimized +fixed-base body-SO inner intentionally uses a transposed mixed-derivative block; +the generated public-output epilogue restores the public layout. Floating +body-SO also requires its gravity-Hessian global workspace even on a small model. +Supply that workspace and reuse the generator's epilogue in a composed audit; +do not transpose the oracle opportunistically or change production math to fix +an incomplete caller. Preserve failed harness evidence separately from defects. + +### 7.z76 Coordinate identity is not physical identity or a prepared-artifact proof + +The schema-1 descriptor owns coordinate/frame meaning, not inertia, actuation or +compiled-code identity. Changing mass should preserve descriptor ID while changing +artifact identity. A dependent joint has null owned slices even when its leader +owns slot zero. Test descriptor-only FK against an independent reference and hand +anchors; comparing two emitters from one wrong table is not enough. + +Prepared consumer loading validates descriptor structure/digest, the embedded +header representation, and header/binary hashes. Parse the saved finite literal +grammar; do not regenerate headers or require URDF/compiler access to check a +prepared artifact. These hashes detect stale/mixed files, not an attacker who +replaces a complete coherent store. Check pointer hits, content hits, concurrent +publication winners and prepared loads, not just the first build. + +Metadata is an additive generated fragment before the final suggested-thread +block. Strip only its marked block when proving prior algorithm bytes unchanged. +An independently emitted metadata-only header can compile with an ordinary host +C++ compiler; the complete grid.cuh remains CUDA and needs a `.cu` translation +unit (or an explicit CUDA language selection), even for a host-only accessor. + +### 7.z77 Workspace views need composition tests, not just size equality + +Share the ordered slots and scratch authorities with the production caller. +Resource-tier names do not identify storage: a shared tier can select deep +Hessian spill. Pair an explicit full/deep view with its evaluator flags and give +each concurrent invocation its own global slot. Capacity/alignment validation +does not establish that static plus dynamic storage fits the enclosing kernel. + +Poison and reuse the allocation, keep other caller regions live, check guards and +run 1/17/32/64/256 threads. Gradient composition must initialize the full public +vaf extent. Menu bounds remain valid even when their weights are zero. An input +initialization loop followed by a single-thread override needs a barrier; the +workspace test's quaternion/activation initialization exposed this under +racecheck although its numerical checks passed. Fence between collective calls +when their scratch is reused, even if today's specialization happens to fence. + +Use the agreed numerical policy. The extra strict entrywise FP32 diagnostic on +iiwa found near-zero cancellation in both the new caller and the byte-identical +stock-kernel output; it did not establish a binder regression. Preserve the +diagnostic failure, compare with the stock kernel exactly, and retain the pinned +consumer's tensor-norm Hessian criterion alongside its entrywise step/gradient, +symmetry, zero-block and finite-difference tests. FP64 keeps its tighter analytic +criterion. Do not silently relabel an exploratory diagnostic as a passed gate. + +Include a branched runtime-transform configuration. Its stock plant-Hessian +launch query omitted the extra 36 scalars per retained body while the actual +carve included them; FP64 memcheck found topology writes beyond dynamic shared +memory. Resolve the launch count through the same layout authority. The composed +view was already sized correctly, so a successful composed call alone would have +missed the production-kernel defect. A manually composed transform loader must +also select the runtime-transform template flag; the default baked loader is not +the same operation in a runtime-transform header. + +Separate cross-kernel rounding from nondeterminism. The extra branched FP64 +runtime-transform composition test differed from the stock kernel in six entries +by at most 8.14e-20, while reference error was below 1e-15 and both storage layouts +and all thread counts were bit-identical. Brian approved a 64-epsilon relative +cross-kernel bound for this one fixture on 2026-10-30, with no absolute floor; +other stock comparisons remain exact. Independent oracle/finite-difference, +finite-output, poison/reuse and exact thread/storage checks are unchanged. +Negative controls reject nonfinite results, zero/nonzero differences and an +above-bound perturbation. Preserve the original diagnostic failures. + +The exact EE scratch tests also reproduce section 1r's homogeneous constant-slot +WAR warning in FP32, not FP64. Keep detailed raw reports and the known-warning +disposition distinct from clean sanitizer passes. The narrow classifier in +test/ee_constant_slot_warnings.py requires the reviewed call sites, counts and +equal constant bytes; malformed reports, other hazards or errors fail closed. +Do not generalize that classifier into permission to ignore racecheck warnings. + +### 7.z78 Fixed base does not mean affine coordinates + +A fixed-base spherical joint retracts on SO(3). Do not select the affine +second-order path using only `floating_base`: use the independent manifold +blocks. Package 4 reproduced zero spherical d2Integrate and missing plant +position-row curvature, then checked the repair against Pinocchio derivatives +and finite differences of the moving gradient. Test the minimum single spherical +joint as well as shifted/mixed blocks. Its topology has one joint, four stored +coordinates and three velocities; topology subspace queries must use joint IDs, +not `range(nq)`. The public shared/spilled pool authority must also cover the +minimum model's retract scratch, not merely larger typical robots. + +The plant tensor differentiates moving-basis G; it is not a fixed-chart or +shooting-defect Hessian. A scalarized fixed-chart finite difference and a +nonzero antisymmetric moving-G component make this distinction executable. +For differences near identity, direct `(1-cos(theta))/theta**2` can cancel well +above 1e-8. Compare independent matrix series and Pinocchio, use stable +coefficients, and do not enlarge tolerances to accommodate the cancellation. + +### 7.z79 Standalone thread primitives do not prove composed callers + +The Package 4 scalar transform passed standalone divergent-lane tests but gave +incorrect FP32 matrices in a suffix caller that selects between local and shared +transform pointers before thread GEMM. The legacy helper passed. Memcheck, +racecheck, synccheck and initcheck all reported clean: none is an arithmetic +oracle for register/local-value reuse. Independent descriptor-only FK caught it. +Forcing inline, initializing the local output, adding an output restrict qualifier +and consolidating return sites did not fix the reproducer. A scalar device-call +boundary, a private always-local input copy, and a private plain multiplication +each passed. This isolates an optimization-sensitive composition, not a general +proof of a GLASS or compiler defect. Keep the scalar writer call boundary and +the permanent actual-shape suffix regression; do not silently replace consumer +GEMM, require an extra full-q copy, or waive the failed arithmetic. + +The tested seven-joint FP32 caller used 96 local bytes versus 32 for the legacy +helper (registers 62/64 versus 56); the 32-joint caller used 62 versus 66 registers +but retained the extra local storage. These are resource measurements, not +timings. Consumer acceptance and a separately authorized focused runtime check +must resolve whether the new path is suitable for production adoption. + +Root cause found 2026-10-31. The failure is not FP32-specific and not a GRiD +emitter defect: a standalone 241-line reproducer with no GRiD or GLASS include +(docs/open-tasks/z79_restrict_select_miscompile_2026-10-31/repro.cu) fails the +same way in FP32 and FP64, nondeterministically between two repeats of one +launch, with `-Xptxas -O0` still failing and `-G` passing, so the fault is in +nvcc 13.2's NVVM stage. The enabler is `__restrict__` on the B operand of the +single-thread product: with the writer inlined, the noalias scopes let the +optimiser treat the writer's stores to the local array as unrelated to the +loads through B, which is that array on one path of the local-or-shared select +(twelve fewer `st.local` in the failing PTX). Removing `__restrict__` from B +alone on `gemm_impl_ct` (GLASS gemm.cuh line 325 at 9a24f37) makes the inlined +writer pass at 56 registers and 32 local bytes; removing it from A or C alone +does nothing; the full-q `joint_transform` route passes in the same pattern. +Fix lands in GLASS (handoff_to_glass_restrict_select_miscompile_2026-10-31.md); +GRiD keeps the boundary until the re-pin, then removes it and relies on the +suffix regression, which fails without the boundary on the unfixed GLASS. +Lesson: a read operand that a caller may select between address spaces or +objects must not be `__restrict__`; this is the third restrict incident on this +toolchain after 1g and the GATO bdsv case. + +Fixed upstream 2026-10-31. GLASS `ef4533c` (receipt `6a82320`) declares no +`__restrict__` on any parameter of its own functions and carries the +regression (`test/cuda/test_select.cu`, both precisions); GRiD re-pinned to +`6a82320` and removed the `__noinline__` boundary from `joint_transform_scalar` +(5f3f219). The GLASS agent reproduced the class on sm_120 / CUDA 13.2, sm_87 / +CUDA 13.2 and sm_72 / CUDA 11.4, so it is long-standing NVVM behaviour, not a +13.2 regression. Verified on the re-pinned wheel: the suffix regression passes +in both precisions, and HJCD's Package 4 probe (Panda and the 24-joint high +fixture; build, check and prepared modes) passes with the HJCD suffix kernel +losing its call frame: Panda FP32 48 registers / 64 stack bytes to 56 / 0, FP64 +80 / 176 to 94 / 40; high24 FP32 56 / 64 to 72 / 0, FP64 88 / 176 to 144 / 40. +The FP64 register growth lowers that probe kernel's thread ceiling (Panda 768 +to 640, high24 640 to 384); the solver kernels are unchanged. Whether the +inlined writer is faster is the timing night's question, not a correctness one. +Policy for GRiD's own emitters stays: no `__restrict__` on a read operand a +caller may select between address spaces or objects. + +### 7.z80 A consumer-probe wave does not run the equivalence runners + +Found 2026-10-31 by the Package 6 skew revolute mimic fixture: its CUDA test +failed to compile with "statement may not appear in a constexpr function" at +the generated `joint(int id, joint_record &)` accessor. The accessor has a +`switch` body and is `constexpr`, legal from C++14 on; the equivalence runner +harness (`cuda_harness._linalg_backend_compile_flags`) and eleven test files +still compiled with `-std=c++11`, a cuBLASDx-era pin. Every generated header +failed the same way (iiwa14: 101 errors under C++11, none under C++17), so the +whole `cuda_equivalents` domain had been red since d559d3e (Codex Package 1, +2026-10-26) and nobody saw it: the wave closed packages on exact-wheel +consumer probes, which compile with the consumers' own C++17 flags, and no +receipt or split-suite run happened between 10-24 and 10-31. Two generator +comments (`_constants_arena.py`, `_plant.py`) recorded the C++11 single-return +rule the accessors broke. Fix: the runners compile with `-std=c++17`, the +header's documented standard; the accessors are an accepted public contract +and stay as they are. Lessons: a generated construct that raises the language +floor must be checked against the harness standard, not only against consumer +builds; and a wave that changes codegen must run at least one equivalence +shard before a package closes, whatever the probes say. The two-pass receipt +would have caught this on pass one, at the cost of a pass. + +### 7.z81 An opt-out that does not change the header is in the input key, not the content key (2026-10-31, strict_collision) + +`build_artifact(strict_collision=False)` is recorded in the code options only when +a consumer opts out, so it is part of the artifact's input key. It is not in the +content key unless the spherizer actually took a different route, because the +content key hashes the generated header and the consumer's units, and a spherizer +that raised no warning generates the same header either way. A CPU test that asserts +`lenient.content_key != strict.content_key` for a model without mesh trouble is +asserting the wrong key; the right assertions are a different input key and the +same content key. The same holds for any switch whose effect is "fail instead of +warn": it changes what can be built, not what was built. + +Two harness conventions from the same day. First, a CUDA test that compiles its own +probe goes through `test/cuda_equivalents/executable_cache.py::cached_nvcc_executable` +with `sources=[runner]`, `include_dirs=[dir holding grid.cuh]` and every nvcc flag +except `-I`, `-o` and the source path; the key follows the quoted-include closure +through the include directories, so the generated header's bytes are in it, and a +second receipt pass or a sweep reuses the executable. Hand-rolled `subprocess.run` +nvcc calls cost the second pass every compile again. Second, helpers shared between +CPU tests and GPU suites (the descriptor-only forward kinematics, the manifold +oracle, the constant-joint fixture robot) live in helper modules under +`test/cuda_equivalents/`, where the split driver's helper-import rule fingerprints +them into every shard that imports them. Importing a test module for its helper +needs a hand-written special case in `run_split_suite._module_fingerprint_paths`, +which is exactly what rotted last time. + +### 7.z82 A lifetime probe must be process-local; device-global free memory moves under a shared GPU (2026-11-01, collision environment runner) + +The environment runner's lifetime check compared `cudaMemGetInfo` free memory before +the owned environment was built and after it was destroyed. Free memory is a +device-global counter: the receipt runs two GPU shards side by side, and a sibling +shard's allocations between the two readings failed the check in pass 1 of the +0.7.0 receipt (deltas of 256 KiB and 2 MiB, both cases, the shard that overlapped +another cuda shard and a wrapper shard). The runner passes alone and failed five of +six runs next to a process that only allocates and frees. + +The probe now captures the environment's four device pointers before destruction, +confirms each is `cudaMemoryTypeDevice`, and after destruction confirms each reports +`cudaMemoryTypeUnregistered` from `cudaPointerGetAttributes`, queried before anything +can reuse the address. That is process-local and deterministic. The same rule applies +to any runner: never compare `cudaMemGetInfo`, `nvidia-smi` memory or any other +device-wide counter across a window in which another process may run; a receipt +shard always has to assume a neighbour. diff --git a/docs/source/api_reference/grid_rbd.rst b/docs/source/api_reference/grid_rbd.rst index 481004b7..94bf94ec 100644 --- a/docs/source/api_reference/grid_rbd.rst +++ b/docs/source/api_reference/grid_rbd.rst @@ -23,6 +23,14 @@ See :doc:`../user_guide/tutorials/python_wrappers` for the guided tour and ``bindings/examples/AGENT_INTEGRATION_GUIDE.md`` for the agent-facing lifecycle notes. +Model inspection +---------------- + +See :doc:`model_coordinates` for coordinate ownership, frame conventions and +prepared-artifact identity. + +.. autofunction:: grid_rbd.describe_model + Registration ------------ @@ -77,14 +85,23 @@ Step Hessian .. py:method:: grid_rbd.RobotHandle.plant_step_hessian(x, u, dt, *, integrator_type="euler", gravity=-9.81) - Return the explicit step Hessian with shape ``(B, 2*NV, 3*NV, 3*NV)`` - for ``x`` of shape ``(B, NQ+NV)`` and ``u`` of shape ``(B, NV)``. - Input derivative axes are ``[dq; dqd; du]`` and output rows are - position tangent followed by velocity. Euler and semi-implicit Euler - are supported on fixed and floating bases; multi-stage RK Hessians are - unsupported. This method is available on the NumPy handle, not the - JAX/PyTorch handles. The generated artifact must include its ``fdsva_so`` - dependency. See :doc:`../user_guide/concepts/algorithms/integrators_and_plant`. + Return the second derivative of the step with shape + ``(B, 2*NV, 3*NV, 3*NV)`` for ``x`` of shape ``(B, NQ+NV)`` and ``u`` of + shape ``(B, NV)``. Input derivative axes are ``[dq; dqd; du]`` and output + rows are position tangent followed by velocity. + + The value is the moving-gradient sensitivity defined in + :doc:`integration_conventions`: the derivative of the native step gradient + along each input tangent direction, where every evaluated gradient uses its + own input tangent basis and predicted-output anchor. On scalar coordinates + this is the ordinary component Hessian. On a floating base or a spherical + joint it is not the symmetric Hessian in one fixed chart and not the + curvature of a nonzero shooting defect, and it need not be symmetric. + Euler and semi-implicit Euler are supported on admitted fixed, floating + and fixed-spherical models; multi-stage schemes are unsupported. This + method is available on the NumPy handle, not the JAX/PyTorch handles. The + generated artifact must include its ``fdsva_so`` dependency. See + :doc:`../user_guide/concepts/algorithms/integrators_and_plant`. JAX and torch handles --------------------- diff --git a/docs/source/api_reference/index.rst b/docs/source/api_reference/index.rst index 1586a0a8..1dea97c4 100644 --- a/docs/source/api_reference/index.rst +++ b/docs/source/api_reference/index.rst @@ -24,3 +24,6 @@ debug parsed robot topology. rbd urdf gridcodegen + model_coordinates + integration_conventions + workspace_composition diff --git a/docs/source/api_reference/integration_conventions.rst b/docs/source/api_reference/integration_conventions.rst new file mode 100644 index 00000000..32083537 --- /dev/null +++ b/docs/source/api_reference/integration_conventions.rst @@ -0,0 +1,194 @@ +Integration, derivatives and model primitives +============================================= + +This page specifies the native GRiD maps used when composing generated code. +The coordinate ownership and frame definitions in :doc:`model_coordinates` +remain authoritative. A configuration has ``nq`` stored entries and ``nv`` +tangent entries; these widths need not agree. + +Configuration and state maps +---------------------------- + +Write :math:`I(q,d)` for integration and :math:`D(a,b)` for difference. +The first argument of difference is the reference, so locally +:math:`I(a,D(a,b))=b`. Scalar independent coordinates use addition and +subtraction, including continuous joints; this is not a wrapped-angle metric. + +A floating pose uses a right SE(3) increment, with local linear coordinates +before angular coordinates. For pose :math:`(p,R)` and increment +:math:`(\rho,\phi)`, translation becomes +:math:`p+R J_l(\phi)\rho`. A spherical joint uses a right SO(3) increment. +Quaternions are unit ``xyzw``. Difference uses the canonical rotation logarithm +and is invariant to quaternion sign. The rotation cut at pi is not smooth; +nonunit or nonfinite inputs are outside the numerical primitive contract. + +For state :math:`x=(q,v)`, the native state retraction and difference are + +.. math:: + + R_x(\delta) &= (I(q,\delta_q), v+\delta_v),\\ + D_x(a,b) &= (D(a_q,b_q), b_v-a_v). + +The velocity subtraction is a component convention, not physical transport +between body frames. Stored state width is ``nq+nv``; tangent state width is +``2*nv``. A different residual requires its own derivatives. + +First derivatives and defect composition +----------------------------------------------- + +Let :math:`y=F(x,u)` be a discrete step. Its native gradient is the derivative +at zero of :math:`D_x(y,F(R_x(\delta_x),u+\delta_u))`, holding nominal y +fixed. Logical rows are ``[dq_next, dv_next]`` and columns ``[dq, dv, du]``. +The torque plant has ``nu=nv`` and a ``2*nv`` by ``3*nv`` gradient; that control +width is not a universal restriction on consumer actuation. + +Both argument Jacobians of configuration difference perturb the corresponding +input using I and differentiate the returned components. With :math:`e=D(a,b)`, + +.. math:: + + D_2(a,b) &= (\partial_d I(a,e))^{-1},\\ + D_1(a,b) &= -D_2(a,b)\partial_q I(a,e). + +They reduce to +I and -I on scalar coordinates, but generally not at nonzero +rotational defects. The state versions append velocity blocks +I and -I. +For defect ``D_x(s,F(x,u))``, compose ``D_2(s,y)*G`` and use ``D_1(s,y)`` for +the successor. For ``D_x(F(x,u),s)``, use ``D_1(y,s)*G`` and ``D_2(y,s)``. +Do not replace these maps with a sign change at a nonzero manifold defect. +Actuation and contact-chain derivatives belong in G before this composition. + +An invertible successor block permits row normalization of a linear constraint. +It does not redefine the nonlinear residual, its merit function or its dual +coordinates. With ``c=D_x(y,s)``, row-normalized blocks are +``A=-solve(D_2,D_1*Gx)``, ``B=-solve(D_2,D_1*Gu)`` and ``c_new=solve(D_2,c)``. +Hold D_2 fixed within that linearization. If the normalized covector is mu, +the original-residual covector is ``solve(D_2.T,mu)``. A solver owns this choice, +including warm starts, conditioning and higher-order terms. + +What plant_step_hessian means +------------------------------------ + +The existing name denotes **moving-gradient sensitivity**: + +.. math:: + + H[o,a,b] = \left.\frac{d}{d\epsilon} + G(R_x(\epsilon e_a),u)[o,b]\right|_{\epsilon=0}. + +For a control direction, perturb u instead. Each evaluated G uses its own input +tangent basis and predicted-output anchor. The logical axes are output, +perturbation, gradient column. The native flat index is +``o*nz*nz + a*nz + b``, ``nz=3*nv``; native G is column-major. + +On scalar Euclidean states this is an ordinary component Hessian. On a manifold +it can be asymmetric. It is **not** the symmetric Hessian in one fixed +input/output chart, nor the Hessian of a nonzero shooting defect. Symmetrization +does not derive the missing transport terms. Fixed-chart weighted curvature, +successor/mixed defect blocks and general second-order transport are not provided +by this API. + +The native second-order step supports Euler and semi-implicit Euler on admitted +scalar, floating and fixed-spherical models. Spherical blocks use SO(3), even +with a fixed base. Multistage step Hessians remain unsupported. The NumPy/CUDA +second-order surface does not imply a JAX/Torch Hessian handle method. Existing +floating-plus-spherical and resource-tier restrictions remain separate; metadata +coverage alone does not establish plant support. The workspace binder in +:doc:`workspace_composition` has a narrower scope than the lower-level plant. + +Thread-local generated primitives +--------------------------------- + +For namespace ``grid`` the configuration entry points are:: + + configuration_difference(from, to, delta); + configuration_difference_jacobians(from, to, J_from, J_to); + bool configuration_difference_jacobian_block( + block_id, from, to, J_from, J_to, capacity); + +Inputs are full independent configurations. Dense outputs contain ``nv`` and +two ``nv*nv`` values, with matrices column-major and off-block zeros written. +The compact form writes two width-by-width matrices. ``capacity`` counts T +elements in **each** output, not bytes. An invalid block or insufficient capacity +returns false with both outputs untouched. Empty coordinate outputs are not read +or written. ``model::coordinate_block_count`` and +``model::coordinate_block(id, record)`` expose kind, ``q_start``, ``v_start``, +``nq`` and ``nv`` from the descriptor's independent configuration partition. +Block IDs are not joint IDs; mimics add no independent block. Tangent widths +are 1, 3 and 6 for scalar, spherical and floating blocks. + +These functions execute on one calling thread. They dynamically allocate no storage, launch +no kernels and impose no barrier. Small automatic blocks have dimensions at +most six, independent of nv. Caller-owned contiguous outputs must be disjoint +from inputs and each other; inputs may coincide. Complete writes do not publish +data to other threads: the caller supplies fences and workspace lifetime. +Dense convenience does not imply dense storage fits a consumer kernel. Compact +storage and actual enclosing-kernel resource admission must be checked together. +The helpers are emitted with integrator/plant derivative algorithm surfaces; +a lightweight kinematics header does not acquire plant code through them. + +Local transforms use the descriptor's effective joint IDs:: + + bool model::joint_transform(joint_id, full_q, out16); + bool model::joint_transform_scalar(joint_id, coordinate, out16); + +Both write all 16 column-major parent-from-child homogeneous entries from the +same resolved symbolic placement. The scalar input is an **absolute native +coordinate**, not an increment or a full configuration. It admits independent +revolute, continuous and prismatic joints only. Dependent/mimic, constant, +helical and multicoordinate joints use the general route. General transforms +read only the selected joint's necessary coordinates; a constant transform +requires no q read. Runtime IDs may differ across lanes. No constant-cell input, +FK traversal, arena, cached pose, mutable table or collective is involved. + +Invalid IDs and unsupported scalar routes return false without output writes. +Both new routes refuse ``runtime_transform`` builds, whose live spatial dynamics +tables do not define the baked homogeneous placement. Existing transform APIs +are unchanged. Keep output disjoint from input, and retain caller publication +and scratch-reuse fences when replacing a legacy helper. + +The scalar writer currently retains a device call boundary: the composed +divergent suffix regression exposed incorrect FP32 results with an inlined +writer on the tested toolchain. Standalone evaluation and memory sanitizers +did not reveal the failure. Account for compiled call-frame/local storage in +the enclosing caller; additive source APIs do not imply runtime cost parity. + +Parameters, identity and mutation +---------------------------------------- + +Derivatives hold non-state/control parameters fixed. Mutability does not imply +derivatives with respect to a parameter. + +.. list-table:: + :header-rows: 1 + :widths: 24 40 36 + + * - Category + - Examples + - Ownership and lifetime + * - Structural model + - Topology, joint kinds/axes, mimic, baked placements and physical data + - Regenerate/rebuild. Descriptor identity excludes inertia and is not + complete artifact identity. + * - Structural generation + - Algorithms, dtype, namespace, targets, table flags, backends/toolchain + - New compatible artifact and prepared association. Respect module-revision + process isolation. + * - Per-call inputs + - q/v/u, dt, gravity, exposed external wrenches, cost targets/weights + - Operation-specific signatures govern exposure; hold buffers through use. + A consumer may compile dt/gravity into its artifact. + * - Enabled mutable tables + - Inertia, joint dynamics, spatial transforms + - Loaded-module state shared by backend views. Quiesce and order readers + before mutation; separate handles do not isolate tables. + * - Execution configuration + - Stream, threads, launch resources, caller workspace + - Caller owns admission, ordering, publication and lifetime. + +``set_transform_params`` changes spatial dynamics transforms, not baked +homogeneous EE kinematics. Joint-dynamics setter defaults can restore baked +values rather than retain the previous mutation; sign friction is nonsmooth at +zero. Prepared loading does not snapshot live parameter state: reapply supported +configuration before evaluation. Physics changes also require review of cached +derivatives, costates and warm starts. No universal runtime parameter buffer, +actuation provider or heterogeneous per-sample model is implied. diff --git a/docs/source/api_reference/model_coordinates.rst b/docs/source/api_reference/model_coordinates.rst new file mode 100644 index 00000000..bd12a1c4 --- /dev/null +++ b/docs/source/api_reference/model_coordinates.rst @@ -0,0 +1,179 @@ +Model coordinates and named frames +================================== + +A model descriptor records the coordinates and frames used by a generated +model. Applications can use it to interpret states and attach tools without +parsing generated functions or keeping a second joint table. It does not +define actuation, select a solver, or certify every operation for every model. + +Inspecting a model +------------------ + +.. code-block:: python + + from grid_rbd import describe_model + + model = describe_model("robot.urdf", floating_base=True) + print(model["nq"], model["nv"], model["descriptor_id"]) + +This call uses the installed parser but needs neither a GPU nor a compiler. +Unknown options and schema versions are errors. Schema 1 uses quaternion +coordinates. The raw generator's legacy Euler-coordinate path is unchanged +but does not emit schema-1 metadata. + +The generated header exposes the same data through ``grid::model``. Library +generation saves ``model_descriptor.json`` beside that header. A Python +handle's ``model_descriptor`` property returns an independent copy of its +baked description. This is always GRiD-native metadata, including when the +handle's output adapter is set to MuJoCo conventions. + +Coordinate ownership +-------------------- + +``nq`` counts configuration entries, ``nv`` counts velocity/tangent entries, +``nj`` counts effective joints, and ``nb`` counts effective bodies. The root +reference has body ID -1 and is excluded from ``nb``. Effective joint and +body IDs follow the parsed tree, not XML tag order. Fixed joints are merged; +planar and non-root floating joints may become scalar joints. The descriptor +reports those effective joints rather than inventing a native representation. + +Each joint records its parent and child body. Use those mappings rather than +coordinate offsets to index bodies. A floating base is an effective body. +A decomposition body remains a body even when it has no mass. + +Independent joints own disjoint ``q_start`` and ``v_start`` blocks. Their +``local_nq`` and ``local_nv`` widths partition the input vectors exactly. +``configuration_layout`` is a derived view of these blocks, sorted by q start. +The historical Python ``num_joints`` property means nq; ``joint_names`` is +indexed by effective joint ID, not by q or v slot. + +.. list-table:: Local parameterizations + :header-rows: 1 + + * - Kind + - Configuration + - Velocity + * - Revolute / continuous + - Angle in radians + - Radians per second + * - Prismatic + - Displacement in metres + - Metres per second + * - Helical / screw + - Angle; pitch in metres per radian + - Radians per second + * - Spherical + - Unit quaternion [x,y,z,w] + - Three local angular components + * - Floating root + - [x,y,z,qx,qy,qz,qw] + - Local [vx,vy,vz,wx,wy,wz] + +Internal spatial vectors often use angular-before-linear ordering. They are +not the public floating velocity vector. Quaternion velocity is a tangent +vector, not four quaternion rates. Invalid quaternions are not normalized +by metadata inspection. + +Mimic joints retain their bodies but own no configuration block: both starts +are null. Their flattened relation is +``theta = multiplier * q[leader.q_start] + offset``. The local velocity is +``multiplier * v[leader.v_start]``. A null leader denotes a constant joint: +its multiplier is zero, and it reads no input slot. Local widths describe +the joint motion, not additional independent coordinates. No multi-DoF mimic +semantics are introduced by the descriptor. + +Limits remain attached to each effective joint. Null means no finite bound, +not zero; continuous joints have no position interval. No quaternion box or +sampling policy is fabricated. Ownership and effort bounds do not define a +control dimension, transmission or underactuation policy. + +Transforms and frames +--------------------- + +``T_AB`` maps coordinates in B into A: ``p_A = T_AB p_B``. JSON stores a +4-by-4 nested row array; generated C++ records use column-major arrays with +index ``r + 4*c``. For each joint, +``T_parent_child(q) = T_parent_joint_zero * M_joint(q)``. +The zero placement includes merged fixed ancestors, evaluated before any +mimic offset. It is not necessarily the original URDF origin matrix. + +The signed unit axis is expressed in the local zero-motion joint frame. +Spherical and floating joints have no single axis. Floating motion is +``[R(quaternion), xyz; 0, 1]``. World composition is parent world pose times +local pose. A fixed named frame F on body B has +``T_world_frame = T_world_body * T_body_frame``. + +Frame IDs have their own namespace: root first, effective joints in ID order, +then retained fixed joints in parser order. IDs are stable only within one +descriptor identity. Joint frames have identity offsets; fixed frames retain +their accumulated placement. This is not an alias table for every original +URDF link discarded during reduction. Names are opaque UTF-8 strings. +Use frame kind as well as name when identifying the typed root entry. + +Generated access +---------------- + +``grid::model::joint(id, joint_record&)`` and +``grid::model::frame(id, frame_record&)`` are constexpr host/device accessors. +They return false for invalid IDs without changing the destination. Numeric +device access does not index host-only arrays. ``joint_name`` and ``frame_name`` +are host constexpr accessors; invalid IDs return null. ``descriptor_json`` and +``descriptor_id`` are host-readable constants. + +``compose_frame_from_body(frame_id, body_id, world_body, world_frame)`` accepts +one already-computed body-world pose and writes one frame-world pose. The two +16-element column-major buffers must be disjoint. This is a thread-scope +operation: no allocation, hidden synchronization, second FK, or world-pose +array is needed. Caller-managed fences still govern data shared across threads. +The function checks the attachment ID and leaves output unchanged on failure. + +With ``runtime_transform=True``, placements describe the nominal baked model, +not current mutable origin parameters. The static composer refuses such models. +Existing runtime-transform operations remain available through their own APIs. + +Identity and prepared deployment +-------------------------------- + +The descriptor ID covers coordinate geometry, names, limits and ownership. +It deliberately excludes inertias, runtime values, algorithms, precision, +compiler options and artifact bytes. Different physical dynamics and binaries +can share a descriptor ID. Use the artifact content identity for build reuse. + +Schema 1 hashes a typed canonical encoding of all fields except +``descriptor_id``. Null, bool, integer, float, string, list and map have separate +tags. Integer payloads are decimal strings. Floats are finite IEEE binary64 +big-endian lowercase hex, with signed zero normalized to positive zero. +Strings retain their Unicode spelling and are UTF-8 encoded; unpaired +surrogates are rejected. Maps sort keys by Unicode code point. The tagged +tree uses compact JSON without ASCII escaping, then SHA256. The implementation +and golden vectors live in ``grid_codegen/model_descriptor.py`` and +``test/test_model_descriptor.py``. Pretty-printing descriptor JSON does not +change its identity. Readers also validate structural invariants; rehashing an +invalid ownership table does not make it valid. + +The exact tag forms are ``["null"]``, ``["bool", value]``, +``["int", decimal_string]``, ``["f64", hex_string]``, ``["str", value]``, +``["list", encoded_elements]`` and +``["map", [[key, encoded_value], ...]]``. A map's keys remain plain strings; +its values are recursively tagged. The top-level digest field alone is omitted. + +``build_artifact(..., expected_descriptor_id=model["descriptor_id"])`` checks +a previously inspected model on cold and reused builds. The returned +``ConsumerArtifact.model_descriptor`` is validated metadata. Prepared +``load_artifact(..., expected_descriptor_id=...)`` checks the saved descriptor, +its generated-header representation, and the header/binary hashes without +reading the original source or invoking a compiler. These are consistency +checks, not authentication against an attacker replacing all files and hashes. + +Consumer artifact schema 2 requires this association. Rebuild older prepared +artifacts once; do not copy a new descriptor onto an old binary. Production +consumer pins are not changed by this migration mechanism. + +Support boundary +---------------- + +A model with zero independent coordinates has valid empty ownership metadata. +That does not promise every wrapper, empty-batch operation, integrator or timing +tool supports it. Existing operation-specific validation remains authoritative. +This interface supplies geometry and coordinate meaning, not general dynamics +providers, contact differentiation, incremental FK caches or solver policies. diff --git a/docs/source/api_reference/workspace_composition.rst b/docs/source/api_reference/workspace_composition.rst new file mode 100644 index 00000000..d8c29528 --- /dev/null +++ b/docs/source/api_reference/workspace_composition.rst @@ -0,0 +1,134 @@ +Composing generated workspaces +============================== + +Generated ``grid_plant`` views describe storage for device calls inside a caller's +kernel. They do not allocate memory, launch kernels, initialize numerical inputs +or choose a CUDA stream. Use ``float`` or ``double`` consistently with the model +and the caller. A view belongs to the generated header that defines it; neither +its layout nor its byte count is portable to another model or generation option. + +Preparation and lifetime +------------------------------ + +Byte and alignment queries are host/device ``constexpr`` functions. The checked +``bind`` functions are noncollective and return false for null, short or misaligned +storage without changing the output view. A successful bind establishes capacity +and alignment only. The caller must supply a real backing allocation of that +capacity, with disjoint live regions and the appropriate address space. + +Before launching, check the *enclosing* kernel's compiled static shared memory +plus requested dynamic shared memory against both the target budget and the +device opt-in limit. Set the kernel's opt-in attribute and respect its thread +limit. The binders do not perform this admission check: a full-shared view can be +well formed even when no kernel on the current device can launch it. + +Numerical evaluators are block collective. Every participating thread must take +the same call path. Fence input staging, output consumption and scratch reuse +with block synchronization. Do not let concurrent blocks, streams or graph +replays share a mutable spill slot. Keep the model and all input/output/workspace +allocations alive until the asynchronous work completes. Read-only model sharing +does not make concurrent model mutation safe. + +Cost menus +---------- + +``tracking_cost_menu_arena`` is emitted when +``GRID_PLANT_HAS_COST_MENU`` is enabled. It owns two views, ``s_frame`` and +``s_scratch``, used by the menu value, gradient and Hessian calls. Its queries are +``alignment()``, ``scratch_offset()``, ``scratch_bytes()`` and ``bytes()``. + +``MX`` and ``MU`` are the nonnegative linear-feature row counts used to instantiate +the corresponding menu calls. They are not the stored state width, tangent state +width, feature input width or control width. Zero rows are supported. Size the +view for the largest instantiated row counts when one allocation is reused. + +``carve(base)`` is the unchecked hot-path equivalent. Use it only after preparation +has established the full allocation capacity and the caller has constructed a +16-byte-aligned address. It performs pointer arithmetic only. Retain checked +``bind`` at untrusted boundaries; unchecked carving is not a resource-check bypass. + +For example, inside a block whose dynamic arena has already been admitted: + +.. code-block:: cuda + + using Menu = grid_plant::tracking_cost_menu_arena; + extern __shared__ __align__(16) unsigned char storage[]; + Menu menu; + if (!Menu::bind(storage, Menu::bytes(), menu)) return; // uniform result + // Stage valid x, u and params, then synchronize the block. + __syncthreads(); + grid_plant::tracking_cost_menu( + value, x, u, params, menu.s_frame, menu.s_scratch, model); + __syncthreads(); + +Bounds, activation parameters, outputs, contact-force scatter and solver storage +remain caller owned. In particular, the menu's barrier bound arrays must remain +valid even when their weight is zero. The view does not replace a weighted +dynamics-Hessian contraction workspace. If an outer arena is only ``alignof(T)`` +aligned, account for padding when aligning the menu's starting address to 16 +bytes; rounding an inner offset alone is insufficient. + +Plant gradients +--------------- + +``plant_step_gradient_arena`` exposes ``bytes()``, ``alignment()`` and +``bind(base, capacity, out)`` in addition to the existing unchecked ``carve``. +The query describes its full-shared layout, not the tier-selected integrator +kernel layout. Generated element counts such as ``s_vaf_count`` are part of the +initialization contract; they count elements, not bytes. + +For a raw composed ``plant_step_gradient`` call, stage ``s_x`` and ``s_u`` and +zero all ``s_vaf_count`` entries before the block enters the evaluator. Reinitialize +that band when reusing poisoned or unrelated storage. Pass all other fields from +the matching view, including topology and runtime-transform scratch. Do not derive +these extents from ``nv``: retained bodies, mimic topology and runtime transforms +can require different storage. The plant gradient may mutate its staged state. + +Plant Hessians +-------------- + +``GRID_PLANT_HAS_HESSIAN_ARENA`` is one for the current fixed-base ``nq == nv > 0`` +composition interface, and zero otherwise. A zero capability does not remove or +redefine the existing tiered floating-base Hessian kernels. + +.. code-block:: cuda + + using Layout = grid_plant::plant_hessian_layout; + using Hessian = grid_plant::plant_step_hessian_arena; + Hessian h; + const bool ok = Hessian::bind(shared, shared_capacity, + spill_slot, spill_capacity, h); + // Handle failure uniformly before entering a collective operation. + if (!ok) return; + // Stage h.s_x and h.s_u cooperatively, then synchronize. + __syncthreads(); + h.evaluate(model, gravity, dt); + __syncthreads(); + // Consume h.s_d2AB before reusing either region. + +``full_shared`` and ``deep_spill`` are explicit storage layouts, not resource-tier +labels. Use ``shared_bytes()`` and ``workspace_bytes()`` for the selected layout; +both regions have 16-byte alignment. Full-shared uses no global workspace and +permits a null spill pointer. Deep-spill places the Hessian, second-order tensors +and contraction pool in the invocation's global slot while retaining the small +hot bands in shared memory. The view's ``evaluate`` selects the matching storage +flags; do not substitute flags inferred from a tier name. + +Euler and semi-implicit Euler are supported by this interface. Output +``s_d2AB`` has the existing row-major ``[2*nv, 3*nv, 3*nv]`` layout, with perturbation +axis before gradient-column axis. Controls here are generalized torques of width +``nv``; coordinate metadata is not an actuator map. No multistage Hessian, +manifold-provider or custom-actuation contract is introduced by these views. + +End-effector inner scratch +------------------------------ + +``grid::EE_POSE_INNER_SCRATCH_BYTES()`` sizes the all-leaf pose inner. +The existing ``EE_GRAD_INNER_SMEM_BYTES()`` sizes the generic all-leaf gradient +inner. When target aliases are generated, use +``EE_TARGET_POSE_INNER_SCRATCH_BYTES()`` and +``EE_TARGET_GRAD_INNER_SCRATCH_BYTES()`` for their selected target. These queries +describe only the inner's temporary pool, not its input, output, XI tables or a +complete enclosing kernel. A fixed named target can have a smaller pool than the +generic all-leaf call. Scratch from one entry point must not be used to size a +differently carved wrapper. diff --git a/docs/source/index.rst b/docs/source/index.rst index 097eea55..a1aa139d 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -187,8 +187,8 @@ change. r = grid_rbd.load_robot("grid_assets/robots/iiwa14.urdf", backend="torch") q = torch.zeros(8, r.nq, device="cuda", requires_grad=True) - qd = torch.zeros(8, r.nq, device="cuda") - u = torch.zeros(8, r.nq, device="cuda") + qd = torch.zeros(8, r.nv, device="cuda") + u = torch.zeros(8, r.nv, device="cuda") qdd = r.forward_dynamics(q, qd, u) # CUDA tensors in and out qdd.sum().backward() # analytic backward step = r.capture("forward_dynamics", q.detach(), qd, u) # CUDA-graph replay @@ -271,6 +271,7 @@ research, cite the original paper and record the software commit or release: user_guide/getting_started/fast_robot_setup user_guide/getting_started/library_overview user_guide/getting_started/compatibility + user_guide/getting_started/migrating_to_0_7 user_guide/glossary .. toctree:: diff --git a/docs/source/user_guide/concepts/algorithms/integrators_and_plant.rst b/docs/source/user_guide/concepts/algorithms/integrators_and_plant.rst index 1f15657b..ad96d178 100644 --- a/docs/source/user_guide/concepts/algorithms/integrators_and_plant.rst +++ b/docs/source/user_guide/concepts/algorithms/integrators_and_plant.rst @@ -117,7 +117,13 @@ The integrator composes forward dynamics (the mass-matrix-inverse path) with the chosen scheme inside one kernel, so the four acceleration stages of ``rk4`` do not require four kernel launches. Its gradient uses the analytical forward-dynamics gradient, and the step Hessian composes the second-order :doc:`fdsva_so` with the integration -map (including the floating-base retract derivatives). This fused integrator +map (including the floating-base retract derivatives). What it computes on a +manifold is the derivative of the moving step gradient, each evaluated +gradient carrying its own tangent basis and output anchor, as defined in +:doc:`../../../api_reference/integration_conventions`. On a floating base +or a spherical joint this is not a fixed-chart Hessian and not the curvature +of a shooting defect; solvers that need those terms derive them from the +difference Jacobians on that page. This fused integrator kernel does not make an arbitrary sequence of Python calls a single launch. On a floating base the integrator applies Pinocchio's SE(3) update to the @@ -170,7 +176,8 @@ state (``N = NX``) or the input (``N = NU``) with a caller matrix; and, on a baked cost frame, ``frame_position_cost`` on ``p_f(q) - p_des``, ``frame_orientation_cost`` on the log-map residual ``log(R_des^T R_f(q))`` with Jacobian ``Jr(e)^-1`` times the frame's body-axes angular Jacobian (no -Euler chart, regular at every orientation), and ``frame_velocity_cost`` on +Euler chart; the principal log map has a branch cut at a relative rotation of +pi), and ``frame_velocity_cost`` on the twist ``J(q) qd - v_des`` with the residual Jacobian ``[d(J qd)/dq | J]`` (fixed base). Cost frames are named joints or fixed joints baked with ``gen_all_code(cost_frames=[...])`` or ``register_robot(cost_frames=[...])`` diff --git a/docs/source/user_guide/concepts/codegen_architecture.rst b/docs/source/user_guide/concepts/codegen_architecture.rst index 2d7eb24d..eb1ab523 100644 --- a/docs/source/user_guide/concepts/codegen_architecture.rst +++ b/docs/source/user_guide/concepts/codegen_architecture.rst @@ -95,7 +95,7 @@ idsva_so −20% and fdsva_so −18% of compute on g1-floating, −13…−52% on ├── forward_dynamics_inner() # reuses s_XImats ├── fd_gradient_inline() # may surgically spill to d_fd_grad_spill ├── idsva_so_{world,body}_inner() - └── fdsva_so_inner() # the rank-3 contraction step + └── fdsva_so_contract() # the rank-3 contraction step If the ``_inner`` building blocks were collapsed into their owning ``_device``, the compositional algorithms would pay the XImats load three diff --git a/docs/source/user_guide/concepts/design_principles.rst b/docs/source/user_guide/concepts/design_principles.rst index 0183853b..d86ac8bc 100644 --- a/docs/source/user_guide/concepts/design_principles.rst +++ b/docs/source/user_guide/concepts/design_principles.rst @@ -90,7 +90,7 @@ otherwise guarantee it a valid pointer; never pass it a ``nullptr`` ``s_temp`` (see the anti-patterns below). Reference points that follow this: ``aba_inner``, -``forward_dynamics_inner``, ``minv_inner``, ``fdsva_so_inner`` (the +``forward_dynamics_inner``, ``minv_inner``, ``fdsva_so_contract`` (the rank-3 contraction sub-step), ``fdsva_so_device``, ``inverse_dynamics_gradient_device``, ``forward_dynamics_gradient_device``, ``integrator_gradient_device``, ``end_effector_pose_gradient_inner``, and the diff --git a/docs/source/user_guide/concepts/mjx_convention.rst b/docs/source/user_guide/concepts/mjx_convention.rst index bc2e8d90..9f7372a8 100644 --- a/docs/source/user_guide/concepts/mjx_convention.rst +++ b/docs/source/user_guide/concepts/mjx_convention.rst @@ -146,7 +146,7 @@ no equivalent. The implementation is ``bindings/grid_rbd/_mujoco.py`` | **end_effector_pose_hessian** ⚠REVISED | H_mjx[i,a,k]=Σ H_pin[i,b,c]G^{-1}[b,a]G^{-1}[c,k] + sym(dpose_pin·∂(G^{-1})/∂θ_k); ∂(G^{-1})/∂θ_a base-lin block=−[e_a]×R^T; **symmetrize** in the 2 tangent idx (GRiD analytic hess IS symmetric; raw Lie 2nd-deriv isn't) | tensor-slab + frame | FD-mjx 2.1e-10 | | **integrator** (euler+rk) | mjx retract: floating pos += dt·qvel[:3] GLOBAL (vs pin SE(3) V(φ), O(dt²) wrong) | retract | mj_integratePos **0.0** | | integrator_gradient | mjx_dIntegrate (base-lin→I, no lin↔ang) + fd value transform + minv_pin_to_mjx velocity rows | retract | FD-mjx 2.7e-10 | - | plant_step | composes fd qdd transform (ω×v) + mjx retract | retract | mj_step (si_euler) 1e-16 | + | plant_step | composes fd qdd transform (ω×v) + mjx retract | retract | mj_step (semi_implicit_euler) 1e-16 | | plant_step_gradient | == integrator_gradient (byte-identical) | retract | diff 0.0 | | plant_step_hessian | retract Jacobian + fdsva_so SO transform, base-localized | retract | ~loc base-rot | | quadratic_state_cost | VALUE qd-convention-DEPENDENT (user supplies mjx x_des/Q); grad/hess qd-block reframe G | composite | 6.42 vs 3.92 | diff --git a/docs/source/user_guide/concepts/operand_validation.rst b/docs/source/user_guide/concepts/operand_validation.rst index 5ee5cde9..ab2c24ea 100644 --- a/docs/source/user_guide/concepts/operand_validation.rst +++ b/docs/source/user_guide/concepts/operand_validation.rst @@ -20,7 +20,10 @@ The rule set - Members - Not checked * - ``configuration`` - - ``(B, num_joints)``; the handle dtype; ``1 <= B <= max_batch``; + - ``(B, num_joints)`` for ``q`` and the position-width operands, + ``(B, num_vel)`` for ``qd``, ``qdd``, ``qdd_opt``, ``u`` and ``u_des`` + (the tangent width, as in Pinocchio and MuJoCo); the handle dtype; + ``1 <= B <= max_batch``; every operand of a call carries the same ``B`` as the leading one. A per-sample ``(num_joints,)`` input is accepted on numpy (batch of one, 1D result) and on JAX under ``vmap`` @@ -60,10 +63,10 @@ The rule set - none - finiteness, ``dt > 0`` -A wrong ``num_joints`` versus ``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 (the ``num_vel`` message) and by the generic -last-dimension rule on torch. +An ``nq``-wide ``qd`` / ``qdd`` / ``u`` on a floating or spherical model (the +padded layout of releases before 2026-09-26) is refused with a migration hint +naming ``nv`` on the numpy and JAX surfaces and by the generic last-dimension +rule on torch. Nothing is padded or sliced for the caller. Where each surface enforces the rules ------------------------------------- diff --git a/docs/source/user_guide/concepts/resource_tier_system.rst b/docs/source/user_guide/concepts/resource_tier_system.rst index 6557009f..150ec9df 100644 --- a/docs/source/user_guide/concepts/resource_tier_system.rst +++ b/docs/source/user_guide/concepts/resource_tier_system.rst @@ -154,7 +154,7 @@ existing call sites are unchanged): * - ``end_effector_pose_gradient_inner`` - ``bool TEMP_IN_SMEM`` - the double-buffered kinematic-chain workspace - * - ``fdsva_so_inner`` + * - ``fdsva_so_contract`` - ``bool SCRATCH_IN_SMEM`` - the 4·NV³ contraction scratch * - ``integrator_inner`` @@ -323,7 +323,7 @@ The tier knob is exposed at the kernel level on every emitted ``_inner`` level, these functions accept ``RESOURCE_TIER`` and a caller-provided ``T *d_workspace`` argument: -* ``fdsva_so_inner(s_df2, s_idsva_so, s_Minv, +* ``fdsva_so_contract(s_df2, s_idsva_so, s_Minv, s_df_du, s_XImats, s_temp, d_workspace, gravity)``: 4*nv³ inner scratch routes between ``s_temp`` (SHARED) and ``d_workspace`` (LITE/MINIMAL). @@ -346,18 +346,22 @@ Sizing constants, called from host code to allocate the right buffers: .. code-block:: cpp - template - constexpr size_t FDSVA_SO_INNER_SMEM_BYTES(); // bytes for s_temp at TIER - template - constexpr size_t FDSVA_SO_INNER_WORKSPACE_BYTES(); // bytes for d_workspace at TIER + template + constexpr size_t FDSVA_SO_INNER_SMEM_BYTES(); // bytes for s_temp + template + constexpr size_t FDSVA_SO_INNER_WORKSPACE_BYTES(); // bytes for d_workspace + template + constexpr bool FDSVA_SO_SCRATCH_IN_SMEM(); // the placement codegen chose for TIER on this robot // Same pattern: FORWARD_DYNAMICS_GRADIENT_DEVICE_INLINE_*, INVERSE_DYNAMICS_GRADIENT_DEVICE_INLINE_*, // END_EFFECTOR_POSE_HESSIAN_DEVICE_INLINE_*, IDSVA_SO_DEVICE_INLINE_* -At ``TIER_SHARED`` the SMEM_BYTES value matches current behavior -(the temp is in shared); at ``TIER_LITE``/``TIER_MINIMAL`` the -SMEM_BYTES value drops (temp moved out) and the WORKSPACE_BYTES -value covers the moved temp. +The sizes are keyed on the placement, not on a tier: with the scratch in +shared memory SMEM_BYTES is 4 nv\ :sup:`3` elements and WORKSPACE_BYTES is +zero; with the scratch in global memory the two swap. +``FDSVA_SO_SCRATCH_IN_SMEM()`` reports which placement the generator +assigned to each tier for this robot, so a caller can size both arenas from +the tier it launches at. Inline-CUDA usage example:: @@ -366,16 +370,15 @@ Inline-CUDA usage example:: // ... slice s_arena for your own buffers ... T *s_grid_temp = /* slice for GRiD primitive */; - // At launch site we passed sizeof(s_grid_temp) = - // grid::FDSVA_SO_INNER_SMEM_BYTES() - // == 0 at MINIMAL — no smem reserved for GRiD temp. - // Workspace was malloc'd to: - // grid::FDSVA_SO_INNER_WORKSPACE_BYTES() - // == 4 * NV^3 * sizeof(T) at MINIMAL. + constexpr bool in_smem = grid::FDSVA_SO_SCRATCH_IN_SMEM(); + // The launch passed grid::FDSVA_SO_INNER_SMEM_BYTES() bytes of + // dynamic shared memory for s_grid_temp (zero when the scratch is in global + // memory) and allocated grid::FDSVA_SO_INNER_WORKSPACE_BYTES() + // bytes of workspace (4 * NV^3 * sizeof(T) in that case). - grid::fdsva_so_inner( + grid::fdsva_so_contract( s_df2, s_idsva_so, s_Minv, s_df_du, s_XImats, - /* s_temp */ nullptr, // unused at MINIMAL + /* s_temp */ s_grid_temp, // unused when in_smem is false /* d_workspace */ workspace, // global mem gravity); } diff --git a/docs/source/user_guide/getting_started/compatibility.rst b/docs/source/user_guide/getting_started/compatibility.rst index aaccfdcf..be872976 100644 --- a/docs/source/user_guide/getting_started/compatibility.rst +++ b/docs/source/user_guide/getting_started/compatibility.rst @@ -4,8 +4,9 @@ Compatibility and known limitations Platforms and toolchain ----------------------- -* There is one artifact per GPU architecture. A robot ``.so`` is compiled for the - ``sm_XX`` of the GPU that registers it (or ``cuda_arch=``); loading it on a +* There is one artifact per GPU architecture unless ``GRID_RBD_CUDA_ARCHS`` names + several and the build is a fat binary. A robot ``.so`` is otherwise compiled for + the ``sm_XX`` of the GPU that registers it (or ``cuda_arch=``); loading it on a different architecture is refused at context creation with a clear error. The model, the API and the generated code carry over between machines; the binary does not. @@ -19,7 +20,8 @@ Platforms and toolchain numpy-only build. With the ``[torch]`` extra the wrapper reads the standard that torch's ATen header guard demands and compiles with it. The torch we run (2.12.0, cu128) compiles with C++17. A torch that demands C++20 needs an nvcc - and host compiler accepting C++20, and that path is untested here. + and host compiler accepting C++20; that path has run only in the Orin smoke + below (torch 2.14.0+cu130), never under a receipt. ``GRID_RBD_CXX_STD`` forces the standard. * The ``[jax]`` / ``[torch]`` extras pin the CPU packages; install the CUDA wheel yourself. We run ``jax[cuda12]`` (``jax-cuda12-plugin`` 0.10.0) and a @@ -61,7 +63,7 @@ an alpha feature outside the receipt. - Ubuntu 24.04.5, Linux 7.0.0-38, x86-64 - full receipt; release timing; install check (a fresh venv with torch 2.11.0+cu128 and jax 0.11.2 from PyPI) - - receipt 2026-10-15; timing 2026-09-27 to 2026-10-07; install check + - receipt 2026-10-24; timing 2026-09-27 to 2026-10-07; install check 2026-10-15 * - sm_87 (compute capability 8.7) - NVIDIA Jetson AGX Orin, 64 GB @@ -98,9 +100,10 @@ carries its own CUDA 12.8 runtime libraries for torch's kernels, and the 615.71.09 does. A ``jax[cuda13]`` plugin, a torch wheel other than cu128, a CUDA 12 toolkit and CPython 3.10, 3.11 or 3.13 on a GPU are untested; the CI wheel builds for CPython 3.10, 3.11 and 3.13 and the CPU test lane on 3.10 and -3.13 run on CPU only. The Jetson rows record smoke runs, not receipts. The -Orin receipt is recorded under ``receipts/`` once the per-architecture receipt -table is on main; the Xavier stays header-only because JetPack 5 ships CUDA +3.13 run on CPU only. The Jetson rows record smoke runs, not receipts. No +secondary receipt has been recorded yet; the first Orin receipt lands under +``receipts/`` through the per-architecture table in +``test/gpu-proof-receipts.yaml``; the Xavier stays header-only because JetPack 5 ships CUDA 11.4 and Python 3.8, below the floors of the generator and the bindings. Runtime contexts @@ -140,14 +143,37 @@ specified in :doc:`../concepts/operand_validation`; layouts are in Native interface stability -------------------------- -* The generated ``grid.cuh`` keeps its inline entry points - (``init_gridData_checked`` gained an optional trailing allocator-pool - argument; the pool-less allocator spellings remain). +* Public generated surfaces are the ones documented on the API reference + pages and the CUDA tutorials: the kernel and ``*_device`` entry points named + in :doc:`../tutorials/cuda_support_status`, the ``grid::model`` metadata and + transform helpers of :doc:`../../api_reference/model_coordinates`, the + configuration primitives of :doc:`../../api_reference/integration_conventions` + and the ``grid_plant`` workspace views of + :doc:`../../api_reference/workspace_composition`. Neither an underscore nor + an ``inline`` spelling decides this: a generated function absent from those + pages is an implementation detail that a later release may rename or + remove. (``init_gridData_checked`` gained an optional trailing allocator-pool + argument; the pool-less allocator spellings remain.) +* Release policy during 0.x: any release may change a documented public + contract, and a release that does so says so in the changelog with the + migration step, as :doc:`migrating_to_0_7` does for this one. Correctness + fixes name the affected operations, the numerical change and any rebuild + they require. The version number records what shipped; it does not promise + which kind of change a release may carry, a deprecation period, a support + window or a response time. What is promised is that a public contract never + changes silently. +* Source compatibility, descriptor schema compatibility, compiled-artifact + reuse and tested numerical support are separate promises. A call that still + compiles does not imply that an old prepared artifact loads: artifact reuse + is decided by the content, toolchain and descriptor checks described on the + model-coordinates page, and a schema change requires one rebuild. * The per-robot wrapper ``.so``'s ``extern "C" grid_rbd_*`` symbols are private to the package: they are consumed only by the pybind core of the same content-keyed build and gained a leading ``ctx_id`` in 0.5.0, the first - packaged release. Do - not link against them directly. + packaged release. Do not link against them directly. +* The two joint-transform helpers in ``grid::model`` are CUDA device + functions; host code reads the same placements through the constexpr + ``joint`` and ``frame`` record accessors. Differentiation --------------- diff --git a/docs/source/user_guide/getting_started/fast_robot_setup.rst b/docs/source/user_guide/getting_started/fast_robot_setup.rst index 6e61a7f3..2c8de4d8 100644 --- a/docs/source/user_guide/getting_started/fast_robot_setup.rst +++ b/docs/source/user_guide/getting_started/fast_robot_setup.rst @@ -340,3 +340,8 @@ See also patterns (stay on device, CUDA graphs, zero-copy interop). * `examples/notebooks/ `_: the guided notebook track (start with ``01_quickstart_iiwa14``). +* :doc:`../../api_reference/model_coordinates`: what ``nq``, ``nv``, joint IDs, + quaternions and named frames mean for the robot you just loaded, and how to + inspect them without a GPU through ``describe_model``. +* :doc:`migrating_to_0_7`: the ordered checklist for moving a 0.6 + deployment to 0.7. diff --git a/docs/source/user_guide/getting_started/installation.rst b/docs/source/user_guide/getting_started/installation.rst index 7694504e..4c563339 100644 --- a/docs/source/user_guide/getting_started/installation.rst +++ b/docs/source/user_guide/getting_started/installation.rst @@ -17,10 +17,11 @@ Release wheels and the source distribution are published on PyPI as This does not compile robot CUDA code or access a GPU. On a platform without a matching wheel pip builds the source distribution, whose small Python -extension requires a C++17 compiler. GRiD is alpha software: APIs may change -between minor versions (see the `changelog +extension requires a C++17 compiler. GRiD is alpha software: a public API +may change between releases, always with a changelog entry and a migration +step (see the `changelog `_ for what each -release ships). +release ships and :doc:`compatibility` for the policy). The base install requires NumPy; the ``jax``, ``torch`` and ``all`` extras select optional framework dependencies (see the :doc:`../../index` quick-start @@ -285,7 +286,7 @@ architectures. Run it with the NVIDIA Container Toolkit: .. code-block:: shell - docker run --rm -it --gpus all -v grid-cache:/var/cache/grid-rbd ghcr.io/a2r-lab/grid:0.5.3 + docker run --rm -it --gpus all -v grid-cache:/var/cache/grid-rbd ghcr.io/a2r-lab/grid:0.7.0 The image's cache store carries one compiled robot library: an iiwa14 binary for sm_86, sm_89, sm_90 and sm_120, compiled with the sm_120 launch @@ -299,7 +300,7 @@ the image's store. The header-only path needs no compile step: ``nvcc -arch=sm_90 -std=c++17 -I /opt/grid/iiwa14 my_kernel.cu`` builds against the baked header. Build the image yourself from the repository root with -``docker build -f install/docker/Dockerfile --build-arg GRID_RBD_VERSION=0.5.3 -t grid:0.5.3 .``; +``docker build -f install/docker/Dockerfile --build-arg GRID_RBD_VERSION=0.7.0 -t grid:0.5.3 .``; the build argument ``GRID_RBD_VERSION`` selects the PyPI version and ``GRID_ARCHS`` the compute capabilities of the precompiled iiwa14 binary. diff --git a/docs/source/user_guide/getting_started/migrating_to_0_7.rst b/docs/source/user_guide/getting_started/migrating_to_0_7.rst new file mode 100644 index 00000000..f6ba2694 --- /dev/null +++ b/docs/source/user_guide/getting_started/migrating_to_0_7.rst @@ -0,0 +1,138 @@ +Migrating from 0.6 to 0.7 +========================= + +Version 0.7.0 changes how a compiled robot is identified, how a consumer +artifact is built and reloaded, how collision margins are specified, and what +the plant's second derivative means on a manifold. Python users who call +``load_robot`` and the batched handle methods need only rebuild their caches. +Consumers who compile the generated header into their own kernels should walk +the steps below in order. Each step names the failure a user meets without it. + +1. Pin and keep the old environment +----------------------------------- + +Install 0.7.0 into a new environment or a new cache directory +(``cache_dir=`` or ``GRID_RBD_CACHE_DIR``) and keep the previous deployment +until the steps below pass. Do not purge a shared cache: entries are +content-keyed, so old and new builds coexist, and a purge removes artifacts +another process may still be using. + +2. Rebuild prepared artifacts once +---------------------------------- + +Consumer artifact schema 2 stores the model descriptor beside the header and +binds the three together. An artifact built by 0.6 has no descriptor, so +``load_artifact`` refuses it rather than guessing. Rebuild with +``build_artifact`` once; do not copy a descriptor file next to an old binary, +because the association check compares hashes and will reject the pair. + +.. code-block:: python + + from grid_rbd import describe_model, build_artifact, load_artifact + + model = describe_model("robot.urdf") + art = build_artifact("myapp", "robot", "robot.urdf", + sources=["kernel.cu"], + algorithm_list=["end_effector_pose"], + expected_descriptor_id=model["descriptor_id"]) + # later, without a compiler or the URDF: + art = load_artifact("myapp", "robot", + expected_descriptor_id=model["descriptor_id"]) + +``expected_descriptor_id`` is optional, but an application that inspected the +model earlier should pass it so a changed URDF cannot silently pair with a +stale binary. The descriptor identity covers coordinates, names, limits and +placements; it does not cover inertias, so two physically different robots can +share one identity. Build reuse is decided by the artifact content key, not by +the descriptor. + +3. Declare Python extensions +---------------------------- + +A consumer that compiles a pybind11 module through ``build_artifact`` now +passes ``python_extension=True``. The build then records the interpreter ABI in +the artifact identity, exports only the module entry point and keeps the +module's static state private, so two revisions or two robots loaded in one +process do not share symbols. Without the flag a build whose object +exports a ``PyInit_*`` symbol is refused before publication, so a pybind module +cannot be recorded as a plain native library. There is no stable-ABI promise: a module built for CPython 3.12 is +reused by CPython 3.12 only. + +Loading a second revision of a module under the same name in one process is +not supported by the interpreter; switch revisions in a fresh process. + +4. Build lookup and prepared execution are different paths +----------------------------------------------------------- + +At build and lookup time GRiD runs the compiler's dependency pass +(``nvcc -MM``) over every compilation unit, so an edit to any header the unit +actually includes, including a transitive or shadowing one, invalidates the +artifact. Manual ``headers=`` lists remain available as a supplement. This +means a warm *lookup* needs the compiler and the sources. A prepared +*application* that calls ``load_artifact`` needs neither; it checks the saved +descriptor, header hash and binary hash and loads. Containers that ship +without a toolchain must use the prepared path. System headers excluded by the +compiler's dependency output are an environment assumption: the toolkit and +host compiler identities are recorded, their contents are not. + +5. Collision margins +-------------------- + +The checkers take a ``CollisionMargins`` with ``environment_margin`` and +``self_margin`` where 0.6 took one scalar. A scalar still compiles and applies +the same value to both checks, so an unchanged call keeps its 0.6 behaviour. +An application that wants clearance against the environment but none between +its own links passes the two values explicitly: + +.. code-block:: cuda + + auto margins = grid_collision::make_collision_margins(0.02f, std::nullopt, 0.0f); + +The host factory rejects non-finite or negative values. See +:doc:`../tutorials/collisions`. + +6. Dimensions +------------- + +The descriptor distinguishes ``nq`` (stored configuration entries), ``nv`` +(tangent entries), ``nj`` (effective joints) and ``nb`` (effective bodies). +On a floating base or a spherical joint ``nq`` exceeds ``nv``; the difference +does not identify a free-flyer, and neither count is an actuator count. Stored +state has ``nq+nv`` entries and tangent state ``2*nv``. A solver's control +width, its linear feature rows (``MX``, ``MU`` on the cost menu) and its +actuation map are the solver's own definitions. See +:doc:`../../api_reference/model_coordinates`. + +7. Defects, duals and the second derivative +------------------------------------------- + +The native difference ``D(a, b)`` is reference-first, and its two Jacobians +reduce to ``-I`` and ``+I`` only on scalar coordinates. A shooting solver +that linearises a nonzero floating or spherical defect must compose the +step gradient with the difference Jacobians at the actual anchors and +transport its multipliers accordingly; a sign change is not a substitute. +``plant_step_hessian`` is the derivative of the moving step gradient, not a +fixed-chart Hessian and not the curvature of a defect. Scalar fixed-base +models are unaffected. The maps, the row normalisation and the covector +relation are specified in :doc:`../../api_reference/integration_conventions`. + +8. Workspaces inside your kernel +-------------------------------- + +``grid_plant`` now exposes checked ``bind`` views for the plant gradient, the +plant Hessian and the cost menu, sized by the same layout the kernels use. +A view establishes capacity and alignment only; the enclosing kernel's static +plus dynamic shared memory must still be admitted against the device limit by +the caller. The Hessian view covers fixed-base ``nq == nv`` models with Euler +and semi-implicit Euler; other configurations keep using the tiered kernels. +See :doc:`../../api_reference/workspace_composition`. + +9. Validate +----------- + +Run your own regression on the rebuilt artifacts, compare a handful of +outputs against the 0.6 deployment, and exercise the prepared path in a +process without a compiler before switching production. The example +``bindings/examples/consumer_artifact_migration.py`` builds a native library +and a declared extension, reuses them, and loads them again from a fresh +process that cannot see the sources or the toolchain. diff --git a/docs/source/user_guide/tutorials/codegen.rst b/docs/source/user_guide/tutorials/codegen.rst index 9ebb44bb..661bf897 100644 --- a/docs/source/user_guide/tutorials/codegen.rst +++ b/docs/source/user_guide/tutorials/codegen.rst @@ -219,3 +219,13 @@ inline. Each ``_device`` routine's dynamic shared-memory requirement is exposed as ``grid::_DEVICE_DYNAMIC_SHARED_MEM_BYTES()``; the :doc:`resource tiers <../concepts/resource_tier_system>` page covers fitting a routine into a tight outer kernel. + +Three reference pages specify what a kernel that includes the header can rely +on: :doc:`../../api_reference/model_coordinates` for coordinate ownership, +joint and frame identifiers and the ``grid::model`` accessors; +:doc:`../../api_reference/integration_conventions` for the native +difference, step derivatives and the thread-local transform and difference +primitives; and :doc:`../../api_reference/workspace_composition` for the +``grid_plant`` workspace views that size and bind storage inside your own +kernel. Consumers that build and reload compiled artifacts through +``grid_rbd`` start from :doc:`../getting_started/migrating_to_0_7`. diff --git a/docs/source/user_guide/tutorials/collisions.rst b/docs/source/user_guide/tutorials/collisions.rst index b78f6ae4..9be1d34f 100644 --- a/docs/source/user_guide/tutorials/collisions.rst +++ b/docs/source/user_guide/tutorials/collisions.rst @@ -55,17 +55,36 @@ Two device entry points share the same baked sphere batch (``NUM_COLLISION_SPHER ``T = double``. The two surfaces are gated against each other on random configurations and environments (``test/cuda_equivalents/test_cuda_collision_warp.py``). -Both checkers take two trailing optional arguments. ``margin`` (metres, default 0) makes the -verdict a hit as soon as any raw clearance is below the margin: robot spheres are tested at -``r + margin`` against the environment and the margin is added to one side of every self pair, so -exact contact at the margin is free and the default reproduces the zero-margin rule exactly. -``collision_distance`` and the cost derivatives are untouched by it; they keep returning the -geometric signed clearance, and ``collision_cost`` applies its own margin. ``check_self`` (default -``true``) set to ``false`` skips the self-pair ranges so the verdict reflects the environment -alone; ``collision_distance`` is environment-only already. On a two-tier model the margin inflates -both tiers, which keeps the broad-to-fine covering argument intact. The gate runs the warp and -block paths against the distance surfaces at 0, 5 mm and 2 cm, with and without the self pairs, -and sweeps each configuration's own minimum clearance from just below to just above the threshold. +Both checkers take two trailing optional arguments: ``margins`` and ``check_self``. +A scalar in the margin position still applies to both environment and self checks; +the default is zero. To choose them independently, pass ``CollisionMargins`` +(``CollisionMargins`` for the warp surface). Its fields are +``environment_margin`` and ``self_margin``, in metres. The host factory validates +finite, nonnegative inputs and resolves optional overrides: + +.. code-block:: cpp + + auto same = grid_collision::make_collision_margins(0.02f); + auto separate = grid_collision::make_collision_margins(0.02f, 0.03f, 0.0f); + // Common 2cm, environment 3cm, self explicitly zero. + auto self_only = grid_collision::make_collision_margins(0.02f, std::nullopt, 0.005f); + // Environment inherits 2cm; self overrides to 5mm. + +Pass the resolved value into the kernel and then into the existing margin position. +Direct host/device construction ``CollisionMargins{environment, self}`` is a +low-level path with the same finite/nonnegative precondition, without runtime +validation. No negative or NaN sentinel means inheritance. ``check_self`` remains +the final argument (default ``true``); setting it to ``false`` skips both self +passes irrespective of ``self_margin``. + +A hit occurs when a raw clearance is below the corresponding margin. Environment +checks inflate the robot radius; self checks inflate only one pair member, not +both. Exact contact at the threshold is free, subject to floating-point rounding. +Both tiers use the same per-side values, preserving the covering argument. +``collision_distance``, normals and derivatives still report raw geometric values; +``collision_cost`` retains its own independent scalar margin. Tests cover asymmetric +and inherited margins, explicit zero, the previous two-call consumer pattern, +block/warp agreement, capsule rows and exactly representable boundary cases. The obstacle set itself comes from plain arrays. ``grid_collision::make_host_environment`` takes one row per obstacle (spheres as ``x y z r``, capsules as the two endpoints and a @@ -86,9 +105,13 @@ Geometry coverage and correctness are intended to match the fine-only verdict for the same representation. * Covering geometry is an approximation of the original robot. Agreement with a fine-tier sphere check is not proof of exact mesh collision detection. -* Missing or unusable meshes fail generation. A failed voxel fill may use an - explicitly warned bounding-box cover, but geometry is never dropped to - produce a partial model. Self-collision pair exclusions must also match your application. +* Missing or unusable meshes fail generation. A failed voxel fill and a model + with no spheres are errors as well, because either would silently change the + clearance rows a consumer bakes; ``strict_collision=False`` on + ``build_artifact`` (``--no-strict-collision`` on ``grid-generate`` and + ``grid-spherize``) accepts the bounding-box cover or the empty model with a + warning instead. Geometry is never dropped to produce a partial model. + Self-collision pair exclusions must also match your application. * Correctness tests live under ``test/cuda_equivalents/test_cuda_collision_*``; they cover geometry, pairs, tiering, costs, and native representations. diff --git a/docs/source/user_guide/tutorials/cuda_validation.rst b/docs/source/user_guide/tutorials/cuda_validation.rst index 64e08825..d8cba307 100644 --- a/docs/source/user_guide/tutorials/cuda_validation.rst +++ b/docs/source/user_guide/tutorials/cuda_validation.rst @@ -83,6 +83,29 @@ an interrupted run without re-running completed shards. See ``CLAUDE.md`` and ``test/run_split_suite.py --help`` for the full contract (RAM-aware compile pool, shard bin-packing, receipt merge/carry). +Compile memory estimates +~~~~~~~~~~~~~~~~~~~~~~~~ + +The compile pool records process-tree peak RSS. Warm variants of one robot +share a lower bound equal to that robot's largest recorded warm-job peak: +a variant measured as a cheap cached relink must not later be admitted as +if a cold compilation were equally small. Unmeasured variants retain the +conservative default when it is larger. This is a prediction, not a memory +limit. The empty pool still admits one job even if its estimate exceeds the +budget, so a single build must fit on the machine. + +On a memory-constrained Jetson, bound both concurrent compile jobs and each +job's compiler processes. The low-level driver honors +``GRID_SPLIT_COMPILE_JOBS`` (for example, 2) and +``GRID_RBD_BUILD_JOBS``. The tracked receipt launcher sets its own profile; +inspect that profile instead of assuming inherited environment overrides +take effect. Check current free RAM and disk before an approved run. A +historical successful pool size is not a guarantee for a cold cache or a +larger robot. + +Targeted checks +~~~~~~~~~~~~~~~ + For a quick targeted check, plain pytest still works: .. code-block:: bash diff --git a/docs/source/user_guide/tutorials/python_wrappers.rst b/docs/source/user_guide/tutorials/python_wrappers.rst index 29e2c656..1d026bbd 100644 --- a/docs/source/user_guide/tutorials/python_wrappers.rst +++ b/docs/source/user_guide/tutorials/python_wrappers.rst @@ -787,6 +787,16 @@ All take/return 2D arrays with axis 0 = batch; cost methods return PyTorch handles. See :doc:`../concepts/algorithms/integrators_and_plant` for input layouts, diagonal weights and derivative scope. +``plant_step_gradient`` uses a full shared-memory arena. It rejects a model +whose arena exceeds the active device's per-block capacity; use +``integrator_gradient(q, qd, u, dt)`` for the equivalent state-transition +Jacobian through the tiered workspace path. The two allocations are not +interchangeable: a spill-selected integrator allocation can be smaller than +the plant layout. Native callers of ``plant_step_gradient_arena::carve`` +must allocate +``grid_plant::PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES()`` bytes. +An enclosing kernel must also budget its own static and dynamic storage. + External forces (``f_ext``) --------------------------- @@ -803,3 +813,7 @@ See also * :doc:`benchmarks`: bench harness and what the methods cost. * :doc:`../concepts/algorithms/index`: algorithm-level docs. +* :doc:`../../api_reference/model_coordinates`: the handle's ``model_descriptor``, + coordinate ownership and named frames. +* :doc:`../../api_reference/integration_conventions`: what ``plant_step_gradient`` + and ``plant_step_hessian`` differentiate on a floating base or a spherical joint. diff --git a/examples/codegen/printGRiD.cu b/examples/codegen/printGRiD.cu index 10927a89..b370bfd7 100644 --- a/examples/codegen/printGRiD.cu +++ b/examples/codegen/printGRiD.cu @@ -1,6 +1,6 @@ /*** Built + run by examples/codegen/print_grid.py (which auto-detects the GPU arch): - nvcc -std=c++11 -o printGRiD.exe printGRiD.cu -gencode arch=compute_,code=sm_ + nvcc -std=c++17 -o printGRiD.exe printGRiD.cu -gencode arch=compute_,code=sm_ ( e.g. 120 for sm_120). Dumps every generated kernel's output for a given grid.cuh. ***/ diff --git a/examples/codegen/print_grid.py b/examples/codegen/print_grid.py index 2de1a94e..27d40f80 100644 --- a/examples/codegen/print_grid.py +++ b/examples/codegen/print_grid.py @@ -68,7 +68,7 @@ def main(): result = subprocess.run( [ "nvcc", - "-std=c++11", + "-std=c++17", "-o", "printGRiD.exe", "printGRiD.cu", diff --git a/examples/notebooks/README.md b/examples/notebooks/README.md index 7131d313..46b3c02a 100644 --- a/examples/notebooks/README.md +++ b/examples/notebooks/README.md @@ -70,7 +70,7 @@ pytest --nbval-lax examples/notebooks/ ``` The first `register_robot` of a robot pays a one-time `nvcc` build. The -measured cold/warm numbers per robot are in the *What to expect: cold vs. warm* +measured cold/warm numbers per robot are in the *Cold and warm build times* table of the [Fast Robot Setup](https://a2r-lab.org/GRiD/docs/user_guide/getting_started/fast_robot_setup.html) page (iiwa14, the robot these notebooks use, is the cheapest). A kernel diff --git a/external/GLASS b/external/GLASS index 9e57178c..6a823209 160000 --- a/external/GLASS +++ b/external/GLASS @@ -1 +1 @@ -Subproject commit 9e57178ca146fa1cecae7e27c22260603b993aa6 +Subproject commit 6a823209322705dfaaf1918b9d0f1ad92d99bf48 diff --git a/external/RBDReference b/external/RBDReference index 7cf00a09..d43ed63b 160000 --- a/external/RBDReference +++ b/external/RBDReference @@ -1 +1 @@ -Subproject commit 7cf00a09d66f62a742fedc05379b9b67f7907280 +Subproject commit d43ed63b5a32c5a54b2958468131ee1bca841d8a diff --git a/external/URDFParser b/external/URDFParser index f391741a..03bf3c33 160000 --- a/external/URDFParser +++ b/external/URDFParser @@ -1 +1 @@ -Subproject commit f391741a221cefdf6b8dcfcfe0a7aed3460943d4 +Subproject commit 03bf3c337dc04cb3cccac3b9e9be9d9f26b05e87 diff --git a/gpu-proof.json b/gpu-proof.json index 0d023eb0..8b92faae 100644 --- a/gpu-proof.json +++ b/gpu-proof.json @@ -14,14 +14,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "79ba79e46e400eea9a6d94f7171164dbd7a79701ed237f537a96c614ac48db6b", + "digest": "ddb0bc18139e55cf1db46349c56d8bb743d9bf55b1197291841ca940bbe47e5d", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -31,10 +32,11 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 309, + "file_count": 367, "included_paths": [ "MANIFEST.in", "_build_resources.py", + "bindings/examples/consumer_artifact_migration.py", "bindings/grid_rbd", "bindings/src", "external/GLASS", @@ -49,6 +51,7 @@ "test/compile_sched.py", "test/conftest.py", "test/cuda_equivalents", + "test/ee_constant_slot_warnings.py", "test/gpu-proof-expected-skips.txt", "test/gpu-proof-policy-release.yaml", "test/gpu-proof-policy.yaml", @@ -62,21 +65,23 @@ "test/run_gpu_proof.sh", "test/run_split_suite.py", "test/run_validation.py", + "test/test_manifold_conventions.py", + "test/test_model_descriptor.py", "tools/check_installed_numerics.py", "tools/check_installed_package.py" ] }, "mode": "local", "repo": { - "branch": "batch-0.6.0-hjcd", - "commit_sha": "ec09c06014e13d9f2728fecd46e7f714c4dd32d4", + "branch": "codex/foundation-batch-1", + "commit_sha": "607efc2c76eae31b4ad95b5f9a10e3077f225ba9", "dirty": false, "github_username": "plancherb1", "remote_url": "git@github.com:A2R-Lab/GRiD.git" }, "schema_version": "3", "session": { - "ended_at": "2026-10-24T15:01:45Z", + "ended_at": "2026-11-01T11:03:25Z", "node_ids": [ "test/cuda_equivalents/test_cuda_batched_host_wrapper.py::test_cuda_batched_host_wrapper_matches_reference[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_batched_host_wrapper.py::test_cuda_batched_host_wrapper_matches_reference[go2-floating]", @@ -140,7 +145,7 @@ "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[g1-floating]", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_generated_header_includes_grid_data_variants_and_no_rnea_alias", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_controls_and_helpers_are_generated", - "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_default_cxx11_compiles_without_mathdx", + "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_default_cxx17_compiles_without_mathdx", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_base_strided_helpers_compile", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_dynamics_grid_data_variant_wrappers_compile", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_kinematics_grid_data_variant_wrappers_compile", @@ -176,6 +181,9 @@ "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-1]", "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-32]", "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-100]", + "test/cuda_equivalents/test_cuda_collision_margins.py::test_native_separate_margins[False]", + "test/cuda_equivalents/test_cuda_collision_margins.py::test_native_separate_margins[True]", + "test/cuda_equivalents/test_cuda_collision_native.py::test_static_separate_margin_thresholds", "test/cuda_equivalents/test_cuda_collision_native.py::test_collision_native_two_tier", "test/cuda_equivalents/test_cuda_collision_native.py::test_collision_native_self_collision", "test/cuda_equivalents/test_cuda_collision_pairs.py::test_collision_distance_pairs_fd", @@ -187,8 +195,70 @@ "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[256]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_two_tier_cascade[32]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_two_tier_cascade[128]", + "test/cuda_equivalents/test_cuda_collision_warp.py::test_separate_environment_self_margins[False]", + "test/cuda_equivalents/test_cuda_collision_warp.py::test_separate_environment_self_margins[True]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_margin_and_self_toggle_single_tier", "test/cuda_equivalents/test_cuda_collision_warp.py::test_margin_and_self_toggle_two_tier_cascade", + "test/cuda_equivalents/test_cuda_constant_branch_mimic_scale.py::test_constant_branch_keeps_nonunit_mimic_scale[float]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe0-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe0-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe0-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe1-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe1-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe1-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe2-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe2-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe2-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe3-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe3-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe3-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe4-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe4-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe4-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe5-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe5-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe5-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-world-so]", + "test/cuda_equivalents/test_cuda_constant_frame_velocity.py::test_constant_frame_velocity[float-False]", + "test/cuda_equivalents/test_cuda_constant_frame_velocity.py::test_constant_frame_velocity[float-True]", + "test/cuda_equivalents/test_cuda_constant_mimic.py::test_constant_mimic_kinematics[float-prismatic]", + "test/cuda_equivalents/test_cuda_constant_mimic.py::test_constant_mimic_kinematics[float-revolute]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe0-body]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe0-world]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe0-coriolis]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe1-body]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe1-world]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe1-coriolis]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe2-body]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe2-world]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe2-coriolis]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe3-body]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe3-world]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe3-coriolis]", + "test/cuda_equivalents/test_cuda_constant_velocity.py::test_constant_branch_velocity_isolation[float]", "test/cuda_equivalents/test_cuda_contact_frame_positions.py::test_contact_frame_positions_and_jacobian[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_contact_frame_positions.py::test_contact_frame_positions_and_jacobian[go2-floating]", "test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py::test_cuda_continuous_joint_matches_reference_at_wrapped_angles[gen3-fixed]", @@ -252,6 +322,9 @@ "test/cuda_equivalents/test_cuda_ee_newton_hessian.py::test_ee_pos_cost_hessian_newton_matches_fd[go2-fixed]", "test/cuda_equivalents/test_cuda_ee_psd_clamp.py::test_ee_pos_cost_hessian_psd_clamp[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_ee_psd_clamp.py::test_ee_pos_cost_hessian_psd_clamp[go2-fixed]", + "test/cuda_equivalents/test_cuda_ee_workspace_queries.py::test_exact_target_scratch[float-iiwa14-iiwa_joint_ee]", + "test/cuda_equivalents/test_cuda_ee_workspace_queries.py::test_exact_target_scratch[float-go2-FR_foot_joint]", + "test/cuda_equivalents/test_cuda_ee_workspace_queries.py::test_exact_target_scratch[float-go2-]", "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[go2-floating]", "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[fr3-fixed]", @@ -1213,6 +1286,7 @@ "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[gen3-floating]", "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[fetch-floating]", "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[baxter-floating]", + "test/cuda_equivalents/test_cuda_glass_handoff.py::test_embedded_glass_restricted_handoffs[float-3e-05-3e-05]", "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[iiwa14-world-frame-floating]", "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[iiwa14-world-frame-fixed]", "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[go2-world-frame-floating]", @@ -1276,6 +1350,9 @@ "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_fext_matches_python_reference", "test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py::test_floating_damped_cuda_matches_damped_oracle", "test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py::test_floating_tanh_friction_cuda_matches_tanh_oracle", + "test/cuda_equivalents/test_cuda_joint_transform_suffix.py::test_scalar_transform_composed_suffix[float-7-False]", + "test/cuda_equivalents/test_cuda_joint_transform_suffix.py::test_scalar_transform_composed_suffix[float-7-True]", + "test/cuda_equivalents/test_cuda_joint_transform_suffix.py::test_scalar_transform_composed_suffix[float-32-True]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch1]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch16]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch256]", @@ -1291,8 +1368,35 @@ "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch1]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch16]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch256]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-zero]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-default]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-wide-x]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-wide-u]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-zero]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-default]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-wide-x]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-wide-u]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-zero]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-default]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-wide-x]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-wide-u]", "test/cuda_equivalents/test_cuda_mjx_tier_invariance.py::test_mjx_second_order_tier_invariance[idsva_so]", "test/cuda_equivalents/test_cuda_mjx_tier_invariance.py::test_mjx_second_order_tier_invariance[fdsva_so]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[constant-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[floating-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[spherical-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[branch-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[empty-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[runtime-fp32]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-mixed_spherical_arm-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-rotated_inertial_arm-True-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-branching_skew_arm-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-helical_arm-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-rotated_inertial_arm-False-True]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-constant-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-zero-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-slider_mimic_fixed_tool-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-fr3-False-False]", "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-iiwa14-fixed]", "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-go2-fixed]", "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-g1-fixed]", @@ -1368,6 +1472,26 @@ "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[go2-floating]", "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[fr3-fixed]", + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[iiwa14-False-float-False]", + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[go2-True-float-False]", + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[go2-True-float-True]", + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[g1-False-float-False]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-float-full]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-float-spill]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[iiwa14-float-full]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[iiwa14-float-spill]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-semi-runtime-float-full]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-semi-runtime-float-spill]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[branching-runtime-double-full]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[branching-runtime-double-spill]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_fixed_tool_values_and_thread_invariance[False]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_fixed_tool_values_and_thread_invariance[True]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[cardinal-False]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[cardinal-True]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[cardinal-constant]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[skew-False]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[skew-True]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[skew-constant]", "test/cuda_equivalents/test_cuda_racecheck_floating.py::test_go2_floating_composition_is_racecheck_clean", "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[go2-fixed]", @@ -1405,6 +1529,7 @@ "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[gen3-floating]", "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fetch-floating]", "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[baxter-floating]", + "test/cuda_equivalents/test_cuda_revolute_mimic_mechanics.py::test_revolute_mimic_dynamics_against_independent_mechanics", "test/cuda_equivalents/test_cuda_safe_init.py::test_checked_initialization_contract[iiwa14]", "test/cuda_equivalents/test_cuda_safe_init.py::test_checked_initialization_contract[go2]", "test/cuda_equivalents/test_cuda_safe_init.py::test_legacy_policy_is_preserved[iiwa14]", @@ -1459,6 +1584,22 @@ "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_matches_reference[float-mixed_spherical_arm.urdf]", "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_thread_invariant[spherical_arm.urdf]", "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_thread_invariant[mixed_spherical_arm.urdf]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-mixed_spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-single_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-two_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-mixed_spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-single_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-two_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-mixed_spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-single_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-two_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-mixed_spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-single_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-two_spherical]", "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_matches_world_reference[spherical_arm.urdf-float]", "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_matches_world_reference[mixed_spherical_arm.urdf-float]", "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_thread_invariant[spherical_arm.urdf]", @@ -1466,6 +1607,10 @@ "test/cuda_equivalents/test_cuda_vendor_glass_alias.py::test_vendor_glass_false_compiles_against_top_level_glass_and_runs", "test/cuda_equivalents/test_cuda_workspace_slots.py::test_cuda_workspace_slots_bit_identity[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_workspace_slots.py::test_cuda_workspace_slots_bit_identity[go2-floating]", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[pose-hessian-True-float]", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[pose-hessian-False-float]", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[plant-gradient-True-float]", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[plant-gradient-False-float]", "test/cuda_equivalents/test_smem_poison.py::test_equivalence_survives_smem_poison[iiwa14 and fixed]", "test/cuda_equivalents/test_smem_poison.py::test_equivalence_survives_smem_poison[go2 and floating]", "test/python_wrappers/test_any_thread_count.py::test_set_threads_per_block_default", @@ -1531,6 +1676,16 @@ "test/python_wrappers/test_centroidal_energy_frame.py::test_frame_jacobian_nondefault_frame[go2]", "test/python_wrappers/test_centroidal_energy_frame.py::test_frame_jacobian_default_target_explicit_frame[go2]", "test/python_wrappers/test_centroidal_energy_frame.py::test_frame_jacobian_dot[go2]", + "test/python_wrappers/test_consumer_artifact.py::test_descriptor_matches_exported_cpp_and_source_free_prepared_load", + "test/python_wrappers/test_consumer_artifact.py::test_compiler_discovered_headers_rekey_native_values", + "test/python_wrappers/test_consumer_artifact.py::test_consumer_migration_example", + "test/python_wrappers/test_consumer_artifact.py::test_shadowing_symlink_preserves_header_file_macro_identity", + "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_dependency_filename_escaping[space name.h]", + "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_dependency_filename_escaping[hash#name.h]", + "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_dependency_filename_escaping[cash$name.h]", + "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_dependency_filename_escaping[cash$$name.h]", + "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_unreadable_dependency_fails_closed", + "test/python_wrappers/test_consumer_artifact.py::test_concurrent_consumer_builds_publish_loadable_winner", "test/python_wrappers/test_consumer_artifact.py::test_artifact_builds_loads_and_matches_the_oracle", "test/python_wrappers/test_consumer_artifact.py::test_second_build_is_a_pointer_hit_and_a_define_rekeys", "test/python_wrappers/test_consumer_artifact.py::test_unchanged_unit_reuses_its_object", @@ -1538,6 +1693,10 @@ "test/python_wrappers/test_consumer_artifact.py::test_load_without_a_toolchain_and_device_refusal", "test/python_wrappers/test_consumer_artifact.py::test_collision_options_emit_the_namespace_and_validate", "test/python_wrappers/test_consumer_artifact.py::test_plant_block_follows_the_request", + "test/python_wrappers/test_consumer_artifact.py::test_declared_python_extension_import_and_prepared_load", + "test/python_wrappers/test_consumer_artifact.py::test_undeclared_python_extension_is_not_published", + "test/python_wrappers/test_consumer_artifact.py::test_source_path_macro_survives_cache_reuse", + "test/python_wrappers/test_consumer_artifact.py::test_two_robot_consumer_statics_are_isolated_in_both_load_orders", "test/python_wrappers/test_contact_fext.py::test_contact_fext_matches_host_map", "test/python_wrappers/test_contact_fext.py::test_contact_fext_floating_dynamics_only_subset_matches_host_map", "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_gradient_hessian[iiwa14-floating-named-iiwa_joint_ee]", @@ -2046,7 +2205,7 @@ "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[g1-floating]", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_generated_header_includes_grid_data_variants_and_no_rnea_alias", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_controls_and_helpers_are_generated", - "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_default_cxx11_compiles_without_mathdx", + "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_default_cxx17_compiles_without_mathdx", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_base_strided_helpers_compile", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_dynamics_grid_data_variant_wrappers_compile", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_kinematics_grid_data_variant_wrappers_compile", @@ -2082,19 +2241,106 @@ "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-1]", "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-32]", "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-100]", + "test/cuda_equivalents/test_cuda_collision_margins.py::test_native_separate_margins[False]", + "test/cuda_equivalents/test_cuda_collision_margins.py::test_native_separate_margins[True]", + "test/cuda_equivalents/test_cuda_collision_native.py::test_static_separate_margin_thresholds", "test/cuda_equivalents/test_cuda_collision_native.py::test_collision_native_two_tier", "test/cuda_equivalents/test_cuda_collision_native.py::test_collision_native_self_collision", "test/cuda_equivalents/test_cuda_collision_pairs.py::test_collision_distance_pairs_fd", "test/cuda_equivalents/test_cuda_collision_selfcc_rows.py::test_self_collision_rows_fd", "test/cuda_equivalents/test_cuda_collision_two_tier.py::test_collision_two_tier_matches_fine", + "-q", + "-rf", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/cuda_00_batched_host_wrapp_branching_ske.xml", + "-m", + "gpu_proof", + "--gpu-proof-enable", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/cuda_00_batched_host_wrapp_branching_ske.json", + "--gpu-proof-shard=cuda_00_batched_host_wrapp_branching_ske", + "--gpu-proof-shard-fingerprint-paths=external/RBDReference,test/cuda_equivalents/test_cuda_batched_host_wrapper.py,test/cuda_equivalents/cuda_batched_host_runner.cu,test/cuda_equivalents/test_cuda_branching_skew_equivalence.py,test/cuda_equivalents/cuda_branching_skew_runner.cu,test/cuda_equivalents/test_cuda_centroidal_equivalence.py,test/cuda_equivalents/cuda_centroidal_smoke_runner.cu,test/cuda_equivalents/cuda_centroidal_mimic_smoke_runner.cu,test/cuda_equivalents/test_cuda_centroidal_host_spill.py,test/cuda_equivalents/cuda_centroidal_host_spill_runner.cu,test/cuda_equivalents/test_cuda_codegen_layout.py,test/cuda_equivalents/test_cuda_collision_complete.py,test/cuda_equivalents/cuda_collision_complete_runner.cu,test/cuda_equivalents/executable_cache.py,test/cuda_equivalents/test_cuda_collision_config_free.py,test/cuda_equivalents/cuda_collision_config_free_runner.cu,test/cuda_equivalents/cuda_collision_self_collision_runner.cu,test/cuda_equivalents/test_cuda_collision_cost.py,test/cuda_equivalents/cuda_collision_cost_runner.cu,test/cuda_equivalents/test_cuda_collision_environment.py,test/cuda_equivalents/cuda_collision_environment_runner.cu,test/cuda_equivalents/test_cuda_collision_warp.py,test/cuda_equivalents/test_cuda_collision_geometry.py,test/cuda_equivalents/cuda_collision_geometry_runner.cu,test/cuda_equivalents/test_cuda_collision_many_hits.py,test/cuda_equivalents/cuda_collision_many_hits_runner.cu,test/cuda_equivalents/test_cuda_collision_margins.py,test/cuda_equivalents/cuda_collision_margins_runner.cu,test/cuda_equivalents/cuda_collision_native_margins_runner.cu,test/cuda_equivalents/test_cuda_collision_native.py,test/cuda_equivalents/cuda_collision_native_runner.cu,test/cuda_equivalents/cuda_collision_native_selfcc_runner.cu,test/cuda_equivalents/test_cuda_collision_pairs.py,test/cuda_equivalents/cuda_collision_pairs_runner.cu,test/cuda_equivalents/test_cuda_collision_selfcc_rows.py,test/cuda_equivalents/cuda_collision_selfcc_rows_runner.cu,test/cuda_equivalents/test_cuda_collision_two_tier.py,test/cuda_equivalents/cuda_collision_two_tier_runner.cu,test/cuda_equivalents/cuda_harness.py" + ], + [ "test/cuda_equivalents/test_cuda_collision_warp.py::test_sphere_tables_match_the_baked_collision_batch", "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[32]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[64]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[256]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_two_tier_cascade[32]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_two_tier_cascade[128]", + "test/cuda_equivalents/test_cuda_collision_warp.py::test_separate_environment_self_margins[False]", + "test/cuda_equivalents/test_cuda_collision_warp.py::test_separate_environment_self_margins[True]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_margin_and_self_toggle_single_tier", "test/cuda_equivalents/test_cuda_collision_warp.py::test_margin_and_self_toggle_two_tier_cascade", + "test/cuda_equivalents/test_cuda_constant_branch_mimic_scale.py::test_constant_branch_keeps_nonunit_mimic_scale[float]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe0-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe0-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe0-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe1-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe1-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe1-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe2-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe2-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe2-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe3-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe3-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe3-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe4-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe4-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe4-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe5-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe5-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe5-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-world-so]", + "test/cuda_equivalents/test_cuda_constant_frame_velocity.py::test_constant_frame_velocity[float-False]", + "test/cuda_equivalents/test_cuda_constant_frame_velocity.py::test_constant_frame_velocity[float-True]", + "test/cuda_equivalents/test_cuda_constant_mimic.py::test_constant_mimic_kinematics[float-prismatic]", + "test/cuda_equivalents/test_cuda_constant_mimic.py::test_constant_mimic_kinematics[float-revolute]", + "-q", + "-rf", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/cuda_01_collision_warp_constant_branch_m.xml", + "-m", + "gpu_proof", + "--gpu-proof-enable", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/cuda_01_collision_warp_constant_branch_m.json", + "--gpu-proof-shard=cuda_01_collision_warp_constant_branch_m", + "--gpu-proof-shard-fingerprint-paths=external/RBDReference,test/cuda_equivalents/test_cuda_collision_warp.py,test/cuda_equivalents/cuda_collision_warp_runner.cu,test/cuda_equivalents/test_cuda_collision_config_free.py,test/cuda_equivalents/test_cuda_constant_branch_mimic_scale.py,test/cuda_equivalents/executable_cache.py,test/cuda_equivalents/constant_model.py,test/cuda_equivalents/test_cuda_constant_derivatives.py,test/cuda_equivalents/cuda_constant_derivatives_runner.cu,test/cuda_equivalents/test_cuda_constant_dynamics_audit.py,test/cuda_equivalents/cuda_constant_dynamics_audit_runner.cu,test/cuda_equivalents/test_cuda_constant_frame_velocity.py,test/cuda_equivalents/test_cuda_constant_mimic.py,test/cuda_equivalents/cuda_prismatic_fixed_target_runner.cu,test/cuda_equivalents/cuda_harness.py" + ], + [ + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe0-body]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe0-world]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe0-coriolis]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe1-body]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe1-world]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe1-coriolis]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe2-body]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe2-world]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe2-coriolis]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe3-body]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe3-world]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe3-coriolis]", + "test/cuda_equivalents/test_cuda_constant_velocity.py::test_constant_branch_velocity_isolation[float]", "test/cuda_equivalents/test_cuda_contact_frame_positions.py::test_contact_frame_positions_and_jacobian[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_contact_frame_positions.py::test_contact_frame_positions_and_jacobian[go2-floating]", "test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py::test_cuda_continuous_joint_matches_reference_at_wrapped_angles[gen3-fixed]", @@ -2158,6 +2404,9 @@ "test/cuda_equivalents/test_cuda_ee_newton_hessian.py::test_ee_pos_cost_hessian_newton_matches_fd[go2-fixed]", "test/cuda_equivalents/test_cuda_ee_psd_clamp.py::test_ee_pos_cost_hessian_psd_clamp[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_ee_psd_clamp.py::test_ee_pos_cost_hessian_psd_clamp[go2-fixed]", + "test/cuda_equivalents/test_cuda_ee_workspace_queries.py::test_exact_target_scratch[float-iiwa14-iiwa_joint_ee]", + "test/cuda_equivalents/test_cuda_ee_workspace_queries.py::test_exact_target_scratch[float-go2-FR_foot_joint]", + "test/cuda_equivalents/test_cuda_ee_workspace_queries.py::test_exact_target_scratch[float-go2-]", "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[go2-floating]", "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[fr3-fixed]", @@ -2788,6 +3037,17 @@ "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics-threads96]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics-threads100]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics-threadssuggested]", + "-q", + "-rf", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/cuda_02_constant_second_or_constant_velo.xml", + "-m", + "gpu_proof", + "--gpu-proof-enable", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/cuda_02_constant_second_or_constant_velo.json", + "--gpu-proof-shard=cuda_02_constant_second_or_constant_velo", + "--gpu-proof-shard-fingerprint-paths=external/RBDReference,test/cuda_equivalents/test_cuda_constant_second_order.py,test/cuda_equivalents/cuda_constant_second_order_runner.cu,test/cuda_equivalents/executable_cache.py,test/cuda_equivalents/constant_model.py,test/cuda_equivalents/test_cuda_constant_velocity.py,test/cuda_equivalents/test_cuda_contact_frame_positions.py,test/cuda_equivalents/cuda_contact_frame_positions_runner.cu,test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py,test/cuda_equivalents/cuda_equivalence_runner.cu,test/cuda_equivalents/grid_runner_select.cuh,test/cuda_equivalents/test_cuda_coriolis.py,test/cuda_equivalents/cuda_coriolis_smoke_runner.cu,test/cuda_equivalents/test_cuda_coriolis_output_spill.py,test/cuda_equivalents/test_cuda_dccrba.py,test/cuda_equivalents/cuda_dccrba_smoke_runner.cu,test/cuda_equivalents/test_cuda_ee_named_depth1.py,test/cuda_equivalents/cuda_ee_named_depth1_runner.cu,test/cuda_equivalents/test_cuda_ee_newton_hessian.py,test/cuda_equivalents/cuda_ee_newton_hessian_runner.cu,test/cuda_equivalents/test_cuda_ee_psd_clamp.py,test/cuda_equivalents/cuda_ee_psd_clamp_runner.cu,test/cuda_equivalents/test_cuda_ee_workspace_queries.py,test/cuda_equivalents/test_cuda_eepose_runtime.py,test/cuda_equivalents/cuda_eepose_runtime_smoke_runner.cu,test/cuda_equivalents/test_cuda_energy_regressor.py,test/cuda_equivalents/cuda_energy_regressor_smoke_runner.cu,test/cuda_equivalents/test_cuda_regressor.py,test/cuda_equivalents/test_cuda_executable_equivalence.py,test/cuda_equivalents/cuda_harness.py,test/cuda_equivalents/cuda_equivalence_runner.cu,test/cuda_equivalents/grid_runner_select.cuh" + ], + [ "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threads32]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threads96]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threads100]", @@ -2872,17 +3132,6 @@ "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threads96]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threads100]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threadssuggested]", - "-q", - "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/cuda_00_batched_host_wrapp_branching_ske.xml", - "-m", - "gpu_proof", - "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/cuda_00_batched_host_wrapp_branching_ske.json", - "--gpu-proof-shard=cuda_00_batched_host_wrapp_branching_ske", - "--gpu-proof-shard-fingerprint-paths=external/RBDReference,test/cuda_equivalents/test_cuda_batched_host_wrapper.py,test/cuda_equivalents/cuda_batched_host_runner.cu,test/cuda_equivalents/test_cuda_branching_skew_equivalence.py,test/cuda_equivalents/cuda_branching_skew_runner.cu,test/cuda_equivalents/test_cuda_centroidal_equivalence.py,test/cuda_equivalents/cuda_centroidal_smoke_runner.cu,test/cuda_equivalents/cuda_centroidal_mimic_smoke_runner.cu,test/cuda_equivalents/test_cuda_centroidal_host_spill.py,test/cuda_equivalents/cuda_centroidal_host_spill_runner.cu,test/cuda_equivalents/test_cuda_codegen_layout.py,test/cuda_equivalents/test_cuda_collision_complete.py,test/cuda_equivalents/cuda_collision_complete_runner.cu,test/cuda_equivalents/executable_cache.py,test/cuda_equivalents/test_cuda_collision_config_free.py,test/cuda_equivalents/cuda_collision_config_free_runner.cu,test/cuda_equivalents/cuda_collision_self_collision_runner.cu,test/cuda_equivalents/test_cuda_collision_cost.py,test/cuda_equivalents/cuda_collision_cost_runner.cu,test/cuda_equivalents/test_cuda_collision_environment.py,test/cuda_equivalents/cuda_collision_environment_runner.cu,test/cuda_equivalents/test_cuda_collision_warp.py,test/cuda_equivalents/test_cuda_collision_geometry.py,test/cuda_equivalents/cuda_collision_geometry_runner.cu,test/cuda_equivalents/test_cuda_collision_many_hits.py,test/cuda_equivalents/cuda_collision_many_hits_runner.cu,test/cuda_equivalents/test_cuda_collision_native.py,test/cuda_equivalents/cuda_collision_native_runner.cu,test/cuda_equivalents/cuda_collision_native_selfcc_runner.cu,test/cuda_equivalents/test_cuda_collision_pairs.py,test/cuda_equivalents/cuda_collision_pairs_runner.cu,test/cuda_equivalents/test_cuda_collision_selfcc_rows.py,test/cuda_equivalents/cuda_collision_selfcc_rows_runner.cu,test/cuda_equivalents/test_cuda_collision_two_tier.py,test/cuda_equivalents/cuda_collision_two_tier_runner.cu,test/cuda_equivalents/test_cuda_collision_warp.py,test/cuda_equivalents/cuda_collision_warp_runner.cu,test/cuda_equivalents/test_cuda_contact_frame_positions.py,test/cuda_equivalents/cuda_contact_frame_positions_runner.cu,test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py,test/cuda_equivalents/cuda_equivalence_runner.cu,test/cuda_equivalents/grid_runner_select.cuh,test/cuda_equivalents/test_cuda_coriolis.py,test/cuda_equivalents/cuda_coriolis_smoke_runner.cu,test/cuda_equivalents/test_cuda_coriolis_output_spill.py,test/cuda_equivalents/test_cuda_dccrba.py,test/cuda_equivalents/cuda_dccrba_smoke_runner.cu,test/cuda_equivalents/test_cuda_ee_named_depth1.py,test/cuda_equivalents/cuda_ee_named_depth1_runner.cu,test/cuda_equivalents/test_cuda_ee_newton_hessian.py,test/cuda_equivalents/cuda_ee_newton_hessian_runner.cu,test/cuda_equivalents/test_cuda_ee_psd_clamp.py,test/cuda_equivalents/cuda_ee_psd_clamp_runner.cu,test/cuda_equivalents/test_cuda_eepose_runtime.py,test/cuda_equivalents/cuda_eepose_runtime_smoke_runner.cu,test/cuda_equivalents/test_cuda_energy_regressor.py,test/cuda_equivalents/cuda_energy_regressor_smoke_runner.cu,test/cuda_equivalents/test_cuda_regressor.py,test/cuda_equivalents/test_cuda_executable_equivalence.py,test/cuda_equivalents/cuda_harness.py,test/cuda_equivalents/cuda_equivalence_runner.cu,test/cuda_equivalents/grid_runner_select.cuh" - ], - [ "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics-threads32]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics-threads96]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics-threads100]", @@ -3085,6 +3334,17 @@ "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[gen3-floating]", "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[fetch-floating]", "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[baxter-floating]", + "-q", + "-rf", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/cuda_03_rizon4_gen3_fetch_baxter_signed_.xml", + "-m", + "gpu_proof", + "--gpu-proof-enable", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/cuda_03_rizon4_gen3_fetch_baxter_signed_.json", + "--gpu-proof-shard=cuda_03_rizon4_gen3_fetch_baxter_signed_", + "--gpu-proof-shard-fingerprint-paths=external/RBDReference,test/cuda_equivalents/test_cuda_executable_equivalence.py,test/cuda_equivalents/test_cuda_f_ext_contact.py,test/cuda_equivalents/cuda_f_ext_contact_runner.cu,test/cuda_equivalents/test_cuda_f_ext_contact_runtime.py,test/cuda_equivalents/cuda_f_ext_contact_runtime_runner.cu,test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py,test/cuda_equivalents/cuda_f_ext_gradient_runner.cu,test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py,test/cuda_equivalents/test_cuda_fd_du_output_spill.py,test/cuda_equivalents/cuda_equivalence_runner.cu,test/cuda_equivalents/grid_runner_select.cuh,test/cuda_equivalents/test_cuda_fd_parameter_gradient.py,test/cuda_equivalents/cuda_fd_parameter_gradient_smoke_runner.cu,test/cuda_equivalents/test_cuda_fext_equivalence.py,test/cuda_equivalents/cuda_harness.py,test/cuda_equivalents/cuda_equivalence_runner.cu,test/cuda_equivalents/grid_runner_select.cuh" + ], + [ "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[iiwa14-floating]", "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[go2-floating]", "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[g1-floating]", @@ -3112,17 +3372,6 @@ "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[gen3-floating]", "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fetch-floating]", "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[baxter-floating]", - "-q", - "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/cuda_01_fetch_baxter_signed_axis_arm_f_e.xml", - "-m", - "gpu_proof", - "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/cuda_01_fetch_baxter_signed_axis_arm_f_e.json", - "--gpu-proof-shard=cuda_01_fetch_baxter_signed_axis_arm_f_e", - "--gpu-proof-shard-fingerprint-paths=external/RBDReference,test/cuda_equivalents/test_cuda_executable_equivalence.py,test/cuda_equivalents/test_cuda_f_ext_contact.py,test/cuda_equivalents/cuda_f_ext_contact_runner.cu,test/cuda_equivalents/test_cuda_f_ext_contact_runtime.py,test/cuda_equivalents/cuda_f_ext_contact_runtime_runner.cu,test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py,test/cuda_equivalents/cuda_f_ext_gradient_runner.cu,test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py,test/cuda_equivalents/test_cuda_fd_du_output_spill.py,test/cuda_equivalents/cuda_equivalence_runner.cu,test/cuda_equivalents/grid_runner_select.cuh,test/cuda_equivalents/test_cuda_fd_parameter_gradient.py,test/cuda_equivalents/cuda_fd_parameter_gradient_smoke_runner.cu,test/cuda_equivalents/test_cuda_fext_equivalence.py,test/cuda_equivalents/test_cuda_floating_values_equivalence.py,test/cuda_equivalents/test_cuda_frame_jacobian.py,test/cuda_equivalents/cuda_frame_jacobian_smoke_runner.cu,test/cuda_equivalents/cuda_harness.py,test/cuda_equivalents/cuda_equivalence_runner.cu,test/cuda_equivalents/grid_runner_select.cuh" - ], - [ "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[go2-fixed]", "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[g1-fixed]", @@ -3141,6 +3390,7 @@ "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[gen3-floating]", "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[fetch-floating]", "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[baxter-floating]", + "test/cuda_equivalents/test_cuda_glass_handoff.py::test_embedded_glass_restricted_handoffs[float-3e-05-3e-05]", "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[iiwa14-world-frame-floating]", "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[iiwa14-world-frame-fixed]", "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[go2-world-frame-floating]", @@ -3204,23 +3454,26 @@ "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_fext_matches_python_reference", "test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py::test_floating_damped_cuda_matches_damped_oracle", "test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py::test_floating_tanh_friction_cuda_matches_tanh_oracle", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch1]", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch16]", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch256]", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch1]", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch16]", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch256]", + "test/cuda_equivalents/test_cuda_joint_transform_suffix.py::test_scalar_transform_composed_suffix[float-7-False]", + "test/cuda_equivalents/test_cuda_joint_transform_suffix.py::test_scalar_transform_composed_suffix[float-7-True]", + "test/cuda_equivalents/test_cuda_joint_transform_suffix.py::test_scalar_transform_composed_suffix[float-32-True]", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/cuda_02_frame_jacobian_hos_idsva_so_worl.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/cuda_04_floating_values_eq_frame_jacobia.xml", "-m", "gpu_proof", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/cuda_02_frame_jacobian_hos_idsva_so_worl.json", - "--gpu-proof-shard=cuda_02_frame_jacobian_hos_idsva_so_worl", - "--gpu-proof-shard-fingerprint-paths=external/RBDReference,test/cuda_equivalents/test_cuda_frame_jacobian_host.py,test/cuda_equivalents/cuda_frame_jacobian_host_runner.cu,test/cuda_equivalents/test_cuda_frame_jacobian.py,test/cuda_equivalents/test_cuda_idsva_so_world_frame.py,test/cuda_equivalents/cuda_idsva_so_world_frame_smoke_runner.cu,test/cuda_equivalents/test_cuda_second_order_fallback.py,test/cuda_equivalents/test_cuda_input_abi.py,test/cuda_equivalents/test_cuda_codegen_layout.py,test/cuda_equivalents/test_cuda_integrator_batch_forces.py,test/cuda_equivalents/cuda_integrator_batch_forces_runner.cu,test/cuda_equivalents/executable_cache.py,test/cuda_equivalents/test_cuda_integrator_equivalence.py,test/cuda_equivalents/test_cuda_integrator_equivalence.py,test/cuda_equivalents/cuda_integrator_smoke_runner.cu,test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py,test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py,test/cuda_equivalents/cuda_harness.py" + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/cuda_04_floating_values_eq_frame_jacobia.json", + "--gpu-proof-shard=cuda_04_floating_values_eq_frame_jacobia", + "--gpu-proof-shard-fingerprint-paths=external/RBDReference,test/cuda_equivalents/test_cuda_floating_values_equivalence.py,test/cuda_equivalents/test_cuda_frame_jacobian.py,test/cuda_equivalents/cuda_frame_jacobian_smoke_runner.cu,test/cuda_equivalents/test_cuda_frame_jacobian_host.py,test/cuda_equivalents/cuda_frame_jacobian_host_runner.cu,test/cuda_equivalents/test_cuda_glass_handoff.py,test/cuda_equivalents/cuda_glass_handoff_runner.cu,test/cuda_equivalents/executable_cache.py,test/cuda_equivalents/test_cuda_idsva_so_world_frame.py,test/cuda_equivalents/cuda_idsva_so_world_frame_smoke_runner.cu,test/cuda_equivalents/test_cuda_second_order_fallback.py,test/cuda_equivalents/test_cuda_input_abi.py,test/cuda_equivalents/test_cuda_codegen_layout.py,test/cuda_equivalents/test_cuda_integrator_batch_forces.py,test/cuda_equivalents/cuda_integrator_batch_forces_runner.cu,test/cuda_equivalents/test_cuda_integrator_equivalence.py,test/cuda_equivalents/test_cuda_integrator_equivalence.py,test/cuda_equivalents/cuda_integrator_smoke_runner.cu,test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py,test/cuda_equivalents/test_cuda_joint_transform_suffix.py,test/cuda_equivalents/cuda_joint_transform_suffix_runner.cu,test/cuda_equivalents/descriptor_oracle.py,test/cuda_equivalents/cuda_harness.py" ], [ + "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch1]", + "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch16]", + "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch256]", + "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch1]", + "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch16]", + "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch256]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[go2-floating-batch1]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[go2-floating-batch16]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[go2-floating-batch256]", @@ -3230,8 +3483,35 @@ "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch1]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch16]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch256]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-zero]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-default]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-wide-x]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-wide-u]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-zero]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-default]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-wide-x]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-wide-u]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-zero]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-default]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-wide-x]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-wide-u]", "test/cuda_equivalents/test_cuda_mjx_tier_invariance.py::test_mjx_second_order_tier_invariance[idsva_so]", "test/cuda_equivalents/test_cuda_mjx_tier_invariance.py::test_mjx_second_order_tier_invariance[fdsva_so]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[constant-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[floating-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[spherical-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[branch-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[empty-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[runtime-fp32]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-mixed_spherical_arm-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-rotated_inertial_arm-True-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-branching_skew_arm-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-helical_arm-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-rotated_inertial_arm-False-True]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-constant-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-zero-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-slider_mimic_fixed_tool-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-fr3-False-False]", "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-iiwa14-fixed]", "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-go2-fixed]", "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-g1-fixed]", @@ -3307,6 +3587,37 @@ "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[go2-floating]", "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[fr3-fixed]", + "-q", + "-rf", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/cuda_05_kinematics_thread__menu_workspac.xml", + "-m", + "gpu_proof", + "--gpu-proof-enable", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/cuda_05_kinematics_thread__menu_workspac.json", + "--gpu-proof-shard=cuda_05_kinematics_thread__menu_workspac", + "--gpu-proof-shard-fingerprint-paths=external/RBDReference,test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py,test/cuda_equivalents/test_cuda_menu_workspace.py,test/cuda_equivalents/cuda_menu_workspace_runner.cu,test/cuda_equivalents/executable_cache.py,test/cuda_equivalents/test_cuda_mjx_tier_invariance.py,test/cuda_equivalents/cuda_mjx_tier_invariance_runner.cu,test/cuda_equivalents/test_cuda_model_descriptor.py,test/cuda_equivalents/cuda_model_descriptor_runner.cu,test/cuda_equivalents/constant_model.py,test/cuda_equivalents/test_cuda_model_primitives.py,test/cuda_equivalents/cuda_model_primitives_runner.cu,test/cuda_equivalents/descriptor_oracle.py,test/cuda_equivalents/manifold_oracle.py,test/cuda_equivalents/test_cuda_momentum_contract.py,test/cuda_equivalents/cuda_momentum_contract_runner.cu,test/cuda_equivalents/test_cuda_integrator_equivalence.py,test/cuda_equivalents/test_cuda_multi_target_position.py,test/cuda_equivalents/cuda_multi_target_position_runner.cu,test/cuda_equivalents/test_cuda_multi_target_position_gradient.py,test/cuda_equivalents/cuda_multi_target_position_gradient_runner.cu,test/cuda_equivalents/test_cuda_multi_target_raw_evaluators.py,test/cuda_equivalents/cuda_multi_target_raw_runner.cu,test/cuda_equivalents/test_cuda_contact_frame_positions.py,test/cuda_equivalents/test_cuda_osc_inertia_spill.py,test/cuda_equivalents/cuda_frame_jacobian_host_runner.cu,test/cuda_equivalents/test_cuda_frame_jacobian.py,test/cuda_equivalents/test_cuda_planar_translation_equivalence.py,test/cuda_equivalents/cuda_equivalence_runner.cu,test/cuda_equivalents/test_cuda_plant_equivalence.py,test/cuda_equivalents/cuda_plant_smoke_runner.cu,test/cuda_equivalents/cuda_harness.py" + ], + [ + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[iiwa14-False-float-False]", + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[go2-True-float-False]", + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[go2-True-float-True]", + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[g1-False-float-False]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-float-full]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-float-spill]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[iiwa14-float-full]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[iiwa14-float-spill]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-semi-runtime-float-full]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-semi-runtime-float-spill]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[branching-runtime-double-full]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[branching-runtime-double-spill]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_fixed_tool_values_and_thread_invariance[False]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_fixed_tool_values_and_thread_invariance[True]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[cardinal-False]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[cardinal-True]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[cardinal-constant]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[skew-False]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[skew-True]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[skew-constant]", "test/cuda_equivalents/test_cuda_racecheck_floating.py::test_go2_floating_composition_is_racecheck_clean", "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[go2-fixed]", @@ -3344,6 +3655,7 @@ "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[gen3-floating]", "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fetch-floating]", "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[baxter-floating]", + "test/cuda_equivalents/test_cuda_revolute_mimic_mechanics.py::test_revolute_mimic_dynamics_against_independent_mechanics", "test/cuda_equivalents/test_cuda_safe_init.py::test_checked_initialization_contract[iiwa14]", "test/cuda_equivalents/test_cuda_safe_init.py::test_checked_initialization_contract[go2]", "test/cuda_equivalents/test_cuda_safe_init.py::test_legacy_policy_is_preserved[iiwa14]", @@ -3398,6 +3710,22 @@ "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_matches_reference[float-mixed_spherical_arm.urdf]", "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_thread_invariant[spherical_arm.urdf]", "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_thread_invariant[mixed_spherical_arm.urdf]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-mixed_spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-single_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-two_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-mixed_spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-single_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-two_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-mixed_spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-single_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-two_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-mixed_spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-single_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-two_spherical]", "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_matches_world_reference[spherical_arm.urdf-float]", "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_matches_world_reference[mixed_spherical_arm.urdf-float]", "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_thread_invariant[spherical_arm.urdf]", @@ -3405,25 +3733,29 @@ "test/cuda_equivalents/test_cuda_vendor_glass_alias.py::test_vendor_glass_false_compiles_against_top_level_glass_and_runs", "test/cuda_equivalents/test_cuda_workspace_slots.py::test_cuda_workspace_slots_bit_identity[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_workspace_slots.py::test_cuda_workspace_slots_bit_identity[go2-floating]", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[pose-hessian-True-float]", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[pose-hessian-False-float]", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[plant-gradient-True-float]", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[plant-gradient-False-float]", "test/cuda_equivalents/test_smem_poison.py::test_equivalence_survives_smem_poison[iiwa14 and fixed]", "test/cuda_equivalents/test_smem_poison.py::test_equivalence_survives_smem_poison[go2 and floating]", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/cuda_03_kinematics_thread__mjx_tier_inva.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/cuda_06_plant_gradient_are_plant_workspa.xml", "-m", "gpu_proof", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/cuda_03_kinematics_thread__mjx_tier_inva.json", - "--gpu-proof-shard=cuda_03_kinematics_thread__mjx_tier_inva", - "--gpu-proof-shard-fingerprint-paths=external/RBDReference,test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py,test/cuda_equivalents/test_cuda_mjx_tier_invariance.py,test/cuda_equivalents/cuda_mjx_tier_invariance_runner.cu,test/cuda_equivalents/test_cuda_momentum_contract.py,test/cuda_equivalents/cuda_momentum_contract_runner.cu,test/cuda_equivalents/test_cuda_integrator_equivalence.py,test/cuda_equivalents/test_cuda_multi_target_position.py,test/cuda_equivalents/cuda_multi_target_position_runner.cu,test/cuda_equivalents/test_cuda_multi_target_position_gradient.py,test/cuda_equivalents/cuda_multi_target_position_gradient_runner.cu,test/cuda_equivalents/test_cuda_multi_target_raw_evaluators.py,test/cuda_equivalents/cuda_multi_target_raw_runner.cu,test/cuda_equivalents/test_cuda_contact_frame_positions.py,test/cuda_equivalents/test_cuda_osc_inertia_spill.py,test/cuda_equivalents/cuda_frame_jacobian_host_runner.cu,test/cuda_equivalents/test_cuda_frame_jacobian.py,test/cuda_equivalents/test_cuda_planar_translation_equivalence.py,test/cuda_equivalents/cuda_equivalence_runner.cu,test/cuda_equivalents/test_cuda_plant_equivalence.py,test/cuda_equivalents/cuda_plant_smoke_runner.cu,test/cuda_equivalents/executable_cache.py,test/cuda_equivalents/test_cuda_racecheck_floating.py,test/cuda_equivalents/cuda_racecheck_floating_runner.cu,test/cuda_equivalents/test_cuda_regressor.py,test/cuda_equivalents/cuda_regressor_smoke_runner.cu,test/cuda_equivalents/test_cuda_regressor_gradient.py,test/cuda_equivalents/cuda_regressor_gradient_runner.cu,test/cuda_equivalents/test_cuda_safe_init.py,test/cuda_equivalents/cuda_safe_init_runner.cu,test/cuda_equivalents/test_cuda_second_order_fallback.py,test/cuda_equivalents/cuda_second_order_smoke_runner.cu,test/cuda_equivalents/test_cuda_spherical_eepose_gradhess_equivalence.py,test/cuda_equivalents/cuda_spherical_eepose_grad_hess_runner.cu,test/cuda_equivalents/test_cuda_spherical_equivalence.py,test/cuda_equivalents/cuda_spherical_runner.cu,test/cuda_equivalents/grid_runner_select.cuh,test/cuda_equivalents/test_cuda_spherical_fdsva_so_equivalence.py,test/cuda_equivalents/cuda_spherical_fdsva_so_runner.cu,test/cuda_equivalents/test_cuda_spherical_integrator_equivalence.py,test/cuda_equivalents/cuda_spherical_integrator_runner.cu,test/cuda_equivalents/test_cuda_spherical_integrator_gradient_equivalence.py,test/cuda_equivalents/cuda_spherical_integrator_gradient_runner.cu,test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py,test/cuda_equivalents/cuda_spherical_kinematics_runner.cu,test/cuda_equivalents/test_cuda_spherical_so_equivalence.py,test/cuda_equivalents/cuda_spherical_so_runner.cu,test/cuda_equivalents/test_cuda_vendor_glass_alias.py,test/cuda_equivalents/test_cuda_workspace_slots.py,test/cuda_equivalents/cuda_workspace_slots_runner.cu,test/cuda_equivalents/test_smem_poison.py,test/cuda_equivalents/cuda_harness.py" + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/cuda_06_plant_gradient_are_plant_workspa.json", + "--gpu-proof-shard=cuda_06_plant_gradient_are_plant_workspa", + "--gpu-proof-shard-fingerprint-paths=external/RBDReference,test/cuda_equivalents/test_cuda_plant_gradient_arena.py,test/cuda_equivalents/executable_cache.py,test/cuda_equivalents/test_cuda_plant_workspace.py,test/cuda_equivalents/cuda_plant_workspace_runner.cu,test/cuda_equivalents/test_cuda_prismatic_fixed_target.py,test/cuda_equivalents/cuda_prismatic_fixed_target_runner.cu,test/cuda_equivalents/test_cuda_racecheck_floating.py,test/cuda_equivalents/cuda_racecheck_floating_runner.cu,test/cuda_equivalents/test_cuda_regressor.py,test/cuda_equivalents/cuda_regressor_smoke_runner.cu,test/cuda_equivalents/test_cuda_regressor_gradient.py,test/cuda_equivalents/cuda_regressor_gradient_runner.cu,test/cuda_equivalents/test_cuda_revolute_mimic_mechanics.py,test/cuda_equivalents/test_cuda_safe_init.py,test/cuda_equivalents/cuda_safe_init_runner.cu,test/cuda_equivalents/test_cuda_second_order_fallback.py,test/cuda_equivalents/cuda_second_order_smoke_runner.cu,test/cuda_equivalents/test_cuda_spherical_eepose_gradhess_equivalence.py,test/cuda_equivalents/cuda_spherical_eepose_grad_hess_runner.cu,test/cuda_equivalents/test_cuda_spherical_equivalence.py,test/cuda_equivalents/cuda_spherical_runner.cu,test/cuda_equivalents/grid_runner_select.cuh,test/cuda_equivalents/test_cuda_spherical_fdsva_so_equivalence.py,test/cuda_equivalents/cuda_spherical_fdsva_so_runner.cu,test/cuda_equivalents/test_cuda_spherical_integrator_equivalence.py,test/cuda_equivalents/cuda_spherical_integrator_runner.cu,test/cuda_equivalents/test_cuda_spherical_integrator_gradient_equivalence.py,test/cuda_equivalents/cuda_spherical_integrator_gradient_runner.cu,test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py,test/cuda_equivalents/cuda_spherical_kinematics_runner.cu,test/cuda_equivalents/test_cuda_spherical_plant_hessian.py,test/cuda_equivalents/cuda_spherical_plant_hessian_runner.cu,test/cuda_equivalents/manifold_oracle.py,test/cuda_equivalents/test_cuda_spherical_so_equivalence.py,test/cuda_equivalents/cuda_spherical_so_runner.cu,test/cuda_equivalents/test_cuda_vendor_glass_alias.py,test/cuda_equivalents/test_cuda_workspace_slots.py,test/cuda_equivalents/cuda_workspace_slots_runner.cu,test/cuda_equivalents/test_cuda_zero_dof_surfaces.py,test/cuda_equivalents/constant_model.py,test/cuda_equivalents/test_smem_poison.py,test/cuda_equivalents/cuda_harness.py" ], [ "test/python_wrappers/test_any_thread_count.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_any_thread_count.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_any_thread_count.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_any_thread_count.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_any_thread_count.json", "--gpu-proof-shard=test_any_thread_count", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_any_thread_count.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3431,9 +3763,9 @@ "test/python_wrappers/test_batch_switch.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_batch_switch.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_batch_switch.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_batch_switch.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_batch_switch.json", "--gpu-proof-shard=test_batch_switch", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_batch_switch.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3441,9 +3773,9 @@ "test/python_wrappers/test_centroidal_energy_frame.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_centroidal_energy_frame.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_centroidal_energy_frame.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_centroidal_energy_frame.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_centroidal_energy_frame.json", "--gpu-proof-shard=test_centroidal_energy_frame", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_centroidal_energy_frame.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3451,19 +3783,19 @@ "test/python_wrappers/test_consumer_artifact.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_consumer_artifact.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_consumer_artifact.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_consumer_artifact.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_consumer_artifact.json", "--gpu-proof-shard=test_consumer_artifact", - "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_consumer_artifact.py,bindings/grid_rbd,bindings/src,external/RBDReference" + "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_consumer_artifact.py,bindings/grid_rbd,bindings/src,external/RBDReference,bindings/examples/consumer_artifact_migration.py" ], [ "test/python_wrappers/test_contact_fext.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_contact_fext.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_contact_fext.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_contact_fext.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_contact_fext.json", "--gpu-proof-shard=test_contact_fext", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_contact_fext.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3471,9 +3803,9 @@ "test/python_wrappers/test_ee_named_target_floating_multileaf.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_ee_named_target_floating_multileaf.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_ee_named_target_floating_multileaf.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_ee_named_target_floating_multileaf.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_ee_named_target_floating_multileaf.json", "--gpu-proof-shard=test_ee_named_target_floating_multileaf", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_ee_named_target_floating_multileaf.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3481,9 +3813,9 @@ "test/python_wrappers/test_f_ext_backward_and_shapes.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_f_ext_backward_and_shapes.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_f_ext_backward_and_shapes.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_f_ext_backward_and_shapes.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_f_ext_backward_and_shapes.json", "--gpu-proof-shard=test_f_ext_backward_and_shapes", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_f_ext_backward_and_shapes.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3491,9 +3823,9 @@ "test/python_wrappers/test_fk_batched.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_fk_batched.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_fk_batched.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_fk_batched.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_fk_batched.json", "--gpu-proof-shard=test_fk_batched", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_fk_batched.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3501,9 +3833,9 @@ "test/python_wrappers/test_floating_q_cotangent.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_floating_q_cotangent.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_floating_q_cotangent.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_floating_q_cotangent.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_floating_q_cotangent.json", "--gpu-proof-shard=test_floating_q_cotangent", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_floating_q_cotangent.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3511,9 +3843,9 @@ "test/python_wrappers/test_fp64_parity.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_fp64_parity.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_fp64_parity.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_fp64_parity.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_fp64_parity.json", "--gpu-proof-shard=test_fp64_parity", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_fp64_parity.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3521,9 +3853,9 @@ "test/python_wrappers/test_g1_plant_hessian_smoke.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_g1_plant_hessian_smoke.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_g1_plant_hessian_smoke.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_g1_plant_hessian_smoke.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_g1_plant_hessian_smoke.json", "--gpu-proof-shard=test_g1_plant_hessian_smoke", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_g1_plant_hessian_smoke.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3531,9 +3863,9 @@ "test/python_wrappers/test_iiwa14_f_ext.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_iiwa14_f_ext.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_iiwa14_f_ext.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_iiwa14_f_ext.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_iiwa14_f_ext.json", "--gpu-proof-shard=test_iiwa14_f_ext", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_iiwa14_f_ext.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3541,9 +3873,9 @@ "test/python_wrappers/test_iiwa14_jax_smoke.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_iiwa14_jax_smoke.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_iiwa14_jax_smoke.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_iiwa14_jax_smoke.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_iiwa14_jax_smoke.json", "--gpu-proof-shard=test_iiwa14_jax_smoke", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_iiwa14_jax_smoke.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3551,9 +3883,9 @@ "test/python_wrappers/test_iiwa14_plant_smoke.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_iiwa14_plant_smoke.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_iiwa14_plant_smoke.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_iiwa14_plant_smoke.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_iiwa14_plant_smoke.json", "--gpu-proof-shard=test_iiwa14_plant_smoke", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_iiwa14_plant_smoke.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3561,9 +3893,9 @@ "test/python_wrappers/test_iiwa14_qdd_aware_grad.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_iiwa14_qdd_aware_grad.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_iiwa14_qdd_aware_grad.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_iiwa14_qdd_aware_grad.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_iiwa14_qdd_aware_grad.json", "--gpu-proof-shard=test_iiwa14_qdd_aware_grad", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_iiwa14_qdd_aware_grad.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3571,9 +3903,9 @@ "test/python_wrappers/test_iiwa14_smoke.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_iiwa14_smoke.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_iiwa14_smoke.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_iiwa14_smoke.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_iiwa14_smoke.json", "--gpu-proof-shard=test_iiwa14_smoke", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_iiwa14_smoke.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3581,9 +3913,9 @@ "test/python_wrappers/test_iiwa14_torch_smoke.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_iiwa14_torch_smoke.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_iiwa14_torch_smoke.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_iiwa14_torch_smoke.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_iiwa14_torch_smoke.json", "--gpu-proof-shard=test_iiwa14_torch_smoke", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_iiwa14_torch_smoke.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3591,9 +3923,9 @@ "test/python_wrappers/test_jax_floating_input_widths.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_jax_floating_input_widths.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_jax_floating_input_widths.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_jax_floating_input_widths.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_jax_floating_input_widths.json", "--gpu-proof-shard=test_jax_floating_input_widths", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_jax_floating_input_widths.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3601,9 +3933,9 @@ "test/python_wrappers/test_jax_host_transfer.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_jax_host_transfer.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_jax_host_transfer.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_jax_host_transfer.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_jax_host_transfer.json", "--gpu-proof-shard=test_jax_host_transfer", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_jax_host_transfer.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3611,9 +3943,9 @@ "test/python_wrappers/test_joint_dynamics.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_joint_dynamics.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_joint_dynamics.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_joint_dynamics.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_joint_dynamics.json", "--gpu-proof-shard=test_joint_dynamics", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_joint_dynamics.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3621,9 +3953,9 @@ "test/python_wrappers/test_joint_dynamics_ffi.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_joint_dynamics_ffi.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_joint_dynamics_ffi.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_joint_dynamics_ffi.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_joint_dynamics_ffi.json", "--gpu-proof-shard=test_joint_dynamics_ffi", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_joint_dynamics_ffi.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3631,9 +3963,9 @@ "test/python_wrappers/test_kinematics_thread_batch_matrix.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_kinematics_thread_batch_matrix.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_kinematics_thread_batch_matrix.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_kinematics_thread_batch_matrix.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_kinematics_thread_batch_matrix.json", "--gpu-proof-shard=test_kinematics_thread_batch_matrix", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_kinematics_thread_batch_matrix.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3641,9 +3973,9 @@ "test/python_wrappers/test_mjx_fixed_base_noop.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_mjx_fixed_base_noop.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_mjx_fixed_base_noop.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_mjx_fixed_base_noop.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_mjx_fixed_base_noop.json", "--gpu-proof-shard=test_mjx_fixed_base_noop", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_mjx_fixed_base_noop.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3651,9 +3983,9 @@ "test/python_wrappers/test_mjx_twins_contexts.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_mjx_twins_contexts.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_mjx_twins_contexts.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_mjx_twins_contexts.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_mjx_twins_contexts.json", "--gpu-proof-shard=test_mjx_twins_contexts", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_mjx_twins_contexts.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3661,9 +3993,9 @@ "test/python_wrappers/test_numpy_host_transfer.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_numpy_host_transfer.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_numpy_host_transfer.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_numpy_host_transfer.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_numpy_host_transfer.json", "--gpu-proof-shard=test_numpy_host_transfer", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_numpy_host_transfer.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3671,9 +4003,9 @@ "test/python_wrappers/test_numpy_vs_async_framework_race.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_numpy_vs_async_framework_race.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_numpy_vs_async_framework_race.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_numpy_vs_async_framework_race.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_numpy_vs_async_framework_race.json", "--gpu-proof-shard=test_numpy_vs_async_framework_race", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_numpy_vs_async_framework_race.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3681,9 +4013,9 @@ "test/python_wrappers/test_operand_validation.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_operand_validation.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_operand_validation.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_operand_validation.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_operand_validation.json", "--gpu-proof-shard=test_operand_validation", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_operand_validation.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3691,9 +4023,9 @@ "test/python_wrappers/test_ps5_value_ops.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_ps5_value_ops.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_ps5_value_ops.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_ps5_value_ops.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_ps5_value_ops.json", "--gpu-proof-shard=test_ps5_value_ops", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_ps5_value_ops.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3701,9 +4033,9 @@ "test/python_wrappers/test_release_numerics.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_release_numerics.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_release_numerics.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_release_numerics.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_release_numerics.json", "--gpu-proof-shard=test_release_numerics", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_release_numerics.py,bindings/grid_rbd,bindings/src,external/RBDReference,tools/check_installed_numerics.py" ], @@ -3711,9 +4043,9 @@ "test/python_wrappers/test_runtime_contexts.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_runtime_contexts.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_runtime_contexts.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_runtime_contexts.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_runtime_contexts.json", "--gpu-proof-shard=test_runtime_contexts", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_runtime_contexts.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3721,9 +4053,9 @@ "test/python_wrappers/test_runtime_inertia.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_runtime_inertia.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_runtime_inertia.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_runtime_inertia.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_runtime_inertia.json", "--gpu-proof-shard=test_runtime_inertia", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_runtime_inertia.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3731,9 +4063,9 @@ "test/python_wrappers/test_runtime_joint_dynamics.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_runtime_joint_dynamics.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_runtime_joint_dynamics.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_runtime_joint_dynamics.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_runtime_joint_dynamics.json", "--gpu-proof-shard=test_runtime_joint_dynamics", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_runtime_joint_dynamics.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3741,9 +4073,9 @@ "test/python_wrappers/test_runtime_transform.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_runtime_transform.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_runtime_transform.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_runtime_transform.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_runtime_transform.json", "--gpu-proof-shard=test_runtime_transform", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_runtime_transform.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3751,9 +4083,9 @@ "test/python_wrappers/test_shared_runtime_lifetime.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_shared_runtime_lifetime.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_shared_runtime_lifetime.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_shared_runtime_lifetime.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_shared_runtime_lifetime.json", "--gpu-proof-shard=test_shared_runtime_lifetime", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_shared_runtime_lifetime.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3761,9 +4093,9 @@ "test/python_wrappers/test_subset_build.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_subset_build.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_subset_build.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_subset_build.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_subset_build.json", "--gpu-proof-shard=test_subset_build", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_subset_build.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3771,9 +4103,9 @@ "test/python_wrappers/test_subset_build_jax.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_subset_build_jax.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_subset_build_jax.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_subset_build_jax.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_subset_build_jax.json", "--gpu-proof-shard=test_subset_build_jax", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_subset_build_jax.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3781,9 +4113,9 @@ "test/python_wrappers/test_subset_build_torch.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_subset_build_torch.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_subset_build_torch.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_subset_build_torch.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_subset_build_torch.json", "--gpu-proof-shard=test_subset_build_torch", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_subset_build_torch.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3791,9 +4123,9 @@ "test/python_wrappers/test_tool.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_tool.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_tool.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_tool.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_tool.json", "--gpu-proof-shard=test_tool", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_tool.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3801,9 +4133,9 @@ "test/python_wrappers/test_torch_host_transfer.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_torch_host_transfer.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_torch_host_transfer.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_torch_host_transfer.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_torch_host_transfer.json", "--gpu-proof-shard=test_torch_host_transfer", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_torch_host_transfer.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3811,9 +4143,9 @@ "test/python_wrappers/test_two_robot_process.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_two_robot_process.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_two_robot_process.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_two_robot_process.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_two_robot_process.json", "--gpu-proof-shard=test_two_robot_process", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_two_robot_process.py,bindings/grid_rbd,bindings/src,external/RBDReference" ], @@ -3821,337 +4153,358 @@ "test/python_wrappers/test_workspace_slots_direct_launch.py", "-q", "-rf", - "--junitxml=test/.split_suite/receipt_20261024_045657/test_workspace_slots_direct_launch.xml", + "--junitxml=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/test_workspace_slots_direct_launch.xml", "--gpu-proof-enable", - "--gpu-proof-out=test/.split_suite/receipt_20261024_045657/receipts/test_workspace_slots_direct_launch.json", + "--gpu-proof-out=/home/plancher/Desktop/GRiD/test/.split_suite/receipt_20261101_c1_pass2/shards/receipts/test_workspace_slots_direct_launch.json", "--gpu-proof-shard=test_workspace_slots_direct_launch", "--gpu-proof-shard-fingerprint-paths=test/python_wrappers/test_workspace_slots_direct_launch.py,bindings/grid_rbd,bindings/src,external/RBDReference" ] ], "shards": [ { - "ended_at": "2026-10-24T11:05:59Z", - "node_count": 888, + "ended_at": "2026-11-01T08:23:04Z", + "node_count": 106, "signer": "plancherb1", "source": "cuda_00_batched_host_wrapp_branching_ske.json", - "started_at": "2026-10-24T10:07:42Z" + "started_at": "2026-11-01T08:16:32Z" + }, + { + "ended_at": "2026-11-01T08:25:23Z", + "node_count": 57, + "signer": "plancherb1", + "source": "cuda_01_collision_warp_constant_branch_m.json", + "started_at": "2026-11-01T08:23:09Z" + }, + { + "ended_at": "2026-11-01T09:17:08Z", + "node_count": 709, + "signer": "plancherb1", + "source": "cuda_02_constant_second_or_constant_velo.json", + "started_at": "2026-11-01T08:23:14Z" + }, + { + "ended_at": "2026-11-01T10:02:10Z", + "node_count": 286, + "signer": "plancherb1", + "source": "cuda_03_rizon4_gen3_fetch_baxter_signed_.json", + "started_at": "2026-11-01T08:25:25Z" }, { - "ended_at": "2026-10-24T12:44:56Z", - "node_count": 229, + "ended_at": "2026-11-01T10:44:37Z", + "node_count": 112, "signer": "plancherb1", - "source": "cuda_01_fetch_baxter_signed_axis_arm_f_e.json", - "started_at": "2026-10-24T11:06:03Z" + "source": "cuda_04_floating_values_eq_frame_jacobia.json", + "started_at": "2026-11-01T09:17:09Z" }, { - "ended_at": "2026-10-24T14:14:59Z", - "node_count": 87, + "ended_at": "2026-11-01T10:55:48Z", + "node_count": 119, "signer": "plancherb1", - "source": "cuda_02_frame_jacobian_hos_idsva_so_worl.json", - "started_at": "2026-10-24T12:45:01Z" + "source": "cuda_05_kinematics_thread__menu_workspac.json", + "started_at": "2026-11-01T10:02:15Z" }, { - "ended_at": "2026-10-24T15:01:45Z", - "node_count": 186, + "ended_at": "2026-11-01T11:03:25Z", + "node_count": 141, "signer": "plancherb1", - "source": "cuda_03_kinematics_thread__mjx_tier_inva.json", - "started_at": "2026-10-24T14:15:03Z" + "source": "cuda_06_plant_gradient_are_plant_workspa.json", + "started_at": "2026-11-01T10:44:42Z" }, { - "ended_at": "2026-10-24T08:57:10Z", + "ended_at": "2026-11-01T07:32:25Z", "node_count": 39, "signer": "plancherb1", "source": "test_any_thread_count.json", - "started_at": "2026-10-24T08:57:08Z" + "started_at": "2026-11-01T07:32:23Z" }, { - "ended_at": "2026-10-24T08:57:15Z", + "ended_at": "2026-11-01T07:32:25Z", "node_count": 5, "signer": "plancherb1", "source": "test_batch_switch.json", - "started_at": "2026-10-24T08:57:13Z" + "started_at": "2026-11-01T07:32:23Z" }, { - "ended_at": "2026-10-24T08:57:21Z", + "ended_at": "2026-11-01T07:32:31Z", "node_count": 19, "signer": "plancherb1", "source": "test_centroidal_energy_frame.json", - "started_at": "2026-10-24T08:57:18Z" + "started_at": "2026-11-01T07:32:29Z" }, { - "ended_at": "2026-10-24T08:57:36Z", - "node_count": 7, + "ended_at": "2026-11-01T07:33:25Z", + "node_count": 21, "signer": "plancherb1", "source": "test_consumer_artifact.json", - "started_at": "2026-10-24T08:57:23Z" + "started_at": "2026-11-01T07:32:29Z" }, { - "ended_at": "2026-10-24T08:57:41Z", + "ended_at": "2026-11-01T07:32:36Z", "node_count": 2, "signer": "plancherb1", "source": "test_contact_fext.json", - "started_at": "2026-10-24T08:57:38Z" + "started_at": "2026-11-01T07:32:34Z" }, { - "ended_at": "2026-10-24T08:57:53Z", + "ended_at": "2026-11-01T07:32:49Z", "node_count": 13, "signer": "plancherb1", "source": "test_ee_named_target_floating_multileaf.json", - "started_at": "2026-10-24T08:57:43Z" + "started_at": "2026-11-01T07:32:39Z" }, { - "ended_at": "2026-10-24T08:58:03Z", + "ended_at": "2026-11-01T07:32:58Z", "node_count": 6, "signer": "plancherb1", "source": "test_f_ext_backward_and_shapes.json", - "started_at": "2026-10-24T08:57:59Z" + "started_at": "2026-11-01T07:32:54Z" }, { - "ended_at": "2026-10-24T08:58:12Z", + "ended_at": "2026-11-01T07:33:07Z", "node_count": 8, "signer": "plancherb1", "source": "test_fk_batched.json", - "started_at": "2026-10-24T08:58:09Z" + "started_at": "2026-11-01T07:33:04Z" }, { - "ended_at": "2026-10-24T08:58:34Z", + "ended_at": "2026-11-01T07:33:34Z", "node_count": 24, "signer": "plancherb1", "source": "test_floating_q_cotangent.json", - "started_at": "2026-10-24T08:58:19Z" + "started_at": "2026-11-01T07:33:14Z" }, { - "ended_at": "2026-10-24T08:58:41Z", + "ended_at": "2026-11-01T07:33:31Z", "node_count": 6, "signer": "plancherb1", "source": "test_fp64_parity.json", - "started_at": "2026-10-24T08:58:39Z" + "started_at": "2026-11-01T07:33:29Z" }, { - "ended_at": "2026-10-24T08:58:46Z", + "ended_at": "2026-11-01T07:33:37Z", "node_count": 2, "signer": "plancherb1", "source": "test_g1_plant_hessian_smoke.json", - "started_at": "2026-10-24T08:58:44Z" + "started_at": "2026-11-01T07:33:34Z" }, { - "ended_at": "2026-10-24T08:58:51Z", + "ended_at": "2026-11-01T07:33:42Z", "node_count": 8, "signer": "plancherb1", "source": "test_iiwa14_f_ext.json", - "started_at": "2026-10-24T08:58:49Z" + "started_at": "2026-11-01T07:33:39Z" }, { - "ended_at": "2026-10-24T08:58:58Z", + "ended_at": "2026-11-01T07:33:43Z", "node_count": 60, "signer": "plancherb1", "source": "test_iiwa14_jax_smoke.json", - "started_at": "2026-10-24T08:58:54Z" + "started_at": "2026-11-01T07:33:39Z" }, { - "ended_at": "2026-10-24T08:59:05Z", + "ended_at": "2026-11-01T07:33:46Z", "node_count": 11, "signer": "plancherb1", "source": "test_iiwa14_plant_smoke.json", - "started_at": "2026-10-24T08:59:04Z" + "started_at": "2026-11-01T07:33:44Z" }, { - "ended_at": "2026-10-24T09:01:12Z", + "ended_at": "2026-11-01T07:36:01Z", "node_count": 8, "signer": "plancherb1", "source": "test_iiwa14_qdd_aware_grad.json", - "started_at": "2026-10-24T08:59:09Z" + "started_at": "2026-11-01T07:33:49Z" }, { - "ended_at": "2026-10-24T09:01:21Z", + "ended_at": "2026-11-01T07:33:51Z", "node_count": 30, "signer": "plancherb1", "source": "test_iiwa14_smoke.json", - "started_at": "2026-10-24T09:01:19Z" + "started_at": "2026-11-01T07:33:49Z" }, { - "ended_at": "2026-10-24T09:01:27Z", + "ended_at": "2026-11-01T07:33:57Z", "node_count": 25, "signer": "plancherb1", "source": "test_iiwa14_torch_smoke.json", - "started_at": "2026-10-24T09:01:24Z" + "started_at": "2026-11-01T07:33:54Z" }, { - "ended_at": "2026-10-24T09:01:36Z", + "ended_at": "2026-11-01T07:34:03Z", "node_count": 32, "signer": "plancherb1", "source": "test_jax_floating_input_widths.json", - "started_at": "2026-10-24T09:01:29Z" + "started_at": "2026-11-01T07:33:59Z" }, { - "ended_at": "2026-10-24T09:01:41Z", + "ended_at": "2026-11-01T07:34:11Z", "node_count": 6, "signer": "plancherb1", "source": "test_jax_host_transfer.json", - "started_at": "2026-10-24T09:01:39Z" + "started_at": "2026-11-01T07:34:09Z" }, { - "ended_at": "2026-10-24T09:22:27Z", + "ended_at": "2026-11-01T08:01:03Z", "node_count": 15, "signer": "plancherb1", "source": "test_joint_dynamics.json", - "started_at": "2026-10-24T09:01:44Z" + "started_at": "2026-11-01T07:34:14Z" }, { - "ended_at": "2026-10-24T09:32:54Z", + "ended_at": "2026-11-01T07:50:49Z", "node_count": 15, "signer": "plancherb1", "source": "test_joint_dynamics_ffi.json", - "started_at": "2026-10-24T09:22:33Z" + "started_at": "2026-11-01T07:36:05Z" }, { - "ended_at": "2026-10-24T09:33:07Z", + "ended_at": "2026-11-01T07:51:06Z", "node_count": 40, "signer": "plancherb1", "source": "test_kinematics_thread_batch_matrix.json", - "started_at": "2026-10-24T09:32:55Z" + "started_at": "2026-11-01T07:50:51Z" }, { - "ended_at": "2026-10-24T09:33:46Z", + "ended_at": "2026-11-01T07:52:18Z", "node_count": 2, "signer": "plancherb1", "source": "test_mjx_fixed_base_noop.json", - "started_at": "2026-10-24T09:33:10Z" + "started_at": "2026-11-01T07:51:11Z" }, { - "ended_at": "2026-10-24T09:33:53Z", + "ended_at": "2026-11-01T07:52:24Z", "node_count": 6, "signer": "plancherb1", "source": "test_mjx_twins_contexts.json", - "started_at": "2026-10-24T09:33:50Z" + "started_at": "2026-11-01T07:52:22Z" }, { - "ended_at": "2026-10-24T09:33:57Z", + "ended_at": "2026-11-01T07:52:28Z", "node_count": 12, "signer": "plancherb1", "source": "test_numpy_host_transfer.json", - "started_at": "2026-10-24T09:33:55Z" + "started_at": "2026-11-01T07:52:27Z" }, { - "ended_at": "2026-10-24T09:34:03Z", + "ended_at": "2026-11-01T07:52:34Z", "node_count": 1, "signer": "plancherb1", "source": "test_numpy_vs_async_framework_race.json", - "started_at": "2026-10-24T09:34:00Z" + "started_at": "2026-11-01T07:52:32Z" }, { - "ended_at": "2026-10-24T09:34:09Z", + "ended_at": "2026-11-01T07:52:39Z", "node_count": 8, "signer": "plancherb1", "source": "test_operand_validation.json", - "started_at": "2026-10-24T09:34:06Z" + "started_at": "2026-11-01T07:52:37Z" }, { - "ended_at": "2026-10-24T09:37:44Z", + "ended_at": "2026-11-01T07:57:27Z", "node_count": 6, "signer": "plancherb1", "source": "test_ps5_value_ops.json", - "started_at": "2026-10-24T09:34:11Z" + "started_at": "2026-11-01T07:52:42Z" }, { - "ended_at": "2026-10-24T09:37:53Z", + "ended_at": "2026-11-01T07:57:42Z", "node_count": 2, "signer": "plancherb1", "source": "test_release_numerics.json", - "started_at": "2026-10-24T09:37:46Z" + "started_at": "2026-11-01T07:57:34Z" }, { - "ended_at": "2026-10-24T09:38:04Z", + "ended_at": "2026-11-01T07:57:57Z", "node_count": 17, "signer": "plancherb1", "source": "test_runtime_contexts.json", - "started_at": "2026-10-24T09:37:56Z" + "started_at": "2026-11-01T07:57:49Z" }, { - "ended_at": "2026-10-24T09:39:52Z", + "ended_at": "2026-11-01T08:00:12Z", "node_count": 9, "signer": "plancherb1", "source": "test_runtime_inertia.json", - "started_at": "2026-10-24T09:38:06Z" + "started_at": "2026-11-01T07:57:59Z" }, { - "ended_at": "2026-10-24T09:56:14Z", + "ended_at": "2026-11-01T08:23:09Z", "node_count": 9, "signer": "plancherb1", "source": "test_runtime_joint_dynamics.json", - "started_at": "2026-10-24T09:39:57Z" + "started_at": "2026-11-01T08:00:15Z" }, { - "ended_at": "2026-10-24T10:00:06Z", + "ended_at": "2026-11-01T08:05:46Z", "node_count": 5, "signer": "plancherb1", "source": "test_runtime_transform.json", - "started_at": "2026-10-24T09:56:20Z" + "started_at": "2026-11-01T08:01:05Z" }, { - "ended_at": "2026-10-24T10:00:13Z", + "ended_at": "2026-11-01T08:05:56Z", "node_count": 6, "signer": "plancherb1", "source": "test_shared_runtime_lifetime.json", - "started_at": "2026-10-24T10:00:11Z" + "started_at": "2026-11-01T08:05:52Z" }, { - "ended_at": "2026-10-24T10:02:33Z", + "ended_at": "2026-11-01T08:09:17Z", "node_count": 13, "signer": "plancherb1", "source": "test_subset_build.json", - "started_at": "2026-10-24T10:00:16Z" + "started_at": "2026-11-01T08:05:57Z" }, { - "ended_at": "2026-10-24T10:04:55Z", + "ended_at": "2026-11-01T08:12:40Z", "node_count": 12, "signer": "plancherb1", "source": "test_subset_build_jax.json", - "started_at": "2026-10-24T10:02:36Z" + "started_at": "2026-11-01T08:09:19Z" }, { - "ended_at": "2026-10-24T10:07:15Z", + "ended_at": "2026-11-01T08:16:05Z", "node_count": 11, "signer": "plancherb1", "source": "test_subset_build_torch.json", - "started_at": "2026-10-24T10:04:57Z" + "started_at": "2026-11-01T08:12:45Z" }, { - "ended_at": "2026-10-24T10:07:19Z", + "ended_at": "2026-11-01T08:16:08Z", "node_count": 6, "signer": "plancherb1", "source": "test_tool.json", - "started_at": "2026-10-24T10:07:17Z" + "started_at": "2026-11-01T08:16:07Z" }, { - "ended_at": "2026-10-24T10:07:23Z", + "ended_at": "2026-11-01T08:16:13Z", "node_count": 3, "signer": "plancherb1", "source": "test_torch_host_transfer.json", - "started_at": "2026-10-24T10:07:22Z" + "started_at": "2026-11-01T08:16:12Z" }, { - "ended_at": "2026-10-24T10:07:29Z", + "ended_at": "2026-11-01T08:16:19Z", "node_count": 1, "signer": "plancherb1", "source": "test_two_robot_process.json", - "started_at": "2026-10-24T10:07:27Z" + "started_at": "2026-11-01T08:16:17Z" }, { - "ended_at": "2026-10-24T10:07:40Z", + "ended_at": "2026-11-01T08:16:31Z", "node_count": 2, "signer": "plancherb1", "source": "test_workspace_slots_direct_launch.json", - "started_at": "2026-10-24T10:07:32Z" + "started_at": "2026-11-01T08:16:22Z" } ], - "started_at": "2026-10-24T08:57:08Z" + "started_at": "2026-11-01T07:32:23Z" }, "shards": [ { "carried": null, "dirty": false, - "ended_at": "2026-10-24T11:05:59Z", + "ended_at": "2026-11-01T08:23:04Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -4167,14 +4520,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "3c2a8c528c05b19fff67cce4c5627e8653cb58014bdeec37b425a690f861e689", + "digest": "a0e189a454ecb16b7e068ac4c15eb46f4d3391661dd107ec8134ad73ed595856", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -4184,7 +4538,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 59, + "file_count": 39, "included_paths": [ "external/RBDReference", "test/cuda_equivalents/cuda_batched_host_runner.cu", @@ -4198,25 +4552,16 @@ "test/cuda_equivalents/cuda_collision_environment_runner.cu", "test/cuda_equivalents/cuda_collision_geometry_runner.cu", "test/cuda_equivalents/cuda_collision_many_hits_runner.cu", + "test/cuda_equivalents/cuda_collision_margins_runner.cu", + "test/cuda_equivalents/cuda_collision_native_margins_runner.cu", "test/cuda_equivalents/cuda_collision_native_runner.cu", "test/cuda_equivalents/cuda_collision_native_selfcc_runner.cu", "test/cuda_equivalents/cuda_collision_pairs_runner.cu", "test/cuda_equivalents/cuda_collision_self_collision_runner.cu", "test/cuda_equivalents/cuda_collision_selfcc_rows_runner.cu", "test/cuda_equivalents/cuda_collision_two_tier_runner.cu", - "test/cuda_equivalents/cuda_collision_warp_runner.cu", - "test/cuda_equivalents/cuda_contact_frame_positions_runner.cu", - "test/cuda_equivalents/cuda_coriolis_smoke_runner.cu", - "test/cuda_equivalents/cuda_dccrba_smoke_runner.cu", - "test/cuda_equivalents/cuda_ee_named_depth1_runner.cu", - "test/cuda_equivalents/cuda_ee_newton_hessian_runner.cu", - "test/cuda_equivalents/cuda_ee_psd_clamp_runner.cu", - "test/cuda_equivalents/cuda_eepose_runtime_smoke_runner.cu", - "test/cuda_equivalents/cuda_energy_regressor_smoke_runner.cu", - "test/cuda_equivalents/cuda_equivalence_runner.cu", "test/cuda_equivalents/cuda_harness.py", "test/cuda_equivalents/executable_cache.py", - "test/cuda_equivalents/grid_runner_select.cuh", "test/cuda_equivalents/test_cuda_batched_host_wrapper.py", "test/cuda_equivalents/test_cuda_branching_skew_equivalence.py", "test/cuda_equivalents/test_cuda_centroidal_equivalence.py", @@ -4228,23 +4573,12 @@ "test/cuda_equivalents/test_cuda_collision_environment.py", "test/cuda_equivalents/test_cuda_collision_geometry.py", "test/cuda_equivalents/test_cuda_collision_many_hits.py", + "test/cuda_equivalents/test_cuda_collision_margins.py", "test/cuda_equivalents/test_cuda_collision_native.py", "test/cuda_equivalents/test_cuda_collision_pairs.py", "test/cuda_equivalents/test_cuda_collision_selfcc_rows.py", "test/cuda_equivalents/test_cuda_collision_two_tier.py", - "test/cuda_equivalents/test_cuda_collision_warp.py", - "test/cuda_equivalents/test_cuda_contact_frame_positions.py", - "test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py", - "test/cuda_equivalents/test_cuda_coriolis.py", - "test/cuda_equivalents/test_cuda_coriolis_output_spill.py", - "test/cuda_equivalents/test_cuda_dccrba.py", - "test/cuda_equivalents/test_cuda_ee_named_depth1.py", - "test/cuda_equivalents/test_cuda_ee_newton_hessian.py", - "test/cuda_equivalents/test_cuda_ee_psd_clamp.py", - "test/cuda_equivalents/test_cuda_eepose_runtime.py", - "test/cuda_equivalents/test_cuda_energy_regressor.py", - "test/cuda_equivalents/test_cuda_executable_equivalence.py", - "test/cuda_equivalents/test_cuda_regressor.py" + "test/cuda_equivalents/test_cuda_collision_warp.py" ] }, "name": "cuda_00_batched_host_wrapp_branching_ske", @@ -4311,7 +4645,7 @@ "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[g1-floating]", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_generated_header_includes_grid_data_variants_and_no_rnea_alias", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_controls_and_helpers_are_generated", - "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_default_cxx11_compiles_without_mathdx", + "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_default_cxx17_compiles_without_mathdx", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_base_strided_helpers_compile", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_dynamics_grid_data_variant_wrappers_compile", "test/cuda_equivalents/test_cuda_codegen_layout.py::test_kinematics_grid_data_variant_wrappers_compile", @@ -4347,19 +4681,221 @@ "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-1]", "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-32]", "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-100]", + "test/cuda_equivalents/test_cuda_collision_margins.py::test_native_separate_margins[False]", + "test/cuda_equivalents/test_cuda_collision_margins.py::test_native_separate_margins[True]", + "test/cuda_equivalents/test_cuda_collision_native.py::test_static_separate_margin_thresholds", "test/cuda_equivalents/test_cuda_collision_native.py::test_collision_native_two_tier", "test/cuda_equivalents/test_cuda_collision_native.py::test_collision_native_self_collision", "test/cuda_equivalents/test_cuda_collision_pairs.py::test_collision_distance_pairs_fd", "test/cuda_equivalents/test_cuda_collision_selfcc_rows.py::test_self_collision_rows_fd", - "test/cuda_equivalents/test_cuda_collision_two_tier.py::test_collision_two_tier_matches_fine", + "test/cuda_equivalents/test_cuda_collision_two_tier.py::test_collision_two_tier_matches_fine" + ], + "started_at": "2026-11-01T08:16:32Z" + }, + { + "carried": null, + "dirty": false, + "ended_at": "2026-11-01T08:25:23Z", + "environment": { + "gpu_info": { + "cuda_visible_devices": null, + "devices": [ + { + "compute_capability": "12.0", + "driver_version": "615.71.09", + "index": "0", + "memory": "32607 MiB", + "name": "NVIDIA GeForce RTX 5090", + "uuid": "GPU-bf781ab0-9632-4b9a-497b-c3b846aae70c" + } + ], + "source": "nvidia-smi" + }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", + "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", + "plugin_version": "0.5.0", + "pytest_version": "9.0.3", + "python_version": "3.12.3" + }, + "fingerprint": { + "algorithm": "sha256-manifest-v2", + "digest": "8defe9d54744759ae6ed2f86d4135c11c5236417bbdd582e6b0d0f43dbc30a29", + "excluded_paths": [ + "gpu-proof.json", + "grid_assets/launch_configs/README.md", + "grid_assets/robots/URDF_SOURCES.md", + "grid_assets/robots/drake/README.md", + "grid_codegen/README.md", + "grid_codegen/collision/HANDOFF.md" + ], + "extra_paths": [], + "file_count": 15, + "included_paths": [ + "external/RBDReference", + "test/cuda_equivalents/constant_model.py", + "test/cuda_equivalents/cuda_collision_warp_runner.cu", + "test/cuda_equivalents/cuda_constant_derivatives_runner.cu", + "test/cuda_equivalents/cuda_constant_dynamics_audit_runner.cu", + "test/cuda_equivalents/cuda_harness.py", + "test/cuda_equivalents/cuda_prismatic_fixed_target_runner.cu", + "test/cuda_equivalents/executable_cache.py", + "test/cuda_equivalents/test_cuda_collision_config_free.py", + "test/cuda_equivalents/test_cuda_collision_warp.py", + "test/cuda_equivalents/test_cuda_constant_branch_mimic_scale.py", + "test/cuda_equivalents/test_cuda_constant_derivatives.py", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py", + "test/cuda_equivalents/test_cuda_constant_frame_velocity.py", + "test/cuda_equivalents/test_cuda_constant_mimic.py" + ] + }, + "name": "cuda_01_collision_warp_constant_branch_m", + "node_ids": [ "test/cuda_equivalents/test_cuda_collision_warp.py::test_sphere_tables_match_the_baked_collision_batch", "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[32]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[64]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[256]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_two_tier_cascade[32]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_two_tier_cascade[128]", + "test/cuda_equivalents/test_cuda_collision_warp.py::test_separate_environment_self_margins[False]", + "test/cuda_equivalents/test_cuda_collision_warp.py::test_separate_environment_self_margins[True]", "test/cuda_equivalents/test_cuda_collision_warp.py::test_margin_and_self_toggle_single_tier", "test/cuda_equivalents/test_cuda_collision_warp.py::test_margin_and_self_toggle_two_tier_cascade", + "test/cuda_equivalents/test_cuda_constant_branch_mimic_scale.py::test_constant_branch_keeps_nonunit_mimic_scale[float]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe0-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe0-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe0-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe1-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe1-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe1-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe2-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe2-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe2-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe3-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe3-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe3-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe4-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe4-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe4-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe5-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe5-frame-jacobian]", + "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe5-frame-jacobian-dot]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-world-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-crba]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-id-gradient]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-body-so]", + "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-world-so]", + "test/cuda_equivalents/test_cuda_constant_frame_velocity.py::test_constant_frame_velocity[float-False]", + "test/cuda_equivalents/test_cuda_constant_frame_velocity.py::test_constant_frame_velocity[float-True]", + "test/cuda_equivalents/test_cuda_constant_mimic.py::test_constant_mimic_kinematics[float-prismatic]", + "test/cuda_equivalents/test_cuda_constant_mimic.py::test_constant_mimic_kinematics[float-revolute]" + ], + "started_at": "2026-11-01T08:23:09Z" + }, + { + "carried": null, + "dirty": false, + "ended_at": "2026-11-01T09:17:08Z", + "environment": { + "gpu_info": { + "cuda_visible_devices": null, + "devices": [ + { + "compute_capability": "12.0", + "driver_version": "615.71.09", + "index": "0", + "memory": "32607 MiB", + "name": "NVIDIA GeForce RTX 5090", + "uuid": "GPU-bf781ab0-9632-4b9a-497b-c3b846aae70c" + } + ], + "source": "nvidia-smi" + }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", + "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", + "plugin_version": "0.5.0", + "pytest_version": "9.0.3", + "python_version": "3.12.3" + }, + "fingerprint": { + "algorithm": "sha256-manifest-v2", + "digest": "c7864c4857230f4d134066ccfb92ef44d2ffffb56ddee20985d1d3c165641f5c", + "excluded_paths": [ + "gpu-proof.json", + "grid_assets/launch_configs/README.md", + "grid_assets/robots/URDF_SOURCES.md", + "grid_assets/robots/drake/README.md", + "grid_codegen/README.md", + "grid_codegen/collision/HANDOFF.md" + ], + "extra_paths": [], + "file_count": 30, + "included_paths": [ + "external/RBDReference", + "test/cuda_equivalents/constant_model.py", + "test/cuda_equivalents/cuda_constant_second_order_runner.cu", + "test/cuda_equivalents/cuda_contact_frame_positions_runner.cu", + "test/cuda_equivalents/cuda_coriolis_smoke_runner.cu", + "test/cuda_equivalents/cuda_dccrba_smoke_runner.cu", + "test/cuda_equivalents/cuda_ee_named_depth1_runner.cu", + "test/cuda_equivalents/cuda_ee_newton_hessian_runner.cu", + "test/cuda_equivalents/cuda_ee_psd_clamp_runner.cu", + "test/cuda_equivalents/cuda_eepose_runtime_smoke_runner.cu", + "test/cuda_equivalents/cuda_energy_regressor_smoke_runner.cu", + "test/cuda_equivalents/cuda_equivalence_runner.cu", + "test/cuda_equivalents/cuda_harness.py", + "test/cuda_equivalents/executable_cache.py", + "test/cuda_equivalents/grid_runner_select.cuh", + "test/cuda_equivalents/test_cuda_constant_second_order.py", + "test/cuda_equivalents/test_cuda_constant_velocity.py", + "test/cuda_equivalents/test_cuda_contact_frame_positions.py", + "test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py", + "test/cuda_equivalents/test_cuda_coriolis.py", + "test/cuda_equivalents/test_cuda_coriolis_output_spill.py", + "test/cuda_equivalents/test_cuda_dccrba.py", + "test/cuda_equivalents/test_cuda_ee_named_depth1.py", + "test/cuda_equivalents/test_cuda_ee_newton_hessian.py", + "test/cuda_equivalents/test_cuda_ee_psd_clamp.py", + "test/cuda_equivalents/test_cuda_ee_workspace_queries.py", + "test/cuda_equivalents/test_cuda_eepose_runtime.py", + "test/cuda_equivalents/test_cuda_energy_regressor.py", + "test/cuda_equivalents/test_cuda_executable_equivalence.py", + "test/cuda_equivalents/test_cuda_regressor.py" + ] + }, + "name": "cuda_02_constant_second_or_constant_velo", + "node_ids": [ + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe0-body]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe0-world]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe0-coriolis]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe1-body]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe1-world]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe1-coriolis]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe2-body]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe2-world]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe2-coriolis]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe3-body]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe3-world]", + "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe3-coriolis]", + "test/cuda_equivalents/test_cuda_constant_velocity.py::test_constant_branch_velocity_isolation[float]", "test/cuda_equivalents/test_cuda_contact_frame_positions.py::test_contact_frame_positions_and_jacobian[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_contact_frame_positions.py::test_contact_frame_positions_and_jacobian[go2-floating]", "test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py::test_cuda_continuous_joint_matches_reference_at_wrapped_angles[gen3-fixed]", @@ -4423,6 +4959,9 @@ "test/cuda_equivalents/test_cuda_ee_newton_hessian.py::test_ee_pos_cost_hessian_newton_matches_fd[go2-fixed]", "test/cuda_equivalents/test_cuda_ee_psd_clamp.py::test_ee_pos_cost_hessian_psd_clamp[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_ee_psd_clamp.py::test_ee_pos_cost_hessian_psd_clamp[go2-fixed]", + "test/cuda_equivalents/test_cuda_ee_workspace_queries.py::test_exact_target_scratch[float-iiwa14-iiwa_joint_ee]", + "test/cuda_equivalents/test_cuda_ee_workspace_queries.py::test_exact_target_scratch[float-go2-FR_foot_joint]", + "test/cuda_equivalents/test_cuda_ee_workspace_queries.py::test_exact_target_scratch[float-go2-]", "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[go2-floating]", "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[fr3-fixed]", @@ -5052,98 +5591,14 @@ "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics-threads32]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics-threads96]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-aba-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-aba-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-aba-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-aba-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-crba-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-crba-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-crba-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-crba-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics_gradient-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics_gradient-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics_gradient-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics_gradient-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics_gradient-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics_gradient-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics_gradient-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics_gradient-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_gradient-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_gradient-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_gradient-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_gradient-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_hessian-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_hessian-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_hessian-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_hessian-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-minv-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-minv-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-minv-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-minv-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-aba-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-aba-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-aba-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-aba-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-crba-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-crba-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-crba-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-crba-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics_gradient-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics_gradient-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics_gradient-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics_gradient-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics_gradient-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics_gradient-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics_gradient-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics_gradient-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_gradient-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_gradient-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_gradient-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_gradient-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_hessian-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_hessian-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_hessian-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_hessian-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics-threadssuggested]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threads32]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threads96]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threads100]", - "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threadssuggested]" + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics-threadssuggested]" ], - "started_at": "2026-10-24T10:07:42Z" + "started_at": "2026-11-01T08:23:14Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T12:44:56Z", + "ended_at": "2026-11-01T10:02:10Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -5159,14 +5614,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "3e59f6281fb0de19e68170353eac50bd3726c83bc8c7bd60c0e725ca123fec57", + "digest": "09f603b23a61099a4fe627036be1181b13c4aacfaa1500bcc478cdf23ef318cc", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -5176,7 +5632,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 19, + "file_count": 16, "included_paths": [ "external/RBDReference", "test/cuda_equivalents/cuda_equivalence_runner.cu", @@ -5184,7 +5640,6 @@ "test/cuda_equivalents/cuda_f_ext_contact_runtime_runner.cu", "test/cuda_equivalents/cuda_f_ext_gradient_runner.cu", "test/cuda_equivalents/cuda_fd_parameter_gradient_smoke_runner.cu", - "test/cuda_equivalents/cuda_frame_jacobian_smoke_runner.cu", "test/cuda_equivalents/cuda_harness.py", "test/cuda_equivalents/grid_runner_select.cuh", "test/cuda_equivalents/test_cuda_executable_equivalence.py", @@ -5194,13 +5649,95 @@ "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py", "test/cuda_equivalents/test_cuda_fd_du_output_spill.py", "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py", - "test/cuda_equivalents/test_cuda_fext_equivalence.py", - "test/cuda_equivalents/test_cuda_floating_values_equivalence.py", - "test/cuda_equivalents/test_cuda_frame_jacobian.py" + "test/cuda_equivalents/test_cuda_fext_equivalence.py" ] }, - "name": "cuda_01_fetch_baxter_signed_axis_arm_f_e", + "name": "cuda_03_rizon4_gen3_fetch_baxter_signed_", "node_ids": [ + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-aba-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-aba-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-aba-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-aba-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-crba-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-crba-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-crba-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-crba-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics_gradient-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics_gradient-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics_gradient-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics_gradient-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics_gradient-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics_gradient-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics_gradient-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics_gradient-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_gradient-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_gradient-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_gradient-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_gradient-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_hessian-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_hessian-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_hessian-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_hessian-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-minv-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-minv-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-minv-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-minv-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-aba-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-aba-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-aba-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-aba-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-crba-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-crba-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-crba-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-crba-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics_gradient-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics_gradient-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics_gradient-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics_gradient-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics_gradient-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics_gradient-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics_gradient-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics_gradient-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_gradient-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_gradient-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_gradient-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_gradient-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_hessian-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_hessian-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_hessian-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_hessian-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics-threadssuggested]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threads32]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threads96]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threads100]", + "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threadssuggested]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics-threads32]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics-threads96]", "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics-threads100]", @@ -5402,41 +5939,14 @@ "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[rizon4-floating]", "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[gen3-floating]", "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[fetch-floating]", - "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[baxter-floating]", - "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[iiwa14-floating]", - "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[go2-floating]", - "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[g1-floating]", - "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[h1_2-floating]", - "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[fr3-floating]", - "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[rizon4-floating]", - "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[gen3-floating]", - "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[fetch-floating]", - "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[baxter-floating]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[iiwa14-fixed]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[go2-fixed]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[g1-fixed]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[h1_2-fixed]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fr3-fixed]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[rizon4-fixed]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[gen3-fixed]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fetch-fixed]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[baxter-fixed]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[iiwa14-floating]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[go2-floating]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[g1-floating]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[h1_2-floating]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fr3-floating]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[rizon4-floating]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[gen3-floating]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fetch-floating]", - "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[baxter-floating]" + "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[baxter-floating]" ], - "started_at": "2026-10-24T11:06:03Z" + "started_at": "2026-11-01T08:25:25Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T14:14:59Z", + "ended_at": "2026-11-01T10:44:37Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -5452,14 +5962,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "f94a2c0a945a240258682083dd0c583425f83ac521adadf06a17135ca0c14fab", + "digest": "529e4524bcd2595736ee604734cd0292e60122a6cd1cd19f7716c67022307b62", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -5469,29 +5980,62 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 17, + "file_count": 23, "included_paths": [ "external/RBDReference", "test/cuda_equivalents/cuda_frame_jacobian_host_runner.cu", + "test/cuda_equivalents/cuda_frame_jacobian_smoke_runner.cu", + "test/cuda_equivalents/cuda_glass_handoff_runner.cu", "test/cuda_equivalents/cuda_harness.py", "test/cuda_equivalents/cuda_idsva_so_world_frame_smoke_runner.cu", "test/cuda_equivalents/cuda_integrator_batch_forces_runner.cu", "test/cuda_equivalents/cuda_integrator_smoke_runner.cu", + "test/cuda_equivalents/cuda_joint_transform_suffix_runner.cu", + "test/cuda_equivalents/descriptor_oracle.py", "test/cuda_equivalents/executable_cache.py", "test/cuda_equivalents/test_cuda_codegen_layout.py", + "test/cuda_equivalents/test_cuda_floating_values_equivalence.py", "test/cuda_equivalents/test_cuda_frame_jacobian.py", "test/cuda_equivalents/test_cuda_frame_jacobian_host.py", + "test/cuda_equivalents/test_cuda_glass_handoff.py", "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py", "test/cuda_equivalents/test_cuda_input_abi.py", "test/cuda_equivalents/test_cuda_integrator_batch_forces.py", "test/cuda_equivalents/test_cuda_integrator_equivalence.py", "test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py", + "test/cuda_equivalents/test_cuda_joint_transform_suffix.py", "test/cuda_equivalents/test_cuda_second_order_fallback.py" ] }, - "name": "cuda_02_frame_jacobian_hos_idsva_so_worl", + "name": "cuda_04_floating_values_eq_frame_jacobia", "node_ids": [ + "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[iiwa14-floating]", + "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[go2-floating]", + "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[g1-floating]", + "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[h1_2-floating]", + "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[fr3-floating]", + "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[rizon4-floating]", + "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[gen3-floating]", + "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[fetch-floating]", + "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[baxter-floating]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[iiwa14-fixed]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[go2-fixed]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[g1-fixed]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[h1_2-fixed]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fr3-fixed]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[rizon4-fixed]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[gen3-fixed]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fetch-fixed]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[baxter-fixed]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[iiwa14-floating]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[go2-floating]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[g1-floating]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[h1_2-floating]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fr3-floating]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[rizon4-floating]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[gen3-floating]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fetch-floating]", + "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[baxter-floating]", "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[go2-fixed]", "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[g1-fixed]", @@ -5510,6 +6054,7 @@ "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[gen3-floating]", "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[fetch-floating]", "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[baxter-floating]", + "test/cuda_equivalents/test_cuda_glass_handoff.py::test_embedded_glass_restricted_handoffs[float-3e-05-3e-05]", "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[iiwa14-world-frame-floating]", "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[iiwa14-world-frame-fixed]", "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[go2-world-frame-floating]", @@ -5573,19 +6118,16 @@ "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_fext_matches_python_reference", "test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py::test_floating_damped_cuda_matches_damped_oracle", "test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py::test_floating_tanh_friction_cuda_matches_tanh_oracle", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch1]", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch16]", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch256]", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch1]", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch16]", - "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch256]" + "test/cuda_equivalents/test_cuda_joint_transform_suffix.py::test_scalar_transform_composed_suffix[float-7-False]", + "test/cuda_equivalents/test_cuda_joint_transform_suffix.py::test_scalar_transform_composed_suffix[float-7-True]", + "test/cuda_equivalents/test_cuda_joint_transform_suffix.py::test_scalar_transform_composed_suffix[float-32-True]" ], - "started_at": "2026-10-24T12:45:01Z" + "started_at": "2026-11-01T09:17:09Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T15:01:45Z", + "ended_at": "2026-11-01T10:55:48Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -5601,14 +6143,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "c2c826bfb275fa8de51a1ebd526214f2a4869c64206f1779f87d0e58b1e1334d", + "digest": "18b2c4ca6e146b6362813290597ba74efaceccd166a8edfe5d3f911726b4dab3", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -5618,64 +6161,50 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 52, + "file_count": 32, "included_paths": [ "external/RBDReference", + "test/cuda_equivalents/constant_model.py", "test/cuda_equivalents/cuda_equivalence_runner.cu", "test/cuda_equivalents/cuda_frame_jacobian_host_runner.cu", "test/cuda_equivalents/cuda_harness.py", + "test/cuda_equivalents/cuda_menu_workspace_runner.cu", "test/cuda_equivalents/cuda_mjx_tier_invariance_runner.cu", + "test/cuda_equivalents/cuda_model_descriptor_runner.cu", + "test/cuda_equivalents/cuda_model_primitives_runner.cu", "test/cuda_equivalents/cuda_momentum_contract_runner.cu", "test/cuda_equivalents/cuda_multi_target_position_gradient_runner.cu", "test/cuda_equivalents/cuda_multi_target_position_runner.cu", "test/cuda_equivalents/cuda_multi_target_raw_runner.cu", "test/cuda_equivalents/cuda_plant_smoke_runner.cu", - "test/cuda_equivalents/cuda_racecheck_floating_runner.cu", - "test/cuda_equivalents/cuda_regressor_gradient_runner.cu", - "test/cuda_equivalents/cuda_regressor_smoke_runner.cu", - "test/cuda_equivalents/cuda_safe_init_runner.cu", - "test/cuda_equivalents/cuda_second_order_smoke_runner.cu", - "test/cuda_equivalents/cuda_spherical_eepose_grad_hess_runner.cu", - "test/cuda_equivalents/cuda_spherical_fdsva_so_runner.cu", - "test/cuda_equivalents/cuda_spherical_integrator_gradient_runner.cu", - "test/cuda_equivalents/cuda_spherical_integrator_runner.cu", - "test/cuda_equivalents/cuda_spherical_kinematics_runner.cu", - "test/cuda_equivalents/cuda_spherical_runner.cu", - "test/cuda_equivalents/cuda_spherical_so_runner.cu", - "test/cuda_equivalents/cuda_workspace_slots_runner.cu", + "test/cuda_equivalents/descriptor_oracle.py", "test/cuda_equivalents/executable_cache.py", - "test/cuda_equivalents/grid_runner_select.cuh", + "test/cuda_equivalents/manifold_oracle.py", "test/cuda_equivalents/test_cuda_contact_frame_positions.py", "test/cuda_equivalents/test_cuda_frame_jacobian.py", "test/cuda_equivalents/test_cuda_integrator_equivalence.py", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py", + "test/cuda_equivalents/test_cuda_menu_workspace.py", "test/cuda_equivalents/test_cuda_mjx_tier_invariance.py", + "test/cuda_equivalents/test_cuda_model_descriptor.py", + "test/cuda_equivalents/test_cuda_model_primitives.py", "test/cuda_equivalents/test_cuda_momentum_contract.py", "test/cuda_equivalents/test_cuda_multi_target_position.py", "test/cuda_equivalents/test_cuda_multi_target_position_gradient.py", "test/cuda_equivalents/test_cuda_multi_target_raw_evaluators.py", "test/cuda_equivalents/test_cuda_osc_inertia_spill.py", "test/cuda_equivalents/test_cuda_planar_translation_equivalence.py", - "test/cuda_equivalents/test_cuda_plant_equivalence.py", - "test/cuda_equivalents/test_cuda_racecheck_floating.py", - "test/cuda_equivalents/test_cuda_regressor.py", - "test/cuda_equivalents/test_cuda_regressor_gradient.py", - "test/cuda_equivalents/test_cuda_safe_init.py", - "test/cuda_equivalents/test_cuda_second_order_fallback.py", - "test/cuda_equivalents/test_cuda_spherical_eepose_gradhess_equivalence.py", - "test/cuda_equivalents/test_cuda_spherical_equivalence.py", - "test/cuda_equivalents/test_cuda_spherical_fdsva_so_equivalence.py", - "test/cuda_equivalents/test_cuda_spherical_integrator_equivalence.py", - "test/cuda_equivalents/test_cuda_spherical_integrator_gradient_equivalence.py", - "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py", - "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py", - "test/cuda_equivalents/test_cuda_vendor_glass_alias.py", - "test/cuda_equivalents/test_cuda_workspace_slots.py", - "test/cuda_equivalents/test_smem_poison.py" + "test/cuda_equivalents/test_cuda_plant_equivalence.py" ] }, - "name": "cuda_03_kinematics_thread__mjx_tier_inva", + "name": "cuda_05_kinematics_thread__menu_workspac", "node_ids": [ + "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch1]", + "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch16]", + "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch256]", + "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch1]", + "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch16]", + "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch256]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[go2-floating-batch1]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[go2-floating-batch16]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[go2-floating-batch256]", @@ -5685,8 +6214,35 @@ "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch1]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch16]", "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch256]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-zero]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-default]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-wide-x]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-wide-u]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-zero]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-default]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-wide-x]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-wide-u]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-zero]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-default]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-wide-x]", + "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-wide-u]", "test/cuda_equivalents/test_cuda_mjx_tier_invariance.py::test_mjx_second_order_tier_invariance[idsva_so]", "test/cuda_equivalents/test_cuda_mjx_tier_invariance.py::test_mjx_second_order_tier_invariance[fdsva_so]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[constant-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[floating-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[spherical-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[branch-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[empty-fp32]", + "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[runtime-fp32]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-mixed_spherical_arm-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-rotated_inertial_arm-True-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-branching_skew_arm-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-helical_arm-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-rotated_inertial_arm-False-True]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-constant-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-zero-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-slider_mimic_fixed_tool-False-False]", + "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-fr3-False-False]", "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-iiwa14-fixed]", "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-go2-fixed]", "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-g1-fixed]", @@ -5761,44 +6317,154 @@ "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_tangent_state_cost_matches_reference", "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[go2-floating]", - "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[fr3-fixed]", - "test/cuda_equivalents/test_cuda_racecheck_floating.py::test_go2_floating_composition_is_racecheck_clean", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[iiwa14-fixed]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[go2-fixed]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[g1-fixed]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[h1_2-fixed]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[fr3-fixed]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[rizon4-fixed]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[gen3-fixed]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[fetch-fixed]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[baxter-fixed]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[iiwa14-floating]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[go2-floating]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[g1-floating]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[h1_2-floating]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[fr3-floating]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[rizon4-floating]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[gen3-floating]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[fetch-floating]", - "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[baxter-floating]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[iiwa14-fixed]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[go2-fixed]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[g1-fixed]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[h1_2-fixed]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fr3-fixed]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[rizon4-fixed]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[gen3-fixed]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fetch-fixed]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[baxter-fixed]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[iiwa14-floating]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[go2-floating]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[g1-floating]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[h1_2-floating]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fr3-floating]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[rizon4-floating]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[gen3-floating]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fetch-floating]", - "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[baxter-floating]", + "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[fr3-fixed]" + ], + "started_at": "2026-11-01T10:02:15Z" + }, + { + "carried": null, + "dirty": false, + "ended_at": "2026-11-01T11:03:25Z", + "environment": { + "gpu_info": { + "cuda_visible_devices": null, + "devices": [ + { + "compute_capability": "12.0", + "driver_version": "615.71.09", + "index": "0", + "memory": "32607 MiB", + "name": "NVIDIA GeForce RTX 5090", + "uuid": "GPU-bf781ab0-9632-4b9a-497b-c3b846aae70c" + } + ], + "source": "nvidia-smi" + }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", + "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", + "plugin_version": "0.5.0", + "pytest_version": "9.0.3", + "python_version": "3.12.3" + }, + "fingerprint": { + "algorithm": "sha256-manifest-v2", + "digest": "962d9fa6c8b28831efd30e472832d44e0f84c061bbb69474dc663b8f7086ec60", + "excluded_paths": [ + "gpu-proof.json", + "grid_assets/launch_configs/README.md", + "grid_assets/robots/URDF_SOURCES.md", + "grid_assets/robots/drake/README.md", + "grid_codegen/README.md", + "grid_codegen/collision/HANDOFF.md" + ], + "extra_paths": [], + "file_count": 43, + "included_paths": [ + "external/RBDReference", + "test/cuda_equivalents/constant_model.py", + "test/cuda_equivalents/cuda_harness.py", + "test/cuda_equivalents/cuda_plant_workspace_runner.cu", + "test/cuda_equivalents/cuda_prismatic_fixed_target_runner.cu", + "test/cuda_equivalents/cuda_racecheck_floating_runner.cu", + "test/cuda_equivalents/cuda_regressor_gradient_runner.cu", + "test/cuda_equivalents/cuda_regressor_smoke_runner.cu", + "test/cuda_equivalents/cuda_safe_init_runner.cu", + "test/cuda_equivalents/cuda_second_order_smoke_runner.cu", + "test/cuda_equivalents/cuda_spherical_eepose_grad_hess_runner.cu", + "test/cuda_equivalents/cuda_spherical_fdsva_so_runner.cu", + "test/cuda_equivalents/cuda_spherical_integrator_gradient_runner.cu", + "test/cuda_equivalents/cuda_spherical_integrator_runner.cu", + "test/cuda_equivalents/cuda_spherical_kinematics_runner.cu", + "test/cuda_equivalents/cuda_spherical_plant_hessian_runner.cu", + "test/cuda_equivalents/cuda_spherical_runner.cu", + "test/cuda_equivalents/cuda_spherical_so_runner.cu", + "test/cuda_equivalents/cuda_workspace_slots_runner.cu", + "test/cuda_equivalents/executable_cache.py", + "test/cuda_equivalents/grid_runner_select.cuh", + "test/cuda_equivalents/manifold_oracle.py", + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py", + "test/cuda_equivalents/test_cuda_plant_workspace.py", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py", + "test/cuda_equivalents/test_cuda_racecheck_floating.py", + "test/cuda_equivalents/test_cuda_regressor.py", + "test/cuda_equivalents/test_cuda_regressor_gradient.py", + "test/cuda_equivalents/test_cuda_revolute_mimic_mechanics.py", + "test/cuda_equivalents/test_cuda_safe_init.py", + "test/cuda_equivalents/test_cuda_second_order_fallback.py", + "test/cuda_equivalents/test_cuda_spherical_eepose_gradhess_equivalence.py", + "test/cuda_equivalents/test_cuda_spherical_equivalence.py", + "test/cuda_equivalents/test_cuda_spherical_fdsva_so_equivalence.py", + "test/cuda_equivalents/test_cuda_spherical_integrator_equivalence.py", + "test/cuda_equivalents/test_cuda_spherical_integrator_gradient_equivalence.py", + "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py", + "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py", + "test/cuda_equivalents/test_cuda_vendor_glass_alias.py", + "test/cuda_equivalents/test_cuda_workspace_slots.py", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py", + "test/cuda_equivalents/test_smem_poison.py" + ] + }, + "name": "cuda_06_plant_gradient_are_plant_workspa", + "node_ids": [ + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[iiwa14-False-float-False]", + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[go2-True-float-False]", + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[go2-True-float-True]", + "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[g1-False-float-False]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-float-full]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-float-spill]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[iiwa14-float-full]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[iiwa14-float-spill]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-semi-runtime-float-full]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-semi-runtime-float-spill]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[branching-runtime-double-full]", + "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[branching-runtime-double-spill]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_fixed_tool_values_and_thread_invariance[False]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_fixed_tool_values_and_thread_invariance[True]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[cardinal-False]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[cardinal-True]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[cardinal-constant]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[skew-False]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[skew-True]", + "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[skew-constant]", + "test/cuda_equivalents/test_cuda_racecheck_floating.py::test_go2_floating_composition_is_racecheck_clean", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[iiwa14-fixed]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[go2-fixed]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[g1-fixed]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[h1_2-fixed]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[fr3-fixed]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[rizon4-fixed]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[gen3-fixed]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[fetch-fixed]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[baxter-fixed]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[iiwa14-floating]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[go2-floating]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[g1-floating]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[h1_2-floating]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[fr3-floating]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[rizon4-floating]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[gen3-floating]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[fetch-floating]", + "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[baxter-floating]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[iiwa14-fixed]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[go2-fixed]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[g1-fixed]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[h1_2-fixed]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fr3-fixed]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[rizon4-fixed]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[gen3-fixed]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fetch-fixed]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[baxter-fixed]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[iiwa14-floating]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[go2-floating]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[g1-floating]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[h1_2-floating]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fr3-floating]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[rizon4-floating]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[gen3-floating]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fetch-floating]", + "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[baxter-floating]", + "test/cuda_equivalents/test_cuda_revolute_mimic_mechanics.py::test_revolute_mimic_dynamics_against_independent_mechanics", "test/cuda_equivalents/test_cuda_safe_init.py::test_checked_initialization_contract[iiwa14]", "test/cuda_equivalents/test_cuda_safe_init.py::test_checked_initialization_contract[go2]", "test/cuda_equivalents/test_cuda_safe_init.py::test_legacy_policy_is_preserved[iiwa14]", @@ -5853,6 +6519,22 @@ "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_matches_reference[float-mixed_spherical_arm.urdf]", "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_thread_invariant[spherical_arm.urdf]", "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_thread_invariant[mixed_spherical_arm.urdf]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-mixed_spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-single_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-two_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-mixed_spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-single_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-two_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-mixed_spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-single_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-two_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-mixed_spherical_arm]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-single_spherical]", + "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-two_spherical]", "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_matches_world_reference[spherical_arm.urdf-float]", "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_matches_world_reference[mixed_spherical_arm.urdf-float]", "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_thread_invariant[spherical_arm.urdf]", @@ -5860,15 +6542,19 @@ "test/cuda_equivalents/test_cuda_vendor_glass_alias.py::test_vendor_glass_false_compiles_against_top_level_glass_and_runs", "test/cuda_equivalents/test_cuda_workspace_slots.py::test_cuda_workspace_slots_bit_identity[iiwa14-fixed]", "test/cuda_equivalents/test_cuda_workspace_slots.py::test_cuda_workspace_slots_bit_identity[go2-floating]", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[pose-hessian-True-float]", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[pose-hessian-False-float]", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[plant-gradient-True-float]", + "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[plant-gradient-False-float]", "test/cuda_equivalents/test_smem_poison.py::test_equivalence_survives_smem_poison[iiwa14 and fixed]", "test/cuda_equivalents/test_smem_poison.py::test_equivalence_survives_smem_poison[go2 and floating]" ], - "started_at": "2026-10-24T14:15:03Z" + "started_at": "2026-11-01T10:44:42Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:57:10Z", + "ended_at": "2026-11-01T07:32:25Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -5884,14 +6570,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "bc889c10a2b3e1918214f50da471bdf38890fb98e0ad170ee54c828f45c275a2", + "digest": "12a89c8c58afc52317cce239c916d3e273e007a3627535c044ddfb0d00ee68ee", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -5901,7 +6588,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -5951,12 +6638,12 @@ "test/python_wrappers/test_any_thread_count.py::test_method_at_block_size[fdsva_so-128]", "test/python_wrappers/test_any_thread_count.py::test_method_at_block_size[fdsva_so-256]" ], - "started_at": "2026-10-24T08:57:08Z" + "started_at": "2026-11-01T07:32:23Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:57:15Z", + "ended_at": "2026-11-01T07:32:25Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -5972,14 +6659,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "34769d64fe5038ba272e555220c3adee859299e87113820cb284a0eb66310bc9", + "digest": "541c741b3c909e41c1fa1b120d04aa07613cda05741da3fd087278de5525847b", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -5989,7 +6677,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6005,12 +6693,12 @@ "test/python_wrappers/test_batch_switch.py::test_global_override_beats_switch", "test/python_wrappers/test_batch_switch.py::test_apply_batch_overlay_from_config" ], - "started_at": "2026-10-24T08:57:13Z" + "started_at": "2026-11-01T07:32:23Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:57:21Z", + "ended_at": "2026-11-01T07:32:31Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6026,14 +6714,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "b1ed66f6e727fb7ebcac2f9e22d22819c8187e57ae7328bc28519258029d15ae", + "digest": "f8f66f8c64fc4907b031c56dcad94c72f895a790a625a18a753e94e5339ffdf4", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6043,7 +6732,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6073,12 +6762,12 @@ "test/python_wrappers/test_centroidal_energy_frame.py::test_frame_jacobian_default_target_explicit_frame[go2]", "test/python_wrappers/test_centroidal_energy_frame.py::test_frame_jacobian_dot[go2]" ], - "started_at": "2026-10-24T08:57:18Z" + "started_at": "2026-11-01T07:32:29Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:57:36Z", + "ended_at": "2026-11-01T07:33:25Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6094,14 +6783,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "180bac30d5f39354509d62c512e488c297bb900de507db15b6f09cdc5fe86db5", + "digest": "3ef7b3589ffc0f0950695a43da5109944f851939cdfede4cc0d0faad0e1a5609", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6111,8 +6801,9 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 29, "included_paths": [ + "bindings/examples/consumer_artifact_migration.py", "bindings/grid_rbd", "bindings/src", "external/RBDReference", @@ -6121,20 +6812,34 @@ }, "name": "test_consumer_artifact", "node_ids": [ + "test/python_wrappers/test_consumer_artifact.py::test_descriptor_matches_exported_cpp_and_source_free_prepared_load", + "test/python_wrappers/test_consumer_artifact.py::test_compiler_discovered_headers_rekey_native_values", + "test/python_wrappers/test_consumer_artifact.py::test_consumer_migration_example", + "test/python_wrappers/test_consumer_artifact.py::test_shadowing_symlink_preserves_header_file_macro_identity", + "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_dependency_filename_escaping[space name.h]", + "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_dependency_filename_escaping[hash#name.h]", + "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_dependency_filename_escaping[cash$name.h]", + "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_dependency_filename_escaping[cash$$name.h]", + "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_unreadable_dependency_fails_closed", + "test/python_wrappers/test_consumer_artifact.py::test_concurrent_consumer_builds_publish_loadable_winner", "test/python_wrappers/test_consumer_artifact.py::test_artifact_builds_loads_and_matches_the_oracle", "test/python_wrappers/test_consumer_artifact.py::test_second_build_is_a_pointer_hit_and_a_define_rekeys", "test/python_wrappers/test_consumer_artifact.py::test_unchanged_unit_reuses_its_object", "test/python_wrappers/test_consumer_artifact.py::test_tampered_identity_is_refused_and_the_content_key_rehits", "test/python_wrappers/test_consumer_artifact.py::test_load_without_a_toolchain_and_device_refusal", "test/python_wrappers/test_consumer_artifact.py::test_collision_options_emit_the_namespace_and_validate", - "test/python_wrappers/test_consumer_artifact.py::test_plant_block_follows_the_request" + "test/python_wrappers/test_consumer_artifact.py::test_plant_block_follows_the_request", + "test/python_wrappers/test_consumer_artifact.py::test_declared_python_extension_import_and_prepared_load", + "test/python_wrappers/test_consumer_artifact.py::test_undeclared_python_extension_is_not_published", + "test/python_wrappers/test_consumer_artifact.py::test_source_path_macro_survives_cache_reuse", + "test/python_wrappers/test_consumer_artifact.py::test_two_robot_consumer_statics_are_isolated_in_both_load_orders" ], - "started_at": "2026-10-24T08:57:23Z" + "started_at": "2026-11-01T07:32:29Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:57:41Z", + "ended_at": "2026-11-01T07:32:36Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6150,14 +6855,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "264cec4554d2cd14a91fee6853c39a041e39d5beade2a3432824321c2cb86b9f", + "digest": "d52a87baf731464d9d4d5c6b441e639992a2554a82f7dd8f338b3cd233c8c8ce", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6167,7 +6873,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6180,12 +6886,12 @@ "test/python_wrappers/test_contact_fext.py::test_contact_fext_matches_host_map", "test/python_wrappers/test_contact_fext.py::test_contact_fext_floating_dynamics_only_subset_matches_host_map" ], - "started_at": "2026-10-24T08:57:38Z" + "started_at": "2026-11-01T07:32:34Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:57:53Z", + "ended_at": "2026-11-01T07:32:49Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6201,14 +6907,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "a78ebe52d3acfb2af91ed6fa7cade20eea8aed3f95d8d6a515be585dbbce1561", + "digest": "ea2277288d77e8dc3b2a2fa88a5f4eaf73131929b3fb0639f8d46ac1d8504f1b", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6218,7 +6925,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6242,12 +6949,12 @@ "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_thread_invariance[iiwa14-fixed-named-iiwa_joint_ee]", "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_root_attached_zero_gradient_hessian" ], - "started_at": "2026-10-24T08:57:43Z" + "started_at": "2026-11-01T07:32:39Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:58:03Z", + "ended_at": "2026-11-01T07:32:58Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6263,14 +6970,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "bd06bc740fc23e471a77b44f989fe41c1bb8b5fd42879527fae6fcf88bb39dd4", + "digest": "31e66e623f3246ce64817c4d2426a8a2bd94a14b691c7f59e71dc2d34790b5c6", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6280,7 +6988,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6297,12 +7005,12 @@ "test/python_wrappers/test_f_ext_backward_and_shapes.py::test_jax_f_ext_broadcast_is_materialized_and_widths_rejected[float32]", "test/python_wrappers/test_f_ext_backward_and_shapes.py::test_torch_native_rejects_f_ext_batch_mismatch[float32]" ], - "started_at": "2026-10-24T08:57:59Z" + "started_at": "2026-11-01T07:32:54Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:58:12Z", + "ended_at": "2026-11-01T07:33:07Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6318,14 +7026,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "6bc7c92f443653e576673d64ceb8f990e76ce00183c3f749668aca7e6bab39fc", + "digest": "575a40a2b74ee8ce5e2082b54417ebe66cefc7e15774e4075f220264712aebeb", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6335,7 +7044,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6354,12 +7063,12 @@ "test/python_wrappers/test_fk_batched.py::test_fk_batched_layout_distinct_per_sample", "test/python_wrappers/test_fk_batched.py::test_fk_batched_floating_base_matches_reference" ], - "started_at": "2026-10-24T08:58:09Z" + "started_at": "2026-11-01T07:33:04Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:58:34Z", + "ended_at": "2026-11-01T07:33:34Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6375,14 +7084,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "db8781f5ec8500c049e7bf4c962e7d6b7ecdb2a888d3255406945d991e98e0c3", + "digest": "1f5b34ad9c919417fda845df33e172f52777f653ca0632c5f58aab3b67cf8276", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6392,7 +7102,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6427,12 +7137,12 @@ "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle1-float32-end_effector_pose-jax]", "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle1-float32-end_effector_pose-torch]" ], - "started_at": "2026-10-24T08:58:19Z" + "started_at": "2026-11-01T07:33:14Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:58:41Z", + "ended_at": "2026-11-01T07:33:31Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6448,14 +7158,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "ea903a1d0ea65b6bbd180a1f3c152eb32f32a946ded07450a92ce3d9f31f5b29", + "digest": "2aae0b0a6388306f417ddc84078858f54cfcc6c12f753d9eba6b2971b1cc083d", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6465,7 +7176,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6482,12 +7193,12 @@ "test/python_wrappers/test_fp64_parity.py::test_fp64_forward_dynamics_tight", "test/python_wrappers/test_fp64_parity.py::test_fp64_materially_tighter_than_fp32" ], - "started_at": "2026-10-24T08:58:39Z" + "started_at": "2026-11-01T07:33:29Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:58:46Z", + "ended_at": "2026-11-01T07:33:37Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6503,14 +7214,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "c3fc68d330d4faba5620e8ebb224f1bea8c03df9bbe99dfdf2602668907141f7", + "digest": "281927d2b77fbf6400cae67697a08ad8e26f2b835dcd0c77a4f9e7952cfa6e46", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6520,7 +7232,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6533,12 +7245,12 @@ "test/python_wrappers/test_g1_plant_hessian_smoke.py::test_plant_step_hessian[euler]", "test/python_wrappers/test_g1_plant_hessian_smoke.py::test_plant_step_hessian[semi_implicit_euler]" ], - "started_at": "2026-10-24T08:58:44Z" + "started_at": "2026-11-01T07:33:34Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:58:51Z", + "ended_at": "2026-11-01T07:33:42Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6554,14 +7266,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "defdb45c262ccd9a5fd7554256adbb94980128f210d05835484639804f48c5cd", + "digest": "a68d3d391429acba702eb66990c940321276d3c757a70631bc8a29e8f3cae262", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6571,7 +7284,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6590,12 +7303,12 @@ "test/python_wrappers/test_iiwa14_f_ext.py::test_torch_inverse_dynamics_f_ext", "test/python_wrappers/test_iiwa14_f_ext.py::test_jax_no_f_ext_residue_after_f_ext_call" ], - "started_at": "2026-10-24T08:58:49Z" + "started_at": "2026-11-01T07:33:39Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:58:58Z", + "ended_at": "2026-11-01T07:33:43Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6611,14 +7324,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "e15a73de9c8f1aef22db449e7de75d812c02bc56c5490a49fce6a90ba15b4945", + "digest": "c9529ee5bfeaadf9317301625f8dd68732ec4c7158b865e4b6955da7dabde14e", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6628,7 +7342,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6699,12 +7413,12 @@ "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_runtime_ee_matches_plain", "test/python_wrappers/test_iiwa14_jax_smoke.py::test_register_idempotent" ], - "started_at": "2026-10-24T08:58:54Z" + "started_at": "2026-11-01T07:33:39Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T08:59:05Z", + "ended_at": "2026-11-01T07:33:46Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6720,14 +7434,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "3eddd0f55326209697e7e23e46400229d934b4c39a91b3a5d14ecd8f559fe216", + "digest": "627e7143937703ae3318e90f03efb15f9acc3110e253be11f4066361ef70db5b", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6737,7 +7452,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6759,12 +7474,12 @@ "test/python_wrappers/test_iiwa14_plant_smoke.py::test_joint_position_barrier", "test/python_wrappers/test_iiwa14_plant_smoke.py::test_joint_torque_barrier_inf_bound" ], - "started_at": "2026-10-24T08:59:04Z" + "started_at": "2026-11-01T07:33:44Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:01:12Z", + "ended_at": "2026-11-01T07:36:01Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6780,14 +7495,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "0be7dbf04760ef5ad9aa37092ecb5dc35263de1195ac47e9b2c7ae32cc0d2289", + "digest": "4473bcf73783734963c6c415301a975f2a75b817e8b6943f7b73c2b6e3f035b7", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6797,7 +7513,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6816,12 +7532,12 @@ "test/python_wrappers/test_iiwa14_qdd_aware_grad.py::test_torch_public_gradient_takes_qdd", "test/python_wrappers/test_iiwa14_qdd_aware_grad.py::test_torch_sysid_grad_unchanged" ], - "started_at": "2026-10-24T08:59:09Z" + "started_at": "2026-11-01T07:33:49Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:01:21Z", + "ended_at": "2026-11-01T07:33:51Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6837,14 +7553,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "3f06bbcae732b88e6fd3d9550a1913dfe93ecd01fc6d3e2ccb56bd17ba4de89d", + "digest": "cafcda7fe5ebb751a3b27577ec9f186fd42a274c26a22a5adaf35eee67d10cb7", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6854,7 +7571,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6895,12 +7612,12 @@ "test/python_wrappers/test_iiwa14_smoke.py::test_integrator_gradient[trapezoidal]", "test/python_wrappers/test_iiwa14_smoke.py::test_integrator_gradient[constant_acceleration]" ], - "started_at": "2026-10-24T09:01:19Z" + "started_at": "2026-11-01T07:33:49Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:01:27Z", + "ended_at": "2026-11-01T07:33:57Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6916,14 +7633,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "a26cb4e82c87b42edc2965ec9cef4836e147714b1610e158759c6609c31c7339", + "digest": "1eb4fcd71eaea92a7c9d7107fa90001e21bf5ad7902b40b65a3c761263cd1113", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -6933,7 +7651,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -6969,12 +7687,12 @@ "test/python_wrappers/test_iiwa14_torch_smoke.py::test_ptier1_com_ccrba_tuple_matches_numpy", "test/python_wrappers/test_iiwa14_torch_smoke.py::test_ptier1_runtime_ee_matches_numpy" ], - "started_at": "2026-10-24T09:01:24Z" + "started_at": "2026-11-01T07:33:54Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:01:36Z", + "ended_at": "2026-11-01T07:34:03Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -6990,14 +7708,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "5fb59168929e3436a6bd9c6cd9c81902421e4ae52062df09759b1235225fdbcf", + "digest": "4a7aca176d7090848b4ac6226e595e4e006cf58c68447d90c94eca9002c55101", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7007,7 +7726,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7050,12 +7769,12 @@ "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-constant_acceleration-integrator]", "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-constant_acceleration-integrator_gradient]" ], - "started_at": "2026-10-24T09:01:29Z" + "started_at": "2026-11-01T07:33:59Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:01:41Z", + "ended_at": "2026-11-01T07:34:11Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7071,14 +7790,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "d1eeef21402b7722d8902fe4098819aa2380372c9d0b2aa16e69d640ae9a490a", + "digest": "81b254069cb0620ef917dfdbff300a93099952445ea4c4db6da7844e033d10cb", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7088,7 +7808,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7105,12 +7825,12 @@ "test/python_wrappers/test_jax_host_transfer.py::test_default_floor_is_the_measured_crossover", "test/python_wrappers/test_jax_host_transfer.py::test_to_host_uses_the_pinned_host_memory_kind_when_offered" ], - "started_at": "2026-10-24T09:01:39Z" + "started_at": "2026-11-01T07:34:09Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:22:27Z", + "ended_at": "2026-11-01T08:01:03Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7126,14 +7846,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "c956b0ddc51b3656c3d69acc8083a0bca824172ac5ed4e94a9b51b6773ce8da7", + "digest": "ce9030fea98ff6e3431ba7a6b6c9b1916e02a78fe390a201853636924638add1", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7143,7 +7864,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7169,12 +7890,12 @@ "test/python_wrappers/test_joint_dynamics.py::test_rbdreference_oracle_dqd_block_matches_fd[fr3]", "test/python_wrappers/test_joint_dynamics.py::test_rbdreference_oracle_dqd_block_matches_fd[fr3-tanh]" ], - "started_at": "2026-10-24T09:01:44Z" + "started_at": "2026-11-01T07:34:14Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:32:54Z", + "ended_at": "2026-11-01T07:50:49Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7190,14 +7911,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "32f850ca72a261634106aab3423abde4be5e7983d6c8110cd0eec6d358280d52", + "digest": "fc3ecbb99cc1541b44c4498edbb74b0bb7a083816f17903338e64d6100c2cbb8", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7207,7 +7929,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7233,12 +7955,12 @@ "test/python_wrappers/test_joint_dynamics_ffi.py::test_second_order_damping_contract[fr3-tanh]", "test/python_wrappers/test_joint_dynamics_ffi.py::test_nle_coriolis_damping_convention[fr3-tanh]" ], - "started_at": "2026-10-24T09:22:33Z" + "started_at": "2026-11-01T07:36:05Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:33:07Z", + "ended_at": "2026-11-01T07:51:06Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7254,14 +7976,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "8f94e9848dbdba78c5330de12c357ba08a87aee41aef8ffa2cb966b99fb74b58", + "digest": "61072d7e846e87492df2854bf7a6d2c63dd15c4bb6f0ff2301c0dce2082febf1", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7271,7 +7994,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7322,12 +8045,12 @@ "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t256-iiwa14]", "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t256-go2]" ], - "started_at": "2026-10-24T09:32:55Z" + "started_at": "2026-11-01T07:50:51Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:33:46Z", + "ended_at": "2026-11-01T07:52:18Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7343,14 +8066,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "60b8cec7b4f32b556f564f6ce7a2c9aec37f93010ce35f82ef81d3f33d71cc44", + "digest": "51bbc88d51dd83fed38472873c39235a0099baf9758d703b8b6ac17b4a06f14e", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7360,7 +8084,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7373,12 +8097,12 @@ "test/python_wrappers/test_mjx_fixed_base_noop.py::test_fixed_base_mujoco_convention_is_noop", "test/python_wrappers/test_mjx_fixed_base_noop.py::test_fixed_base_mujoco_convention_is_noop_jax_torch" ], - "started_at": "2026-10-24T09:33:10Z" + "started_at": "2026-11-01T07:51:11Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:33:53Z", + "ended_at": "2026-11-01T07:52:24Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7394,14 +8118,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "d9022c6658df239410391353bd6f0e00845e256475edb226dae6e9c8c62f100c", + "digest": "7fe3834de4b0b4bc53ea672cd1d5aef99b22ea2a2574d9728e22f4bd72c0dcf8", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7411,7 +8136,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7428,12 +8153,12 @@ "test/python_wrappers/test_mjx_twins_contexts.py::test_no_direct_op_or_twin_writes_outside_its_output", "test/python_wrappers/test_mjx_twins_contexts.py::test_jax_mujoco_forward_and_grad" ], - "started_at": "2026-10-24T09:33:50Z" + "started_at": "2026-11-01T07:52:22Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:33:57Z", + "ended_at": "2026-11-01T07:52:28Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7449,14 +8174,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "32392bfe1c1750b85f25ad3200a80d544e262799df533806f83cfe9d76a2542e", + "digest": "bd9bf7e48dc372db114e15c479b5d9791301ae725e86543a6b0ab126b39f52de", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7466,7 +8192,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7489,12 +8215,12 @@ "test/python_wrappers/test_numpy_host_transfer.py::test_no_direct_op_writes_outside_its_output", "test/python_wrappers/test_numpy_host_transfer.py::test_bad_out_is_refused" ], - "started_at": "2026-10-24T09:33:55Z" + "started_at": "2026-11-01T07:52:27Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:34:03Z", + "ended_at": "2026-11-01T07:52:34Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7510,14 +8236,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "5e1684015a770c15c0bed6fb71a8aa438ed4035d3d813a035baa2adc114e6d04", + "digest": "b0447aa1dd7e870e1dc7e063d0e5a96e9e54376452dcf36ca8ffc3237f888cbd", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7527,7 +8254,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7539,12 +8266,12 @@ "node_ids": [ "test/python_wrappers/test_numpy_vs_async_framework_race.py::test_numpy_call_is_not_clobbered_by_in_flight_jax_work" ], - "started_at": "2026-10-24T09:34:00Z" + "started_at": "2026-11-01T07:52:32Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:34:09Z", + "ended_at": "2026-11-01T07:52:39Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7560,14 +8287,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "6abda96f0562eda125e934198e7b5020f47e373ca46e37f00490837e5c354ea3", + "digest": "7f4266e1818c46905c38881c311755f8e376d184e73904c92b99ec6d834d620a", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7577,7 +8305,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7596,12 +8324,12 @@ "test/python_wrappers/test_operand_validation.py::test_plant_operands_must_carry_the_leading_batch", "test/python_wrappers/test_operand_validation.py::test_tool_wrench_and_runtime_target_are_validated" ], - "started_at": "2026-10-24T09:34:06Z" + "started_at": "2026-11-01T07:52:37Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:37:44Z", + "ended_at": "2026-11-01T07:57:27Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7617,14 +8345,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "3a8b5eacc4358c245fbaa7c9545a71530a0699b09dea8e94c002c241ae67874f", + "digest": "36243a7a7dcb2dc709b1ec154aa2131816b763674cf0fd675903dede2e38df02", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7634,7 +8363,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7651,12 +8380,12 @@ "test/python_wrappers/test_ps5_value_ops.py::test_cmm_time_variation", "test/python_wrappers/test_ps5_value_ops.py::test_dccrba_cmm_mimic_supported" ], - "started_at": "2026-10-24T09:34:11Z" + "started_at": "2026-11-01T07:52:42Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:37:53Z", + "ended_at": "2026-11-01T07:57:42Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7672,14 +8401,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "01af9b7d2c12c5eb576bf4f685f60c8aca48534cc3b19563b01c8a3270e4306a", + "digest": "0fa46618abb5d289c744ac7bdacb678988b3acee33e179f016d4aff1e5a2d879", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7689,7 +8419,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 24, + "file_count": 29, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7703,12 +8433,12 @@ "test/python_wrappers/test_release_numerics.py::test_release_numerical_matrix[iiwa14-False]", "test/python_wrappers/test_release_numerics.py::test_release_numerical_matrix[go2-True]" ], - "started_at": "2026-10-24T09:37:46Z" + "started_at": "2026-11-01T07:57:34Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:38:04Z", + "ended_at": "2026-11-01T07:57:57Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7724,14 +8454,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "18c75aca8a4c28e07d052dabded0c5adb3f46b8ebfcd6e740d40d5b75c5d5068", + "digest": "e91cddebdfcc7baaebad4574c2dbe9d7efbce6e05a21dadba9c62197c7cf348d", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7741,7 +8472,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7769,12 +8500,12 @@ "test/python_wrappers/test_runtime_contexts.py::test_replay_admission_racing_mutation_and_close_never_deadlocks", "test/python_wrappers/test_runtime_contexts.py::test_construction_does_not_create_the_default_context_and_a_slab_still_installs" ], - "started_at": "2026-10-24T09:37:56Z" + "started_at": "2026-11-01T07:57:49Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:39:52Z", + "ended_at": "2026-11-01T08:00:12Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7790,14 +8521,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "0935e501482f2ac93c44c42a91796218de461af5afcd7cc63a140075086c9d66", + "digest": "7c6c4c80f44fd4f307cc7aaee95cb044d9a1378324d1b2ce2d5bf6c158a35f8c", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7807,7 +8539,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7827,12 +8559,12 @@ "test/python_wrappers/test_runtime_inertia.py::test_inertia_poke_seen_across_surfaces", "test/python_wrappers/test_runtime_inertia.py::test_runtime_equals_baked_so" ], - "started_at": "2026-10-24T09:38:06Z" + "started_at": "2026-11-01T07:57:59Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T09:56:14Z", + "ended_at": "2026-11-01T08:23:09Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7848,14 +8580,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "74fda523edd09e9f1ac4245dde7903f51a4c419d86ebbb087ec009438c9f6d7b", + "digest": "567497379d58a9203a605190cae3b948d97d834bb6ccfbecfd6b483d62c4a6e8", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7865,7 +8598,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7885,12 +8618,12 @@ "test/python_wrappers/test_runtime_joint_dynamics.py::test_fr3_mimic_untouched_equals_baked_and_zero_toggles", "test/python_wrappers/test_runtime_joint_dynamics.py::test_fr3_tanh_width_row_pokes_and_matches_analytic" ], - "started_at": "2026-10-24T09:39:57Z" + "started_at": "2026-11-01T08:00:15Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T10:00:06Z", + "ended_at": "2026-11-01T08:05:46Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7906,14 +8639,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "bc588b59394a397ddf8a54b2c3884ceb9b0e4d96b3519ec99995b8f49b72d5c3", + "digest": "a060c094a9c918f1eebd201cd22d2506e19a28cc0e900ff0b72fd7011c860aa9", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7923,7 +8657,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7939,12 +8673,12 @@ "test/python_wrappers/test_runtime_transform.py::test_transform_poke_seen_across_surfaces", "test/python_wrappers/test_runtime_transform.py::test_thread_invariance" ], - "started_at": "2026-10-24T09:56:20Z" + "started_at": "2026-11-01T08:01:05Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T10:00:13Z", + "ended_at": "2026-11-01T08:05:56Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -7960,14 +8694,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "431d3c7e4713eef3591246294a5bfe0be061786fc61bf43ced5a69bae247f803", + "digest": "f1a3b721a75fba86119106677a4c382abb96e63a97340502e0e73dd2ce5ab06d", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -7977,7 +8712,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -7994,12 +8729,12 @@ "test/python_wrappers/test_shared_runtime_lifetime.py::test_late_framework_view_preserves_live_inertia[torch]", "test/python_wrappers/test_shared_runtime_lifetime.py::test_late_framework_view_preserves_live_inertia[jax]" ], - "started_at": "2026-10-24T10:00:11Z" + "started_at": "2026-11-01T08:05:52Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T10:02:33Z", + "ended_at": "2026-11-01T08:09:17Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -8015,14 +8750,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "dbd0aae7536d857f93446e037c490d014f1c91b4dac00894a8cd45ff940751ab", + "digest": "e4dcadbd3cd4f1880193ff614e070d07a390ba820965921b7895a240ee7c5fff", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -8032,7 +8768,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -8056,12 +8792,12 @@ "test/python_wrappers/test_subset_build.py::test_subset_so_is_smaller_than_default", "test/python_wrappers/test_subset_build.py::test_subset_does_not_rekey_default" ], - "started_at": "2026-10-24T10:00:16Z" + "started_at": "2026-11-01T08:05:57Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T10:04:55Z", + "ended_at": "2026-11-01T08:12:40Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -8077,14 +8813,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "31329781257d957e78d48346112de417a0f23d4e71d99dcb7e05c79dd2bd8872", + "digest": "445331fc96809e4e07f76f403bd4107b708a3358dbadfa21644e05d232564e3a", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -8094,7 +8831,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -8117,12 +8854,12 @@ "test/python_wrappers/test_subset_build_jax.py::test_subset_so_is_smaller_than_default", "test/python_wrappers/test_subset_build_jax.py::test_subset_does_not_rekey_default" ], - "started_at": "2026-10-24T10:02:36Z" + "started_at": "2026-11-01T08:09:19Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T10:07:15Z", + "ended_at": "2026-11-01T08:16:05Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -8138,14 +8875,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "55bff1e0bbf07115d41629639b9efcefaa5bff03c3a43653899d026606c37e1a", + "digest": "54b498592aa465cd899f174580bf1aa5d194ca44c709926a761cc8d929a8993a", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -8155,7 +8893,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -8177,12 +8915,12 @@ "test/python_wrappers/test_subset_build_torch.py::test_subset_so_is_smaller_than_default", "test/python_wrappers/test_subset_build_torch.py::test_subset_does_not_rekey_default" ], - "started_at": "2026-10-24T10:04:57Z" + "started_at": "2026-11-01T08:12:45Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T10:07:19Z", + "ended_at": "2026-11-01T08:16:08Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -8198,14 +8936,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "2c9b204e464c91322c2b509ff8c22ec5ef827b905305ef135df4256222442eb2", + "digest": "76ba7fd7454490a77a73efec30713884c9e2790c9651f1f0f5b7f1b4b0731960", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -8215,7 +8954,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -8232,12 +8971,12 @@ "test/python_wrappers/test_tool.py::test_tool_fext_matches_host_map", "test/python_wrappers/test_tool.py::test_detach_restores_baked" ], - "started_at": "2026-10-24T10:07:17Z" + "started_at": "2026-11-01T08:16:07Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T10:07:23Z", + "ended_at": "2026-11-01T08:16:13Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -8253,14 +8992,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "64d9f7057b7ec2e6c65537a77f285a6d1808072e29d314c73fe96c29c76cff8e", + "digest": "90846de535f2f8e3036cea2cc85783d3c6afa79e34cb598ca1b7e5783bb36fb6", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -8270,7 +9010,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -8284,12 +9024,12 @@ "test/python_wrappers/test_torch_host_transfer.py::test_replay_into_matches_eager_and_reuses_the_mirror", "test/python_wrappers/test_torch_host_transfer.py::test_copy_to_host_refuses_mismatched_tuple" ], - "started_at": "2026-10-24T10:07:22Z" + "started_at": "2026-11-01T08:16:12Z" }, { "carried": null, "dirty": false, - "ended_at": "2026-10-24T10:07:29Z", + "ended_at": "2026-11-01T08:16:19Z", "environment": { "gpu_info": { "cuda_visible_devices": null, @@ -8305,14 +9045,15 @@ ], "source": "nvidia-smi" }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", + "plugin_version": "0.5.0", "pytest_version": "9.0.3", "python_version": "3.12.3" }, "fingerprint": { "algorithm": "sha256-manifest-v2", - "digest": "ac2c3d6ba93959e889a202ee3335ed2c4f5679e9464a076edad85b9dc9833b8d", + "digest": "3ce3eb62d3b83cc9cf50f2f914fceb2dc9fc0bf3d67aafc17beb52c585b149f7", "excluded_paths": [ "gpu-proof.json", "grid_assets/launch_configs/README.md", @@ -8322,7 +9063,7 @@ "grid_codegen/collision/HANDOFF.md" ], "extra_paths": [], - "file_count": 23, + "file_count": 28, "included_paths": [ "bindings/grid_rbd", "bindings/src", @@ -8334,9796 +9075,10777 @@ "node_ids": [ "test/python_wrappers/test_two_robot_process.py::test_two_so_pools_are_independent" ], - "started_at": "2026-10-24T10:07:27Z" + "started_at": "2026-11-01T08:16:17Z" + }, + { + "carried": null, + "dirty": false, + "ended_at": "2026-11-01T08:16:31Z", + "environment": { + "gpu_info": { + "cuda_visible_devices": null, + "devices": [ + { + "compute_capability": "12.0", + "driver_version": "615.71.09", + "index": "0", + "memory": "32607 MiB", + "name": "NVIDIA GeForce RTX 5090", + "uuid": "GPU-bf781ab0-9632-4b9a-497b-c3b846aae70c" + } + ], + "source": "nvidia-smi" + }, + "nvcc_version": "nvcc: NVIDIA (R) Cuda compiler driver\nCopyright (c) 2005-2026 NVIDIA Corporation\nBuilt on Fri_May_08_10:53:34_AM_PDT_2026\nCuda compilation tools, release 13.2, V13.2.86\nBuild cuda_13.2.r13.2/compiler.37953736_0", + "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", + "plugin_version": "0.5.0", + "pytest_version": "9.0.3", + "python_version": "3.12.3" + }, + "fingerprint": { + "algorithm": "sha256-manifest-v2", + "digest": "b059d0e470ccb5b18248910e3dacf5d532f57e1cc9b81fbaa992d4c94b52261e", + "excluded_paths": [ + "gpu-proof.json", + "grid_assets/launch_configs/README.md", + "grid_assets/robots/URDF_SOURCES.md", + "grid_assets/robots/drake/README.md", + "grid_codegen/README.md", + "grid_codegen/collision/HANDOFF.md" + ], + "extra_paths": [], + "file_count": 28, + "included_paths": [ + "bindings/grid_rbd", + "bindings/src", + "external/RBDReference", + "test/python_wrappers/test_workspace_slots_direct_launch.py" + ] + }, + "name": "test_workspace_slots_direct_launch", + "node_ids": [ + "test/python_wrappers/test_workspace_slots_direct_launch.py::test_direct_launch_bit_identical_under_reduced_slots[jax]", + "test/python_wrappers/test_workspace_slots_direct_launch.py::test_direct_launch_bit_identical_under_reduced_slots[torch]" + ], + "started_at": "2026-11-01T08:16:22Z" + } + ], + "signature": { + "value": "Ql00cUPoIbsGoGvwJGEjNX1eOSr+HPblBNp4X+Pj9/uJ6PuALw1NsJO+4dzzneFfLexNfZhYp/YOrNJL7U2uAg==" + }, + "signer": { + "algorithm": "ed25519", + "backend": "ssh-local", + "github_user": "plancherb1", + "key_fingerprint": "SHA256:hfyUcDJTDLry3j7nJ9V2X8iEIgNFmFJQKGQirOdyWtY" + }, + "tests": [ + { + "checks": [], + "duration_s": 2.8949, + "node_id": "test/cuda_equivalents/test_cuda_batched_host_wrapper.py::test_cuda_batched_host_wrapper_matches_reference[iiwa14-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 4.2585, + "node_id": "test/cuda_equivalents/test_cuda_batched_host_wrapper.py::test_cuda_batched_host_wrapper_matches_reference[go2-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 3.3458, + "node_id": "test/cuda_equivalents/test_cuda_branching_skew_equivalence.py::test_cuda_skew_aba_minv_matches_reference[branching]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 2.9214, + "node_id": "test/cuda_equivalents/test_cuda_branching_skew_equivalence.py::test_cuda_skew_aba_minv_matches_reference[serial]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 5.767, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_mimic_safe_matches_reference[fr3-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 13.4886, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_mimic_safe_matches_reference[h1_2-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 5.3667, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_mimic_safe_matches_reference[iiwa14-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 7.394, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_mimic_safe_matches_reference[fr3-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 20.1781, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_mimic_safe_matches_reference[h1_2-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 7.6473, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[iiwa14-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 9.6629, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[go2-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 20.8307, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[g1-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 7.6663, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[rizon4-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 7.8584, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[gen3-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 11.0518, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[fetch-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 12.972, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[baxter-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 15.0254, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[iiwa14-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 19.4482, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[go2-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 43.4708, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[g1-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 14.089, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[rizon4-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 13.8739, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[gen3-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 21.988, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[fetch-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 24.5873, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[baxter-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 2.4276, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_host_spill.py::test_cuda_centroidal_host_spill_matches_reference[iiwa14-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 5.4348, + "node_id": "test/cuda_equivalents/test_cuda_centroidal_host_spill.py::test_cuda_centroidal_host_spill_matches_reference[go2-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.6493, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_fixed_default_header_keeps_gradient_paths_all_shared", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.5686, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_fixed_forced_low_shared_header_selects_fallbacks", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.586, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_integrator_gradient_codegen_now_emits[floating-integrators]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.3497, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_integrator_gradient_codegen_now_emits[floating-all]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.4015, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_gradient_codegen_now_emits[fixed-dynamics-gradients]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.4731, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_gradient_codegen_now_emits[fixed-second-order]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.558, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_gradient_codegen_now_emits[floating-dynamics-gradients]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.5604, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_gradient_codegen_now_emits[floating-second-order]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.3228, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_nongradient_codegen_still_works[dynamics-core]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.3057, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_nongradient_codegen_still_works[kinematics]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.0, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_uses_parent_topology[serial_chain]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.0, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_uses_parent_topology[base_fanout]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.0, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_uses_parent_topology[base_rooted_independent_chains]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.0, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_uses_parent_topology[moving_joint_fanout]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.0, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_uses_parent_topology[deep_moving_joint_fanout]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.1261, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_is_topology_gated[iiwa14-False]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.4638, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_is_topology_gated[go2-False]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.1622, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_is_topology_gated[gen3-False]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.1702, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_is_topology_gated[rizon4-False]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.212, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_is_topology_gated[fr3-True]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.3035, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_is_topology_gated[fetch-True]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.2999, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_codegen_does_not_select_algorithms_by_fixture_name_or_filename", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.1921, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_header_does_not_require_second_order_kernels[iiwa14-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.5327, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_header_does_not_require_second_order_kernels[go2-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.9936, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_second_order_opt_in_header_compiles[iiwa14-idsva-body-frame-only]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.1776, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_second_order_opt_in_header_compiles[iiwa14-idsva-body-frame-fdsva]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.3213, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_second_order_opt_in_header_compiles[go2-idsva-body-frame-only]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.0089, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_second_order_opt_in_header_compiles[iiwa14-idsva-body-frame-world-frame]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.1306, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[iiwa14-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.4719, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[go2-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.0199, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[g1-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.309, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[fetch-fixed]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.2048, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[iiwa14-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.5577, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[go2-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.0963, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[g1-floating]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.5758, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_generated_header_includes_grid_data_variants_and_no_rnea_alias", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.3914, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_controls_and_helpers_are_generated", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.9247, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_default_cxx17_compiles_without_mathdx", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.9359, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_base_strided_helpers_compile", + "outcome": "passed", + "phase": "call" }, { - "carried": null, - "dirty": false, - "ended_at": "2026-10-24T10:07:40Z", - "environment": { - "gpu_info": { - "cuda_visible_devices": null, - "devices": [ - { - "compute_capability": "12.0", - "driver_version": "615.71.09", - "index": "0", - "memory": "32607 MiB", - "name": "NVIDIA GeForce RTX 5090", - "uuid": "GPU-bf781ab0-9632-4b9a-497b-c3b846aae70c" - } - ], - "source": "nvidia-smi" - }, - "platform": "Linux-7.0.0-38-generic-x86_64-with-glibc2.39", - "plugin_version": "0.4.1", - "pytest_version": "9.0.3", - "python_version": "3.12.3" - }, - "fingerprint": { - "algorithm": "sha256-manifest-v2", - "digest": "1554f23c2acdfa22d7edee2128b158922485a55c8099945d618f3e8b2a09dbe6", - "excluded_paths": [ - "gpu-proof.json", - "grid_assets/launch_configs/README.md", - "grid_assets/robots/URDF_SOURCES.md", - "grid_assets/robots/drake/README.md", - "grid_codegen/README.md", - "grid_codegen/collision/HANDOFF.md" - ], - "extra_paths": [], - "file_count": 23, - "included_paths": [ - "bindings/grid_rbd", - "bindings/src", - "external/RBDReference", - "test/python_wrappers/test_workspace_slots_direct_launch.py" - ] - }, - "name": "test_workspace_slots_direct_launch", - "node_ids": [ - "test/python_wrappers/test_workspace_slots_direct_launch.py::test_direct_launch_bit_identical_under_reduced_slots[jax]", - "test/python_wrappers/test_workspace_slots_direct_launch.py::test_direct_launch_bit_identical_under_reduced_slots[torch]" - ], - "started_at": "2026-10-24T10:07:32Z" - } - ], - "signature": { - "value": "++dsWUUoPwQdOPaeTb1j+lUkp6PMjoCp+OKK75+fsG9tDR1JnntwqiN4T2rYtxuAoJOC4zQcsdD00B6jsh6gDg==" - }, - "signer": { - "algorithm": "ed25519", - "backend": "ssh-local", - "github_user": "plancherb1", - "key_fingerprint": "SHA256:hfyUcDJTDLry3j7nJ9V2X8iEIgNFmFJQKGQirOdyWtY" - }, - "tests": [ + "checks": [], + "duration_s": 7.9659, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_dynamics_grid_data_variant_wrappers_compile", + "outcome": "passed", + "phase": "call" + }, { "checks": [], - "duration_s": 2.9284, - "node_id": "test/cuda_equivalents/test_cuda_batched_host_wrapper.py::test_cuda_batched_host_wrapper_matches_reference[iiwa14-fixed]", + "duration_s": 1.2015, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_kinematics_grid_data_variant_wrappers_compile", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.9869, - "node_id": "test/cuda_equivalents/test_cuda_batched_host_wrapper.py::test_cuda_batched_host_wrapper_matches_reference[go2-floating]", + "duration_s": 1.6144, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_fixed_kinematics_derivative_wrappers_compile", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.389, - "node_id": "test/cuda_equivalents/test_cuda_branching_skew_equivalence.py::test_cuda_skew_aba_minv_matches_reference[branching]", + "duration_s": 2.9966, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_kinematics_derivative_wrappers_compile", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0748, - "node_id": "test/cuda_equivalents/test_cuda_branching_skew_equivalence.py::test_cuda_skew_aba_minv_matches_reference[serial]", + "duration_s": 1.2806, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_grid_data_variant_invalid_wrappers_fail_to_compile[dynamics_api_rejects_kinematics_grid_data-\\n#include \"grid.cuh\"\\n\\nint main() {\\n using T = float;\\n grid::gridData *data = nullptr;\\n grid::robotModel *model = nullptr;\\n cudaStream_t *streams = nullptr;\\n dim3 blocks(1, 1, 1);\\n dim3 threads(32, 1, 1);\\n grid::inverse_dynamics(\\n data, model, static_cast(-9.81), 1, blocks, threads, streams);\\n return 0;\\n}\\n-inverse_dynamics requires all-data or dynamics gridData]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.8065, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_mimic_safe_matches_reference[fr3-fixed]", + "duration_s": 1.1934, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_grid_data_variant_invalid_wrappers_fail_to_compile[kinematics_api_rejects_dynamics_grid_data-\\n#include \"grid.cuh\"\\n\\nint main() {\\n using T = float;\\n grid::gridData *data = nullptr;\\n grid::robotModel *model = nullptr;\\n cudaStream_t *streams = nullptr;\\n dim3 blocks(1, 1, 1);\\n dim3 threads(32, 1, 1);\\n grid::end_effector_pose(\\n data, model, 1, blocks, threads, streams);\\n return 0;\\n}\\n-end_effector_pose requires all-data or kinematics gridData]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.1076, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_mimic_safe_matches_reference[h1_2-fixed]", + "duration_s": 0.3274, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_dynamics_core_profile_generates_only_core_dynamics_hosts", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.4515, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_mimic_safe_matches_reference[iiwa14-fixed]", + "duration_s": 0.3657, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_kinematics_profile_generates_kinematics_hosts_only", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.3543, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_mimic_safe_matches_reference[fr3-floating]", + "duration_s": 0.2668, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_algorithm_list_override_expands_dependencies", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 17.7775, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_mimic_safe_matches_reference[h1_2-floating]", + "duration_s": 1.4801, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_joint_limits_land_at_true_q_offsets", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.4404, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[iiwa14-fixed]", + "duration_s": 1.7642, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_tracking_cost_fc_overloads_emit_and_compile", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.2199, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[go2-fixed]", + "duration_s": 1.003, + "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_arena_carve_structs_compile", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 18.5781, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[g1-fixed]", + "duration_s": 1.9027, + "node_id": "test/cuda_equivalents/test_cuda_collision_complete.py::test_complete_collision_model_matches_cpu[1]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.4046, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[rizon4-fixed]", + "duration_s": 1.9495, + "node_id": "test/cuda_equivalents/test_cuda_collision_complete.py::test_complete_collision_model_matches_cpu[32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.4374, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[gen3-fixed]", + "duration_s": 1.9122, + "node_id": "test/cuda_equivalents/test_cuda_collision_complete.py::test_complete_collision_model_matches_cpu[100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.7801, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[fetch-fixed]", + "duration_s": 1.7219, + "node_id": "test/cuda_equivalents/test_cuda_collision_config_free.py::test_collision_config_free", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 12.4231, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[baxter-fixed]", + "duration_s": 2.2029, + "node_id": "test/cuda_equivalents/test_cuda_collision_config_free.py::test_collision_config_free_real_robot", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.2731, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[iiwa14-floating]", + "duration_s": 3.0762, + "node_id": "test/cuda_equivalents/test_cuda_collision_config_free.py::test_collision_self_collision", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.3018, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[go2-floating]", + "duration_s": 2.5448, + "node_id": "test/cuda_equivalents/test_cuda_collision_cost.py::test_collision_cost_fd", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 32.6223, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[g1-floating]", + "duration_s": 2.1573, + "node_id": "test/cuda_equivalents/test_cuda_collision_environment.py::test_owned_environment_matches_a_hand_upload[single]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.4207, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[rizon4-floating]", + "duration_s": 2.2572, + "node_id": "test/cuda_equivalents/test_cuda_collision_environment.py::test_owned_environment_matches_a_hand_upload[two]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.3444, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[gen3-floating]", + "duration_s": 1.6413, + "node_id": "test/cuda_equivalents/test_cuda_collision_geometry.py::test_collision_geometry", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 17.8572, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[fetch-floating]", + "duration_s": 0.1729, + "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[single-env-1]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 20.138, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_equivalence.py::test_cuda_centroidal_matches_reference[baxter-floating]", + "duration_s": 0.1629, + "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[single-env-32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3628, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_host_spill.py::test_cuda_centroidal_host_spill_matches_reference[iiwa14-fixed]", + "duration_s": 0.1587, + "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[single-env-100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1024, - "node_id": "test/cuda_equivalents/test_cuda_centroidal_host_spill.py::test_cuda_centroidal_host_spill_matches_reference[go2-floating]", + "duration_s": 0.1706, + "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[single-self-1]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5142, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_fixed_default_header_keeps_gradient_paths_all_shared", + "duration_s": 0.1569, + "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[single-self-32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5051, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_fixed_forced_low_shared_header_selects_fallbacks", + "duration_s": 0.1603, + "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[single-self-100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5362, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_integrator_gradient_codegen_now_emits[floating-integrators]", + "duration_s": 0.1639, + "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-env-1]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.1604, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_integrator_gradient_codegen_now_emits[floating-all]", + "duration_s": 0.1615, + "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-env-32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3427, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_gradient_codegen_now_emits[fixed-dynamics-gradients]", + "duration_s": 0.1477, + "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-env-100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3972, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_gradient_codegen_now_emits[fixed-second-order]", + "duration_s": 0.1525, + "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-1]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.483, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_gradient_codegen_now_emits[floating-dynamics-gradients]", + "duration_s": 0.1661, + "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4271, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_gradient_codegen_now_emits[floating-second-order]", + "duration_s": 0.1593, + "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2743, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_nongradient_codegen_still_works[dynamics-core]", + "duration_s": 2.5596, + "node_id": "test/cuda_equivalents/test_cuda_collision_margins.py::test_native_separate_margins[False]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2941, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_mimic_nongradient_codegen_still_works[kinematics]", + "duration_s": 2.7394, + "node_id": "test/cuda_equivalents/test_cuda_collision_margins.py::test_native_separate_margins[True]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_uses_parent_topology[serial_chain]", + "duration_s": 1.3959, + "node_id": "test/cuda_equivalents/test_cuda_collision_native.py::test_static_separate_margin_thresholds", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_uses_parent_topology[base_fanout]", + "duration_s": 2.7815, + "node_id": "test/cuda_equivalents/test_cuda_collision_native.py::test_collision_native_two_tier", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_uses_parent_topology[base_rooted_independent_chains]", + "duration_s": 2.937, + "node_id": "test/cuda_equivalents/test_cuda_collision_native.py::test_collision_native_self_collision", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_uses_parent_topology[moving_joint_fanout]", + "duration_s": 2.221, + "node_id": "test/cuda_equivalents/test_cuda_collision_pairs.py::test_collision_distance_pairs_fd", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_uses_parent_topology[deep_moving_joint_fanout]", + "duration_s": 2.0156, + "node_id": "test/cuda_equivalents/test_cuda_collision_selfcc_rows.py::test_self_collision_rows_fd", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1142, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_is_topology_gated[iiwa14-False]", + "duration_s": 2.548, + "node_id": "test/cuda_equivalents/test_cuda_collision_two_tier.py::test_collision_two_tier_matches_fine", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3624, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_is_topology_gated[go2-False]", + "duration_s": 0.3008, + "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_sphere_tables_match_the_baked_collision_batch", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1477, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_is_topology_gated[gen3-False]", + "duration_s": 2.6751, + "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1516, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_is_topology_gated[rizon4-False]", + "duration_s": 2.9473, + "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[64]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1913, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_is_topology_gated[fr3-True]", + "duration_s": 2.6223, + "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[256]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2746, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_idsva_so_reference_order_repair_is_topology_gated[fetch-True]", + "duration_s": 3.631, + "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_two_tier_cascade[32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2881, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_codegen_does_not_select_algorithms_by_fixture_name_or_filename", + "duration_s": 3.5438, + "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_two_tier_cascade[128]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.0179, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_header_does_not_require_second_order_kernels[iiwa14-floating]", + "duration_s": 9.3286, + "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_separate_environment_self_margins[False]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3587, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_header_does_not_require_second_order_kernels[go2-floating]", + "duration_s": 9.3655, + "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_separate_environment_self_margins[True]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.7728, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_second_order_opt_in_header_compiles[iiwa14-idsva-body-frame-only]", + "duration_s": 4.4982, + "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_margin_and_self_toggle_single_tier", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.947, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_second_order_opt_in_header_compiles[iiwa14-idsva-body-frame-fdsva]", + "duration_s": 5.1318, + "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_margin_and_self_toggle_two_tier_cascade", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.0503, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_second_order_opt_in_header_compiles[go2-idsva-body-frame-only]", + "duration_s": 1.3762, + "node_id": "test/cuda_equivalents/test_cuda_constant_branch_mimic_scale.py::test_constant_branch_keeps_nonunit_mimic_scale[float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.7911, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_second_order_opt_in_header_compiles[iiwa14-idsva-body-frame-world-frame]", + "duration_s": 1.2156, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe0-id-gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1225, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[iiwa14-fixed]", + "duration_s": 1.2114, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe0-frame-jacobian]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3786, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[go2-fixed]", + "duration_s": 1.1798, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe0-frame-jacobian-dot]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.9192, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[g1-fixed]", + "duration_s": 1.2654, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe1-id-gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3162, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[fetch-fixed]", + "duration_s": 1.3438, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe1-frame-jacobian]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1819, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[iiwa14-floating]", + "duration_s": 1.3112, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe1-frame-jacobian-dot]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4326, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[go2-floating]", + "duration_s": 1.3235, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe2-id-gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.0263, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_d2ee_spill_tiers_are_size_and_base_selected[g1-floating]", + "duration_s": 1.6649, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe2-frame-jacobian]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.514, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_generated_header_includes_grid_data_variants_and_no_rnea_alias", + "duration_s": 1.7424, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe2-frame-jacobian-dot]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2791, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_controls_and_helpers_are_generated", + "duration_s": 1.5374, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe3-id-gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.707, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_backend_default_cxx11_compiles_without_mathdx", + "duration_s": 1.5453, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe3-frame-jacobian]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.7292, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_linalg_base_strided_helpers_compile", + "duration_s": 1.3376, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe3-frame-jacobian-dot]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.0901, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_dynamics_grid_data_variant_wrappers_compile", + "duration_s": 3.2987, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe4-id-gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.9831, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_kinematics_grid_data_variant_wrappers_compile", + "duration_s": 3.2852, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe4-frame-jacobian]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3674, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_fixed_kinematics_derivative_wrappers_compile", + "duration_s": 3.0888, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe4-frame-jacobian-dot]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5541, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_floating_kinematics_derivative_wrappers_compile", + "duration_s": 2.4158, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe5-id-gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.0126, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_grid_data_variant_invalid_wrappers_fail_to_compile[dynamics_api_rejects_kinematics_grid_data-\\n#include \"grid.cuh\"\\n\\nint main() {\\n using T = float;\\n grid::gridData *data = nullptr;\\n grid::robotModel *model = nullptr;\\n cudaStream_t *streams = nullptr;\\n dim3 blocks(1, 1, 1);\\n dim3 threads(32, 1, 1);\\n grid::inverse_dynamics(\\n data, model, static_cast(-9.81), 1, blocks, threads, streams);\\n return 0;\\n}\\n-inverse_dynamics requires all-data or dynamics gridData]", + "duration_s": 3.1656, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe5-frame-jacobian]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.0036, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_grid_data_variant_invalid_wrappers_fail_to_compile[kinematics_api_rejects_dynamics_grid_data-\\n#include \"grid.cuh\"\\n\\nint main() {\\n using T = float;\\n grid::gridData *data = nullptr;\\n grid::robotModel *model = nullptr;\\n cudaStream_t *streams = nullptr;\\n dim3 blocks(1, 1, 1);\\n dim3 threads(32, 1, 1);\\n grid::end_effector_pose(\\n data, model, 1, blocks, threads, streams);\\n return 0;\\n}\\n-end_effector_pose requires all-data or kinematics gridData]", + "duration_s": 2.6081, + "node_id": "test/cuda_equivalents/test_cuda_constant_derivatives.py::test_constant_derivatives[derivative_probe5-frame-jacobian-dot]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3221, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_dynamics_core_profile_generates_only_core_dynamics_hosts", + "duration_s": 1.6641, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-crba]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2586, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_kinematics_profile_generates_kinematics_hosts_only", + "duration_s": 1.5325, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-id-gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2285, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_algorithm_list_override_expands_dependencies", + "duration_s": 1.6808, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-body-so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.1966, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_joint_limits_land_at_true_q_offsets", + "duration_s": 1.6514, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe0-world-so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6007, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_tracking_cost_fc_overloads_emit_and_compile", + "duration_s": 1.5254, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-crba]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.8715, - "node_id": "test/cuda_equivalents/test_cuda_codegen_layout.py::test_arena_carve_structs_compile", + "duration_s": 1.7218, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-id-gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7496, - "node_id": "test/cuda_equivalents/test_cuda_collision_complete.py::test_complete_collision_model_matches_cpu[1]", + "duration_s": 1.4041, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-body-so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.667, - "node_id": "test/cuda_equivalents/test_cuda_collision_complete.py::test_complete_collision_model_matches_cpu[32]", + "duration_s": 1.2863, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe1-world-so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7053, - "node_id": "test/cuda_equivalents/test_cuda_collision_complete.py::test_complete_collision_model_matches_cpu[100]", + "duration_s": 1.181, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-crba]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4974, - "node_id": "test/cuda_equivalents/test_cuda_collision_config_free.py::test_collision_config_free", + "duration_s": 1.346, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-id-gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.1423, - "node_id": "test/cuda_equivalents/test_cuda_collision_config_free.py::test_collision_config_free_real_robot", + "duration_s": 1.7289, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-body-so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8594, - "node_id": "test/cuda_equivalents/test_cuda_collision_config_free.py::test_collision_self_collision", + "duration_s": 1.6668, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe2-world-so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6062, - "node_id": "test/cuda_equivalents/test_cuda_collision_cost.py::test_collision_cost_fd", + "duration_s": 1.7729, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-crba]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.1261, - "node_id": "test/cuda_equivalents/test_cuda_collision_environment.py::test_owned_environment_matches_a_hand_upload[single]", + "duration_s": 1.586, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-id-gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3334, - "node_id": "test/cuda_equivalents/test_cuda_collision_environment.py::test_owned_environment_matches_a_hand_upload[two]", + "duration_s": 1.6564, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-body-so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6844, - "node_id": "test/cuda_equivalents/test_cuda_collision_geometry.py::test_collision_geometry", + "duration_s": 1.668, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe3-world-so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2343, - "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[single-env-1]", + "duration_s": 1.3178, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-crba]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2234, - "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[single-env-32]", + "duration_s": 1.7394, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-id-gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2309, - "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[single-env-100]", + "duration_s": 1.6725, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-body-so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2222, - "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[single-self-1]", + "duration_s": 1.5356, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe4-world-so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2272, - "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[single-self-32]", + "duration_s": 1.25, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-crba]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2151, - "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[single-self-100]", + "duration_s": 1.2748, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-id-gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2293, - "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-env-1]", + "duration_s": 1.2549, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-body-so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2183, - "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-env-32]", + "duration_s": 1.2014, + "node_id": "test/cuda_equivalents/test_cuda_constant_dynamics_audit.py::test_constant_dynamics_audit[dynamics_probe5-world-so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2244, - "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-env-100]", + "duration_s": 1.6766, + "node_id": "test/cuda_equivalents/test_cuda_constant_frame_velocity.py::test_constant_frame_velocity[float-False]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2399, - "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-1]", + "duration_s": 1.6549, + "node_id": "test/cuda_equivalents/test_cuda_constant_frame_velocity.py::test_constant_frame_velocity[float-True]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2205, - "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-32]", + "duration_s": 4.2115, + "node_id": "test/cuda_equivalents/test_cuda_constant_mimic.py::test_constant_mimic_kinematics[float-prismatic]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2243, - "node_id": "test/cuda_equivalents/test_cuda_collision_many_hits.py::test_collision_many_hits[two-self-100]", + "duration_s": 4.67, + "node_id": "test/cuda_equivalents/test_cuda_constant_mimic.py::test_constant_mimic_kinematics[float-revolute]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8744, - "node_id": "test/cuda_equivalents/test_cuda_collision_native.py::test_collision_native_two_tier", + "duration_s": 1.4568, + "node_id": "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe0-body]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9361, - "node_id": "test/cuda_equivalents/test_cuda_collision_native.py::test_collision_native_self_collision", + "duration_s": 1.2578, + "node_id": "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe0-world]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.1873, - "node_id": "test/cuda_equivalents/test_cuda_collision_pairs.py::test_collision_distance_pairs_fd", + "duration_s": 1.313, + "node_id": "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe0-coriolis]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.0224, - "node_id": "test/cuda_equivalents/test_cuda_collision_selfcc_rows.py::test_self_collision_rows_fd", + "duration_s": 1.2426, + "node_id": "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe1-body]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5052, - "node_id": "test/cuda_equivalents/test_cuda_collision_two_tier.py::test_collision_two_tier_matches_fine", + "duration_s": 1.263, + "node_id": "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe1-world]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2171, - "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_sphere_tables_match_the_baked_collision_batch", + "duration_s": 1.2508, + "node_id": "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe1-coriolis]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3304, - "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[32]", + "duration_s": 1.208, + "node_id": "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe2-body]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3006, - "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[64]", + "duration_s": 1.1853, + "node_id": "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe2-world]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.302, - "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_single_tier[256]", + "duration_s": 1.1798, + "node_id": "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe2-coriolis]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5766, - "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_two_tier_cascade[32]", + "duration_s": 1.2801, + "node_id": "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe3-body]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.547, - "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_warp_matches_block_two_tier_cascade[128]", + "duration_s": 1.307, + "node_id": "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe3-world]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.0142, - "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_margin_and_self_toggle_single_tier", + "duration_s": 1.2973, + "node_id": "test/cuda_equivalents/test_cuda_constant_second_order.py::test_constant_second_order[second_probe3-coriolis]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.0134, - "node_id": "test/cuda_equivalents/test_cuda_collision_warp.py::test_margin_and_self_toggle_two_tier_cascade", + "duration_s": 3.0404, + "node_id": "test/cuda_equivalents/test_cuda_constant_velocity.py::test_constant_branch_velocity_isolation[float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7398, + "duration_s": 1.7997, "node_id": "test/cuda_equivalents/test_cuda_contact_frame_positions.py::test_contact_frame_positions_and_jacobian[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.2781, + "duration_s": 3.5804, "node_id": "test/cuda_equivalents/test_cuda_contact_frame_positions.py::test_contact_frame_positions_and_jacobian[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.3479, + "duration_s": 7.7521, "node_id": "test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py::test_cuda_continuous_joint_matches_reference_at_wrapped_angles[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.7428, + "duration_s": 15.0427, "node_id": "test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py::test_cuda_continuous_joint_matches_reference_at_wrapped_angles[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.3022, + "duration_s": 20.8536, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.384, + "duration_s": 20.5011, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.8236, + "duration_s": 22.7589, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 18.6085, + "duration_s": 22.3299, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.1476, + "duration_s": 18.6432, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.9696, + "duration_s": 18.6196, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.0407, + "duration_s": 19.618, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.5109, + "duration_s": 20.0298, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.4384, + "duration_s": 20.621, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.2771, + "duration_s": 19.9185, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.4988, + "duration_s": 21.0603, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 18.8883, + "duration_s": 23.849, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 21.483, + "duration_s": 25.5602, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.2973, + "duration_s": 19.1476, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.3734, + "duration_s": 19.499, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.2611, + "duration_s": 19.2661, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.8284, + "duration_s": 19.7788, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.1154, + "duration_s": 20.3065, "node_id": "test/cuda_equivalents/test_cuda_coriolis.py::test_cuda_coriolis_matrix_matches_reference[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 9.2763, + "duration_s": 9.6888, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.9263, + "duration_s": 9.7722, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.5517, + "duration_s": 12.6217, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.2347, + "duration_s": 16.7723, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.4075, + "duration_s": 9.2378, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.1412, + "duration_s": 9.3138, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.1095, + "duration_s": 9.4699, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 9.0551, + "duration_s": 9.9657, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.366, + "duration_s": 12.5135, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.7732, + "duration_s": 9.5759, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.733, + "duration_s": 12.8732, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.8082, + "duration_s": 17.7068, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 23.6799, + "duration_s": 25.3015, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.8855, + "duration_s": 9.5524, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.8268, + "duration_s": 9.3195, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.7182, + "duration_s": 9.33, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 9.7888, + "duration_s": 10.2247, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.1194, + "duration_s": 10.6668, "node_id": "test/cuda_equivalents/test_cuda_coriolis_output_spill.py::test_cuda_coriolis_spill_matches_full[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.1018, + "duration_s": 15.2486, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.186, + "duration_s": 15.4257, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 17.7596, + "duration_s": 18.9561, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 27.1788, + "duration_s": 28.3861, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.0193, + "duration_s": 15.0786, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.8016, + "duration_s": 14.8267, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.7397, + "duration_s": 14.8751, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.5185, + "duration_s": 15.7183, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.5314, + "duration_s": 16.6986, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.8358, + "duration_s": 15.5796, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.1902, + "duration_s": 17.1312, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 20.9116, + "duration_s": 22.5189, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 36.1475, + "duration_s": 37.9455, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.9411, + "duration_s": 15.9191, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.8799, + "duration_s": 15.6708, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.9407, + "duration_s": 16.2907, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.7984, + "duration_s": 16.9405, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.2254, + "duration_s": 17.2018, "node_id": "test/cuda_equivalents/test_cuda_dccrba.py::test_cuda_dccrba_matches_reference[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.0629, + "duration_s": 7.8152, "node_id": "test/cuda_equivalents/test_cuda_ee_named_depth1.py::test_cuda_ee_named_depth1_pose_matches_reference[go2-floating-imu_joint]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.9732, + "duration_s": 2.2247, "node_id": "test/cuda_equivalents/test_cuda_ee_newton_hessian.py::test_ee_pos_cost_hessian_newton_matches_fd[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7152, + "duration_s": 2.9109, "node_id": "test/cuda_equivalents/test_cuda_ee_newton_hessian.py::test_ee_pos_cost_hessian_newton_matches_fd[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.0611, + "duration_s": 2.3408, "node_id": "test/cuda_equivalents/test_cuda_ee_psd_clamp.py::test_ee_pos_cost_hessian_psd_clamp[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.001, + "duration_s": 3.0314, "node_id": "test/cuda_equivalents/test_cuda_ee_psd_clamp.py::test_ee_pos_cost_hessian_psd_clamp[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.6759, + "duration_s": 1.457, + "node_id": "test/cuda_equivalents/test_cuda_ee_workspace_queries.py::test_exact_target_scratch[float-iiwa14-iiwa_joint_ee]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.9273, + "node_id": "test/cuda_equivalents/test_cuda_ee_workspace_queries.py::test_exact_target_scratch[float-go2-FR_foot_joint]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.9781, + "node_id": "test/cuda_equivalents/test_cuda_ee_workspace_queries.py::test_exact_target_scratch[float-go2-]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 14.579, "node_id": "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.8477, + "duration_s": 17.7547, "node_id": "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.9719, + "duration_s": 14.8846, "node_id": "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.4504, + "duration_s": 17.8848, "node_id": "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 17.132, + "duration_s": 18.1474, "node_id": "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_matches_reference[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3829, + "duration_s": 2.5223, "node_id": "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_thread_invariance[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.558, + "duration_s": 2.6516, "node_id": "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_thread_invariance[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.6458, + "duration_s": 6.1136, "node_id": "test/cuda_equivalents/test_cuda_eepose_runtime.py::test_cuda_eepose_runtime_thread_invariance[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.184, + "duration_s": 4.6724, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.2396, + "duration_s": 4.7506, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.4552, + "duration_s": 6.8495, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.6527, + "duration_s": 9.3697, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.9548, + "duration_s": 4.4739, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.8113, + "duration_s": 4.2944, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.8035, + "duration_s": 4.3423, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.5524, + "duration_s": 5.0685, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.7251, + "duration_s": 6.3661, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.4323, + "duration_s": 4.596, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.928, + "duration_s": 5.337, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.2752, + "duration_s": 7.831, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 9.4876, + "duration_s": 10.3117, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.4539, + "duration_s": 4.9337, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.4827, + "duration_s": 4.8271, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.4226, + "duration_s": 4.7973, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1625, + "duration_s": 5.733, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.5169, + "duration_s": 6.0855, "node_id": "test/cuda_equivalents/test_cuda_energy_regressor.py::test_cuda_energy_regressor_matches_reference[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6921, + "duration_s": 2.847, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7264, + "duration_s": 2.7332, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6592, + "duration_s": 2.7771, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1839, + "duration_s": 5.4028, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6736, + "duration_s": 2.7723, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6732, + "duration_s": 2.8078, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6723, + "duration_s": 2.775, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1367, + "duration_s": 5.3557, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6423, + "duration_s": 2.8313, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7252, + "duration_s": 2.8188, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6383, + "duration_s": 2.8004, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1581, + "duration_s": 5.3847, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6777, + "duration_s": 2.7637, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6706, + "duration_s": 2.7881, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6352, + "duration_s": 2.8433, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1481, + "duration_s": 5.4337, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6648, + "duration_s": 2.8507, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6377, + "duration_s": 2.7727, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7054, + "duration_s": 2.7732, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1466, + "duration_s": 5.4345, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6537, + "duration_s": 2.7793, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6518, + "duration_s": 2.8289, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6732, + "duration_s": 2.7867, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1588, + "duration_s": 5.4843, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6755, + "duration_s": 2.8571, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7017, + "duration_s": 2.8748, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6903, + "duration_s": 2.8249, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2453, + "duration_s": 5.5474, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6484, + "duration_s": 2.8427, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6497, + "duration_s": 2.7941, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6457, + "duration_s": 2.8147, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1352, + "duration_s": 5.4602, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6353, + "duration_s": 2.806, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6367, + "duration_s": 2.8911, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7127, + "duration_s": 2.8076, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2379, + "duration_s": 5.4667, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7168, + "duration_s": 2.8006, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7798, + "duration_s": 2.8189, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6697, + "duration_s": 2.7929, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1895, + "duration_s": 5.4164, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[iiwa14-fixed-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9527, + "duration_s": 3.082, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9462, + "duration_s": 3.0284, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9812, + "duration_s": 3.0367, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4186, + "duration_s": 5.7933, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9094, + "duration_s": 3.1504, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.898, + "duration_s": 3.1107, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0035, + "duration_s": 3.0469, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4768, + "duration_s": 5.7778, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9295, + "duration_s": 3.147, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9588, + "duration_s": 3.0643, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.976, + "duration_s": 3.1015, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.44, + "duration_s": 5.8415, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9303, + "duration_s": 3.0853, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9809, + "duration_s": 3.0951, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9235, + "duration_s": 3.1213, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4922, + "duration_s": 5.9002, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9106, + "duration_s": 3.1456, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9766, + "duration_s": 3.1525, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9407, + "duration_s": 3.0939, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4028, + "duration_s": 5.8446, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9439, + "duration_s": 3.119, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9821, + "duration_s": 3.1162, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.909, + "duration_s": 3.1247, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4429, + "duration_s": 5.8304, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9435, + "duration_s": 3.1933, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9942, + "duration_s": 3.1708, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9726, + "duration_s": 3.2181, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4802, + "duration_s": 5.8786, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9719, + "duration_s": 3.0571, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9178, + "duration_s": 3.1214, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9121, + "duration_s": 3.0723, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4734, + "duration_s": 5.7335, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9983, + "duration_s": 3.1178, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9522, + "duration_s": 3.2118, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9606, + "duration_s": 3.1559, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4452, + "duration_s": 5.8067, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0881, + "duration_s": 3.1075, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0425, + "duration_s": 3.2334, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0109, + "duration_s": 3.1602, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.5092, + "duration_s": 5.8869, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[go2-fixed-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5463, + "duration_s": 3.7071, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4859, + "duration_s": 3.6636, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5716, + "duration_s": 3.6232, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.9762, + "duration_s": 6.3947, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.484, + "duration_s": 3.6989, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5648, + "duration_s": 3.6782, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4833, + "duration_s": 3.6627, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.0636, + "duration_s": 6.2722, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5291, + "duration_s": 3.6601, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5884, + "duration_s": 3.7124, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5073, + "duration_s": 3.7357, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.0147, + "duration_s": 6.2892, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.6093, + "duration_s": 3.6324, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4596, + "duration_s": 3.5895, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5207, + "duration_s": 3.6221, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.9973, + "duration_s": 6.2827, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4808, + "duration_s": 3.5912, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5663, + "duration_s": 3.7052, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5387, + "duration_s": 3.6097, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.0727, + "duration_s": 6.3057, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4714, + "duration_s": 3.6493, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5445, + "duration_s": 3.7648, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5625, + "duration_s": 3.774, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.1053, + "duration_s": 6.3574, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.624, + "duration_s": 3.7165, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5758, + "duration_s": 3.6795, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5982, + "duration_s": 3.7089, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.1079, + "duration_s": 6.4002, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4896, + "duration_s": 3.6275, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5081, + "duration_s": 3.7181, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4853, + "duration_s": 3.6658, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.0275, + "duration_s": 6.3522, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4828, + "duration_s": 3.7007, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4947, + "duration_s": 3.624, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5497, + "duration_s": 3.6998, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.9403, + "duration_s": 6.3144, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.8185, + "duration_s": 3.9896, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.7257, + "duration_s": 3.9322, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.7959, + "duration_s": 4.0498, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.3087, + "duration_s": 6.6526, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[g1-fixed-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.152, + "duration_s": 4.5269, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.1724, + "duration_s": 4.4498, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.2505, + "duration_s": 4.4253, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.7568, + "duration_s": 7.2327, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.4063, + "duration_s": 4.6358, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.3247, + "duration_s": 4.5273, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.2567, + "duration_s": 4.5391, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.9995, + "duration_s": 7.3646, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.6195, + "duration_s": 4.8187, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.605, + "duration_s": 4.8317, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.5101, + "duration_s": 4.7443, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.5174, + "duration_s": 7.9512, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.3736, + "duration_s": 4.6191, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.3067, + "duration_s": 4.5989, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.3056, + "duration_s": 4.4696, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.9883, + "duration_s": 7.3099, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.3092, + "duration_s": 4.5227, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.3853, + "duration_s": 4.483, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.2196, + "duration_s": 4.5343, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.9088, + "duration_s": 7.2235, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.4226, + "duration_s": 4.5946, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.4623, + "duration_s": 4.5838, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.4106, + "duration_s": 4.6793, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.1919, + "duration_s": 7.5029, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.589, + "duration_s": 4.8302, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.6603, + "duration_s": 4.8573, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.5093, + "duration_s": 4.8664, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.485, + "duration_s": 7.8612, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.2735, + "duration_s": 4.4517, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.2267, + "duration_s": 4.4705, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.1387, + "duration_s": 4.4008, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.6745, + "duration_s": 7.1733, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.288, + "duration_s": 4.5089, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.2366, + "duration_s": 4.5161, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.2576, + "duration_s": 4.5517, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.6586, + "duration_s": 7.1969, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.5527, + "duration_s": 7.6937, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.5253, + "duration_s": 7.7932, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.4784, + "duration_s": 7.7588, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.2367, + "duration_s": 10.8372, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[h1_2-fixed-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8265, + "duration_s": 2.9038, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7401, + "duration_s": 2.9508, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7758, + "duration_s": 2.9518, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2749, + "duration_s": 5.6338, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7807, + "duration_s": 3.0593, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7678, + "duration_s": 2.9383, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8371, + "duration_s": 2.9856, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3381, + "duration_s": 5.5428, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7549, + "duration_s": 2.8702, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7891, + "duration_s": 2.917, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.785, + "duration_s": 2.8833, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.425, + "duration_s": 5.589, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.804, + "duration_s": 2.9259, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8041, + "duration_s": 2.955, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7931, + "duration_s": 2.8947, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3255, + "duration_s": 5.5977, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8766, + "duration_s": 2.8351, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7697, + "duration_s": 2.9435, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7841, + "duration_s": 2.8325, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3147, + "duration_s": 5.5379, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7263, + "duration_s": 2.9283, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7588, + "duration_s": 2.8924, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.82, + "duration_s": 2.8776, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3019, + "duration_s": 5.5442, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7888, + "duration_s": 2.8801, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8492, + "duration_s": 2.9565, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8019, + "duration_s": 2.8668, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4184, + "duration_s": 5.5815, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7655, + "duration_s": 2.8917, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7229, + "duration_s": 2.8577, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7618, + "duration_s": 2.9077, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2278, + "duration_s": 5.6717, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8151, + "duration_s": 2.8629, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7105, + "duration_s": 2.9165, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.758, + "duration_s": 2.8736, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2739, + "duration_s": 5.4084, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7985, + "duration_s": 2.9612, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7932, + "duration_s": 2.9656, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8945, + "duration_s": 2.89, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3132, + "duration_s": 5.653, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fr3-fixed-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6673, + "duration_s": 2.8745, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.685, + "duration_s": 2.88, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7013, + "duration_s": 2.9053, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1821, + "duration_s": 5.5026, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7235, + "duration_s": 2.8096, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6834, + "duration_s": 2.8193, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7244, + "duration_s": 2.8118, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.268, + "duration_s": 5.5281, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6682, + "duration_s": 2.8199, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.695, + "duration_s": 2.8558, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6886, + "duration_s": 2.8911, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2178, + "duration_s": 5.5748, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7042, + "duration_s": 3.0651, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7258, + "duration_s": 2.8506, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6918, + "duration_s": 2.8714, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2197, + "duration_s": 5.5386, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7026, + "duration_s": 2.872, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7141, + "duration_s": 2.7987, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6958, + "duration_s": 2.8611, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2341, + "duration_s": 5.497, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7308, + "duration_s": 2.9452, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7081, + "duration_s": 3.2165, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7095, + "duration_s": 2.8372, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1949, + "duration_s": 5.5517, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6975, + "duration_s": 2.8942, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6841, + "duration_s": 2.9005, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7048, + "duration_s": 2.8584, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1757, + "duration_s": 5.4995, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.669, + "duration_s": 2.855, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7407, + "duration_s": 3.7221, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7753, + "duration_s": 2.8478, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1305, + "duration_s": 5.3857, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7413, + "duration_s": 2.8243, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7232, + "duration_s": 2.8034, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7084, + "duration_s": 2.8379, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2043, + "duration_s": 5.373, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6995, + "duration_s": 2.8044, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6997, + "duration_s": 2.8464, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7468, + "duration_s": 3.006, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2204, + "duration_s": 5.5641, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[rizon4-fixed-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7105, + "duration_s": 2.8749, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6965, + "duration_s": 2.8033, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7459, + "duration_s": 2.8122, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1621, + "duration_s": 5.3838, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6446, + "duration_s": 2.8265, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6795, + "duration_s": 2.8428, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7615, + "duration_s": 2.8808, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2164, + "duration_s": 5.7352, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6824, + "duration_s": 2.8901, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6935, + "duration_s": 2.8519, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6973, + "duration_s": 2.7937, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1928, + "duration_s": 5.4575, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7412, + "duration_s": 2.8301, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6915, + "duration_s": 2.8271, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.713, + "duration_s": 2.8395, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1702, + "duration_s": 5.4765, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6803, + "duration_s": 2.8353, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6728, + "duration_s": 2.8845, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6779, + "duration_s": 2.8969, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1497, + "duration_s": 5.5164, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6598, + "duration_s": 2.8349, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7006, + "duration_s": 2.8899, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6803, + "duration_s": 2.8351, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2917, + "duration_s": 5.519, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6785, + "duration_s": 2.8632, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6849, + "duration_s": 2.8371, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7191, + "duration_s": 2.8028, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.194, + "duration_s": 5.5444, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7035, + "duration_s": 2.8334, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6893, + "duration_s": 2.8056, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7021, + "duration_s": 2.9014, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1651, + "duration_s": 5.584, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6744, + "duration_s": 3.008, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7923, + "duration_s": 3.1085, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6808, + "duration_s": 2.896, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1726, + "duration_s": 5.5758, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7, + "duration_s": 2.8452, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6692, + "duration_s": 2.991, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6954, + "duration_s": 2.892, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2012, + "duration_s": 5.5824, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[gen3-fixed-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8389, + "duration_s": 3.0352, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8406, + "duration_s": 2.9963, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8648, + "duration_s": 3.0262, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3228, + "duration_s": 5.6568, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8151, + "duration_s": 3.0039, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8026, + "duration_s": 2.9346, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9047, + "duration_s": 2.9695, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.34, + "duration_s": 5.4986, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8168, + "duration_s": 2.9892, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8416, + "duration_s": 2.9517, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8286, + "duration_s": 2.9317, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4321, + "duration_s": 5.7575, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8107, + "duration_s": 3.0309, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8277, + "duration_s": 3.0787, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8291, + "duration_s": 3.2376, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3226, + "duration_s": 5.6548, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9027, + "duration_s": 3.0032, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8041, + "duration_s": 3.0325, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8353, + "duration_s": 2.9606, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.371, + "duration_s": 5.5854, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8204, + "duration_s": 2.9828, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9448, + "duration_s": 2.9963, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8329, + "duration_s": 3.0661, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3607, + "duration_s": 5.6751, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8749, + "duration_s": 2.937, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9001, + "duration_s": 2.9494, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9028, + "duration_s": 3.0172, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3554, + "duration_s": 5.8119, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8506, + "duration_s": 2.9949, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8272, + "duration_s": 2.9737, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8165, + "duration_s": 3.3666, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3774, + "duration_s": 5.6069, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8094, + "duration_s": 3.0037, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8496, + "duration_s": 2.9712, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8568, + "duration_s": 2.9244, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3184, + "duration_s": 5.652, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0619, + "duration_s": 3.117, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9782, + "duration_s": 3.1541, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.985, + "duration_s": 3.1283, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4292, + "duration_s": 5.8054, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[fetch-fixed-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0806, + "duration_s": 3.3054, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1198, + "duration_s": 3.2316, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0778, + "duration_s": 3.2636, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.6421, + "duration_s": 5.9922, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1005, + "duration_s": 3.2424, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0641, + "duration_s": 3.2317, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1141, + "duration_s": 3.2922, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.5082, + "duration_s": 5.8601, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1072, + "duration_s": 3.2613, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0788, + "duration_s": 3.2148, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0549, + "duration_s": 3.2459, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.6181, + "duration_s": 5.9101, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0592, + "duration_s": 3.1687, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0696, + "duration_s": 3.2178, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1076, + "duration_s": 3.2052, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.5866, + "duration_s": 5.8096, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1377, + "duration_s": 3.2628, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0799, + "duration_s": 3.167, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1029, + "duration_s": 3.2443, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.6585, + "duration_s": 5.8481, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0867, + "duration_s": 3.2594, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1593, + "duration_s": 3.2716, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0833, + "duration_s": 3.2361, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.6527, + "duration_s": 5.9079, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.2181, + "duration_s": 3.3707, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1003, + "duration_s": 3.3136, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.134, + "duration_s": 3.389, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.7852, + "duration_s": 5.9929, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0794, + "duration_s": 3.2154, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1177, + "duration_s": 3.309, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0601, + "duration_s": 3.2223, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.5861, + "duration_s": 5.9619, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1397, + "duration_s": 3.2744, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1133, + "duration_s": 3.3173, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0652, + "duration_s": 3.3651, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.6794, + "duration_s": 5.9356, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1674, + "duration_s": 3.3677, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.2621, + "duration_s": 3.3809, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1766, + "duration_s": 3.307, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.6669, + "duration_s": 6.1801, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[baxter-fixed-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6174, + "duration_s": 2.7807, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6129, + "duration_s": 2.7846, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6713, + "duration_s": 2.7218, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.0805, + "duration_s": 5.4611, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5977, + "duration_s": 2.7776, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5897, + "duration_s": 2.7561, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5826, + "duration_s": 2.7064, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.0852, + "duration_s": 5.3729, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5605, + "duration_s": 2.7599, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5578, + "duration_s": 2.7682, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6322, + "duration_s": 2.7284, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1523, + "duration_s": 5.3195, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6041, + "duration_s": 2.722, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6033, + "duration_s": 2.7108, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5907, + "duration_s": 2.7177, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1383, + "duration_s": 5.4305, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5853, + "duration_s": 2.7644, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6124, + "duration_s": 2.7548, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5987, + "duration_s": 2.728, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.0184, + "duration_s": 5.3984, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5727, + "duration_s": 2.7176, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5874, + "duration_s": 2.7361, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6512, + "duration_s": 2.7066, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1042, + "duration_s": 5.3843, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6068, + "duration_s": 2.7165, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6768, + "duration_s": 2.7493, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5924, + "duration_s": 2.7312, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.0937, + "duration_s": 5.2888, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5774, + "duration_s": 2.6929, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6123, + "duration_s": 2.6953, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6248, + "duration_s": 2.7153, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.0543, + "duration_s": 5.3741, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5987, + "duration_s": 2.7415, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5901, + "duration_s": 2.7269, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5685, + "duration_s": 2.7409, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.0887, + "duration_s": 5.3107, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6092, + "duration_s": 2.6822, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6022, + "duration_s": 2.7867, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6402, + "duration_s": 2.7386, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.0646, + "duration_s": 5.3681, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_fixed_base_generated_cuda_matches_python_reference[signed_axis_arm-fixed-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3616, + "duration_s": 1.4743, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4274, + "duration_s": 1.4626, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3475, + "duration_s": 1.4736, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5248, + "duration_s": 2.661, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3818, + "duration_s": 1.4788, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4061, + "duration_s": 1.4262, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4334, + "duration_s": 1.4196, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4714, + "duration_s": 2.5988, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.399, + "duration_s": 1.4699, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3609, + "duration_s": 1.495, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3686, + "duration_s": 1.4158, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5701, + "duration_s": 2.6598, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3516, + "duration_s": 1.4387, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3494, + "duration_s": 1.4309, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3639, + "duration_s": 1.5241, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4942, + "duration_s": 2.6213, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.421, + "duration_s": 1.4289, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3899, + "duration_s": 1.4761, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3579, + "duration_s": 1.4473, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5152, + "duration_s": 2.7479, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3813, + "duration_s": 1.4694, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4445, + "duration_s": 1.4488, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3757, + "duration_s": 1.4419, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5172, + "duration_s": 2.6837, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3925, + "duration_s": 1.5233, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.377, + "duration_s": 1.459, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.448, + "duration_s": 1.493, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5341, + "duration_s": 2.6431, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3662, + "duration_s": 1.4318, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3765, + "duration_s": 1.5401, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3501, + "duration_s": 1.4634, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.568, + "duration_s": 2.6965, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3965, + "duration_s": 1.4783, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3515, + "duration_s": 1.4688, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.369, + "duration_s": 1.5076, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.487, + "duration_s": 2.6808, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4477, + "duration_s": 1.4618, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3782, + "duration_s": 1.4979, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3755, + "duration_s": 1.5174, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5159, + "duration_s": 2.6237, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[iiwa14-floating-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7138, + "duration_s": 1.7159, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6679, + "duration_s": 1.6921, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6551, + "duration_s": 1.7814, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8423, + "duration_s": 2.9425, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6477, + "duration_s": 1.7907, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6134, + "duration_s": 1.6925, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7013, + "duration_s": 1.6773, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7511, + "duration_s": 2.9407, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6976, + "duration_s": 1.6825, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6555, + "duration_s": 1.6876, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6485, + "duration_s": 1.7338, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8047, + "duration_s": 2.9019, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6351, + "duration_s": 1.7842, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6246, + "duration_s": 1.6823, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6839, + "duration_s": 1.7025, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7632, + "duration_s": 2.9793, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.64, + "duration_s": 1.6926, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6898, + "duration_s": 1.7279, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6452, + "duration_s": 1.7974, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8211, + "duration_s": 2.9175, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6263, + "duration_s": 1.7462, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6355, + "duration_s": 1.8315, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7229, + "duration_s": 1.7042, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7772, + "duration_s": 3.0044, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6777, + "duration_s": 1.7063, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7057, + "duration_s": 1.7068, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6318, + "duration_s": 1.8031, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7343, + "duration_s": 2.8749, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6727, + "duration_s": 1.7025, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6044, + "duration_s": 1.7678, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7018, + "duration_s": 1.6694, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7298, + "duration_s": 2.9003, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6328, + "duration_s": 1.6477, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7001, + "duration_s": 1.7257, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6684, + "duration_s": 1.7737, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7828, + "duration_s": 2.8983, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7771, + "duration_s": 1.8124, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6852, + "duration_s": 1.8175, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7061, + "duration_s": 1.7813, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8842, + "duration_s": 3.0168, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[go2-floating-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.1927, + "duration_s": 2.3411, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2781, + "duration_s": 2.2791, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2323, + "duration_s": 2.3942, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4313, + "duration_s": 3.6174, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2036, + "duration_s": 2.3407, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3039, + "duration_s": 2.4206, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2198, + "duration_s": 2.3055, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4727, + "duration_s": 3.589, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2508, + "duration_s": 2.296, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3079, + "duration_s": 2.4493, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.262, + "duration_s": 2.309, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4165, + "duration_s": 3.628, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2638, + "duration_s": 2.3242, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.289, + "duration_s": 2.3682, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2065, + "duration_s": 2.3589, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4607, + "duration_s": 3.6183, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2209, + "duration_s": 2.3343, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2294, + "duration_s": 2.3426, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.1983, + "duration_s": 2.3552, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4477, + "duration_s": 3.6464, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2589, + "duration_s": 2.3473, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3242, + "duration_s": 2.4096, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3064, + "duration_s": 2.3497, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4108, + "duration_s": 3.6342, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3416, + "duration_s": 2.3449, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2385, + "duration_s": 2.42, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.213, + "duration_s": 2.3393, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.531, + "duration_s": 3.6737, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2602, + "duration_s": 2.2904, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2037, + "duration_s": 2.3463, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2755, + "duration_s": 2.2586, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.3472, + "duration_s": 3.5808, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2653, + "duration_s": 2.2661, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2049, + "duration_s": 2.3809, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2155, + "duration_s": 2.2864, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4014, + "duration_s": 3.5686, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4481, + "duration_s": 2.4586, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.377, + "duration_s": 2.536, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4526, + "duration_s": 2.5447, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.506, + "duration_s": 3.7129, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[g1-floating-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9795, + "duration_s": 2.9393, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0055, + "duration_s": 3.059, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9453, + "duration_s": 3.0182, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.9156, + "duration_s": 4.2473, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9696, + "duration_s": 3.1617, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.941, + "duration_s": 3.1509, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9826, + "duration_s": 3.1334, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.1789, + "duration_s": 4.4227, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0816, + "duration_s": 3.1411, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0408, + "duration_s": 3.164, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0472, + "duration_s": 3.2014, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.2416, + "duration_s": 4.583, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0051, + "duration_s": 3.196, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0103, + "duration_s": 3.1396, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0409, + "duration_s": 3.1256, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.2408, + "duration_s": 4.4657, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9848, + "duration_s": 3.132, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0283, + "duration_s": 3.0964, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.981, + "duration_s": 3.1333, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.0847, + "duration_s": 4.3489, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0558, + "duration_s": 3.2512, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0032, + "duration_s": 3.229, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0278, + "duration_s": 3.2701, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.258, + "duration_s": 4.6298, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1406, + "duration_s": 3.3375, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1364, + "duration_s": 3.1815, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0999, + "duration_s": 3.2918, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.34, + "duration_s": 4.6117, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.858, + "duration_s": 3.1694, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.93, + "duration_s": 3.1802, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.924, + "duration_s": 3.0052, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.1158, + "duration_s": 4.2898, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.964, + "duration_s": 3.1644, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9784, + "duration_s": 3.1054, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9695, + "duration_s": 3.1579, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.0216, + "duration_s": 4.4029, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.8979, + "duration_s": 5.1532, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.8845, + "duration_s": 5.0669, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.9512, + "duration_s": 5.0018, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.3085, + "duration_s": 6.4285, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[h1_2-floating-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4349, + "duration_s": 1.5976, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5293, + "duration_s": 1.5387, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4464, + "duration_s": 1.4964, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5787, + "duration_s": 2.8548, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4784, + "duration_s": 1.5367, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5053, + "duration_s": 1.564, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4826, + "duration_s": 1.6402, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5778, + "duration_s": 2.7374, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4456, + "duration_s": 1.5604, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5394, + "duration_s": 1.5193, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4489, + "duration_s": 1.5946, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6486, + "duration_s": 2.7449, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4616, + "duration_s": 1.6021, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5262, + "duration_s": 1.5317, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4944, + "duration_s": 1.6287, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6093, + "duration_s": 2.8644, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5234, + "duration_s": 1.6028, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.447, + "duration_s": 1.5352, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4573, + "duration_s": 1.5596, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5973, + "duration_s": 2.7116, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5325, + "duration_s": 1.5077, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4486, + "duration_s": 1.5148, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4703, + "duration_s": 1.587, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6209, + "duration_s": 2.7437, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5442, + "duration_s": 1.5363, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4619, + "duration_s": 1.5909, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.455, + "duration_s": 1.5508, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.658, + "duration_s": 2.8015, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5401, + "duration_s": 1.5271, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4443, + "duration_s": 1.6268, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4359, + "duration_s": 1.5284, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6753, + "duration_s": 2.714, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.46, + "duration_s": 1.5491, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.476, + "duration_s": 1.608, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4475, + "duration_s": 1.5285, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6591, + "duration_s": 2.7333, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5031, + "duration_s": 1.5675, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5007, + "duration_s": 1.6135, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4936, + "duration_s": 1.5297, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7168, + "duration_s": 2.7939, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fr3-floating-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4132, + "duration_s": 1.4676, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3915, + "duration_s": 1.5016, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3898, + "duration_s": 1.4929, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.543, + "duration_s": 2.6706, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4047, + "duration_s": 2.0974, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4626, + "duration_s": 1.6077, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4015, + "duration_s": 1.5205, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.557, + "duration_s": 3.1289, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3977, + "duration_s": 1.7685, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3848, + "duration_s": 1.5883, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3882, + "duration_s": 1.6206, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6248, + "duration_s": 2.991, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3973, + "duration_s": 1.8149, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3811, + "duration_s": 1.6852, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3952, + "duration_s": 1.7444, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5236, + "duration_s": 2.7656, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3822, + "duration_s": 1.7688, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4745, + "duration_s": 1.5954, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4094, + "duration_s": 1.8176, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5145, + "duration_s": 3.031, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3969, + "duration_s": 1.8152, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4247, + "duration_s": 1.6145, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4029, + "duration_s": 1.5869, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6367, + "duration_s": 3.3692, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4293, + "duration_s": 1.4749, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.403, + "duration_s": 1.7859, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4212, + "duration_s": 1.7106, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5676, + "duration_s": 3.4509, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4238, + "duration_s": 1.772, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4671, + "duration_s": 1.7592, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4191, + "duration_s": 1.9568, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5339, + "duration_s": 3.4181, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3727, + "duration_s": 1.8282, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4229, + "duration_s": 1.4572, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4017, + "duration_s": 1.9059, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6053, + "duration_s": 3.3106, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4341, + "duration_s": 1.7393, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4156, + "duration_s": 2.0579, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4088, + "duration_s": 1.6593, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5457, + "duration_s": 3.5211, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[rizon4-floating-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4134, + "duration_s": 1.6756, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4511, + "duration_s": 1.8844, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4011, + "duration_s": 1.5358, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5362, + "duration_s": 2.8393, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.398, + "duration_s": 1.6066, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4091, + "duration_s": 1.6538, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3965, + "duration_s": 1.6033, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5112, + "duration_s": 3.5123, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4708, + "duration_s": 1.9898, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3842, + "duration_s": 1.7081, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.407, + "duration_s": 1.6411, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5119, + "duration_s": 3.575, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4234, + "duration_s": 1.5053, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3703, + "duration_s": 1.7112, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4499, + "duration_s": 1.7599, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5804, + "duration_s": 3.2292, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.376, + "duration_s": 1.9338, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3912, + "duration_s": 2.0636, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4218, + "duration_s": 1.6831, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.52, + "duration_s": 3.2522, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4545, + "duration_s": 1.9496, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3969, + "duration_s": 1.5886, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3991, + "duration_s": 1.48, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5419, + "duration_s": 3.3266, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3924, + "duration_s": 1.619, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3943, + "duration_s": 1.6499, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4722, + "duration_s": 1.7824, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5632, + "duration_s": 3.2131, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4045, + "duration_s": 1.804, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3774, + "duration_s": 1.7425, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.449, + "duration_s": 1.9507, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.539, + "duration_s": 3.036, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4449, + "duration_s": 1.4794, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4102, + "duration_s": 1.4778, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.385, + "duration_s": 1.5826, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.495, + "duration_s": 3.4369, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4174, + "duration_s": 1.785, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.418, + "duration_s": 1.8389, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4819, + "duration_s": 1.8672, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5276, + "duration_s": 3.5893, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[gen3-floating-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5284, + "duration_s": 1.7704, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5301, + "duration_s": 1.9738, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6134, + "duration_s": 1.827, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6662, + "duration_s": 2.8271, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5617, + "duration_s": 1.6082, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5878, + "duration_s": 1.6497, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5205, + "duration_s": 1.641, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6809, + "duration_s": 3.1943, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.8592, + "duration_s": 2.0586, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5425, + "duration_s": 1.7734, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5397, + "duration_s": 1.8732, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6606, + "duration_s": 3.3, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5341, + "duration_s": 1.7629, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5632, + "duration_s": 2.1367, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5276, + "duration_s": 1.6679, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6653, + "duration_s": 3.2443, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5435, + "duration_s": 2.1457, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5352, + "duration_s": 2.0215, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4914, + "duration_s": 1.8169, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6651, + "duration_s": 3.3416, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5631, + "duration_s": 1.8518, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5288, + "duration_s": 1.8967, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5353, + "duration_s": 2.0004, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7172, + "duration_s": 3.004, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5367, + "duration_s": 2.1716, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5328, + "duration_s": 2.0348, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5444, + "duration_s": 2.1488, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6842, + "duration_s": 3.4157, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5286, + "duration_s": 2.0725, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.525, + "duration_s": 1.7154, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.548, + "duration_s": 1.7698, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6761, + "duration_s": 3.1467, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.542, + "duration_s": 2.0419, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5513, + "duration_s": 2.1276, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5341, + "duration_s": 1.7437, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.6229, + "duration_s": 3.4189, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6719, + "duration_s": 2.0383, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6632, + "duration_s": 2.0498, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.643, + "duration_s": 1.9495, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7552, + "duration_s": 3.467, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[fetch-floating-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7787, + "duration_s": 2.0278, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7519, + "duration_s": 2.3875, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7997, + "duration_s": 2.1333, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8903, + "duration_s": 3.6228, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7971, + "duration_s": 2.5084, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7422, + "duration_s": 2.2828, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.8076, + "duration_s": 2.3875, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9001, + "duration_s": 3.3792, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.8431, + "duration_s": 2.4386, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.8012, + "duration_s": 2.426, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7587, + "duration_s": 2.3585, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9739, + "duration_s": 3.7758, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7603, + "duration_s": 2.4306, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.799, + "duration_s": 2.1418, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7498, + "duration_s": 2.2781, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9723, + "duration_s": 3.0725, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7718, + "duration_s": 1.9395, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7743, + "duration_s": 1.9783, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7487, + "duration_s": 2.104, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9419, + "duration_s": 3.2847, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7883, + "duration_s": 1.8949, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.8053, + "duration_s": 2.1676, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7812, + "duration_s": 2.4301, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9524, + "duration_s": 3.0746, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7794, + "duration_s": 1.9343, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.8195, + "duration_s": 1.877, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7996, + "duration_s": 1.9304, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9896, + "duration_s": 3.7617, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7744, + "duration_s": 2.0066, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.8106, + "duration_s": 1.8345, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.772, + "duration_s": 1.8821, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.916, + "duration_s": 3.0515, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7764, + "duration_s": 1.9056, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.8182, + "duration_s": 1.84, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7458, + "duration_s": 2.221, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9209, + "duration_s": 3.8167, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.8374, + "duration_s": 1.9929, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.8834, + "duration_s": 1.9628, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.8367, + "duration_s": 2.1006, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9971, + "duration_s": 3.3626, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[baxter-floating-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2853, + "duration_s": 1.4343, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-inverse_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2889, + "duration_s": 1.407, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-inverse_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3039, + "duration_s": 1.5429, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-inverse_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4484, + "duration_s": 3.3902, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-inverse_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3162, + "duration_s": 1.5204, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-minv-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2962, + "duration_s": 1.4654, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-minv-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.295, + "duration_s": 1.3794, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-minv-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4778, + "duration_s": 2.9024, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-minv-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3019, + "duration_s": 1.8901, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-forward_dynamics-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2809, + "duration_s": 1.6928, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-forward_dynamics-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3188, + "duration_s": 1.4113, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-forward_dynamics-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4561, + "duration_s": 2.6369, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-forward_dynamics-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2963, + "duration_s": 1.4309, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-aba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3258, + "duration_s": 1.6034, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-aba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2799, + "duration_s": 1.8566, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-aba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4695, + "duration_s": 2.6151, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-aba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3018, + "duration_s": 1.4017, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-crba-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3033, + "duration_s": 1.3471, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-crba-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.28, + "duration_s": 1.378, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-crba-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4299, + "duration_s": 3.0132, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-crba-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2953, + "duration_s": 1.8115, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-inverse_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2939, + "duration_s": 1.7439, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-inverse_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3031, + "duration_s": 1.3879, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-inverse_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.471, + "duration_s": 2.7083, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-inverse_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3276, + "duration_s": 1.4856, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-forward_dynamics_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2999, + "duration_s": 1.4989, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-forward_dynamics_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3016, + "duration_s": 1.952, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-forward_dynamics_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4538, + "duration_s": 2.5994, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-forward_dynamics_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3121, + "duration_s": 1.3633, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-end_effector_pose-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3133, + "duration_s": 1.3945, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-end_effector_pose-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3227, + "duration_s": 1.3754, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-end_effector_pose-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4508, + "duration_s": 2.7369, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-end_effector_pose-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2762, + "duration_s": 1.7308, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-end_effector_pose_gradient-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2777, + "duration_s": 1.6745, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-end_effector_pose_gradient-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3063, + "duration_s": 1.4714, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-end_effector_pose_gradient-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4355, + "duration_s": 2.5403, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-end_effector_pose_gradient-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3144, + "duration_s": 1.4544, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-end_effector_pose_hessian-threads32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3123, + "duration_s": 1.3839, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-end_effector_pose_hessian-threads96]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.331, + "duration_s": 1.3661, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-end_effector_pose_hessian-threads100]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5179, + "duration_s": 2.6143, "node_id": "test/cuda_equivalents/test_cuda_executable_equivalence.py::test_floating_base_generated_cuda_matches_python_reference[signed_axis_arm-floating-end_effector_pose_hessian-threadssuggested]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.6107, + "duration_s": 4.6787, "node_id": "test/cuda_equivalents/test_cuda_f_ext_contact.py::test_f_ext_contact_frame_map_fd", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.1792, + "duration_s": 4.3907, "node_id": "test/cuda_equivalents/test_cuda_f_ext_contact_runtime.py::test_f_ext_contact_runtime_map_fd", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4375, + "duration_s": 2.669, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7461, + "duration_s": 3.0838, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.6529, + "duration_s": 7.4143, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 18.977, + "duration_s": 20.0903, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9564, + "duration_s": 3.3349, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3031, + "duration_s": 2.5242, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3064, + "duration_s": 2.4633, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5307, + "duration_s": 3.7425, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.1428, + "duration_s": 4.372, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9927, + "duration_s": 3.1428, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.3671, + "duration_s": 3.6975, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.7201, + "duration_s": 12.4011, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 18.3157, + "duration_s": 19.4091, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.2732, + "duration_s": 3.4574, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.082, + "duration_s": 3.2124, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0985, + "duration_s": 3.1544, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.3823, + "duration_s": 4.6578, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.8684, + "duration_s": 5.2986, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py::test_cuda_f_ext_gradient_equivalence[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.79, + "duration_s": 4.7181, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2625, + "duration_s": 5.4049, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.8579, + "duration_s": 12.2514, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 35.5741, + "duration_s": 36.8836, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.924, + "duration_s": 5.8565, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.7193, + "duration_s": 4.4863, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.6398, + "duration_s": 4.7385, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.6388, + "duration_s": 6.8344, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.7562, + "duration_s": 7.8681, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.8431, + "duration_s": 5.7544, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.3843, + "duration_s": 6.413, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 21.3342, + "duration_s": 21.677, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 33.6755, + "duration_s": 35.4062, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.449, + "duration_s": 6.2678, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.983, + "duration_s": 5.9277, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.8942, + "duration_s": 5.7691, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.1581, + "duration_s": 8.5671, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.838, + "duration_s": 8.7365, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_spill_matches_full[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 24.5916, + "duration_s": 25.789, "node_id": "test/cuda_equivalents/test_cuda_f_ext_gradient_spill.py::test_cuda_f_ext_gradient_h2plus_launches", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 33.6054, + "duration_s": 33.1034, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 49.6044, + "duration_s": 48.4098, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 162.4625, + "duration_s": 164.5939, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 831.8762, + "duration_s": 859.8675, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 67.962, + "duration_s": 70.4771, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 30.544, + "duration_s": 30.8465, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 29.8186, + "duration_s": 29.916, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 64.4153, + "duration_s": 63.0479, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 90.9388, + "duration_s": 90.9453, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 79.4112, + "duration_s": 77.9974, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 59.329, + "duration_s": 57.9659, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 389.5033, + "duration_s": 396.917, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 701.7356, + "duration_s": 733.8808, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 85.1216, + "duration_s": 85.2894, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 52.6202, + "duration_s": 51.8622, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 52.4934, + "duration_s": 51.8111, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 104.1922, + "duration_s": 109.6127, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 109.2913, + "duration_s": 110.4206, "node_id": "test/cuda_equivalents/test_cuda_fd_du_output_spill.py::test_cuda_fd_du_output_spill_matches_full[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.6847, + "duration_s": 5.0694, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2454, + "duration_s": 5.7261, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.6668, + "duration_s": 17.5517, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.5015, + "duration_s": 6.0509, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.7239, + "duration_s": 5.1646, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.7556, + "duration_s": 5.1178, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.4065, + "duration_s": 7.8845, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.9857, + "duration_s": 9.5059, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.4767, + "duration_s": 7.0373, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 37.7216, + "duration_s": 39.6282, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.1473, + "duration_s": 6.2992, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.6112, + "duration_s": 6.0012, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.7154, + "duration_s": 6.043, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 9.2824, + "duration_s": 9.4971, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.0712, + "duration_s": 11.4004, "node_id": "test/cuda_equivalents/test_cuda_fd_parameter_gradient.py::test_cuda_fd_parameter_gradient_matches_reference[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 18.1283, + "duration_s": 18.6714, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 26.2527, + "duration_s": 26.9677, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 91.0724, + "duration_s": 93.643, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 497.7577, + "duration_s": 509.39, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 34.238, + "duration_s": 35.3566, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.9036, + "duration_s": 16.1154, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.3222, + "duration_s": 15.7925, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 36.0476, + "duration_s": 37.0091, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 50.5206, + "duration_s": 52.0888, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 44.5127, + "duration_s": 44.4246, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 32.8344, + "duration_s": 33.1409, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 210.2668, + "duration_s": 211.8809, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 508.7607, + "duration_s": 515.4439, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 42.8863, + "duration_s": 42.3441, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 28.4818, + "duration_s": 28.7742, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 28.3214, + "duration_s": 28.5999, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 60.011, + "duration_s": 60.0298, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 61.7937, + "duration_s": 62.0227, "node_id": "test/cuda_equivalents/test_cuda_fext_equivalence.py::test_cuda_fext_three_way_equivalence[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 18.5034, + "duration_s": 3.1539, "node_id": "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 20.1888, + "duration_s": 3.0263, "node_id": "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 127.1762, + "duration_s": 3.8525, "node_id": "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.6885, + "duration_s": 4.9862, "node_id": "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.7232, + "duration_s": 2.8079, "node_id": "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.9247, + "duration_s": 2.7314, "node_id": "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.1157, + "duration_s": 2.7552, "node_id": "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 32.5549, + "duration_s": 2.9226, "node_id": "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 35.4342, + "duration_s": 3.1516, "node_id": "test/cuda_equivalents/test_cuda_floating_values_equivalence.py::test_floating_value_only_matches_python_reference[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.0155, + "duration_s": 7.4282, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.9846, + "duration_s": 8.5327, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 12.6459, + "duration_s": 13.2427, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 26.6479, + "duration_s": 28.7316, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.9669, + "duration_s": 8.2583, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.0444, + "duration_s": 7.4327, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.1011, + "duration_s": 7.432, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.7065, + "duration_s": 9.1858, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 9.5668, + "duration_s": 10.0744, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.3806, + "duration_s": 8.8194, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 9.0374, + "duration_s": 9.6507, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.6982, + "duration_s": 14.6203, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 20.2655, + "duration_s": 22.2898, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.4875, + "duration_s": 9.1097, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.3359, + "duration_s": 9.055, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.3684, + "duration_s": 9.0017, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 9.7093, + "duration_s": 10.4797, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.4698, + "duration_s": 11.2929, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian.py::test_cuda_frame_jacobian_matches_reference[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.9844, + "duration_s": 7.3997, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.2713, + "duration_s": 7.7577, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.2374, + "duration_s": 14.4185, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 26.4479, + "duration_s": 27.9013, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.5894, + "duration_s": 8.0283, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.8791, + "duration_s": 7.3137, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.7922, + "duration_s": 6.8571, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.2889, + "duration_s": 8.8244, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 12.3765, + "duration_s": 12.99, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 9.1229, + "duration_s": 9.357, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.1782, + "duration_s": 10.7131, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 23.4741, + "duration_s": 25.7338, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 42.6775, + "duration_s": 46.2192, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.9492, + "duration_s": 9.3977, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.9804, + "duration_s": 9.3804, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.9781, + "duration_s": 9.0813, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.8596, + "duration_s": 11.4485, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.4051, + "duration_s": 12.1339, "node_id": "test/cuda_equivalents/test_cuda_frame_jacobian_host.py::test_cuda_frame_jacobian_host_matches_reference[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.3906, + "duration_s": 3.3572, + "node_id": "test/cuda_equivalents/test_cuda_glass_handoff.py::test_embedded_glass_restricted_handoffs[float-3e-05-3e-05]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 3.549, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[iiwa14-world-frame-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.634, + "duration_s": 3.8342, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[iiwa14-world-frame-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.0668, + "duration_s": 5.4908, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[go2-world-frame-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.3015, + "duration_s": 4.5773, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[go2-world-frame-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.57, + "duration_s": 6.985, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[g1-world-frame-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.4896, + "duration_s": 8.1839, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[g1-world-frame-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 17.5767, + "duration_s": 19.1951, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[h1_2-world-frame-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 59.5009, + "duration_s": 65.5609, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[h1_2-world-frame-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4815, + "duration_s": 3.8593, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[fr3-world-frame-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.179, + "duration_s": 3.4099, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[fr3-world-frame-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.3812, + "duration_s": 3.4431, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[rizon4-world-frame-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9559, + "duration_s": 3.1528, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[rizon4-world-frame-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4285, + "duration_s": 3.5364, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[gen3-world-frame-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.011, + "duration_s": 3.1394, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[gen3-world-frame-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.9737, + "duration_s": 4.2629, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[fetch-world-frame-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.4572, + "duration_s": 3.5935, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[fetch-world-frame-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.3253, + "duration_s": 4.5733, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[baxter-world-frame-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.7562, + "duration_s": 4.9796, "node_id": "test/cuda_equivalents/test_cuda_idsva_so_world_frame.py::test_cuda_world_frame_matches_python_reference[baxter-world-frame-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.0357, + "duration_s": 1.0721, "node_id": "test/cuda_equivalents/test_cuda_input_abi.py::test_input_block_is_three_num_pos_slots[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3713, + "duration_s": 1.4276, "node_id": "test/cuda_equivalents/test_cuda_input_abi.py::test_input_block_is_three_num_pos_slots[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5191, + "duration_s": 0.5492, "node_id": "test/cuda_equivalents/test_cuda_input_abi.py::test_input_block_is_three_num_pos_slots[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5675, + "duration_s": 1.6384, "node_id": "test/cuda_equivalents/test_cuda_input_abi.py::test_floating_slot_padding_is_real_and_fixed_base_hides_it", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.9501, + "duration_s": 4.1025, "node_id": "test/cuda_equivalents/test_cuda_integrator_batch_forces.py::test_integrator_batch_forces[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.3677, + "duration_s": 6.6433, "node_id": "test/cuda_equivalents/test_cuda_integrator_batch_forces.py::test_integrator_batch_forces[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 62.7544, + "duration_s": 66.4486, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[iiwa14-integrator-fixed-TIER_SHARED]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 62.9649, + "duration_s": 70.6406, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[iiwa14-integrator-fixed-TIER_LITE]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 83.6293, + "duration_s": 91.4065, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[iiwa14-integrator-floating-TIER_SHARED]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 77.8561, + "duration_s": 81.7596, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[iiwa14-integrator-floating-TIER_LITE]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 67.3352, + "duration_s": 70.62, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[go2-integrator-fixed-TIER_SHARED]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 67.4804, + "duration_s": 70.5469, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[go2-integrator-fixed-TIER_LITE]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 95.849, + "duration_s": 99.3818, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[go2-integrator-floating-TIER_SHARED]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 92.3493, + "duration_s": 94.3404, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[go2-integrator-floating-TIER_LITE]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 70.5075, + "duration_s": 74.056, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[fr3-integrator-fixed-TIER_SHARED]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 70.8245, + "duration_s": 74.0347, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[fr3-integrator-fixed-TIER_LITE]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 90.014, + "duration_s": 93.2534, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[fr3-integrator-floating-TIER_SHARED]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 89.6043, + "duration_s": 94.2578, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[fr3-integrator-floating-TIER_LITE]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 127.5753, + "duration_s": 132.2944, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[g1-integrator-fixed-TIER_SHARED]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 124.4503, + "duration_s": 128.8893, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[g1-integrator-fixed-TIER_LITE]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 172.9337, + "duration_s": 179.1437, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[g1-integrator-floating-TIER_SHARED]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 164.8543, + "duration_s": 171.6903, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[g1-integrator-floating-TIER_LITE]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 279.3776, + "duration_s": 290.7286, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[h1_2-integrator-fixed-TIER_SHARED]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 281.6543, + "duration_s": 300.8347, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[h1_2-integrator-fixed-TIER_LITE]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 343.3613, + "duration_s": 366.9211, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[h1_2-integrator-floating-TIER_SHARED]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 340.5026, + "duration_s": 370.8851, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[h1_2-integrator-floating-TIER_LITE]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 60.1259, + "duration_s": 76.3003, "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[rizon4-integrator-fixed-TIER_SHARED]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 59.7847, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[rizon4-integrator-fixed-TIER_LITE]", + "duration_s": 79.8491, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[rizon4-integrator-fixed-TIER_LITE]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 95.5096, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[rizon4-integrator-floating-TIER_SHARED]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 95.8982, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[rizon4-integrator-floating-TIER_LITE]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 79.2917, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[gen3-integrator-fixed-TIER_SHARED]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 79.1846, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[gen3-integrator-fixed-TIER_LITE]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 94.6616, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[gen3-integrator-floating-TIER_SHARED]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 95.8068, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[gen3-integrator-floating-TIER_LITE]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 91.4159, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[fetch-integrator-fixed-TIER_SHARED]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 90.9408, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[fetch-integrator-fixed-TIER_LITE]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 132.9659, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[fetch-integrator-floating-TIER_SHARED]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 132.4306, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[fetch-integrator-floating-TIER_LITE]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 105.3451, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[baxter-integrator-fixed-TIER_SHARED]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 114.6968, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[baxter-integrator-fixed-TIER_LITE]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 134.0214, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[baxter-integrator-floating-TIER_SHARED]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 133.689, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[baxter-integrator-floating-TIER_LITE]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 19.9142, + "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_fext_matches_python_reference", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 4.0393, + "node_id": "test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py::test_floating_damped_cuda_matches_damped_oracle", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 9.973, + "node_id": "test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py::test_floating_tanh_friction_cuda_matches_tanh_oracle", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.0244, + "node_id": "test/cuda_equivalents/test_cuda_joint_transform_suffix.py::test_scalar_transform_composed_suffix[float-7-False]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.3641, + "node_id": "test/cuda_equivalents/test_cuda_joint_transform_suffix.py::test_scalar_transform_composed_suffix[float-7-True]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 3.9147, + "node_id": "test/cuda_equivalents/test_cuda_joint_transform_suffix.py::test_scalar_transform_composed_suffix[float-32-True]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.9339, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch1]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 27.2614, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch16]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 431.1088, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch256]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 4.3523, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch1]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 28.261, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch16]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 416.0647, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch256]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 2.9708, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[go2-floating-batch1]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 41.5747, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[go2-floating-batch16]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 575.6693, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[go2-floating-batch256]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 5.9438, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[go2-floating-batch1]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 78.3586, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[rizon4-integrator-floating-TIER_SHARED]", + "duration_s": 32.7961, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[go2-floating-batch16]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 78.0963, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[rizon4-integrator-floating-TIER_LITE]", + "duration_s": 470.8096, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[go2-floating-batch256]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 59.8343, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[gen3-integrator-fixed-TIER_SHARED]", + "duration_s": 10.677, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch1]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 59.7151, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[gen3-integrator-fixed-TIER_LITE]", + "duration_s": 38.1978, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch16]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 77.9607, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[gen3-integrator-floating-TIER_SHARED]", + "duration_s": 472.8397, + "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch256]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 78.1058, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[gen3-integrator-floating-TIER_LITE]", + "duration_s": 0.9089, + "node_id": "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-zero]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 72.6998, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[fetch-integrator-fixed-TIER_SHARED]", + "duration_s": 0.9298, + "node_id": "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-default]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 72.8366, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[fetch-integrator-fixed-TIER_LITE]", + "duration_s": 1.0702, + "node_id": "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-wide-x]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 98.0197, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[fetch-integrator-floating-TIER_SHARED]", + "duration_s": 0.9608, + "node_id": "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[iiwa14-float-wide-u]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 98.0432, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[fetch-integrator-floating-TIER_LITE]", + "duration_s": 0.8389, + "node_id": "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-zero]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 80.9214, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[baxter-integrator-fixed-TIER_SHARED]", + "duration_s": 0.8405, + "node_id": "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-default]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 81.1325, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[baxter-integrator-fixed-TIER_LITE]", + "duration_s": 1.1684, + "node_id": "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-wide-x]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 102.4165, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[baxter-integrator-floating-TIER_SHARED]", + "duration_s": 0.9035, + "node_id": "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[go2-float-wide-u]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 102.5709, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_matches_python_reference[baxter-integrator-floating-TIER_LITE]", + "duration_s": 0.8731, + "node_id": "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-zero]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.8525, - "node_id": "test/cuda_equivalents/test_cuda_integrator_equivalence.py::test_cuda_integrator_fext_matches_python_reference", + "duration_s": 0.9082, + "node_id": "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-default]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 34.6045, - "node_id": "test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py::test_floating_damped_cuda_matches_damped_oracle", + "duration_s": 0.9799, + "node_id": "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-wide-x]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 43.6405, - "node_id": "test/cuda_equivalents/test_cuda_joint_dynamics_equivalence.py::test_floating_tanh_friction_cuda_matches_tanh_oracle", + "duration_s": 0.9093, + "node_id": "test/cuda_equivalents/test_cuda_menu_workspace.py::test_menu_workspace[indy7-float-wide-u]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.183, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch1]", + "duration_s": 16.3047, + "node_id": "test/cuda_equivalents/test_cuda_mjx_tier_invariance.py::test_mjx_second_order_tier_invariance[idsva_so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 26.2959, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch16]", + "duration_s": 28.7246, + "node_id": "test/cuda_equivalents/test_cuda_mjx_tier_invariance.py::test_mjx_second_order_tier_invariance[fdsva_so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 422.3248, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[iiwa14-fixed-batch256]", + "duration_s": 0.8168, + "node_id": "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[constant-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.4454, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch1]", + "duration_s": 0.8355, + "node_id": "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[floating-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 28.0549, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch16]", + "duration_s": 0.8079, + "node_id": "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[spherical-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 406.024, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[iiwa14-fixed-batch256]", + "duration_s": 0.8033, + "node_id": "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[branch-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.476, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[go2-floating-batch1]", + "duration_s": 0.7901, + "node_id": "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[empty-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 27.3398, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[go2-floating-batch16]", + "duration_s": 0.841, + "node_id": "test/cuda_equivalents/test_cuda_model_descriptor.py::test_device_metadata_and_single_pose_composition[runtime-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 430.2185, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_ee_pose_thread_invariance[go2-floating-batch256]", + "duration_s": 1.1413, + "node_id": "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-mixed_spherical_arm-False-False]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.7412, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[go2-floating-batch1]", + "duration_s": 1.2354, + "node_id": "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-rotated_inertial_arm-True-False]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 29.453, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[go2-floating-batch16]", + "duration_s": 1.1317, + "node_id": "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-branching_skew_arm-False-False]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 408.8079, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[go2-floating-batch256]", + "duration_s": 1.0844, + "node_id": "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-helical_arm-False-False]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.1322, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch1]", + "duration_s": 1.1505, + "node_id": "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-rotated_inertial_arm-False-True]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 33.909, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch16]", + "duration_s": 1.0491, + "node_id": "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-constant-False-False]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 416.5947, - "node_id": "test/cuda_equivalents/test_cuda_kinematics_thread_invariance.py::test_frame_jacobian_thread_invariance[g1-floating-batch256]", + "duration_s": 0.9876, + "node_id": "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-zero-False-False]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.08, - "node_id": "test/cuda_equivalents/test_cuda_mjx_tier_invariance.py::test_mjx_second_order_tier_invariance[idsva_so]", + "duration_s": 1.0632, + "node_id": "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-slider_mimic_fixed_tool-False-False]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 22.002, - "node_id": "test/cuda_equivalents/test_cuda_mjx_tier_invariance.py::test_mjx_second_order_tier_invariance[fdsva_so]", + "duration_s": 1.257, + "node_id": "test/cuda_equivalents/test_cuda_model_primitives.py::test_model_primitives[float-fr3-False-False]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.4313, + "duration_s": 5.2836, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.6936, + "duration_s": 5.4275, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.9925, + "duration_s": 6.8266, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 9.334, + "duration_s": 8.3275, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.5923, + "duration_s": 5.4116, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.4364, + "duration_s": 5.2568, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.3858, + "duration_s": 5.4058, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.8065, + "duration_s": 5.6822, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.0372, + "duration_s": 6.0292, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.4938, + "duration_s": 9.2083, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.7217, + "duration_s": 9.4346, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.1924, + "duration_s": 11.2143, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 15.214, + "duration_s": 13.2301, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.6385, + "duration_s": 9.3578, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.5022, + "duration_s": 9.1097, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.5788, + "duration_s": 9.1366, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.9482, + "duration_s": 9.5572, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 12.2355, + "duration_s": 9.8601, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[double-TIER_MINIMAL-baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.4711, + "duration_s": 5.2396, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.8618, + "duration_s": 5.7011, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.5071, + "duration_s": 7.6452, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.3755, + "duration_s": 9.7385, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.7642, + "duration_s": 5.6125, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.5127, + "duration_s": 5.3888, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.6059, + "duration_s": 5.304, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.0785, + "duration_s": 5.6677, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.2965, + "duration_s": 5.8223, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.7377, + "duration_s": 8.7535, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 12.0236, + "duration_s": 9.3206, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.0814, + "duration_s": 11.5686, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.2744, + "duration_s": 14.1687, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.7544, + "duration_s": 8.994, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.6783, + "duration_s": 9.1384, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.6582, + "duration_s": 8.927, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 12.3546, + "duration_s": 13.2496, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 12.5773, + "duration_s": 13.4713, "node_id": "test/cuda_equivalents/test_cuda_momentum_contract.py::test_full_momentum_contract[float-TIER_SHARED-baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.769, + "duration_s": 1.7587, "node_id": "test/cuda_equivalents/test_cuda_multi_target_position.py::test_multi_target_position[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.8925, + "duration_s": 2.7503, "node_id": "test/cuda_equivalents/test_cuda_multi_target_position.py::test_multi_target_position[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.0492, + "duration_s": 1.9029, "node_id": "test/cuda_equivalents/test_cuda_multi_target_position_gradient.py::test_multi_target_position_gradient[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1741, + "duration_s": 3.1192, "node_id": "test/cuda_equivalents/test_cuda_multi_target_position_gradient.py::test_multi_target_position_gradient[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.6848, + "duration_s": 1.5781, "node_id": "test/cuda_equivalents/test_cuda_multi_target_raw_evaluators.py::test_raw_multi_target_evaluators_equal_the_device_fns[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.2517, + "duration_s": 3.2346, "node_id": "test/cuda_equivalents/test_cuda_multi_target_raw_evaluators.py::test_raw_multi_target_evaluators_equal_the_device_fns[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2379, + "duration_s": 4.8066, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.6887, + "duration_s": 5.9325, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.8284, + "duration_s": 10.3062, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 23.5746, + "duration_s": 23.9158, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.966, + "duration_s": 6.0697, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.2109, + "duration_s": 4.6226, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3434, + "duration_s": 5.0936, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.7293, + "duration_s": 6.2989, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.1064, + "duration_s": 9.2813, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.4574, + "duration_s": 5.4867, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.2321, + "duration_s": 6.3703, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 20.4901, + "duration_s": 20.8878, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 37.8014, + "duration_s": 39.7401, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.5676, + "duration_s": 5.4583, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.5621, + "duration_s": 5.6182, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.4563, + "duration_s": 5.5675, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.1486, + "duration_s": 7.3646, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.7552, + "duration_s": 7.8264, "node_id": "test/cuda_equivalents/test_cuda_osc_inertia_spill.py::test_cuda_osc_inertia_spill_matches_reference[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.9461, + "duration_s": 4.697, "node_id": "test/cuda_equivalents/test_cuda_planar_translation_equivalence.py::test_cuda_decomposed_joint_matches_reference[planar_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.9399, + "duration_s": 4.8762, "node_id": "test/cuda_equivalents/test_cuda_planar_translation_equivalence.py::test_cuda_decomposed_joint_matches_reference[translation_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.5011, + "duration_s": 5.408, "node_id": "test/cuda_equivalents/test_cuda_planar_translation_equivalence.py::test_cuda_decomposed_joint_matches_reference[skew_planar_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1712, + "duration_s": 4.2366, "node_id": "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_plant_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.9689, + "duration_s": 7.0917, "node_id": "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_plant_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.6955, + "duration_s": 5.4809, "node_id": "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_plant_matches_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3008, + "duration_s": 4.9726, "node_id": "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_plant_step_hessian_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.9568, + "duration_s": 8.2573, "node_id": "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_plant_step_hessian_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.109, + "duration_s": 4.8795, "node_id": "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_plant_centroidal_costs_match_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.9285, + "duration_s": 7.4592, "node_id": "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_plant_centroidal_costs_match_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.6449, + "duration_s": 4.9022, "node_id": "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_plant_centroidal_costs_match_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.0638, + "duration_s": 7.3437, "node_id": "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_tangent_state_cost_matches_reference", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1389, + "duration_s": 3.9283, "node_id": "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 8.0734, + "duration_s": 7.4918, "node_id": "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.7069, + "duration_s": 4.4116, "node_id": "test/cuda_equivalents/test_cuda_plant_equivalence.py::test_cuda_cost_menu_matches_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.2115, + "duration_s": 102.6179, + "node_id": "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[iiwa14-False-float-False]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.2841, + "node_id": "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[go2-True-float-False]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.1833, + "node_id": "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[go2-True-float-True]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 256.561, + "node_id": "test/cuda_equivalents/test_cuda_plant_gradient_arena.py::test_full_shared_plant_gradient_bounds[g1-False-float-False]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.4772, + "node_id": "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-float-full]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.3949, + "node_id": "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-float-spill]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.4889, + "node_id": "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[iiwa14-float-full]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.5481, + "node_id": "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[iiwa14-float-spill]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.4814, + "node_id": "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-semi-runtime-float-full]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.3836, + "node_id": "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[acrobot-semi-runtime-float-spill]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.9527, + "node_id": "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[branching-runtime-double-full]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 2.6103, + "node_id": "test/cuda_equivalents/test_cuda_plant_workspace.py::test_composed_workspace[branching-runtime-double-spill]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 3.5647, + "node_id": "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_fixed_tool_values_and_thread_invariance[False]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 3.4791, + "node_id": "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_fixed_tool_values_and_thread_invariance[True]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 4.6427, + "node_id": "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[cardinal-False]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 6.3028, + "node_id": "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[cardinal-True]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 6.0748, + "node_id": "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[cardinal-constant]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 4.3777, + "node_id": "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[skew-False]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 5.4449, + "node_id": "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[skew-True]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 4.3421, + "node_id": "test/cuda_equivalents/test_cuda_prismatic_fixed_target.py::test_prismatic_dynamics_against_independent_mechanics[skew-constant]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 6.417, "node_id": "test/cuda_equivalents/test_cuda_racecheck_floating.py::test_go2_floating_composition_is_racecheck_clean", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.663, + "duration_s": 3.1107, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.0849, + "duration_s": 3.5895, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.0121, + "duration_s": 6.6334, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.7506, + "duration_s": 14.8919, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.0519, + "duration_s": 3.8139, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.7278, + "duration_s": 3.1649, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.6772, + "duration_s": 3.9843, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.519, + "duration_s": 4.4417, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.8674, + "duration_s": 4.2624, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.0959, + "duration_s": 3.5067, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.7532, + "duration_s": 4.136, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 12.2515, + "duration_s": 12.1755, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 29.4972, + "duration_s": 32.2839, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.4793, + "duration_s": 3.8198, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.1421, + "duration_s": 3.6847, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.1763, + "duration_s": 3.555, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.1622, + "duration_s": 4.6358, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.5543, + "duration_s": 5.1093, "node_id": "test/cuda_equivalents/test_cuda_regressor.py::test_cuda_regressor_matches_reference[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.6512, + "duration_s": 4.6853, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.7425, + "duration_s": 4.0896, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 11.9931, + "duration_s": 11.9616, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 38.3322, + "duration_s": 41.6735, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.305, + "duration_s": 6.7684, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.7916, + "duration_s": 4.9821, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.6345, + "duration_s": 4.5664, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.5163, + "duration_s": 6.9794, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.1728, + "duration_s": 6.3438, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4109, + "duration_s": 5.5768, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.8923, + "duration_s": 6.0327, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.1326, + "duration_s": 15.1885, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 45.6407, + "duration_s": 45.8959, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[h1_2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.5947, + "duration_s": 6.6694, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.5402, + "duration_s": 4.7131, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.4895, + "duration_s": 4.658, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.7714, + "duration_s": 7.0075, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.9108, + "duration_s": 7.1839, "node_id": "test/cuda_equivalents/test_cuda_regressor_gradient.py::test_cuda_regressor_gradient_matches_reference[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 9.8107, + "duration_s": 5.0094, + "node_id": "test/cuda_equivalents/test_cuda_revolute_mimic_mechanics.py::test_revolute_mimic_dynamics_against_independent_mechanics", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 7.8681, "node_id": "test/cuda_equivalents/test_cuda_safe_init.py::test_checked_initialization_contract[iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 17.582, + "duration_s": 15.8325, "node_id": "test/cuda_equivalents/test_cuda_safe_init.py::test_checked_initialization_contract[go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.979, + "duration_s": 5.4496, "node_id": "test/cuda_equivalents/test_cuda_safe_init.py::test_legacy_policy_is_preserved[iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.0307, + "duration_s": 0.9095, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_fixed_second_order_forced_fallback_matches_python_reference[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2192, + "duration_s": 1.1263, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_fixed_second_order_forced_fallback_matches_python_reference[go2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.1649, + "duration_s": 1.0189, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_fixed_second_order_forced_fallback_matches_python_reference[fr3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.4826, + "duration_s": 2.442, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_fixed_second_order_forced_fallback_matches_python_reference[g1-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 39.5791, + "duration_s": 39.2716, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_fixed_second_order_forced_fallback_matches_python_reference[h1_2-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.0382, + "duration_s": 0.9282, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_fixed_second_order_forced_fallback_matches_python_reference[rizon4-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.0762, + "duration_s": 0.8889, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_fixed_second_order_forced_fallback_matches_python_reference[gen3-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3735, + "duration_s": 1.3313, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_fixed_second_order_forced_fallback_matches_python_reference[fetch-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5638, + "duration_s": 1.413, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_fixed_second_order_forced_fallback_matches_python_reference[baxter-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3422, + "duration_s": 0.982, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_floating_second_order_diagnostic_matches_python_reference[iiwa14-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5729, + "duration_s": 1.3673, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_floating_second_order_diagnostic_matches_python_reference[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3829, + "duration_s": 1.0899, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_floating_second_order_diagnostic_matches_python_reference[fr3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.5349, + "duration_s": 2.3288, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_floating_second_order_diagnostic_matches_python_reference[g1-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3192, + "duration_s": 1.0291, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_floating_second_order_diagnostic_matches_python_reference[rizon4-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3177, + "duration_s": 1.0142, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_floating_second_order_diagnostic_matches_python_reference[gen3-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.5623, + "duration_s": 1.3365, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_floating_second_order_diagnostic_matches_python_reference[fetch-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.7639, + "duration_s": 1.4893, "node_id": "test/cuda_equivalents/test_cuda_second_order_fallback.py::test_floating_second_order_diagnostic_matches_python_reference[baxter-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.3026, + "duration_s": 2.9093, "node_id": "test/cuda_equivalents/test_cuda_spherical_eepose_gradhess_equivalence.py::test_cuda_spherical_eepose_gradhess_matches_reference[float-spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.1869, + "duration_s": 2.9623, "node_id": "test/cuda_equivalents/test_cuda_spherical_eepose_gradhess_equivalence.py::test_cuda_spherical_eepose_gradhess_matches_reference[float-mixed_spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.6782, + "duration_s": 3.1671, "node_id": "test/cuda_equivalents/test_cuda_spherical_eepose_gradhess_equivalence.py::test_cuda_spherical_eepose_gradhess_thread_invariant[spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.8451, + "duration_s": 3.2937, "node_id": "test/cuda_equivalents/test_cuda_spherical_eepose_gradhess_equivalence.py::test_cuda_spherical_eepose_gradhess_thread_invariant[mixed_spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.0833, + "duration_s": 5.3621, "node_id": "test/cuda_equivalents/test_cuda_spherical_equivalence.py::test_cuda_spherical_inverse_dynamics_matches_reference[spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.3591, + "duration_s": 6.644, "node_id": "test/cuda_equivalents/test_cuda_spherical_equivalence.py::test_cuda_spherical_inverse_dynamics_matches_reference[mixed_spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 12.8397, + "duration_s": 12.0321, "node_id": "test/cuda_equivalents/test_cuda_spherical_equivalence.py::test_cuda_spherical_thread_invariant[spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 18.0546, + "duration_s": 16.6975, "node_id": "test/cuda_equivalents/test_cuda_spherical_equivalence.py::test_cuda_spherical_thread_invariant[mixed_spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.6091, + "duration_s": 3.0781, "node_id": "test/cuda_equivalents/test_cuda_spherical_equivalence.py::test_cuda_spherical_inverse_dynamics_gradient_matches_reference[spherical_arm.urdf-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.3686, + "duration_s": 3.8272, "node_id": "test/cuda_equivalents/test_cuda_spherical_equivalence.py::test_cuda_spherical_inverse_dynamics_gradient_matches_reference[mixed_spherical_arm.urdf-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.2202, + "duration_s": 3.7432, "node_id": "test/cuda_equivalents/test_cuda_spherical_equivalence.py::test_cuda_spherical_forward_dynamics_gradient_matches_reference[spherical_arm.urdf-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 5.3225, + "duration_s": 4.7026, "node_id": "test/cuda_equivalents/test_cuda_spherical_equivalence.py::test_cuda_spherical_forward_dynamics_gradient_matches_reference[mixed_spherical_arm.urdf-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.2846, + "duration_s": 5.6571, "node_id": "test/cuda_equivalents/test_cuda_spherical_equivalence.py::test_cuda_spherical_fdsva_so_matches_reference[spherical_arm.urdf-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.8626, + "duration_s": 7.3821, "node_id": "test/cuda_equivalents/test_cuda_spherical_equivalence.py::test_cuda_spherical_fdsva_so_matches_reference[mixed_spherical_arm.urdf-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.2409, + "duration_s": 5.5839, "node_id": "test/cuda_equivalents/test_cuda_spherical_fdsva_so_equivalence.py::test_cuda_spherical_fdsva_so_matches_world_reference[spherical_arm.urdf-perf-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.003, + "duration_s": 5.4637, "node_id": "test/cuda_equivalents/test_cuda_spherical_fdsva_so_equivalence.py::test_cuda_spherical_fdsva_so_matches_world_reference[spherical_arm.urdf-cold-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.8876, + "duration_s": 7.0235, "node_id": "test/cuda_equivalents/test_cuda_spherical_fdsva_so_equivalence.py::test_cuda_spherical_fdsva_so_matches_world_reference[mixed_spherical_arm.urdf-perf-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.6547, + "duration_s": 6.8966, "node_id": "test/cuda_equivalents/test_cuda_spherical_fdsva_so_equivalence.py::test_cuda_spherical_fdsva_so_matches_world_reference[mixed_spherical_arm.urdf-cold-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.0983, + "duration_s": 5.5295, "node_id": "test/cuda_equivalents/test_cuda_spherical_fdsva_so_equivalence.py::test_cuda_spherical_fdsva_so_thread_invariant[spherical_arm.urdf-perf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 6.0227, + "duration_s": 5.3792, "node_id": "test/cuda_equivalents/test_cuda_spherical_fdsva_so_equivalence.py::test_cuda_spherical_fdsva_so_thread_invariant[spherical_arm.urdf-cold]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.7809, + "duration_s": 7.0094, "node_id": "test/cuda_equivalents/test_cuda_spherical_fdsva_so_equivalence.py::test_cuda_spherical_fdsva_so_thread_invariant[mixed_spherical_arm.urdf-perf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.413, + "duration_s": 6.8969, "node_id": "test/cuda_equivalents/test_cuda_spherical_fdsva_so_equivalence.py::test_cuda_spherical_fdsva_so_thread_invariant[mixed_spherical_arm.urdf-cold]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.7156, + "duration_s": 12.7541, "node_id": "test/cuda_equivalents/test_cuda_spherical_integrator_equivalence.py::test_cuda_spherical_integrator_matches_reference[spherical_arm.urdf-float-0.0001-0.0001]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.4109, + "duration_s": 9.8562, "node_id": "test/cuda_equivalents/test_cuda_spherical_integrator_equivalence.py::test_cuda_spherical_integrator_matches_reference[mixed_spherical_arm.urdf-float-0.0001-0.0001]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 13.5377, + "duration_s": 12.7838, "node_id": "test/cuda_equivalents/test_cuda_spherical_integrator_equivalence.py::test_cuda_spherical_integrator_thread_invariant[spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.5479, + "duration_s": 9.673, "node_id": "test/cuda_equivalents/test_cuda_spherical_integrator_equivalence.py::test_cuda_spherical_integrator_thread_invariant[mixed_spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 14.5262, + "duration_s": 13.7271, "node_id": "test/cuda_equivalents/test_cuda_spherical_integrator_gradient_equivalence.py::test_spherical_integrator_gradient_matches_reference[mixed_spherical_arm-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.692, + "duration_s": 0.4558, "node_id": "test/cuda_equivalents/test_cuda_spherical_integrator_gradient_equivalence.py::test_spherical_integrator_gradient_thread_invariance[mixed_spherical_arm-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 9.6823, + "duration_s": 9.1046, "node_id": "test/cuda_equivalents/test_cuda_spherical_integrator_gradient_equivalence.py::test_spherical_integrator_gradient_matches_reference[spherical_arm-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.6818, + "duration_s": 0.442, "node_id": "test/cuda_equivalents/test_cuda_spherical_integrator_gradient_equivalence.py::test_spherical_integrator_gradient_thread_invariance[spherical_arm-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.3309, + "duration_s": 2.0817, "node_id": "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_matches_reference[float-spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.439, - "node_id": "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_matches_reference[float-mixed_spherical_arm.urdf]", + "duration_s": 2.1609, + "node_id": "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_matches_reference[float-mixed_spherical_arm.urdf]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 2.469, + "node_id": "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_thread_invariant[spherical_arm.urdf]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 2.5582, + "node_id": "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_thread_invariant[mixed_spherical_arm.urdf]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.2509, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-spherical_arm]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.3039, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-mixed_spherical_arm]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.1762, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-single_spherical]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.4587, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-euler-float-two_spherical]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.2905, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-spherical_arm]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.3225, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-mixed_spherical_arm]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.191, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-single_spherical]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.3819, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[shared-semi_implicit_euler-float-two_spherical]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.2475, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-spherical_arm]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.3181, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-mixed_spherical_arm]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.1962, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-single_spherical]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.3825, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-euler-float-two_spherical]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.2576, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-spherical_arm]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.3341, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-mixed_spherical_arm]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.2433, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-single_spherical]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.4907, + "node_id": "test/cuda_equivalents/test_cuda_spherical_plant_hessian.py::test_spherical_plant_hessian[minimal-semi_implicit_euler-float-two_spherical]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 3.0913, + "node_id": "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_matches_world_reference[spherical_arm.urdf-float]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 3.11, + "node_id": "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_matches_world_reference[mixed_spherical_arm.urdf-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0348, - "node_id": "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_thread_invariant[spherical_arm.urdf]", + "duration_s": 3.0943, + "node_id": "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_thread_invariant[spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.0788, - "node_id": "test/cuda_equivalents/test_cuda_spherical_kinematics_equivalence.py::test_cuda_spherical_kinematics_thread_invariant[mixed_spherical_arm.urdf]", + "duration_s": 3.1084, + "node_id": "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_thread_invariant[mixed_spherical_arm.urdf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5868, - "node_id": "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_matches_world_reference[spherical_arm.urdf-float]", + "duration_s": 6.8031, + "node_id": "test/cuda_equivalents/test_cuda_vendor_glass_alias.py::test_vendor_glass_false_compiles_against_top_level_glass_and_runs", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.6556, - "node_id": "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_matches_world_reference[mixed_spherical_arm.urdf-float]", + "duration_s": 10.1417, + "node_id": "test/cuda_equivalents/test_cuda_workspace_slots.py::test_cuda_workspace_slots_bit_identity[iiwa14-fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.5465, - "node_id": "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_thread_invariant[spherical_arm.urdf]", + "duration_s": 21.0373, + "node_id": "test/cuda_equivalents/test_cuda_workspace_slots.py::test_cuda_workspace_slots_bit_identity[go2-floating]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.6128, - "node_id": "test/cuda_equivalents/test_cuda_spherical_so_equivalence.py::test_cuda_spherical_idsva_so_thread_invariant[mixed_spherical_arm.urdf]", + "duration_s": 0.8426, + "node_id": "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[pose-hessian-True-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 7.2992, - "node_id": "test/cuda_equivalents/test_cuda_vendor_glass_alias.py::test_vendor_glass_false_compiles_against_top_level_glass_and_runs", + "duration_s": 0.8506, + "node_id": "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[pose-hessian-False-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 10.849, - "node_id": "test/cuda_equivalents/test_cuda_workspace_slots.py::test_cuda_workspace_slots_bit_identity[iiwa14-fixed]", + "duration_s": 0.611, + "node_id": "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[plant-gradient-True-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 21.9809, - "node_id": "test/cuda_equivalents/test_cuda_workspace_slots.py::test_cuda_workspace_slots_bit_identity[go2-floating]", + "duration_s": 0.6903, + "node_id": "test/cuda_equivalents/test_cuda_zero_dof_surfaces.py::test_zero_dof_device_surfaces[plant-gradient-False-float]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 16.4322, + "duration_s": 2.4779, "node_id": "test/cuda_equivalents/test_smem_poison.py::test_equivalence_survives_smem_poison[iiwa14 and fixed]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 27.9389, + "duration_s": 4.7724, "node_id": "test/cuda_equivalents/test_smem_poison.py::test_equivalence_survives_smem_poison[go2 and floating]", "outcome": "passed", "phase": "call" @@ -18137,14 +19859,14 @@ }, { "checks": [], - "duration_s": 0.0001, + "duration_s": 0.0, "node_id": "test/python_wrappers/test_any_thread_count.py::test_set_threads_per_block_zero_resets_to_autotuned_default", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0002, + "duration_s": 0.0001, "node_id": "test/python_wrappers/test_any_thread_count.py::test_set_threads_per_block_rejects_negative", "outcome": "passed", "phase": "call" @@ -18410,21 +20132,21 @@ }, { "checks": [], - "duration_s": 0.0002, + "duration_s": 0.0001, "node_id": "test/python_wrappers/test_batch_switch.py::test_set_readback_clear", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2514, + "duration_s": 0.3585, "node_id": "test/python_wrappers/test_batch_switch.py::test_values_identical_across_regimes", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0002, + "duration_s": 0.0005, "node_id": "test/python_wrappers/test_batch_switch.py::test_global_override_beats_switch", "outcome": "passed", "phase": "call" @@ -18438,7 +20160,7 @@ }, { "checks": [], - "duration_s": 0.2904, + "duration_s": 0.2864, "node_id": "test/python_wrappers/test_centroidal_energy_frame.py::test_com[iiwa14]", "outcome": "passed", "phase": "call" @@ -18452,14 +20174,14 @@ }, { "checks": [], - "duration_s": 0.0101, + "duration_s": 0.0099, "node_id": "test/python_wrappers/test_centroidal_energy_frame.py::test_energy[iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0011, + "duration_s": 0.001, "node_id": "test/python_wrappers/test_centroidal_energy_frame.py::test_generalized_gravity[iiwa14]", "outcome": "passed", "phase": "call" @@ -18473,14 +20195,14 @@ }, { "checks": [], - "duration_s": 0.0011, + "duration_s": 0.0009, "node_id": "test/python_wrappers/test_centroidal_energy_frame.py::test_frame_jacobian[iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0013, + "duration_s": 0.0014, "node_id": "test/python_wrappers/test_centroidal_energy_frame.py::test_frame_jacobian_nondefault_frame[iiwa14]", "outcome": "passed", "phase": "call" @@ -18501,35 +20223,35 @@ }, { "checks": [], - "duration_s": 0.0041, + "duration_s": 0.0042, "node_id": "test/python_wrappers/test_centroidal_energy_frame.py::test_osc_inertia[iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1416, + "duration_s": 0.0292, "node_id": "test/python_wrappers/test_centroidal_energy_frame.py::test_com[go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0032, + "duration_s": 0.0031, "node_id": "test/python_wrappers/test_centroidal_energy_frame.py::test_ccrba[go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0146, + "duration_s": 0.0149, "node_id": "test/python_wrappers/test_centroidal_energy_frame.py::test_energy[go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0014, + "duration_s": 0.0015, "node_id": "test/python_wrappers/test_centroidal_energy_frame.py::test_generalized_gravity[go2]", "outcome": "passed", "phase": "call" @@ -18564,196 +20286,294 @@ }, { "checks": [], - "duration_s": 0.0013, + "duration_s": 0.0012, "node_id": "test/python_wrappers/test_centroidal_energy_frame.py::test_frame_jacobian_dot[go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4918, + "duration_s": 2.2511, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_descriptor_matches_exported_cpp_and_source_free_prepared_load", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 7.0119, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_compiler_discovered_headers_rekey_native_values", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 9.8783, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_consumer_migration_example", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.9012, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_shadowing_symlink_preserves_header_file_macro_identity", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.013, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_dependency_filename_escaping[space name.h]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.013, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_dependency_filename_escaping[hash#name.h]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.0124, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_dependency_filename_escaping[cash$name.h]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.0129, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_dependency_filename_escaping[cash$$name.h]", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.0126, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_real_compiler_unreadable_dependency_fails_closed", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 1.9844, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_concurrent_consumer_builds_publish_loadable_winner", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 2.1577, "node_id": "test/python_wrappers/test_consumer_artifact.py::test_artifact_builds_loads_and_matches_the_oracle", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.349, + "duration_s": 4.0603, "node_id": "test/python_wrappers/test_consumer_artifact.py::test_second_build_is_a_pointer_hit_and_a_define_rekeys", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.9809, + "duration_s": 4.4823, "node_id": "test/python_wrappers/test_consumer_artifact.py::test_unchanged_unit_reuses_its_object", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.3941, + "duration_s": 2.4644, "node_id": "test/python_wrappers/test_consumer_artifact.py::test_tampered_identity_is_refused_and_the_content_key_rehits", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.0782, + "duration_s": 1.8854, "node_id": "test/python_wrappers/test_consumer_artifact.py::test_load_without_a_toolchain_and_device_refusal", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.1966, + "duration_s": 2.9898, "node_id": "test/python_wrappers/test_consumer_artifact.py::test_collision_options_emit_the_namespace_and_validate", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.2029, + "duration_s": 3.6364, "node_id": "test/python_wrappers/test_consumer_artifact.py::test_plant_block_follows_the_request", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4195, + "duration_s": 8.3086, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_declared_python_extension_import_and_prepared_load", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.38, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_undeclared_python_extension_is_not_published", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.9398, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_source_path_macro_survives_cache_reuse", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 3.1494, + "node_id": "test/python_wrappers/test_consumer_artifact.py::test_two_robot_consumer_statics_are_isolated_in_both_load_orders", + "outcome": "passed", + "phase": "call" + }, + { + "checks": [], + "duration_s": 0.4026, "node_id": "test/python_wrappers/test_contact_fext.py::test_contact_fext_matches_host_map", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.8082, + "duration_s": 0.8037, "node_id": "test/python_wrappers/test_contact_fext.py::test_contact_fext_floating_dynamics_only_subset_matches_host_map", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.8158, + "duration_s": 1.9038, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_gradient_hessian[iiwa14-floating-named-iiwa_joint_ee]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.7427, + "duration_s": 0.7932, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_gradient_hessian[go2-floating-named-imu_joint]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.8191, + "duration_s": 0.8805, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_gradient_hessian[baxter-floating-named-right_hand_camera_axis]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.9819, + "duration_s": 1.0489, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_gradient_hessian[go2-floating-multileaf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.029, + "duration_s": 1.0682, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_gradient_hessian[baxter-floating-multileaf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3517, + "duration_s": 0.418, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_gradient_hessian[iiwa14-fixed-named-iiwa_joint_ee]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2788, + "duration_s": 0.3359, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_thread_invariance[iiwa14-floating-named-iiwa_joint_ee]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5718, + "duration_s": 0.6237, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_thread_invariance[go2-floating-named-imu_joint]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.8764, + "duration_s": 0.908, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_thread_invariance[baxter-floating-named-right_hand_camera_axis]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5849, + "duration_s": 0.598, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_thread_invariance[go2-floating-multileaf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.7347, + "duration_s": 0.7836, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_thread_invariance[baxter-floating-multileaf]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2029, + "duration_s": 0.252, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_multileaf_thread_invariance[iiwa14-fixed-named-iiwa_joint_ee]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.6312, + "duration_s": 0.659, "node_id": "test/python_wrappers/test_ee_named_target_floating_multileaf.py::test_ee_named_root_attached_zero_gradient_hessian", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.7048, + "duration_s": 0.6418, "node_id": "test/python_wrappers/test_f_ext_backward_and_shapes.py::test_jax_gradient_is_taken_at_the_external_force[float32-inverse_dynamics]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.269, + "duration_s": 0.2596, "node_id": "test/python_wrappers/test_f_ext_backward_and_shapes.py::test_jax_gradient_is_taken_at_the_external_force[float32-forward_dynamics]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0684, + "duration_s": 0.0669, "node_id": "test/python_wrappers/test_f_ext_backward_and_shapes.py::test_jax_jit_does_not_capture_the_force_as_a_constant[float32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0898, + "duration_s": 0.0855, "node_id": "test/python_wrappers/test_f_ext_backward_and_shapes.py::test_torch_and_jax_force_conditioned_gradients_agree[float32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0906, + "duration_s": 0.0853, "node_id": "test/python_wrappers/test_f_ext_backward_and_shapes.py::test_jax_f_ext_broadcast_is_materialized_and_widths_rejected[float32]", "outcome": "passed", "phase": "call" @@ -18767,238 +20587,238 @@ }, { "checks": [], - "duration_s": 1.5515, + "duration_s": 1.3464, "node_id": "test/python_wrappers/test_fk_batched.py::test_fk_batched_matches_reference[iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2399, + "duration_s": 0.2935, "node_id": "test/python_wrappers/test_fk_batched.py::test_fk_batched_matches_reference[gen3]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.6038, + "duration_s": 0.6259, "node_id": "test/python_wrappers/test_fk_batched.py::test_fk_batched_matches_reference[go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3194, + "duration_s": 0.3535, "node_id": "test/python_wrappers/test_fk_batched.py::test_fk_batched_matches_reference[fr3]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.053, + "duration_s": 0.1128, "node_id": "test/python_wrappers/test_fk_batched.py::test_fk_batched_variant_invariant_to_block_threads[thread]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0512, + "duration_s": 0.1088, "node_id": "test/python_wrappers/test_fk_batched.py::test_fk_batched_variant_invariant_to_block_threads[warp]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0552, + "duration_s": 0.1085, "node_id": "test/python_wrappers/test_fk_batched.py::test_fk_batched_layout_distinct_per_sample", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.54, + "duration_s": 0.5627, "node_id": "test/python_wrappers/test_fk_batched.py::test_fk_batched_floating_base_matches_reference", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.4228, + "duration_s": 1.4522, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_jax_q_gradient_matches_ambient_finite_differences[float32-inverse_dynamics-unit-quat]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0778, + "duration_s": 0.0927, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_jax_q_gradient_matches_ambient_finite_differences[float32-inverse_dynamics-non-unit-quat]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3696, + "duration_s": 0.3902, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_jax_q_gradient_matches_ambient_finite_differences[float32-forward_dynamics-unit-quat]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1221, + "duration_s": 0.1417, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_jax_q_gradient_matches_ambient_finite_differences[float32-forward_dynamics-non-unit-quat]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3903, + "duration_s": 0.4055, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_jax_q_gradient_matches_ambient_finite_differences[float32-end_effector_pose-unit-quat]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0502, + "duration_s": 0.0589, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_jax_q_gradient_matches_ambient_finite_differences[float32-end_effector_pose-non-unit-quat]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2346, + "duration_s": 0.2503, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_torch_q_gradient_matches_ambient_finite_differences[float32-inverse_dynamics-unit-quat]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0542, + "duration_s": 0.0555, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_torch_q_gradient_matches_ambient_finite_differences[float32-inverse_dynamics-non-unit-quat]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0831, + "duration_s": 0.0868, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_torch_q_gradient_matches_ambient_finite_differences[float32-forward_dynamics-unit-quat]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0655, + "duration_s": 0.0767, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_torch_q_gradient_matches_ambient_finite_differences[float32-forward_dynamics-non-unit-quat]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.053, + "duration_s": 0.0643, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_torch_q_gradient_matches_ambient_finite_differences[float32-end_effector_pose-unit-quat]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.038, + "duration_s": 0.049, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_torch_q_gradient_matches_ambient_finite_differences[float32-end_effector_pose-non-unit-quat]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.236, + "duration_s": 0.2597, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle0-float32-inverse_dynamics-jax]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0647, + "duration_s": 0.0899, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle0-float32-inverse_dynamics-torch]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1623, + "duration_s": 0.3106, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle0-float32-forward_dynamics-jax]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0656, + "duration_s": 0.0916, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle0-float32-forward_dynamics-torch]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2238, + "duration_s": 0.2567, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle0-float32-end_effector_pose-jax]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1945, + "duration_s": 0.0979, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle0-float32-end_effector_pose-torch]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2466, + "duration_s": 0.2823, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle1-float32-inverse_dynamics-jax]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0651, + "duration_s": 0.0892, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle1-float32-inverse_dynamics-torch]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1603, + "duration_s": 0.2709, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle1-float32-forward_dynamics-jax]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0684, + "duration_s": 0.0987, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle1-float32-forward_dynamics-torch]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2652, + "duration_s": 0.2304, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle1-float32-end_effector_pose-jax]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0659, + "duration_s": 0.0963, "node_id": "test/python_wrappers/test_floating_q_cotangent.py::test_spherical_public_q_gradient[spherical_handle1-float32-end_effector_pose-torch]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.251, + "duration_s": 0.1138, "node_id": "test/python_wrappers/test_fp64_parity.py::test_dtype_roundtrip", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0279, + "duration_s": 0.0285, "node_id": "test/python_wrappers/test_fp64_parity.py::test_fp64_inverse_dynamics_tight", "outcome": "passed", "phase": "call" @@ -19026,21 +20846,21 @@ }, { "checks": [], - "duration_s": 0.0289, + "duration_s": 0.027, "node_id": "test/python_wrappers/test_fp64_parity.py::test_fp64_materially_tighter_than_fp32", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4467, + "duration_s": 0.3658, "node_id": "test/python_wrappers/test_g1_plant_hessian_smoke.py::test_plant_step_hessian[euler]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1037, + "duration_s": 0.0998, "node_id": "test/python_wrappers/test_g1_plant_hessian_smoke.py::test_plant_step_hessian[semi_implicit_euler]", "outcome": "passed", "phase": "call" @@ -19054,7 +20874,7 @@ }, { "checks": [], - "duration_s": 0.3046, + "duration_s": 0.3143, "node_id": "test/python_wrappers/test_iiwa14_f_ext.py::test_inverse_dynamics_f_ext", "outcome": "passed", "phase": "call" @@ -19089,14 +20909,14 @@ }, { "checks": [], - "duration_s": 0.1992, + "duration_s": 0.2265, "node_id": "test/python_wrappers/test_iiwa14_f_ext.py::test_torch_inverse_dynamics_f_ext", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.6909, + "duration_s": 0.7569, "node_id": "test/python_wrappers/test_iiwa14_f_ext.py::test_jax_no_f_ext_residue_after_f_ext_call", "outcome": "passed", "phase": "call" @@ -19110,14 +20930,14 @@ }, { "checks": [], - "duration_s": 0.1278, + "duration_s": 0.1303, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_inverse_dynamics_eager_matches_plain", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0318, + "duration_s": 0.0319, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_inverse_dynamics_jit_matches_eager", "outcome": "passed", "phase": "call" @@ -19131,14 +20951,14 @@ }, { "checks": [], - "duration_s": 0.0252, + "duration_s": 0.0246, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_inverse_dynamics_honors_qdd_under_jit", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0195, + "duration_s": 0.0177, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_jax_f_ext_parity_vs_numpy", "outcome": "passed", "phase": "call" @@ -19152,14 +20972,14 @@ }, { "checks": [], - "duration_s": 0.0286, + "duration_s": 0.0279, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_inverse_dynamics_under_vmap", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2687, + "duration_s": 0.2607, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_minv_eager_matches_plain", "outcome": "passed", "phase": "call" @@ -19173,329 +20993,329 @@ }, { "checks": [], - "duration_s": 0.0015, + "duration_s": 0.0013, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_forward_dynamics_eager_matches_plain", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0009, + "duration_s": 0.0012, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_aba_eager_matches_plain", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0015, + "duration_s": 0.0016, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_aba_matches_forward_dynamics", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0088, + "duration_s": 0.0087, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_crba_eager_matches_plain", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0011, + "duration_s": 0.001, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_crba_is_symmetric", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0019, + "duration_s": 0.0018, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_minv_and_crba_invert", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0084, + "duration_s": 0.0088, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_end_effector_pose_eager_matches_plain", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0099, + "duration_s": 0.0106, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_end_effector_pose_jit_matches_eager", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.073, + "duration_s": 0.0743, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_end_effector_pose_gradient_eager_matches_plain", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0747, + "duration_s": 0.0507, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_end_effector_pose_gradient_jit_matches_eager", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0164, + "duration_s": 0.0178, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_end_effector_pose_hessian_eager_matches_plain", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0135, + "duration_s": 0.0131, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_end_effector_pose_hessian_jit_matches_eager", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1854, + "duration_s": 0.1227, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_inverse_dynamics_gradient_eager_matches_plain", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0678, + "duration_s": 0.0578, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_inverse_dynamics_gradient_jit_matches_eager", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0128, + "duration_s": 0.0093, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_forward_dynamics_grad_eager_matches_plain", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0414, + "duration_s": 0.0353, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_forward_dynamics_grad_jit_matches_eager", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0508, + "duration_s": 0.0428, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_idsva_so_eager_matches_plain", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0511, + "duration_s": 0.0437, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_idsva_so_jit_matches_eager", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0132, + "duration_s": 0.0103, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_fdsva_so_eager_matches_plain", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0468, + "duration_s": 0.0414, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_fdsva_so_jit_matches_eager", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0113, + "duration_s": 0.0086, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_eager_matches_plain[euler]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0105, + "duration_s": 0.008, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_eager_matches_plain[semi_implicit_euler]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.011, + "duration_s": 0.0083, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_eager_matches_plain[constant_acceleration]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0107, + "duration_s": 0.0091, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_eager_matches_plain[trapezoidal]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0101, + "duration_s": 0.0099, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_eager_matches_plain[midpoint]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0105, + "duration_s": 0.0103, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_eager_matches_plain[rk4]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0777, + "duration_s": 0.0741, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_gradient_eager_matches_plain[euler]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0127, + "duration_s": 0.0102, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_gradient_eager_matches_plain[semi_implicit_euler]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0128, + "duration_s": 0.0099, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_gradient_eager_matches_plain[constant_acceleration]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.013, + "duration_s": 0.0106, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_gradient_eager_matches_plain[trapezoidal]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0122, + "duration_s": 0.0102, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_gradient_eager_matches_plain[midpoint]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0117, + "duration_s": 0.0105, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_gradient_eager_matches_plain[rk4]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.012, + "duration_s": 0.0122, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_jit_matches_eager", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.054, + "duration_s": 0.0536, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_integrator_gradient_jit_matches_eager", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1056, + "duration_s": 0.0767, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_all_methods_jit", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0112, + "duration_s": 0.01, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_array_matches_plain[generalized_gravity]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0111, + "duration_s": 0.0088, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_array_matches_plain[nonlinear_effects]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0124, + "duration_s": 0.0098, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_array_matches_plain[coriolis_matrix]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0114, + "duration_s": 0.0102, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_array_matches_plain[kinetic_energy_regressor]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0107, + "duration_s": 0.0088, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_array_matches_plain[potential_energy_regressor]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0111, + "duration_s": 0.0094, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_array_matches_plain[energy]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0776, + "duration_s": 0.0664, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_array_matches_plain[cmm_time_variation]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0855, + "duration_s": 0.0792, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_array_matches_plain[dccrba]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0118, + "duration_s": 0.0094, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_array_matches_plain[frame_jacobian]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.013, + "duration_s": 0.01, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_array_matches_plain[frame_jacobian_dot]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0229, + "duration_s": 0.0177, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_array_matches_plain[osc_inertia]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2021, + "duration_s": 0.1751, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_com_ccrba_tuple_matches_plain", "outcome": "passed", "phase": "call" @@ -19509,21 +21329,21 @@ }, { "checks": [], - "duration_s": 0.1349, + "duration_s": 0.1114, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_ptier1_runtime_ee_matches_plain", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0618, + "duration_s": 0.1264, "node_id": "test/python_wrappers/test_iiwa14_jax_smoke.py::test_register_idempotent", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2689, + "duration_s": 0.1717, "node_id": "test/python_wrappers/test_iiwa14_plant_smoke.py::test_quadratic_state_cost", "outcome": "passed", "phase": "call" @@ -19537,49 +21357,49 @@ }, { "checks": [], - "duration_s": 0.0368, + "duration_s": 0.0359, "node_id": "test/python_wrappers/test_iiwa14_plant_smoke.py::test_plant_step", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0164, + "duration_s": 0.0162, "node_id": "test/python_wrappers/test_iiwa14_plant_smoke.py::test_plant_step_gradient", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0251, + "duration_s": 0.0247, "node_id": "test/python_wrappers/test_iiwa14_plant_smoke.py::test_plant_step_hessian[euler]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0237, + "duration_s": 0.0233, "node_id": "test/python_wrappers/test_iiwa14_plant_smoke.py::test_plant_step_hessian[semi_implicit_euler]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0071, + "duration_s": 0.007, "node_id": "test/python_wrappers/test_iiwa14_plant_smoke.py::test_com_cost", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0065, + "duration_s": 0.0064, "node_id": "test/python_wrappers/test_iiwa14_plant_smoke.py::test_momentum_cost", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0013, + "duration_s": 0.0014, "node_id": "test/python_wrappers/test_iiwa14_plant_smoke.py::test_ee_pos_cost", "outcome": "passed", "phase": "call" @@ -19600,56 +21420,56 @@ }, { "checks": [], - "duration_s": 0.1018, + "duration_s": 0.1046, "node_id": "test/python_wrappers/test_iiwa14_qdd_aware_grad.py::test_numpy_gradient_depends_on_qdd", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.7967, + "duration_s": 0.6672, "node_id": "test/python_wrappers/test_iiwa14_qdd_aware_grad.py::test_jax_autograd_through_nonzero_qdd", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0639, + "duration_s": 0.0591, "node_id": "test/python_wrappers/test_iiwa14_qdd_aware_grad.py::test_jax_autograd_qdd_none_regression", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1727, + "duration_s": 0.1779, "node_id": "test/python_wrappers/test_iiwa14_qdd_aware_grad.py::test_jax_public_gradient_takes_qdd", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0872, + "duration_s": 0.0864, "node_id": "test/python_wrappers/test_iiwa14_qdd_aware_grad.py::test_torch_autograd_through_nonzero_qdd", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0101, + "duration_s": 0.0064, "node_id": "test/python_wrappers/test_iiwa14_qdd_aware_grad.py::test_torch_autograd_qdd_none_regression", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0007, + "duration_s": 0.0006, "node_id": "test/python_wrappers/test_iiwa14_qdd_aware_grad.py::test_torch_public_gradient_takes_qdd", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0068, + "duration_s": 0.0094, "node_id": "test/python_wrappers/test_iiwa14_qdd_aware_grad.py::test_torch_sysid_grad_unchanged", "outcome": "passed", "phase": "call" @@ -19663,7 +21483,7 @@ }, { "checks": [], - "duration_s": 0.2717, + "duration_s": 0.2915, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_inverse_dynamics", "outcome": "passed", "phase": "call" @@ -19677,7 +21497,7 @@ }, { "checks": [], - "duration_s": 0.0042, + "duration_s": 0.0043, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_minv", "outcome": "passed", "phase": "call" @@ -19705,7 +21525,7 @@ }, { "checks": [], - "duration_s": 0.0036, + "duration_s": 0.0037, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_end_effector_pose", "outcome": "passed", "phase": "call" @@ -19733,21 +21553,21 @@ }, { "checks": [], - "duration_s": 0.0494, + "duration_s": 0.1109, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_register_idempotent", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.027, + "duration_s": 0.0369, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_get_robot_roundtrip", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0038, + "duration_s": 0.015, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_get_robot_missing_raises", "outcome": "passed", "phase": "call" @@ -19768,7 +21588,7 @@ }, { "checks": [], - "duration_s": 0.0452, + "duration_s": 0.0449, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_idsva_so_honors_qdd", "outcome": "passed", "phase": "call" @@ -19803,14 +21623,14 @@ }, { "checks": [], - "duration_s": 0.0078, + "duration_s": 0.0077, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_integrator[rk4]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0039, + "duration_s": 0.004, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_integrator[trapezoidal]", "outcome": "passed", "phase": "call" @@ -19838,42 +21658,42 @@ }, { "checks": [], - "duration_s": 0.0237, + "duration_s": 0.0236, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_integrator_gradient[midpoint]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0467, + "duration_s": 0.0475, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_integrator_gradient[rk4]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0238, + "duration_s": 0.0246, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_integrator_gradient[trapezoidal]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0119, + "duration_s": 0.0123, "node_id": "test/python_wrappers/test_iiwa14_smoke.py::test_integrator_gradient[constant_acceleration]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0891, + "duration_s": 0.0894, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_forward_parity", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0191, + "duration_s": 0.019, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_idsva_so_parity", "outcome": "passed", "phase": "call" @@ -19887,63 +21707,63 @@ }, { "checks": [], - "duration_s": 0.3711, + "duration_s": 0.3852, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_param_op_forward_parity_vs_jax", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0457, + "duration_s": 0.0443, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_autograd_inverse_dynamics", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0067, + "duration_s": 0.0068, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_autograd_forward_dynamics", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0062, + "duration_s": 0.0063, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_autograd_aba", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0062, + "duration_s": 0.0063, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_autograd_integrator", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.7196, + "duration_s": 0.6757, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_autograd_inverse_dynamics_wrt_params", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2409, + "duration_s": 0.2362, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_autograd_forward_dynamics_wrt_params", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0222, + "duration_s": 0.0223, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_cuda_graph_replay", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0004, + "duration_s": 0.0005, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_capture_over_max_batch_raises", "outcome": "passed", "phase": "call" @@ -19971,7 +21791,7 @@ }, { "checks": [], - "duration_s": 0.0002, + "duration_s": 0.0003, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_ptier1_array_matches_numpy[kinetic_energy_regressor]", "outcome": "passed", "phase": "call" @@ -19999,7 +21819,7 @@ }, { "checks": [], - "duration_s": 0.0003, + "duration_s": 0.0004, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_ptier1_array_matches_numpy[dccrba]", "outcome": "passed", "phase": "call" @@ -20027,7 +21847,7 @@ }, { "checks": [], - "duration_s": 0.0009, + "duration_s": 0.001, "node_id": "test/python_wrappers/test_iiwa14_torch_smoke.py::test_ptier1_com_ccrba_tuple_matches_numpy", "outcome": "passed", "phase": "call" @@ -20041,252 +21861,252 @@ }, { "checks": [], - "duration_s": 0.2874, + "duration_s": 0.2497, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_idsva_so_pack_matches_numpy[go2-eager-1]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0537, + "duration_s": 0.0467, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_idsva_so_pack_matches_numpy[go2-eager-3]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0401, + "duration_s": 0.0387, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_idsva_so_pack_matches_numpy[go2-jit-1]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0408, + "duration_s": 0.0403, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_idsva_so_pack_matches_numpy[go2-jit-3]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0101, + "duration_s": 0.0098, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[go2-euler-integrator]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0847, + "duration_s": 0.0868, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[go2-euler-integrator_gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0096, + "duration_s": 0.0108, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[go2-semi_implicit_euler-integrator]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0366, + "duration_s": 0.0407, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[go2-semi_implicit_euler-integrator_gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0095, + "duration_s": 0.0102, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[go2-midpoint-integrator]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0394, + "duration_s": 0.0407, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[go2-midpoint-integrator_gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.01, + "duration_s": 0.0106, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[go2-rk4-integrator]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0394, + "duration_s": 0.0405, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[go2-rk4-integrator_gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0096, + "duration_s": 0.0105, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[go2-trapezoidal-integrator]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0382, + "duration_s": 0.0399, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[go2-trapezoidal-integrator_gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0102, + "duration_s": 0.0105, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[go2-constant_acceleration-integrator]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0382, + "duration_s": 0.0412, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[go2-constant_acceleration-integrator_gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2313, + "duration_s": 0.2478, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_idsva_so_pack_matches_numpy[g1-eager-1]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0475, + "duration_s": 0.0503, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_idsva_so_pack_matches_numpy[g1-eager-3]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0388, + "duration_s": 0.0407, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_idsva_so_pack_matches_numpy[g1-jit-1]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0445, + "duration_s": 0.0451, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_idsva_so_pack_matches_numpy[g1-jit-3]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0101, + "duration_s": 0.0107, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-euler-integrator]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0808, + "duration_s": 0.0869, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-euler-integrator_gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0102, + "duration_s": 0.0112, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-semi_implicit_euler-integrator]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.15, + "duration_s": 0.0456, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-semi_implicit_euler-integrator_gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0098, + "duration_s": 0.0115, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-midpoint-integrator]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0452, + "duration_s": 0.0467, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-midpoint-integrator_gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0139, + "duration_s": 0.0113, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-rk4-integrator]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0484, + "duration_s": 0.0471, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-rk4-integrator_gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0105, + "duration_s": 0.0108, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-trapezoidal-integrator]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0459, + "duration_s": 0.0468, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-trapezoidal-integrator_gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0109, + "duration_s": 0.0107, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-constant_acceleration-integrator]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0445, + "duration_s": 0.0462, "node_id": "test/python_wrappers/test_jax_floating_input_widths.py::test_floating_integrator_pack_matches_numpy[g1-constant_acceleration-integrator_gradient]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4092, + "duration_s": 0.4342, "node_id": "test/python_wrappers/test_jax_host_transfer.py::test_to_host_matches_device_get_for_array_and_pytree", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0571, + "duration_s": 0.058, "node_id": "test/python_wrappers/test_jax_host_transfer.py::test_to_host_is_size_aware_and_every_route_returns_the_same_values[all-pinned]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0029, + "duration_s": 0.0035, "node_id": "test/python_wrappers/test_jax_host_transfer.py::test_to_host_is_size_aware_and_every_route_returns_the_same_values[all-device_get]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0036, + "duration_s": 0.0029, "node_id": "test/python_wrappers/test_jax_host_transfer.py::test_to_host_is_size_aware_and_every_route_returns_the_same_values[mixed]", "outcome": "passed", "phase": "call" @@ -20300,196 +22120,196 @@ }, { "checks": [], - "duration_s": 0.0017, + "duration_s": 0.002, "node_id": "test/python_wrappers/test_jax_host_transfer.py::test_to_host_uses_the_pinned_host_memory_kind_when_offered", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3077, + "duration_s": 0.3411, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_damping_friction_value_path[iiwa14-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1328, + "duration_s": 0.1469, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_damping_gradient_path[iiwa14-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.6871, + "duration_s": 0.7662, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_gradient_delta_is_damping_diag_only_no_friction[iiwa14-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1268, + "duration_s": 0.1412, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_default_off_matches_bare_oracle_and_differs_from_on[iiwa14-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2517, + "duration_s": 0.27, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_damping_friction_value_path[fr3-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2289, + "duration_s": 0.2444, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_damping_gradient_path[fr3-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.2809, + "duration_s": 1.4053, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_gradient_delta_is_damping_diag_only_no_friction[fr3-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2138, + "duration_s": 0.2258, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_default_off_matches_bare_oracle_and_differs_from_on[fr3-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2493, + "duration_s": 0.2644, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_damping_friction_value_path[fr3-tanh-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2218, + "duration_s": 0.2464, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_damping_gradient_path[fr3-tanh-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5666, + "duration_s": 0.6321, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_gradient_delta_is_damping_diag_only_no_friction[fr3-tanh-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2066, + "duration_s": 0.349, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_default_off_matches_bare_oracle_and_differs_from_on[fr3-tanh-fp32]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1996, + "duration_s": 0.1381, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_rbdreference_oracle_dqd_block_matches_fd[iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2028, + "duration_s": 0.2234, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_rbdreference_oracle_dqd_block_matches_fd[fr3]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2038, + "duration_s": 0.2231, "node_id": "test/python_wrappers/test_joint_dynamics.py::test_rbdreference_oracle_dqd_block_matches_fd[fr3-tanh]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1793, + "duration_s": 0.1879, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_value_path_numpy_jax_torch_parity[iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2526, + "duration_s": 0.2914, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_gradient_path_numpy_jax_torch_parity[iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0183, + "duration_s": 0.0213, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_damped_differs_from_undamped[iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1535, + "duration_s": 0.1442, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_second_order_damping_contract[iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0005, + "duration_s": 0.0006, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_nle_coriolis_damping_convention[iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1087, + "duration_s": 0.1262, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_value_path_numpy_jax_torch_parity[fr3]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2294, + "duration_s": 0.2617, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_gradient_path_numpy_jax_torch_parity[fr3]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0276, + "duration_s": 0.0325, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_damped_differs_from_undamped[fr3]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2198, + "duration_s": 0.3679, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_second_order_damping_contract[fr3]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0005, + "duration_s": 0.0006, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_nle_coriolis_damping_convention[fr3]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0607, + "duration_s": 0.0662, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_value_path_numpy_jax_torch_parity[fr3-tanh]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0188, + "duration_s": 0.0202, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_gradient_path_numpy_jax_torch_parity[fr3-tanh]", "outcome": "passed", "phase": "call" @@ -20503,371 +22323,371 @@ }, { "checks": [], - "duration_s": 0.4374, + "duration_s": 0.5521, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_second_order_damping_contract[fr3-tanh]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0005, + "duration_s": 0.0006, "node_id": "test/python_wrappers/test_joint_dynamics_ffi.py::test_nle_coriolis_damping_convention[fr3-tanh]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 1.053, + "duration_s": 1.1414, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_thread_count_invariant[B1-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1261, + "duration_s": 0.1949, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_thread_count_invariant[B1-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0482, + "duration_s": 0.1069, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_thread_count_invariant[B16-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0461, + "duration_s": 0.1068, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_thread_count_invariant[B16-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0482, + "duration_s": 0.1072, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_thread_count_invariant[B256-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0507, + "duration_s": 0.107, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_thread_count_invariant[B256-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0449, + "duration_s": 0.1062, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_thread_count_invariant[B1-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0479, + "duration_s": 0.1075, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_thread_count_invariant[B1-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0486, + "duration_s": 0.1044, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_thread_count_invariant[B16-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0493, + "duration_s": 0.1086, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_thread_count_invariant[B16-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0481, + "duration_s": 0.1048, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_thread_count_invariant[B256-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0491, + "duration_s": 0.1098, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_thread_count_invariant[B256-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1998, + "duration_s": 0.2638, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t1-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.7494, + "duration_s": 0.8246, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t1-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.165, + "duration_s": 0.2263, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t2-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4827, + "duration_s": 0.5517, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t2-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1656, + "duration_s": 0.2298, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t16-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5597, + "duration_s": 0.6584, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t16-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1647, + "duration_s": 0.2325, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t32-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4902, + "duration_s": 0.5742, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t32-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1677, + "duration_s": 0.2436, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t64-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5648, + "duration_s": 0.6829, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t64-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1641, + "duration_s": 0.2342, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t128-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4909, + "duration_s": 0.5681, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t128-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1684, + "duration_s": 0.2365, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t256-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5677, + "duration_s": 0.6858, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_matches_reference[t256-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1692, + "duration_s": 0.2406, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t1-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4941, + "duration_s": 0.5822, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t1-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1725, + "duration_s": 0.2381, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t2-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5138, + "duration_s": 0.5708, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t2-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2433, + "duration_s": 0.3179, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t16-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4939, + "duration_s": 0.569, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t16-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1668, + "duration_s": 0.2246, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t32-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4963, + "duration_s": 0.5623, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t32-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1674, + "duration_s": 0.2275, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t64-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5834, + "duration_s": 0.651, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t64-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1674, + "duration_s": 0.2281, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t128-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4967, + "duration_s": 0.5582, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t128-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1673, + "duration_s": 0.226, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t256-iiwa14]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.5778, + "duration_s": 0.6624, "node_id": "test/python_wrappers/test_kinematics_thread_batch_matrix.py::test_end_effector_pose_gradient_matches_reference[t256-go2]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 35.1162, + "duration_s": 66.0159, "node_id": "test/python_wrappers/test_mjx_fixed_base_noop.py::test_fixed_base_mujoco_convention_is_noop", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.7131, + "duration_s": 0.658, "node_id": "test/python_wrappers/test_mjx_fixed_base_noop.py::test_fixed_base_mujoco_convention_is_noop_jax_torch", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2467, + "duration_s": 0.1194, "node_id": "test/python_wrappers/test_mjx_twins_contexts.py::test_numpy_mujoco_twins_run_and_differ_from_pinocchio", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0018, + "duration_s": 0.0023, "node_id": "test/python_wrappers/test_mjx_twins_contexts.py::test_numpy_mujoco_twins_on_an_explicit_context", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.233, + "duration_s": 0.2812, "node_id": "test/python_wrappers/test_mjx_twins_contexts.py::test_torch_mujoco_forward_and_backward", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0162, + "duration_s": 0.0301, "node_id": "test/python_wrappers/test_mjx_twins_contexts.py::test_numpy_mujoco_twins_match_the_torch_twins", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0206, + "duration_s": 0.0228, "node_id": "test/python_wrappers/test_mjx_twins_contexts.py::test_no_direct_op_or_twin_writes_outside_its_output", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.447, + "duration_s": 0.499, "node_id": "test/python_wrappers/test_mjx_twins_contexts.py::test_jax_mujoco_forward_and_grad", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1886, + "duration_s": 0.1028, "node_id": "test/python_wrappers/test_numpy_host_transfer.py::test_pinned_empty_is_page_locked_and_in_the_compute_dtype", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0231, + "duration_s": 0.0271, "node_id": "test/python_wrappers/test_numpy_host_transfer.py::test_out_receives_the_result_as_views_and_matches_default[pinned-idsva_so]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0005, + "duration_s": 0.0006, "node_id": "test/python_wrappers/test_numpy_host_transfer.py::test_out_receives_the_result_as_views_and_matches_default[pinned-fdsva_so]", "outcome": "passed", "phase": "call" @@ -20916,14 +22736,14 @@ }, { "checks": [], - "duration_s": 0.0005, + "duration_s": 0.0006, "node_id": "test/python_wrappers/test_numpy_host_transfer.py::test_direct_download_ops_fill_every_element", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0454, + "duration_s": 0.0555, "node_id": "test/python_wrappers/test_numpy_host_transfer.py::test_no_direct_op_writes_outside_its_output", "outcome": "passed", "phase": "call" @@ -20937,70 +22757,70 @@ }, { "checks": [], - "duration_s": 0.5261, + "duration_s": 0.5585, "node_id": "test/python_wrappers/test_numpy_vs_async_framework_race.py::test_numpy_call_is_not_clobbered_by_in_flight_jax_work", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2379, + "duration_s": 0.1037, "node_id": "test/python_wrappers/test_operand_validation.py::test_numpy_rejects_zero_batch_and_shape_faults", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0003, + "duration_s": 0.0004, "node_id": "test/python_wrappers/test_operand_validation.py::test_numpy_coerces_layout_and_dtype", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0021, + "duration_s": 0.004, "node_id": "test/python_wrappers/test_operand_validation.py::test_numpy_refuses_a_broadcast_force_and_the_padded_width", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0518, + "duration_s": 0.0742, "node_id": "test/python_wrappers/test_operand_validation.py::test_torch_native_checks", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4002, + "duration_s": 0.4603, "node_id": "test/python_wrappers/test_operand_validation.py::test_jax_python_checks", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1689, + "duration_s": 0.2041, "node_id": "test/python_wrappers/test_operand_validation.py::test_jax_native_handler_checks_every_operand", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0288, + "duration_s": 0.0524, "node_id": "test/python_wrappers/test_operand_validation.py::test_plant_operands_must_carry_the_leading_batch", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0002, + "duration_s": 0.0003, "node_id": "test/python_wrappers/test_operand_validation.py::test_tool_wrench_and_runtime_target_are_validated", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1277, + "duration_s": 0.1141, "node_id": "test/python_wrappers/test_ps5_value_ops.py::test_coriolis_matrix", "outcome": "passed", "phase": "call" @@ -21014,14 +22834,14 @@ }, { "checks": [], - "duration_s": 0.0033, + "duration_s": 0.0035, "node_id": "test/python_wrappers/test_ps5_value_ops.py::test_potential_energy_regressor", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.003, + "duration_s": 0.0029, "node_id": "test/python_wrappers/test_ps5_value_ops.py::test_dccrba", "outcome": "passed", "phase": "call" @@ -21035,42 +22855,42 @@ }, { "checks": [], - "duration_s": 113.5162, + "duration_s": 156.268, "node_id": "test/python_wrappers/test_ps5_value_ops.py::test_dccrba_cmm_mimic_supported", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 2.9803, + "duration_s": 3.7322, "node_id": "test/python_wrappers/test_release_numerics.py::test_release_numerical_matrix[iiwa14-False]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.7021, + "duration_s": 4.741, "node_id": "test/python_wrappers/test_release_numerics.py::test_release_numerical_matrix[go2-True]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.2307, + "duration_s": 0.1095, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_two_artifacts_have_independent_default_contexts", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0018, + "duration_s": 0.002, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_foreign_id_is_rejected_by_the_other_artifact", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0012, + "duration_s": 0.0014, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_two_contexts_in_one_artifact_are_isolated", "outcome": "passed", "phase": "call" @@ -21084,49 +22904,49 @@ }, { "checks": [], - "duration_s": 0.0112, + "duration_s": 0.0069, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_explicit_context_carves_its_own_slab_and_a_tiny_slab_fails_cleanly", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0614, + "duration_s": 0.062, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_close_racing_with_submission_never_crashes", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3176, + "duration_s": 0.3675, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_torch_and_jax_views_dispatch_to_an_explicit_context", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0009, + "duration_s": 0.001, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_model_version_bumps_on_parameter_mutation_only", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1156, + "duration_s": 0.1946, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_dropped_explicit_context_is_finalized_and_close_is_idempotent", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0014, + "duration_s": 0.0012, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_explicit_workspace_cap_is_honoured_and_results_hold", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0429, + "duration_s": 0.0641, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_default_context_reset_invalidates_a_deferred_backward", "outcome": "passed", "phase": "call" @@ -21140,35 +22960,35 @@ }, { "checks": [], - "duration_s": 0.1342, + "duration_s": 0.1748, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_graph_replay_is_refused_after_mutation_or_close", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0303, + "duration_s": 0.0435, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_torch_backward_rejects_a_mutated_model_and_fresh_forward_recovers", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.688, + "duration_s": 0.7705, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_jax_vjp_rejects_a_mutated_model_and_a_jitted_grad_follows_it", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.5749, + "duration_s": 4.8916, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_replay_admission_racing_mutation_and_close_never_deadlocks", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0266, + "duration_s": 0.044, "node_id": "test/python_wrappers/test_runtime_contexts.py::test_construction_does_not_create_the_default_context_and_a_slab_still_installs", "outcome": "passed", "phase": "call" @@ -21182,14 +23002,14 @@ }, { "checks": [], - "duration_s": 0.3777, + "duration_s": 0.2819, "node_id": "test/python_wrappers/test_runtime_inertia.py::test_baked_params_reproduce_baked_result", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1205, + "duration_s": 0.1331, "node_id": "test/python_wrappers/test_runtime_inertia.py::test_mutated_table_matches_oracle_fp32", "outcome": "passed", "phase": "call" @@ -21203,7 +23023,7 @@ }, { "checks": [], - "duration_s": 0.0332, + "duration_s": 0.0345, "node_id": "test/python_wrappers/test_runtime_inertia.py::test_floating_runtime_equals_baked", "outcome": "passed", "phase": "call" @@ -21224,42 +23044,42 @@ }, { "checks": [], - "duration_s": 100.6588, + "duration_s": 130.9398, "node_id": "test/python_wrappers/test_runtime_inertia.py::test_inertia_poke_seen_across_surfaces", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0178, + "duration_s": 0.0189, "node_id": "test/python_wrappers/test_runtime_inertia.py::test_runtime_equals_baked_so", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0001, + "duration_s": 0.0002, "node_id": "test/python_wrappers/test_runtime_joint_dynamics.py::test_metadata", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1171, + "duration_s": 0.1131, "node_id": "test/python_wrappers/test_runtime_joint_dynamics.py::test_untouched_runtime_equals_baked_bitwise", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0173, + "duration_s": 0.0196, "node_id": "test/python_wrappers/test_runtime_joint_dynamics.py::test_poke_matches_analytic_bias", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0003, + "duration_s": 0.0004, "node_id": "test/python_wrappers/test_runtime_joint_dynamics.py::test_zeroing_table_equals_bare", "outcome": "passed", "phase": "call" @@ -21280,21 +23100,21 @@ }, { "checks": [], - "duration_s": 66.2742, + "duration_s": 109.4729, "node_id": "test/python_wrappers/test_runtime_joint_dynamics.py::test_poke_seen_across_surfaces", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 339.0292, + "duration_s": 476.4452, "node_id": "test/python_wrappers/test_runtime_joint_dynamics.py::test_fr3_mimic_untouched_equals_baked_and_zero_toggles", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 339.3662, + "duration_s": 445.4173, "node_id": "test/python_wrappers/test_runtime_joint_dynamics.py::test_fr3_tanh_width_row_pokes_and_matches_analytic", "outcome": "passed", "phase": "call" @@ -21308,21 +23128,21 @@ }, { "checks": [], - "duration_s": 0.1621, + "duration_s": 0.1367, "node_id": "test/python_wrappers/test_runtime_transform.py::test_default_params_match_baked", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0288, + "duration_s": 0.0321, "node_id": "test/python_wrappers/test_runtime_transform.py::test_perturbed_matches_recodegen", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.4662, + "duration_s": 0.6468, "node_id": "test/python_wrappers/test_runtime_transform.py::test_transform_poke_seen_across_surfaces", "outcome": "passed", "phase": "call" @@ -21336,42 +23156,42 @@ }, { "checks": [], - "duration_s": 1.4261, + "duration_s": 1.3774, "node_id": "test/python_wrappers/test_shared_runtime_lifetime.py::test_sibling_close_does_not_reset_the_shared_runtime", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1512, + "duration_s": 0.2835, "node_id": "test/python_wrappers/test_shared_runtime_lifetime.py::test_reverse_close_order_and_gc", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.299, + "duration_s": 0.4678, "node_id": "test/python_wrappers/test_shared_runtime_lifetime.py::test_framework_pool_owner_survives_installer_gc[torch]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.6252, + "duration_s": 0.8175, "node_id": "test/python_wrappers/test_shared_runtime_lifetime.py::test_framework_pool_owner_survives_installer_gc[jax]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0737, + "duration_s": 0.1671, "node_id": "test/python_wrappers/test_shared_runtime_lifetime.py::test_late_framework_view_preserves_live_inertia[torch]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0849, + "duration_s": 0.1686, "node_id": "test/python_wrappers/test_shared_runtime_lifetime.py::test_late_framework_view_preserves_live_inertia[jax]", "outcome": "passed", "phase": "call" @@ -21385,42 +23205,42 @@ }, { "checks": [], - "duration_s": 0.131, + "duration_s": 0.12, "node_id": "test/python_wrappers/test_subset_build.py::test_default_value_methods_match_oracle", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0181, + "duration_s": 0.0191, "node_id": "test/python_wrappers/test_subset_build.py::test_default_derivative_methods_match_oracle", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0005, + "duration_s": 0.0006, "node_id": "test/python_wrappers/test_subset_build.py::test_default_second_order_and_integrator_run", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0195, + "duration_s": 0.0199, "node_id": "test/python_wrappers/test_subset_build.py::test_subset_requested_algos_match_oracle", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0071, + "duration_s": 0.0076, "node_id": "test/python_wrappers/test_subset_build.py::test_subset_transitive_deps_work", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0002, + "duration_s": 0.0001, "node_id": "test/python_wrappers/test_subset_build.py::test_subset_unrequested_raises_clean_error[idsva_so-]", "outcome": "passed", "phase": "call" @@ -21455,42 +23275,42 @@ }, { "checks": [], - "duration_s": 0.0001, + "duration_s": 0.0005, "node_id": "test/python_wrappers/test_subset_build.py::test_subset_so_is_smaller_than_default", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.032, + "duration_s": 0.0352, "node_id": "test/python_wrappers/test_subset_build.py::test_subset_does_not_rekey_default", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1483, + "duration_s": 0.1258, "node_id": "test/python_wrappers/test_subset_build_jax.py::test_subset_requested_algos_match_oracle", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.3817, + "duration_s": 0.4187, "node_id": "test/python_wrappers/test_subset_build_jax.py::test_subset_transitive_deps_work", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0256, + "duration_s": 0.0284, "node_id": "test/python_wrappers/test_subset_build_jax.py::test_subset_jit_matches_eager", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0266, + "duration_s": 0.0296, "node_id": "test/python_wrappers/test_subset_build_jax.py::test_subset_vmap_compose", "outcome": "passed", "phase": "call" @@ -21518,7 +23338,7 @@ }, { "checks": [], - "duration_s": 0.0059, + "duration_s": 0.0017, "node_id": "test/python_wrappers/test_subset_build_jax.py::test_subset_unrequested_raises_clean_error[end_effector_pose-]", "outcome": "passed", "phase": "call" @@ -21532,49 +23352,49 @@ }, { "checks": [], - "duration_s": 0.0762, + "duration_s": 0.0874, "node_id": "test/python_wrappers/test_subset_build_jax.py::test_default_built_algos_run", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0002, + "duration_s": 0.0003, "node_id": "test/python_wrappers/test_subset_build_jax.py::test_subset_so_is_smaller_than_default", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.03, + "duration_s": 0.0316, "node_id": "test/python_wrappers/test_subset_build_jax.py::test_subset_does_not_rekey_default", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0475, + "duration_s": 0.0302, "node_id": "test/python_wrappers/test_subset_build_torch.py::test_subset_requested_algos_match_oracle", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0483, + "duration_s": 0.0565, "node_id": "test/python_wrappers/test_subset_build_torch.py::test_subset_transitive_deps_work", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1162, + "duration_s": 0.1351, "node_id": "test/python_wrappers/test_subset_build_torch.py::test_subset_forward_dynamics_backward_vjp", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0002, + "duration_s": 0.0003, "node_id": "test/python_wrappers/test_subset_build_torch.py::test_subset_unrequested_raises_clean_error[idsva_so-]", "outcome": "passed", "phase": "call" @@ -21588,14 +23408,14 @@ }, { "checks": [], - "duration_s": 0.0001, + "duration_s": 0.0003, "node_id": "test/python_wrappers/test_subset_build_torch.py::test_subset_unrequested_raises_clean_error[crba-]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0001, + "duration_s": 0.0002, "node_id": "test/python_wrappers/test_subset_build_torch.py::test_subset_unrequested_raises_clean_error[end_effector_pose-]", "outcome": "passed", "phase": "call" @@ -21609,21 +23429,21 @@ }, { "checks": [], - "duration_s": 0.0185, + "duration_s": 0.0218, "node_id": "test/python_wrappers/test_subset_build_torch.py::test_default_built_algos_run", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0002, + "duration_s": 0.0005, "node_id": "test/python_wrappers/test_subset_build_torch.py::test_subset_so_is_smaller_than_default", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0312, + "duration_s": 0.0338, "node_id": "test/python_wrappers/test_subset_build_torch.py::test_subset_does_not_rekey_default", "outcome": "passed", "phase": "call" @@ -21637,49 +23457,49 @@ }, { "checks": [], - "duration_s": 0.398, + "duration_s": 0.2989, "node_id": "test/python_wrappers/test_tool.py::test_attach_payload_matches_oracle", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.1159, + "duration_s": 0.1287, "node_id": "test/python_wrappers/test_tool.py::test_se3_tip_matches_oracle", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0003, + "duration_s": 0.0004, "node_id": "test/python_wrappers/test_tool.py::test_mid_chain_attach", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.115, + "duration_s": 0.1279, "node_id": "test/python_wrappers/test_tool.py::test_tool_fext_matches_host_map", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0002, + "duration_s": 0.0003, "node_id": "test/python_wrappers/test_tool.py::test_detach_restores_baked", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0615, + "duration_s": 0.0708, "node_id": "test/python_wrappers/test_torch_host_transfer.py::test_pinned_mirror_matches_cpu_for_single_and_tuple_outputs", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 0.0148, + "duration_s": 0.0168, "node_id": "test/python_wrappers/test_torch_host_transfer.py::test_replay_into_matches_eager_and_reuses_the_mirror", "outcome": "passed", "phase": "call" @@ -21693,21 +23513,21 @@ }, { "checks": [], - "duration_s": 0.7847, + "duration_s": 0.8418, "node_id": "test/python_wrappers/test_two_robot_process.py::test_two_so_pools_are_independent", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 4.499, + "duration_s": 5.1239, "node_id": "test/python_wrappers/test_workspace_slots_direct_launch.py::test_direct_launch_bit_identical_under_reduced_slots[jax]", "outcome": "passed", "phase": "call" }, { "checks": [], - "duration_s": 3.2418, + "duration_s": 4.0032, "node_id": "test/python_wrappers/test_workspace_slots_direct_launch.py::test_direct_launch_bit_identical_under_reduced_slots[torch]", "outcome": "passed", "phase": "call" diff --git a/grid_assets/robots/revolute_mimic_fixed_tool.urdf b/grid_assets/robots/revolute_mimic_fixed_tool.urdf new file mode 100644 index 00000000..348a368a --- /dev/null +++ b/grid_assets/robots/revolute_mimic_fixed_tool.urdf @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/grid_assets/robots/slider_fixed_tool.urdf b/grid_assets/robots/slider_fixed_tool.urdf new file mode 100644 index 00000000..42053065 --- /dev/null +++ b/grid_assets/robots/slider_fixed_tool.urdf @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/grid_assets/robots/slider_mimic_fixed_tool.urdf b/grid_assets/robots/slider_mimic_fixed_tool.urdf new file mode 100644 index 00000000..d3aa742e --- /dev/null +++ b/grid_assets/robots/slider_mimic_fixed_tool.urdf @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/grid_codegen/GRiDCodeGenerator.py b/grid_codegen/GRiDCodeGenerator.py index 6ff3ce85..2ae8a941 100644 --- a/grid_codegen/GRiDCodeGenerator.py +++ b/grid_codegen/GRiDCodeGenerator.py @@ -1176,11 +1176,26 @@ def gen_all_code(self, include_base_inertia = False, include_homogenous_transfor if collision_spec is not None: from .algorithms._collision import normalize_collision_tiers self.gen_collision_namespace(normalize_collision_tiers(collision_spec)) + self.gen_add_fragment_mark("model_metadata") + # The schema-1 descriptor requires quaternion coordinates (the bindings + # always parse that way). + self.model_descriptor = None + if self.robot.using_quaternion: + from .model_descriptor import descriptor_from_robot + from .model_metadata_cpp import emit_model_metadata + self.model_descriptor = descriptor_from_robot( + self.robot, runtime_transform=bool(self.runtime_transform)) + self.gen_add_code_line('// Model descriptor begins (schema 1).') + self.gen_add_code_lines(emit_model_metadata(self.model_descriptor, self.file_namespace).splitlines()) + from .model_primitives import emit_joint_transforms, emit_configuration_primitives + self.gen_add_code_line('// Model thread primitives begin.') + self.gen_add_code_lines(emit_joint_transforms(self).splitlines()) + if algorithms & {"integrator", "integrator_gradient", "integrator_with_gradient", "fdsva_so"}: + self.gen_add_code_lines(emit_configuration_primitives(self.model_descriptor, self.file_namespace).splitlines()) + self.gen_add_code_line('// Model thread primitives end.') + self.gen_add_code_line('// Model descriptor ends.') self.gen_add_fragment_mark("suggested_threads") - # Per-algorithm suggested thread counts (2026-10-21, GATO issue 19): read the - # widest block-parallel loop on each kernel's call tree back from the text - # emitted above and reopen the namespace with one constant per algorithm. - # Additive and last, so no kernel body changes. + # Suggested thread counts are emitted last; they derive from the text above. from .suggested_threads import derive as _derive_suggested_threads, emit_block as _emit_suggested_block from .algo_registry import launch_config_descriptors as _lc_descriptors self._suggested_threads, self._suggested_threads_unknown = _derive_suggested_threads( diff --git a/grid_codegen/_constants_arena.py b/grid_codegen/_constants_arena.py index 1d9fc569..c2f334f8 100644 --- a/grid_codegen/_constants_arena.py +++ b/grid_codegen/_constants_arena.py @@ -8,7 +8,7 @@ from .helpers._gpu_err import legacy_wrapper_lines from .algorithms._idsva_so import idsva_so_body_frame_tot_span -from .algo_registry import (ALGO_DESCRIPTORS, arena_ctx_from_codegen, compose_arena_full, +from .algo_registry import (arena_ctx_from_codegen, compose_arena_full, compose_arena_rungs, ARENA_COMPOSED_KEYS, ARENA_RUNG_KEYS) @@ -17,10 +17,8 @@ def _tier_bytes_lines(macro, counts): source line: three if-constexpr branches over the per-tier t_counts. String-for-string identical to the hand-written wall it replaced (C3 table-drive, 2026-09-08).""" - # ONE return statement (a nested ternary), not an `if constexpr` chain: the - # sizers are constexpr (GATO ask 2026-09-20) and the equivalence runners - # compile with -std=c++11, where a constexpr function body must be exactly - # one return. Same values per tier as the chain it replaced. + # One return expression (a nested ternary) so the sizer is constexpr; + # the same per-tier values as an if-constexpr chain. return ["template __host__ __device__ constexpr size_t " + macro + "() { " "return (TIER == TIER_SHARED) ? grid_shared_arena_bytes(" + str(counts[0]) + ", TOPOLOGY_HELPERS_COUNT) " ": (TIER == TIER_LITE) ? grid_shared_arena_bytes(" + str(counts[1]) + ", TOPOLOGY_HELPERS_COUNT) " @@ -396,19 +394,8 @@ def select_shared_tier_3way(*t_counts): self._integrator_gradient_dqdd_count = _integrator_gradient_D_qdd_count self._integrator_gradient_dAB_count = _integrator_gradient_dAB_count inverse_dynamics_gradient_temp_layout = self.gen_inverse_dynamics_gradient_temp_layout() - # Mimic robots emit a DENSE serial inverse_dynamics_gradient inner (no sparse-band spill), so - # its inner scratch is the dense count; the selective-spill tier doesn't - # apply (its band offsets are meaningless for the dense layout). Use the - # dense count for full AND selective so the kernel always sizes smem for - # the dense buffers and never selects a too-small selective arena. - _inverse_dynamics_gradient_has_mimic = self.robot_has_mimic_joints() inverse_dynamics_gradient_temp_count = self.gen_inverse_dynamics_gradient_inner_temp_mem_size() - inverse_dynamics_gradient_selective_temp_count = ( - inverse_dynamics_gradient_temp_count if _inverse_dynamics_gradient_has_mimic - else inverse_dynamics_gradient_temp_layout["selective_shared_count"] - ) forward_dynamics_gradient_temp_count = self.gen_forward_dynamics_gradient_inner_temp_mem_size() - forward_dynamics_gradient_selective_temp_count = max(self.gen_minv_inner_temp_mem_size(), inverse_dynamics_gradient_selective_temp_count) # s_vaf is body-indexed (NB bodies). For a MIMIC robot NB > nv so size # 18*NB; non-mimic keeps 18*nv/18*n (byte-identical; floating non-mimic has # nv > NB so 18*nv already covers the body writes). The id_device path uses @@ -429,19 +416,6 @@ def select_shared_tier_3way(*t_counts): # byte-identical; FLOATING n>nv reserves the wider input slot (old 3*nv+fb # under-reserved -> smem overrun on the s_q_qd_u load). forward_dynamics_gradient_t_count_full = (3*n) + (2*nv*nv) + (_vaf_cnt) + nv + (nv*nv) + forward_dynamics_gradient_temp_count + XI_size + rt_xfixed_reserve - inverse_dynamics_gradient_t_count_selective = inverse_dynamics_gradient_t_count_full - inverse_dynamics_gradient_temp_count + inverse_dynamics_gradient_selective_temp_count - forward_dynamics_gradient_t_count_selective = forward_dynamics_gradient_t_count_full - forward_dynamics_gradient_temp_count + forward_dynamics_gradient_selective_temp_count - inverse_dynamics_gradient_t_count_emergency = inverse_dynamics_gradient_t_count_full - inverse_dynamics_gradient_temp_count - forward_dynamics_gradient_t_count_emergency = forward_dynamics_gradient_t_count_full - forward_dynamics_gradient_temp_count - # fd_du OUTPUT-spill rung (h2_plus de-gate): the emergency rung routes the - # whole inner s_temp pool to d_workspace but keeps the OUTPUT bands s_dc_du - # (2*nv*nv id-gradient band) + s_Minv (nv*nv mass matrix) in smem -> on a big - # floating robot (h2_plus) those 3*nv*nv floats leave the arena at ~105KB, - # still over sm_120's ~99KB. This rung additionally repoints s_dc_du/s_Minv - # to the L2-pinned SO band (crba/fdsva_so output-spill style), shrinking the - # arena by 3*nv*nv (~77KB) so h2_plus launches (~28KB). Inner pool stays in - # the GRAD section (offset 0); the outputs sit in the disjoint SO section. - forward_dynamics_gradient_t_count_output_spill = forward_dynamics_gradient_t_count_emergency - (2*nv*nv + nv*nv) # Per-tier picks (perf, lite, minimal). The existing single-pick flags # (inverse_dynamics_gradient_spill_tier etc.) are kept = perf pick so today's emit paths # are byte-for-byte unchanged; the lite/minimal indices are exposed @@ -481,7 +455,6 @@ def select_shared_tier_3way(*t_counts): # spherical/floating (nq>nv) base the n+2*nv form under-sizes the launch smem # by 3*(nq-nv) floats -> the aba batch kernel OOBs in # load_update_XImats_helpers. Mirrors the fd/minv 3*n input slots. - crba_input_t_count = n + nv # ABA surgical-spill ladder, 3 rungs. The 140*NJ+138 inner scratch band # keeps its hot recursion in smem and spills only the cold sub-band when # possible: @@ -1314,6 +1287,9 @@ def _idsva_world_ws_floats(pick): _tier_ternary_line("CRBA_TEMP_IN_SMEM", "bool", (("true" if self.crba_spill_tier_3way[0] <= 1 else "false"), ("true" if self.crba_spill_tier_3way[1] <= 1 else "false"), ("true" if self.crba_spill_tier_3way[2] <= 1 else "false"))), # s_M output in smem ONLY at rung0; spilled to the SO band at rung1/rung2. Mirror DCCRBA_OUTPUT_IN_SMEM's ==0. _tier_ternary_line("CRBA_M_IN_SMEM", "bool", (("true" if self.crba_spill_tier_3way[0] == 0 else "false"), ("true" if self.crba_spill_tier_3way[1] == 0 else "false"), ("true" if self.crba_spill_tier_3way[2] == 0 else "false"))), + "// EE pose scratch query begins.", + "template __host__ __device__ constexpr size_t EE_POSE_INNER_SCRATCH_BYTES() { return sizeof(T) * static_cast(" + str(self.gen_end_effector_pose_inner_temp_mem_size()) + "); }", + "// EE pose scratch query ends.", "// --- end_effector_pose_gradient_inner (chain workspace) ---", "template __host__ __device__ constexpr size_t EE_GRAD_INNER_SMEM_BYTES() { return TEMP_IN_SMEM ? sizeof(T) * static_cast(" + str(self.gen_end_effector_pose_gradient_inner_temp_mem_size()) + ") : static_cast(0); }", "template __host__ __device__ constexpr size_t EE_GRAD_INNER_WORKSPACE_BYTES() { return TEMP_IN_SMEM ? static_cast(0) : sizeof(T) * static_cast(" + str(self.gen_end_effector_pose_gradient_inner_temp_mem_size()) + "); }", diff --git a/grid_codegen/abi_specs.py b/grid_codegen/abi_specs.py index bbcb287a..4645a897 100644 --- a/grid_codegen/abi_specs.py +++ b/grid_codegen/abi_specs.py @@ -32,7 +32,7 @@ """ from __future__ import annotations -from dataclasses import dataclass, field +from dataclasses import dataclass @dataclass(frozen=True) diff --git a/grid_codegen/algorithms/_collision.py b/grid_codegen/algorithms/_collision.py index a63b3a34..3faca05c 100644 --- a/grid_codegen/algorithms/_collision.py +++ b/grid_codegen/algorithms/_collision.py @@ -59,10 +59,11 @@ def resolve_spherized_urdf(value, source_urdf): # Shared wording for the margin and self-toggle parameters of every config_free (G7/G10, HJCD asks # 2026-10-13, shipped 0.6.0). The margin never touches collision_distance, normals or the cost # gradients: those keep the geometric signed clearance, and collision_cost applies its own margin. -MARGIN_PARAM_DOC = "margin is the clearance below which a sphere counts as a hit (metres, default 0): robot radii are tested at r + margin" +MARGIN_PARAM_DOC = "margins supplies environment_margin and self_margin in metres; a scalar applies to both (default 0)" CHECK_SELF_PARAM_DOC = "check_self = false skips the baked self-pair ranges (environment only; default true)" -MARGIN_NOTE = ("A hit iff any raw clearance is below T margin (exact contact at the margin is free); verdict-identical to the " - "zero-margin rule at margin 0; collision_distance and the cost derivatives are untouched.") +MARGIN_NOTE = ("A hit iff raw clearance is below its corresponding margin (exact contact is free). " + "Both values must be finite and nonnegative; make_collision_margins validates on the host. " + "Self pairs inflate only one member; collision_distance and cost derivatives are untouched.") def base_chain_links(urdf_root): @@ -322,13 +323,15 @@ def build_self_cc_ranges(robot, anchor): return ranges -def collision_spec_from_urdf(robot, urdf_path, resolution, mesh_resolution=None, out_path=None): +def collision_spec_from_urdf(robot, urdf_path, resolution, mesh_resolution=None, out_path=None, strict=True): """One-call URDF -> single-tier collision_spec (for gen_all_code). Spherizes `urdf_path` (custom trimesh/analytic spherizer, foam-compatible output) then binds the spheres to GRiD frames via build_sphere_tiers. Returns {anchor, offset, radius, self_cc_ranges} -- the dict - gen_all_code's collision_spec kwarg expects. `resolution` = sphere spacing (m).""" + gen_all_code's collision_spec kwarg expects. `resolution` = sphere spacing (m). `strict` + is the spherizer's: a voxel-fill fallback or an empty model is an error unless False.""" from ._spherize import spherize_urdf - sph_path = spherize_urdf(urdf_path, resolution, out_path=out_path, mesh_resolution=mesh_resolution) + sph_path = spherize_urdf(urdf_path, resolution, out_path=out_path, mesh_resolution=mesh_resolution, + strict=strict) tier = build_sphere_tiers(robot, {"all": sph_path})["all"] return {"anchor": tier["anchor"], "offset": tier["offset"], "radius": tier["radius"], "self_cc_ranges": tier["self_cc_ranges"]} @@ -424,7 +427,7 @@ def _broad_tier_from_rows(robot, anchor, pa, pb, radius): "self_cc_ranges": build_self_cc_ranges(robot, b_anchor)} -def native_collision_spec_from_urdf(robot, urdf_path, resolution=0.05, mesh_resolution=None): +def native_collision_spec_from_urdf(robot, urdf_path, resolution=0.05, mesh_resolution=None, strict=True): """URDF -> NATIVE capsule-row collision spec (opt-in, `--collision-native`): {"tiers": [broad sphere tier, fine CAPSULE tier]}. The fine tier carries "pb" (endpoint-b offsets) alongside "offset" (endpoint a) — "pb" present is what flags a capsule tier all @@ -438,7 +441,7 @@ def native_collision_spec_from_urdf(robot, urdf_path, resolution=0.05, mesh_reso residual_spheres = {} if residual_links: from ._spherize import spherize_urdf - sph_path = spherize_urdf(urdf_path, resolution, mesh_resolution=mesh_resolution) + sph_path = spherize_urdf(urdf_path, resolution, mesh_resolution=mesh_resolution, strict=strict) parsed = parse_spherized_urdf(sph_path) residual_spheres = {ln: parsed.get(ln, []) for ln in residual_links} frames = sphere_anchor_frames(robot, list(native.keys()), urdf_path) @@ -464,7 +467,7 @@ def native_collision_spec_from_urdf(robot, urdf_path, resolution=0.05, mesh_reso return {"tiers": [broad, fine]} -def multi_tier_collision_spec_from_urdf(robot, urdf_path, resolutions, mesh_resolution=None): +def multi_tier_collision_spec_from_urdf(robot, urdf_path, resolutions, mesh_resolution=None, strict=True): """One-call URDF -> MULTI-tier collision_spec (`{"tiers": [...]}` for gen_all_code). Spherizes `urdf_path` once per resolution and binds each to GRiD frames. `resolutions` = iterable of sphere spacings (m); sorted DESCENDING so the returned tiers run COARSEST->FINEST (config_free @@ -473,10 +476,10 @@ def multi_tier_collision_spec_from_urdf(robot, urdf_path, resolutions, mesh_reso spec (byte-identical to collision_spec_from_urdf).""" res = sorted({float(r) for r in resolutions}, reverse=True) # coarsest (largest spacing) first if len(res) == 1: - return collision_spec_from_urdf(robot, urdf_path, res[0], mesh_resolution=mesh_resolution) + return collision_spec_from_urdf(robot, urdf_path, res[0], mesh_resolution=mesh_resolution, strict=strict) names = ["broad", "fine"] if len(res) == 2 else ["tier%d" % i for i in range(len(res))] from ._spherize import spherize_urdf - outputs = {names[i]: spherize_urdf(urdf_path, res[i], mesh_resolution=mesh_resolution) + outputs = {names[i]: spherize_urdf(urdf_path, res[i], mesh_resolution=mesh_resolution, strict=strict) for i in range(len(res))} built = build_sphere_tiers(robot, outputs) tiers = [] @@ -873,7 +876,7 @@ def gen_collision_namespace(self, tiers): self.gen_add_code_line("__device__") self.gen_add_code_line("bool config_free(const T *s_q, const grid::robotModel *d_robotModel, " "const Environment &env, T *s_sphere_pos, T *s_sphere_r, T *d_workspace = nullptr, " - "T margin = static_cast(0), bool check_self = true) {", True) + "typename CollisionMargins::type margins = {}, bool check_self = true) {", True) self.gen_add_code_line("__shared__ int s_cc_hit;") self.gen_add_code_line("if (threadIdx.x == 0 && threadIdx.y == 0) s_cc_hit = 0; // published by the extractor's barriers") self.gen_add_code_line("grid::multi_target_position_device(s_sphere_pos, s_q, d_robotModel, d_workspace);") @@ -882,14 +885,14 @@ def gen_collision_namespace(self, tiers): self.gen_add_code_line("if (check_self) {", True) self.gen_add_parallel_loop("k", "NUM_COLLISION_SELF_CC_RANGES") self.gen_add_code_line("const int i = g_collision_self_cc_ranges[3*k], j0 = g_collision_self_cc_ranges[3*k+1], j1 = g_collision_self_cc_ranges[3*k+2];") - self.gen_add_code_line("const T ix = s_sphere_pos[3*i], iy = s_sphere_pos[3*i+1], iz = s_sphere_pos[3*i+2], ir = s_sphere_r[i] + margin;") + self.gen_add_code_line("const T ix = s_sphere_pos[3*i], iy = s_sphere_pos[3*i+1], iz = s_sphere_pos[3*i+2], ir = s_sphere_r[i] + margins.self_margin;") self.gen_add_code_line("for (int j = j0; j <= j1; ++j) {", True) self.gen_add_code_line("if (grid_cc_sphere_sphere(ix, iy, iz, ir, s_sphere_pos[3*j], s_sphere_pos[3*j+1], s_sphere_pos[3*j+2], s_sphere_r[j]) < static_cast(0)) { atomicExch(&s_cc_hit, 1); break; }") self.gen_add_end_control_flow() self.gen_add_end_control_flow() self.gen_add_end_control_flow() self.gen_add_parallel_loop("i", "NUM_COLLISION_SPHERES") - self.gen_add_code_line("if (grid_cc_sphere_in_environment(env, s_sphere_pos[3*i], s_sphere_pos[3*i+1], s_sphere_pos[3*i+2], s_sphere_r[i] + margin)) atomicExch(&s_cc_hit, 1);") + self.gen_add_code_line("if (grid_cc_sphere_in_environment(env, s_sphere_pos[3*i], s_sphere_pos[3*i+1], s_sphere_pos[3*i+2], s_sphere_r[i] + margins.environment_margin)) atomicExch(&s_cc_hit, 1);") self.gen_add_end_control_flow() self.gen_add_sync() self.gen_add_code_line("const bool is_free = (s_cc_hit == 0);") @@ -931,7 +934,7 @@ def gen_collision_namespace(self, tiers): self.gen_add_code_line("bool config_free(const T *s_q, const grid::robotModel *d_robotModel, " "const Environment &env, T *s_broad_pos, T *s_broad_r, " "T *s_fine_pos, T *s_fine_r, T *d_workspace = nullptr, " - "int *dbg_fine_rechecked = nullptr, T margin = static_cast(0), bool check_self = true) {", True) + "int *dbg_fine_rechecked = nullptr, typename CollisionMargins::type margins = {}, bool check_self = true) {", True) self.gen_add_code_line("// Optional debug output is one block-shared destination, written by thread zero.") self.gen_add_code_line("__shared__ int s_cc_hit;") self.gen_add_code_line("__shared__ unsigned long long s_cc_mask;") @@ -947,14 +950,14 @@ def gen_collision_namespace(self, tiers): self.gen_add_code_line("if (check_self) {", True) self.gen_add_parallel_loop("k", "NUM_COLLISION_SELF_CC_RANGES" + bcap) self.gen_add_code_line("const int i = g_collision_self_cc_ranges" + bsfx + "[3*k], j0 = g_collision_self_cc_ranges" + bsfx + "[3*k+1], j1 = g_collision_self_cc_ranges" + bsfx + "[3*k+2];") - self.gen_add_code_line("const T ix = s_broad_pos[3*i], iy = s_broad_pos[3*i+1], iz = s_broad_pos[3*i+2], ir = s_broad_r[i] + margin;") + self.gen_add_code_line("const T ix = s_broad_pos[3*i], iy = s_broad_pos[3*i+1], iz = s_broad_pos[3*i+2], ir = s_broad_r[i] + margins.self_margin;") self.gen_add_code_line("for (int j = j0; j <= j1; ++j) {", True) self.gen_add_code_line("if (grid_cc_sphere_sphere(ix, iy, iz, ir, s_broad_pos[3*j], s_broad_pos[3*j+1], s_broad_pos[3*j+2], s_broad_r[j]) < static_cast(0)) my_mask |= (1ull << g_collision_sphere_link" + bsfx + "[i]) | (1ull << g_collision_sphere_link" + bsfx + "[j]);") self.gen_add_end_control_flow() self.gen_add_end_control_flow() self.gen_add_end_control_flow() self.gen_add_parallel_loop("i", "NUM_COLLISION_SPHERES" + bcap) - self.gen_add_code_line("if (grid_cc_sphere_in_environment(env, s_broad_pos[3*i], s_broad_pos[3*i+1], s_broad_pos[3*i+2], s_broad_r[i] + margin)) my_mask |= 1ull << g_collision_sphere_link" + bsfx + "[i];") + self.gen_add_code_line("if (grid_cc_sphere_in_environment(env, s_broad_pos[3*i], s_broad_pos[3*i+1], s_broad_pos[3*i+2], s_broad_r[i] + margins.environment_margin)) my_mask |= 1ull << g_collision_sphere_link" + bsfx + "[i];") self.gen_add_end_control_flow() self.gen_add_code_line("if (my_mask) atomicOr(&s_cc_mask, my_mask);") self.gen_add_sync() @@ -972,7 +975,7 @@ def gen_collision_namespace(self, tiers): self.gen_add_parallel_loop("k", "NUM_COLLISION_SELF_CC_RANGES") self.gen_add_code_line("const int i = g_collision_self_cc_ranges[3*k], j0 = g_collision_self_cc_ranges[3*k+1], j1 = g_collision_self_cc_ranges[3*k+2];") self.gen_add_code_line("if (((hit_mask >> g_collision_sphere_link[i]) & 1ull) == 0ull) continue;") - self.gen_add_code_line("const T ix = s_fine_pos[3*i], iy = s_fine_pos[3*i+1], iz = s_fine_pos[3*i+2], ir = s_fine_r[i] + margin;") + self.gen_add_code_line("const T ix = s_fine_pos[3*i], iy = s_fine_pos[3*i+1], iz = s_fine_pos[3*i+2], ir = s_fine_r[i] + margins.self_margin;") self.gen_add_code_line("for (int j = j0; j <= j1; ++j) {", True) self.gen_add_code_line("if (grid_cc_sphere_sphere(ix, iy, iz, ir, s_fine_pos[3*j], s_fine_pos[3*j+1], s_fine_pos[3*j+2], s_fine_r[j]) < static_cast(0)) { atomicExch(&s_cc_hit, 1); break; }") self.gen_add_end_control_flow() @@ -980,7 +983,7 @@ def gen_collision_namespace(self, tiers): self.gen_add_end_control_flow() self.gen_add_parallel_loop("i", "NUM_COLLISION_SPHERES") self.gen_add_code_line("if (((hit_mask >> g_collision_sphere_link[i]) & 1ull) == 0ull) continue;") - self.gen_add_code_line("if (grid_cc_sphere_in_environment(env, s_fine_pos[3*i], s_fine_pos[3*i+1], s_fine_pos[3*i+2], s_fine_r[i] + margin)) atomicExch(&s_cc_hit, 1);") + self.gen_add_code_line("if (grid_cc_sphere_in_environment(env, s_fine_pos[3*i], s_fine_pos[3*i+1], s_fine_pos[3*i+2], s_fine_r[i] + margins.environment_margin)) atomicExch(&s_cc_hit, 1);") self.gen_add_end_control_flow() self.gen_add_sync() self.gen_add_code_line("const bool is_free = (s_cc_hit == 0);") @@ -1251,29 +1254,29 @@ def emit_place(sfx, cap, dst): # --- config_free --- self.gen_add_func_doc("config_free: warp-uniform collision-free verdict from caller-held joint world transforms", - [MARGIN_NOTE.replace("T margin", "float margin"), + [MARGIN_NOTE, "check_self = false skips the baked self-pair ranges (environment only); identical verdict at true."], ["s_Xworld is the column-major 4x4 world transform per movable joint", "env is the runtime obstacle set (float)", "w_scratch is the per-warp float scratch of W_SCRATCH_FLOATS", - "margin is the clearance below which a sphere counts as a hit (metres, default 0)", + MARGIN_PARAM_DOC + "; warp margins use float", "check_self = false skips the self-pair ranges (default true)"], None) self.gen_add_code_line("template ") self.gen_add_code_line("__device__ bool config_free(const T *s_Xworld, const Environment &env, float *w_scratch, " - "float margin = 0.0f, bool check_self = true) {", True) + "CollisionMargins margins = {}, bool check_self = true) {", True) self.gen_add_code_line("const int lane = threadIdx.x & 31;") self.gen_add_code_line("float *w_pos = w_scratch;") self.gen_add_code_line("bool hit = false;") if broad is None: emit_place("", "", "w_pos") self.gen_add_code_line("for (int s = lane; s < NUM_SPHERES; s += 32) {", True) - self.gen_add_code_line("hit |= grid_cc_sphere_in_environment(env, w_pos[3*s], w_pos[3*s+1], w_pos[3*s+2], sphere_radius[s] + margin);") + self.gen_add_code_line("hit |= grid_cc_sphere_in_environment(env, w_pos[3*s], w_pos[3*s+1], w_pos[3*s+2], sphere_radius[s] + margins.environment_margin);") self.gen_add_end_control_flow() self.gen_add_code_line("__syncwarp(FULL_MASK);") self.gen_add_code_line("if (check_self) {", True) self.gen_add_code_line("for (int k = lane; k < NUM_COLLISION_SELF_CC_RANGES; k += 32) {", True) self.gen_add_code_line("const int i = g_collision_self_cc_ranges[3*k], j0 = g_collision_self_cc_ranges[3*k+1], j1 = g_collision_self_cc_ranges[3*k+2];") - self.gen_add_code_line("const float ix = w_pos[3*i], iy = w_pos[3*i+1], iz = w_pos[3*i+2], ir = sphere_radius[i] + margin;") + self.gen_add_code_line("const float ix = w_pos[3*i], iy = w_pos[3*i+1], iz = w_pos[3*i+2], ir = sphere_radius[i] + margins.self_margin;") self.gen_add_code_line("for (int j = j0; j <= j1 && !hit; ++j) {", True) self.gen_add_code_line("hit |= grid_cc_sphere_sphere(ix, iy, iz, ir, w_pos[3*j], w_pos[3*j+1], w_pos[3*j+2], sphere_radius[j]) < 0.0f;") self.gen_add_end_control_flow() @@ -1289,13 +1292,13 @@ def emit_place(sfx, cap, dst): emit_place(bsfx, bcap, "w_broad") self.gen_add_code_line("unsigned long long flagged = 0ull;") self.gen_add_code_line("for (int s = lane; s < NUM_SPHERES" + bcap + "; s += 32) {", True) - self.gen_add_code_line("if (grid_cc_sphere_in_environment(env, w_broad[3*s], w_broad[3*s+1], w_broad[3*s+2], sphere_radius" + bsfx + "[s] + margin)) flagged |= 1ull << sphere_anchor" + bsfx + "[s];") + self.gen_add_code_line("if (grid_cc_sphere_in_environment(env, w_broad[3*s], w_broad[3*s+1], w_broad[3*s+2], sphere_radius" + bsfx + "[s] + margins.environment_margin)) flagged |= 1ull << sphere_anchor" + bsfx + "[s];") self.gen_add_end_control_flow() self.gen_add_code_line("__syncwarp(FULL_MASK);") self.gen_add_code_line("if (check_self) {", True) self.gen_add_code_line("for (int k = lane; k < NUM_COLLISION_SELF_CC_RANGES" + bcap + "; k += 32) {", True) self.gen_add_code_line("const int i = g_collision_self_cc_ranges" + bsfx + "[3*k], j0 = g_collision_self_cc_ranges" + bsfx + "[3*k+1], j1 = g_collision_self_cc_ranges" + bsfx + "[3*k+2];") - self.gen_add_code_line("const float ix = w_broad[3*i], iy = w_broad[3*i+1], iz = w_broad[3*i+2], ir = sphere_radius" + bsfx + "[i] + margin;") + self.gen_add_code_line("const float ix = w_broad[3*i], iy = w_broad[3*i+1], iz = w_broad[3*i+2], ir = sphere_radius" + bsfx + "[i] + margins.self_margin;") self.gen_add_code_line("for (int j = j0; j <= j1; ++j) {", True) self.gen_add_code_line("if (grid_cc_sphere_sphere(ix, iy, iz, ir, w_broad[3*j], w_broad[3*j+1], w_broad[3*j+2], sphere_radius" + bsfx + "[j]) < 0.0f) flagged |= (1ull << sphere_anchor" + bsfx + "[i]) | (1ull << sphere_anchor" + bsfx + "[j]);") self.gen_add_end_control_flow() @@ -1315,14 +1318,14 @@ def emit_place(sfx, cap, dst): self.gen_add_code_line("const float py = static_cast(X[1]*ox + X[5]*oy + X[9]*oz + X[13]);") self.gen_add_code_line("const float pz = static_cast(X[2]*ox + X[6]*oy + X[10]*oz + X[14]);") self.gen_add_code_line("w_pos[3*s] = px; w_pos[3*s+1] = py; w_pos[3*s+2] = pz;") - self.gen_add_code_line("if ((flagged >> a) & 1ull) hit |= grid_cc_sphere_in_environment(env, px, py, pz, sphere_radius[s] + margin);") + self.gen_add_code_line("if ((flagged >> a) & 1ull) hit |= grid_cc_sphere_in_environment(env, px, py, pz, sphere_radius[s] + margins.environment_margin);") self.gen_add_end_control_flow() self.gen_add_code_line("__syncwarp(FULL_MASK);") self.gen_add_code_line("if (check_self) {", True) self.gen_add_code_line("for (int k = lane; k < NUM_COLLISION_SELF_CC_RANGES; k += 32) {", True) self.gen_add_code_line("const int i = g_collision_self_cc_ranges[3*k], j0 = g_collision_self_cc_ranges[3*k+1], j1 = g_collision_self_cc_ranges[3*k+2];") self.gen_add_code_line("if (!((flagged >> sphere_anchor[i]) & 1ull)) continue;") - self.gen_add_code_line("const float ix = w_pos[3*i], iy = w_pos[3*i+1], iz = w_pos[3*i+2], ir = sphere_radius[i] + margin;") + self.gen_add_code_line("const float ix = w_pos[3*i], iy = w_pos[3*i+1], iz = w_pos[3*i+2], ir = sphere_radius[i] + margins.self_margin;") self.gen_add_code_line("for (int j = j0; j <= j1 && !hit; ++j) {", True) self.gen_add_code_line("hit |= grid_cc_sphere_sphere(ix, iy, iz, ir, w_pos[3*j], w_pos[3*j+1], w_pos[3*j+2], sphere_radius[j]) < 0.0f;") self.gen_add_end_control_flow() @@ -1459,13 +1462,13 @@ def _gen_collision_namespace_capsule(self, tiers): self.gen_add_code_line("__device__") self.gen_add_code_line("bool config_free(const T *s_q, const grid::robotModel *d_robotModel, " "const Environment &env, T *s_seg_pos, T *s_row_r, T *d_workspace = nullptr, " - "T margin = static_cast(0), bool check_self = true) {", True) + "typename CollisionMargins::type margins = {}, bool check_self = true) {", True) self.gen_add_code_line("grid::multi_target_position_device(s_seg_pos, s_q, d_robotModel, d_workspace);") self.gen_add_code_line("load_collision_row_radii(s_row_r);") self.gen_add_sync() - self.gen_add_code_line("if (check_self && grid_cc_self_collision_capsules(s_seg_pos, s_row_r, g_collision_self_cc_ranges, NUM_COLLISION_SELF_CC_RANGES, margin)) return false;") + self.gen_add_code_line("if (check_self && grid_cc_self_collision_capsules(s_seg_pos, s_row_r, g_collision_self_cc_ranges, NUM_COLLISION_SELF_CC_RANGES, margins.self_margin)) return false;") self.gen_add_code_line("for (int i = 0; i < NUM_COLLISION_ROWS; ++i) {", True) - self.gen_add_code_line("if (grid_cc_capsule_in_environment(env, grid_cc_row_capsule(s_seg_pos, s_row_r, i, margin))) return false;") + self.gen_add_code_line("if (grid_cc_capsule_in_environment(env, grid_cc_row_capsule(s_seg_pos, s_row_r, i, margins.environment_margin))) return false;") self.gen_add_end_control_flow() self.gen_add_code_line("return true;") self.gen_add_end_function() @@ -1495,7 +1498,7 @@ def _gen_collision_namespace_capsule(self, tiers): self.gen_add_code_line("bool config_free(const T *s_q, const grid::robotModel *d_robotModel, " "const Environment &env, T *s_broad_pos, T *s_broad_r, " "T *s_seg_pos, T *s_row_r, T *d_workspace = nullptr, " - "int *dbg_fine_rechecked = nullptr, T margin = static_cast(0), bool check_self = true) {", True) + "int *dbg_fine_rechecked = nullptr, typename CollisionMargins::type margins = {}, bool check_self = true) {", True) self.gen_add_code_line("grid::multi_target_position" + bsfx + "_device(s_broad_pos, s_q, d_robotModel, d_workspace);") self.gen_add_code_line("load_collision_radii" + bsfx + "(s_broad_r);") self.gen_add_sync() @@ -1504,7 +1507,7 @@ def _gen_collision_namespace_capsule(self, tiers): self.gen_add_sync() self.gen_add_code_line("return grid_cc_config_free_capsule(env,") self.gen_add_code_line(" s_broad_pos, s_broad_r, g_collision_self_cc_ranges" + bsfx + ", NUM_COLLISION_SELF_CC_RANGES" + bcap + ", NUM_COLLISION_SPHERES" + bcap + ", g_collision_sphere_link" + bsfx + ",") - self.gen_add_code_line(" s_seg_pos, s_row_r, g_collision_self_cc_ranges, NUM_COLLISION_SELF_CC_RANGES, NUM_COLLISION_ROWS, g_collision_row_link, dbg_fine_rechecked, margin, check_self);") + self.gen_add_code_line(" s_seg_pos, s_row_r, g_collision_self_cc_ranges, NUM_COLLISION_SELF_CC_RANGES, NUM_COLLISION_ROWS, g_collision_row_link, dbg_fine_rechecked, margins, check_self);") self.gen_add_end_function() # ---- differentiable collision PRIMITIVES + cost, capsule rows ---- diff --git a/grid_codegen/algorithms/_coriolis.py b/grid_codegen/algorithms/_coriolis.py index 4dcae18f..b819bac6 100644 --- a/grid_codegen/algorithms/_coriolis.py +++ b/grid_codegen/algorithms/_coriolis.py @@ -93,6 +93,11 @@ def _coriolis_metadata(self): joint = robot.get_joint_by_id(i) is_mimic = getattr(joint, "is_mimic", False) alpha = float(joint.get_mimic_multiplier()) if is_mimic else 1.0 + if alpha == 0.0: + # The body still transports motion and inertia, but owns no local + # velocity columns or reduced Coriolis rows/columns. + body_col_start.append(len(col_body)) + continue vi = robot.get_joint_index_v(i) if not isinstance(vi, (list, tuple, np.ndarray)): vi = [vi] @@ -266,7 +271,7 @@ def gen_coriolis_matrix_inner(self): f"static const int cor_col_s_sign[] = {{ {_coriolis_int_array(md['col_s_sign'])} }};", f"static const int cor_body_col_start[] = {{ {_coriolis_int_array(md['body_col_start'])} }};", "static const T cor_col_alpha[] = { " + ", ".join( - "static_cast(" + repr(a) + ")" for a in md['col_alpha']) + " };", + "static_cast(" + repr(a) + ")" for a in (md['col_alpha'] or [0.0])) + " };", f"static const int cor_job_icol[] = {{ {_coriolis_int_array(md['job_icol'])} }};", f"static const int cor_job_tcol[] = {{ {_coriolis_int_array(md['job_tcol'])} }};", f"static const int cor_job_body[] = {{ {_coriolis_int_array(md['job_body'])} }};", diff --git a/grid_codegen/algorithms/_crba.py b/grid_codegen/algorithms/_crba.py index f1a3c239..99ee3e1f 100644 --- a/grid_codegen/algorithms/_crba.py +++ b/grid_codegen/algorithms/_crba.py @@ -1,5 +1,4 @@ import numpy as np -import copy from grid_codegen.helpers._code_generation_helpers import gen_launch_pair, gen_emit_host_result_transfer, gen_workspace_repoint_line, host_mode_flags, host_q_qd_input_transfer_lines, host_std_func_params, mangle_host_func_defs, wrap_host_single_call_timing from grid_codegen.helpers._code_generation_helpers import gen_host_wrapper_head @@ -118,14 +117,24 @@ def _gen_crba_inner_mimic_fixed(self, NB): for ind in range(NB): vi = self._v_slot_cpp(ind) alpha_i = self._alpha_for_jid(ind) - s_ind = self.robot.get_S_index_by_id(ind) - s_sign = self.robot.get_S_sign_by_id(ind) + if alpha_i == 0.0: + continue # IC was propagated above; no reduced row/column is owned. # fh = IC[ind] * S[ind] = S_sign * column s_ind of IC[ind] self.gen_add_code_line("// body " + str(ind) + " -> v-slot " + str(vi) + " (alpha=" + repr(alpha_i) + ")") - self.gen_add_code_line("for (int r = 0; r < 6; r++) { s_fh[r] = static_cast(" + repr(float(s_sign)) + ") * s_temp[" + str(36*ind + 6*s_ind) + " + r]; }") - # diagonal: H[vi,vi] += alpha_i^2 * (S^T fh) = alpha_i^2 * S_sign * fh[s_ind] - diag_coeff = float(alpha_i) * float(alpha_i) * float(s_sign) - self.gen_add_code_line("s_M[" + str(vi + nv*vi) + "] += static_cast(" + repr(diag_coeff) + ") * s_fh[" + str(s_ind) + "];") + if self.robot.S_is_cardinal_by_id(ind): + s_ind = self.robot.get_S_index_by_id(ind) + s_sign = self.robot.get_S_sign_by_id(ind) + self.gen_add_code_line("for (int r = 0; r < 6; r++) { s_fh[r] = static_cast(" + repr(float(s_sign)) + ") * s_temp[" + str(36*ind + 6*s_ind) + " + r]; }") + diag_coeff = float(alpha_i) * float(alpha_i) * float(s_sign) + self.gen_add_code_line("s_M[" + str(vi + nv*vi) + "] += static_cast(" + repr(diag_coeff) + ") * s_fh[" + str(s_ind) + "];") + else: + motion = self.robot._get_flat_S_by_id(ind) + terms = ["static_cast(" + repr(float(v)) + ") * s_temp[" + str(36*ind + 6*r) + " + r]" + for r, v in enumerate(motion) if v != 0] + self.gen_add_code_line("for (int r = 0; r < 6; r++) { s_fh[r] = " + " + ".join(terms) + "; }") + terms = ["static_cast(" + repr(float(alpha_i)**2 * float(v)) + ") * s_fh[" + str(r) + "]" + for r, v in enumerate(motion) if v != 0] + self.gen_add_code_line("s_M[" + str(vi + nv*vi) + "] += " + " + ".join(terms) + ";") # walk ancestors j = ind cur = "s_fh" @@ -136,10 +145,18 @@ def _gen_crba_inner_mimic_fixed(self, NB): j = self.robot.get_parent_id(j) vj = self._v_slot_cpp(j) alpha_j = self._alpha_for_jid(j) - sj_ind = self.robot.get_S_index_by_id(j) - sj_sign = self.robot.get_S_sign_by_id(j) - contrib_coeff = float(alpha_i) * float(alpha_j) * float(sj_sign) - self.gen_add_code_line("{ T contribution = static_cast(" + repr(contrib_coeff) + ") * " + nxt + "[" + str(sj_ind) + "];") + if alpha_j == 0.0: + cur, nxt = nxt, cur # Keep transport through the constant body. + continue + if self.robot.S_is_cardinal_by_id(j): + sj_ind = self.robot.get_S_index_by_id(j) + sj_sign = self.robot.get_S_sign_by_id(j) + contrib_coeff = float(alpha_i) * float(alpha_j) * float(sj_sign) + self.gen_add_code_line("{ T contribution = static_cast(" + repr(contrib_coeff) + ") * " + nxt + "[" + str(sj_ind) + "];") + else: + terms = ["static_cast(" + repr(float(alpha_i) * float(alpha_j) * float(v)) + ") * " + nxt + "[" + str(r) + "]" + for r, v in enumerate(self.robot._get_flat_S_by_id(j)) if v != 0] + self.gen_add_code_line("{ T contribution = " + " + ".join(terms) + ";") self.gen_add_code_line(" s_M[" + str(vi + nv*vj) + "] += contribution; s_M[" + str(vj + nv*vi) + "] += contribution; }") cur, nxt = nxt, cur self.gen_add_end_control_flow() @@ -734,6 +751,8 @@ def _gen_crba_mimic_floating_phase2(self, NJ): for ind in range(1, NJ): vi = self._v_slot_cpp(ind) alpha_i = self._alpha_for_jid(ind) + if alpha_i == 0.0: + continue # Phase 1 already propagated IC; this body owns no column. s_ind = self.robot.get_S_index_by_id(ind) s_sign = self.robot.get_S_sign_by_id(ind) self.gen_add_code_line("// body " + str(ind) + " -> v-slot " + str(vi) + " (alpha=" + repr(alpha_i) + ")") @@ -751,6 +770,9 @@ def _gen_crba_mimic_floating_phase2(self, NJ): j = self.robot.get_parent_id(j) vj = self._v_slot_cpp(j) alpha_j = self._alpha_for_jid(j) + if alpha_j == 0.0: + cur, nxt = nxt, cur # Transport through it without projecting. + continue sj_ind = self.robot.get_S_index_by_id(j) sj_sign = self.robot.get_S_sign_by_id(j) contrib_coeff = float(alpha_i) * float(alpha_j) * float(sj_sign) diff --git a/grid_codegen/algorithms/_eepose_gradient_hessian.py b/grid_codegen/algorithms/_eepose_gradient_hessian.py index e37ecfc0..320f88c6 100644 --- a/grid_codegen/algorithms/_eepose_gradient_hessian.py +++ b/grid_codegen/algorithms/_eepose_gradient_hessian.py @@ -485,6 +485,8 @@ def _eepose_grad_chain_metadata(self, all_ees, anchor_override=None): anchors.append(ee if anchor_override is None else anchor_override[ee_idx]) jobs = [] for j in chain: + if self._alpha_for_jid(j) == 0.0: + continue # Retain the FK chain, but there is no local column. S = _np.asarray(self.robot.get_S_by_id(j), dtype=_np.float64) if S.ndim == 1: S = S.reshape(-1, 1) @@ -580,8 +582,15 @@ def group_jacobian_jobs(self, fill_jobs, anchors): for entry in flat_jobs: ee_idx, _anc, job = entry _groups.setdefault((ee_idx, job["vi"]), []).append(entry) - single_jobs = [grp[0] for grp in _groups.values() if len(grp) == 1] - multi_groups = [grp for grp in _groups.values() if len(grp) > 1] + single_jobs, multi_groups = [], [] + for grp in _groups.values(): + # The disjoint-column fast path writes an unscaled axis. A lone mimic + # contribution still needs its multiplier, even after constant jobs + # have been removed from this column. + if len(grp) == 1 and (not has_mimic or self._alpha_for_jid(grp[0][2]["j"]) == 1.0): + single_jobs.append(grp[0]) + else: + multi_groups.append(grp) return single_jobs, multi_groups, has_mimic @@ -1182,6 +1191,8 @@ def _eepose_hessian_chain_metadata(self, all_ees, anchor_override=None): dof_info = [] intra_pairs = [] for chain_pos, j in enumerate(chain): + if self._alpha_for_jid(j) == 0.0: + continue # Constant joints contribute no derivative pairs. S = _np.asarray(self.robot.get_S_by_id(j), dtype=_np.float64) if S.ndim == 1: S = S.reshape(-1, 1) @@ -2700,17 +2711,19 @@ def _ee_pose_inner_angle_expr(self, jid): """The C expression for joint jid's LOCAL angle theta in terms of s_q. Non-mimic joints read their own dense q slot. MIMIC joints have no q slot of their own — their angle is mult*q[source] + offset (chained mimics are pre-flattened by the - parser, so the target is always a real q-owning joint). This inline fold is what + parser; a chain ending at a fixed joint is a constant). This inline fold is what lets the standalone (d_robotModel-free) FK inner support mimic robots without the general load_update path's s_q_eff scratch.""" joint = self.robot.get_joint_by_id(jid) if joint is not None and joint.is_mimic_joint(): src = int(joint.get_mimic_target_id()) + mult = float(joint.get_mimic_multiplier()) + off = float(joint.get_mimic_offset()) + if src == -1 or mult == 0.0: + return "static_cast(" + str(off) + ")" src_slot = self.robot.get_joint_index_q(src) if isinstance(src_slot, (list, tuple)): src_slot = src_slot[0] - mult = float(joint.get_mimic_multiplier()) - off = float(joint.get_mimic_offset()) expr = "static_cast(" + str(mult) + ") * s_q[" + str(src_slot) + "]" if off != 0.0: expr += " + static_cast(" + str(off) + ")" @@ -3082,7 +3095,7 @@ def gen_ee_pose_fk_batched_kernel(self): # buffer; serial chains with identical Ss pass nullptr (the loader skips the # topology copy). This kernel hand-declares its shared arena, so the nullptr case # is explicit here (callers using gen_declare_shared_arena get it automatically). - if not self.robot.is_serial_chain() or not self.robot.are_Ss_identical(list(range(n))): + if self.gen_topology_helpers_size() > 0: self.gen_add_code_line("__shared__ int s_topology_helpers[" + str(self.gen_topology_helpers_size()) + "];") else: self.gen_add_code_line("int *s_topology_helpers = nullptr;") @@ -3286,6 +3299,9 @@ def gen_ee_target_aliases(self, include_pose = True, include_gradient = True, in if include_pose: self.gen_add_code_lines([ + "// EE target pose scratch query begins.", + "template __host__ __device__ constexpr size_t EE_TARGET_POSE_INNER_SCRATCH_BYTES() { return sizeof(T) * size_t(" + str(self.gen_end_effector_pose_inner_temp_mem_size(getattr(self, "_ee_target_name", ""))) + "); }", + "// EE target pose scratch query ends.", "template ", "__device__ __forceinline__", "void end_effector_pose_target_inner(Args... args) { " @@ -3299,6 +3315,9 @@ def gen_ee_target_aliases(self, include_pose = True, include_gradient = True, in if include_gradient: self.gen_add_code_lines([ + "// EE target gradient scratch query begins.", + "template __host__ __device__ constexpr size_t EE_TARGET_GRAD_INNER_SCRATCH_BYTES() { return sizeof(T) * size_t(" + str(self.gen_end_effector_pose_gradient_inner_temp_mem_size(getattr(self, "_ee_target_name", ""))) + "); }", + "// EE target gradient scratch query ends.", "template ", "__device__ __forceinline__", "void end_effector_pose_gradient_target_inner(Args... args) { " diff --git a/grid_codegen/algorithms/_fdsva_so.py b/grid_codegen/algorithms/_fdsva_so.py index 144b6c3c..94746fab 100644 --- a/grid_codegen/algorithms/_fdsva_so.py +++ b/grid_codegen/algorithms/_fdsva_so.py @@ -10,7 +10,7 @@ # Shared block-parallel emit primitives (also used by _idsva_so). See _mjx_blockpar. import os -from ._mjx_blockpar import bpfor as _bpfor, bpctrl as _bpctrl, stride_rc as _fbp +from ._mjx_blockpar import bpfor as _bpfor, stride_rc as _fbp from grid_codegen.helpers._code_generation_helpers import _gen_mjx_build_R_lines, gen_workspace_repoint_line, host_mode_flags, host_std_func_params, mangle_host_func_defs, wrap_host_single_call_timing @@ -195,7 +195,8 @@ def _fdsva_so_minv_tile(n): the untiled loop (A/B knob).""" if os.environ.get("GRID_FDSVA_SO_MINV_TILE", "") == "1": return 1 - return max(d for d in range(1, 9) if n % d == 0 and n // d >= 2) + # A one-DoF model has only one i-tile; its valid untiled width is still 1. + return max((d for d in range(1, 9) if n % d == 0 and n // d >= 2), default=1) def gen_fdsva_so_contract_temp_mem_size(self): @@ -757,12 +758,6 @@ def _emit_fdsva_so_kernel_body_for_flags(self, n, NUM_POS, use_global_tensors, u # Inner-controlled placement: forward_dynamics_inner slices its own Minv-F # from s_temp (smem; this kernel does not surgically spill Minv/FD-F — its # tiers spill the SO outputs / df_du / Minv instead). - # Canonical: build the shared helper ARGS via gen_insert_helpers_function_call - # (was a bespoke "make a def-params string then .replace() the types out" hack, - # which silently dropped/duplicated args when the helper signature changed). - fd_start = "forward_dynamics_inner(s_qdd, s_q, s_qd, s_u, " + self.gen_insert_helpers_function_call() - fd_end = "s_temp, nullptr, nullptr, gravity);" # trailing nullptr = no external forces - # B3 dedup (so_audit_plan): the timed (single_call_timing) and untimed kernel # bodies differed ONLY by the per-timestep `k*...PER_TIMESTEP +` offset prefix # on the global/workspace pointers (the untimed body indexes per-timestep k; diff --git a/grid_codegen/algorithms/_forward_dynamics_gradient.py b/grid_codegen/algorithms/_forward_dynamics_gradient.py index 7b4a01c4..7c0c2141 100644 --- a/grid_codegen/algorithms/_forward_dynamics_gradient.py +++ b/grid_codegen/algorithms/_forward_dynamics_gradient.py @@ -21,7 +21,6 @@ def gen_forward_dynamics_gradient_inner_python(self, use_qdd_Minv_input = False, # is the inverse_dynamics_gradient da_df band, handled separately). After Minv returns, the # c+vaf/ID code reuses these bytes (the steps run sequentially). self.gen_minv_inner_function_call(f_in_smem_expr = "true") - # updated_var_names = dict(s_c_name = "s_temp", s_vaf_name = "&s_temp[" + str(n) + "]", s_temp_name = "&s_temp[" + str(19*n) + "]") updated_var_names = dict(s_c_name = "s_temp", s_temp_name = "&s_temp[" + str(n) + "]", d_f_ext_name = d_f_ext_name) self.gen_inverse_dynamics_inner_function_call(compute_c = True, use_qdd_input = False, updated_var_names = updated_var_names) self.gen_forward_dynamics_finish_function_call(updated_var_names) diff --git a/grid_codegen/algorithms/_frame_jacobian.py b/grid_codegen/algorithms/_frame_jacobian.py index fcbdd4f3..cdfb51f5 100644 --- a/grid_codegen/algorithms/_frame_jacobian.py +++ b/grid_codegen/algorithms/_frame_jacobian.py @@ -113,6 +113,8 @@ def frame_jacobian_jobs(self): else: vinds = list(vinds) alpha = self._alpha_for_jid(jj) if HAS_MIMIC else 1.0 + if alpha == 0.0: + continue # Constant bodies own no Jacobian column. for c in range(S.shape[1]): vi = vinds[c] if c < len(vinds) else vinds[-1] ang = [float(S[0, c]), float(S[1, c]), float(S[2, c])] @@ -520,6 +522,10 @@ def gen_frame_jacobian_dot_device(self): self.gen_add_code_line("vj[0]=vp[0]+(wp[1]*dz-wp[2]*dy); vj[1]=vp[1]+(wp[2]*dx-wp[0]*dz); vj[2]=vp[2]+(wp[0]*dy-wp[1]*dx);") self.gen_add_end_control_flow() # this column's own contribution: w += sc*(R_j S_ang), v += sc*(R_j S_lin) + if 0.0 in s_alpha: + # Retain the parent rigid transport above, but never read the + # constant body's placeholder velocity slot (possibly nonexistent). + self.gen_add_code_line("if (fjd_alpha[t] == static_cast(0)) continue;") self.gen_add_code_line("T a0=fjd_ang[3*t],a1=fjd_ang[3*t+1],a2=fjd_ang[3*t+2];") self.gen_add_code_line("T l0=fjd_lin[3*t],l1=fjd_lin[3*t+1],l2=fjd_lin[3*t+2];") self.gen_add_code_line("T aw0=Xj[0]*a0+Xj[4]*a1+Xj[8]*a2, aw1=Xj[1]*a0+Xj[5]*a1+Xj[9]*a2, aw2=Xj[2]*a0+Xj[6]*a1+Xj[10]*a2;") @@ -546,6 +552,8 @@ def gen_frame_jacobian_dot_device(self): vinds = self.robot.get_joint_index_v(jj) vinds = list(vinds) if isinstance(vinds, (list, tuple, np.ndarray)) else [vinds] alpha = self._alpha_for_jid(jj) if HAS_MIMIC else 1.0 + if alpha == 0.0: + continue for c in range(S.shape[1]): vi = vinds[c] if c < len(vinds) else vinds[-1] jobs.append((jid, jj, int(vi), diff --git a/grid_codegen/algorithms/_idsva_so.py b/grid_codegen/algorithms/_idsva_so.py index a72a5e4d..e8a25bf2 100644 --- a/grid_codegen/algorithms/_idsva_so.py +++ b/grid_codegen/algorithms/_idsva_so.py @@ -84,7 +84,8 @@ def _idsva_so_fold_jobs(groups, alphas, NV, NB): as an index into a tiny distinct-value table (products of mimic multipliers; almost all 1.0). - Returns (job_start[NV^3+1], job_src[NB^3], job_wid[NB^3], wvals).""" + Constant columns have no reduced destination and contribute no jobs. + Returns (job_start[NV^3+1], job_src, job_wid, wvals).""" job_start, job_src, job_wid = [0], [], [] wvals, windex = [], {} for va in range(NV): @@ -92,9 +93,15 @@ def _idsva_so_fold_jobs(groups, alphas, NV, NB): for vc in range(NV): for ii in groups[va]: wa = alphas[ii] + if wa == 0.0: + continue for jj in groups[vb]: + if alphas[jj] == 0.0: + continue wab = wa * alphas[jj] for kk in groups[vc]: + if alphas[kk] == 0.0: + continue w = wab * alphas[kk] if w not in windex: windex[w] = len(wvals) @@ -102,7 +109,8 @@ def _idsva_so_fold_jobs(groups, alphas, NV, NB): job_src.append(ii * NB * NB + jj * NB + kk) job_wid.append(windex[w]) job_start.append(len(job_src)) - assert len(job_src) == NB ** 3, "idsva_so mimic fold: job list must cover the full internal tensor" + active = sum(a != 0.0 for a in alphas) + assert len(job_src) == active ** 3, "idsva_so mimic fold: job list must cover every active internal cell" assert len(wvals) <= 256, "idsva_so mimic fold: >256 distinct fold weights (widen so_fold_wid)" return job_start, job_src, job_wid, wvals @@ -110,6 +118,8 @@ def _idsva_so_fold_jobs(groups, alphas, NV, NB): def _idsva_so_emit_baked_array(self, decl, values, fmt=str, per_line=32): """Emit `decl = { ... };` with the initializer chunked across lines (the fold job tables are ~NB^3 entries — a single joined line would be MBs).""" + # Empty-coordinate models have no jobs; keep a legal inert storage element. + values = values if len(values) else [0] chunks = [", ".join(fmt(v) for v in values[i:i + per_line]) for i in range(0, len(values), per_line)] self.gen_add_code_lines([decl + " = {"] @@ -1580,6 +1590,8 @@ def _internal_cols_for_body(b): # slot and scale by the joint's mimic multiplier (mirrors the oracle's # `_qd = alpha_i * qd[inds_v_true]`). The fold below applies the remaining alpha # factors on the OTHER axes. + if 0.0 in metadata['int_alpha']: + self.gen_add_code_line("if (int_alpha[vel] == static_cast(0)) continue;") self.gen_add_code_line("T qd_v = int_alpha[vel] * s_qd[int_true_vel[vel]];") self.gen_add_code_line("T qdd_v = int_alpha[vel] * s_qdd[int_true_vel[vel]];") self.gen_add_code_line("vJ[jid*6 + row] += S_vel[vel*6 + row] * qd_v;") @@ -1783,6 +1795,8 @@ def _internal_cols_for_body(b): self.gen_add_code_line("for (int ii = 0; ii < SO_N_INT; ++ii) {", True) self.gen_add_code_line("for (int jj = 0; jj < SO_N_INT; ++jj) {", True) self.gen_add_code_line("for (int kk = 0; kk < SO_N_INT; ++kk) {", True) + if 0.0 in metadata['int_alpha']: + self.gen_add_code_line("if (int_alpha[ii] == static_cast(0) || int_alpha[jj] == static_cast(0) || int_alpha[kk] == static_cast(0)) continue;") self.gen_add_code_line("T val = so_internal[blk*SO_N_INT*SO_N_INT*SO_N_INT + ii*SO_N_INT*SO_N_INT + jj*SO_N_INT + kk];") self.gen_add_code_line("if (val != static_cast(0)) {", True) self.gen_add_code_line("T w = int_alpha[ii] * int_alpha[jj] * int_alpha[kk];") @@ -2191,8 +2205,12 @@ def gen_idsva_so_body_frame_inner(self): self.gen_add_parallel_loop('i','2*6*NUM_BODIES') self.gen_add_code_line('int joint = i / 6;') if is_mimic: - self.gen_add_code_line('if (joint < NUM_BODIES) vJ[i] = S[i] * (body_alpha[joint] * s_qd[body_vslot[joint]]);') - self.gen_add_code_line('else { int jj = joint - NUM_BODIES; aJ[i - 6*NUM_BODIES] = S[i - 6*NUM_BODIES] * (body_alpha[jj] * s_qdd[body_vslot[jj]]); }') + if 0.0 in body_alpha: + self.gen_add_code_line('if (joint < NUM_BODIES) vJ[i] = body_alpha[joint] == T(0) ? T(0) : S[i] * (body_alpha[joint] * s_qd[body_vslot[joint]]);') + self.gen_add_code_line('else { int jj = joint - NUM_BODIES; aJ[i - 6*NUM_BODIES] = body_alpha[jj] == T(0) ? T(0) : S[i - 6*NUM_BODIES] * (body_alpha[jj] * s_qdd[body_vslot[jj]]); }') + else: + self.gen_add_code_line('if (joint < NUM_BODIES) vJ[i] = S[i] * (body_alpha[joint] * s_qd[body_vslot[joint]]);') + self.gen_add_code_line('else { int jj = joint - NUM_BODIES; aJ[i - 6*NUM_BODIES] = S[i - 6*NUM_BODIES] * (body_alpha[jj] * s_qdd[body_vslot[jj]]); }') else: self.gen_add_code_line('if (joint < NUM_BODIES) vJ[i] = S[i] * s_qd[joint];') self.gen_add_code_line('else aJ[i - 6*NUM_BODIES] = S[i - 6*NUM_BODIES] * s_qdd[joint - NUM_BODIES];') @@ -2766,6 +2784,8 @@ def visit(k): # the identical fixed sum order in a single unrollable loop. groups = {} for b in range(NB): + if fold_alpha[b] == 0.0: + continue groups.setdefault(fold_vslot[b], []).append(b) assert sorted(groups) == list(range(NV)), \ "idsva_so mimic fold: v-slot groups must cover 0..NV-1" @@ -3486,6 +3506,8 @@ def gen_idsva_so_world_frame_inner(self): if is_mimic: # vel is an INTERNAL slot; read the (shared) reduced qd/qdd slot and scale by the # body's mimic multiplier (mirrors oracle's `_qd = alpha_i * qd[inds_v_true]`). + if 0.0 in metadata['int_alpha']: + self.gen_add_code_line("if (wf_int_alpha[vel] == static_cast(0)) continue;") self.gen_add_code_line("T a_qd = wf_int_alpha[vel] * s_qd[wf_int_true[vel]]; T a_qdd = wf_int_alpha[vel] * s_qdd[wf_int_true[vel]];") self.gen_add_code_line("for (int row = 0; row < 6; ++row) { fs_vJ[row] += S_vel[vel*6 + row] * a_qd; fs_aJ[row] += S_vel[vel*6 + row] * a_qdd; }") else: @@ -3872,6 +3894,8 @@ def gen_idsva_so_world_frame_inner(self): # written exactly once, so the zeroing pass is gone too. wf_groups = {} for s, tv in enumerate(metadata['int_true_vel']): + if metadata['int_alpha'][s] == 0.0: + continue wf_groups.setdefault(tv, []).append(s) assert sorted(wf_groups) == list(range(NV)), \ "idsva_so world fold: true-vel groups must cover 0..NV-1" diff --git a/grid_codegen/algorithms/_integrator.py b/grid_codegen/algorithms/_integrator.py index 3d7381f4..903f44cc 100644 --- a/grid_codegen/algorithms/_integrator.py +++ b/grid_codegen/algorithms/_integrator.py @@ -1014,8 +1014,7 @@ def gen_integrator_arena_carve_struct(self): temp = self.gen_forward_dynamics_inner_temp_mem_size(minv_f_in_smem=not spill_minv_F) layout = self._resolve_arena_layout( _integrator_kernel_extra_t_buffers(self, nq, nv), temp, - include_topology_helpers = (not self.robot.is_serial_chain() - or not self.robot.are_Ss_identical(list(range(nq)))), + include_topology_helpers = self.gen_topology_helpers_size() > 0, ximat_size = self.gen_get_XI_size(False, False), apply_runtime_transform_band = getattr(self, "runtime_transform", False)) self.gen_arena_carve_struct( diff --git a/grid_codegen/algorithms/_integrator_gradient.py b/grid_codegen/algorithms/_integrator_gradient.py index 974443fa..6d3d2975 100644 --- a/grid_codegen/algorithms/_integrator_gradient.py +++ b/grid_codegen/algorithms/_integrator_gradient.py @@ -1287,8 +1287,7 @@ def gen_integrator_du_arena_carve_struct(self): _integrator_du_extra_t_buffers(self, nq, n, fb, dqdd_in_smem, dab_in_smem, compute_x_kp1 = True), inner_temp_size, - include_topology_helpers = (not self.robot.is_serial_chain() - or not self.robot.are_Ss_identical(list(range(nq)))), + include_topology_helpers = self.gen_topology_helpers_size() > 0, ximat_size = self.gen_get_XI_size(False, False), apply_runtime_transform_band = getattr(self, "runtime_transform", False)) self.gen_arena_carve_struct( @@ -2114,6 +2113,86 @@ def _integ_hess_colblock(self, n, half, off, dst, ddst, Pv, Pd, with_cross): +SPHERICAL_HESSIAN_SCRATCH = 6 + 36 + 2 * 216 + + +def _gen_integrator_hessian_spherical_body(self): + """Compose admitted fixed-spherical moving-G derivatives, not chart Hessians. + + Reuse the dead fdsva pool after its trailing fence. One SO(3) block at a + time uses the public GLASS SE(3) restriction, avoiding a second derivative + formula and any model-sized thread-local tensor. + """ + n = self.robot.get_num_vel() + nz = 3*n + # _plant_hessian_pool_size owns this floor for shared and spilled storage. + add = self.gen_add_code_line + self.gen_fdsva_so_device_function_call( + scratch_in_smem_expr='SCRATCH_IN_SMEM', fd_grad_use_spill_expr='FD_GRAD_USE_SPILL', + contract_in_smem_expr='CONTRACT_IN_SMEM', d_workspace_pool_name='d_workspace', + d_fd_grad_spill_name='d_fd_grad_spill', s_fdsva_temp_name='s_fdsva_temp') + self.gen_add_sync() + add('const bool si = IT == IntegratorType::SEMI_IMPLICIT_EULER;') + add('auto D2 = [&](int i, int p, int q) -> T {') + add(f' int pb=p/{n}, qb=q/{n}, j=p%{n}, k=q%{n};') + add(f' const int nn={n*n}, nnn={n*n*n};') + add(f' if(pb==0 && qb==0) return s_df2[i*nn+j*{n}+k];') + add(f' if(pb==1 && qb==1) return s_df2[2*nnn+i*nn+j*{n}+k];') + add(f' if(pb==1 && qb==0) return s_df2[nnn+i*nn+j*{n}+k];') + add(f' if(pb==0 && qb==1) return s_df2[nnn+i*nn+k*{n}+j];') + add(f' if(pb==2 && qb==0) return s_df2[3*nnn+i*nn+j*{n}+k];') + add(f' if(pb==0 && qb==2) return s_df2[3*nnn+i*nn+k*{n}+j];') + add(' return T(0); };') + add('auto Vgrad = [&](int i, int a) -> T {') + add(f' int block=a/{n}, j=a%{n};') + add(f' if(block==0) return dt*s_df_du[j*{n}+i];') + add(f' if(block==1) return T(i==j)+dt*s_df_du[{n*n}+j*{n}+i];') + add(f' return dt*s_Minv[(i<=j)?j*{n}+i:i*{n}+j]; }};') + self.gen_add_parallel_loop('index', str(2*n*nz*nz)) + add(f'int o=index/{nz*nz}, a=(index/{nz})%{nz}, b=index%{nz};') + add(f's_d2AB[index]=(o>={n}?dt:(si?dt*dt:T(0)))*D2(o%{n},b,a);') + self.gen_add_end_control_flow() + self.gen_add_sync() + add('T *pool=SCRATCH_IN_SMEM?s_temp:d_workspace;') + add('T *w=pool, *J=pool+6, *Hq=pool+42, *Hv=pool+258;') + for joint in self.robot.get_joints_ordered_by_id(): + if joint.jtype != 'spherical': + continue + indices = self.robot.get_joint_index_v(joint.get_id()) + start = indices[0] + add('{') + add(f'constexpr int offset={start};') + self.gen_add_serial_ops() + add('for(int k=0;k<3;++k) { w[k]=T(0); w[k+3]=dt*(s_qd[offset+k]+(si?dt*s_qdd[offset+k]:T(0))); }') + add('glass::thread::se3_retract_jacobian_v(w,w+3,J);') + add('glass::thread::se3_retract_hessian(w,w+3,Hq);') + add('glass::thread::se3_retract_hessian(w,w+3,Hv);') + self.gen_add_end_control_flow() + self.gen_add_sync() + self.gen_add_parallel_loop('index', str(3*nz*nz)) + add(f'int i=index/{nz*nz}, a=(index/{nz})%{nz}, b=index%{nz};') + add('T value=T(0);') + add('if(!si) {') + add(f' int k=a-{n}-offset, col=b-offset;') + add(' if(k>=0 && k<3) {') + add(' if(col>=0 && col<3) value=dt*Hq[(k+3)*36+(col+3)*6+i+3];') + add(f' col=b-{n}-offset;') + add(' if(col>=0 && col<3) value=dt*dt*Hv[(k+3)*36+(col+3)*6+i+3];') + add(' }') + add('} else {') + add(' for(int k=0;k<3;++k) {') + add(' T va=Vgrad(offset+k,a); int col=b-offset;') + add(' if(col>=0 && col<3) value+=dt*Hq[(k+3)*36+(col+3)*6+i+3]*va;') + add(' for(int m=0;m<3;++m) value+=dt*dt*Hv[(k+3)*36+(m+3)*6+i+3]*va*Vgrad(offset+m,b);') + add(' value+=dt*dt*J[(k+3)*6+i+3]*D2(offset+k,b,a);') + add(' }') + add('}') + add(f's_d2AB[(offset+i)*{nz*nz}+a*{nz}+b]=value;') + self.gen_add_end_control_flow() + self.gen_add_sync() + add('}') + + def gen_integrator_hessian_device(self): """Emit `integrator_hessian_device` — the second-order sensitivity of the integrator step x_{k+1} = [q; v] as ONE inner that composes @@ -2121,7 +2200,7 @@ def gen_integrator_hessian_device(self): s_df2) and assembles the s_d2AB surface (the plant_step_hessian output). Output s_d2AB has shape (2*NUM_VEL, 3*NUM_VEL, 3*NUM_VEL) in row-major - (C-order) flat layout: H[o*nz*nz + a*nz + b] = d^2 x_{k+1}[o] / dz[a] dz[b], + (C-order) flat layout: H[o*nz*nz + a*nz + b] = d G[o,b] / dz[a], z = [dq(nv); dqd(nv); du(nv)], output rows = [position-tangent(nv); velocity(nv)]. Assembly (mirrors RBDReference._PlantMixin._d2qdd_tangent + plant_step_hessian): @@ -2130,8 +2209,9 @@ def gen_integrator_hessian_device(self): (each nv^3, laid out [i*nv*nv + j*nv + k]) with the z-block structure [q,q]=d2a_dqdq; [qd,qd]=d2a_dvdv; [qd,q]=d2a_dvdq, [q,qd]=d2a_dvdq^T(jk); [u,q]=d2a_dtdq, [q,u]=d2a_dtdq^T(jk); all u-u / u-qd / qd-u blocks = 0. - Velocity rows (bottom nv) = dt*D2 (both Euler and SI-Euler). Position rows - (top nv) = 0 (Euler) or dt*dt*D2 (SI-Euler, q_{k+1}=q+dt*v_{k+1}). + Scalar fixed-base rows use the affine assembly below. Spherical/floating + position rows include their retract derivatives; moving input/output bases + make this tensor distinct from a fixed-chart Hessian on a manifold. Scope: EULER + SEMI_IMPLICIT_EULER, BOTH fixed and floating base (floating is routed to gen_integrator_hessian_device_floating, the SE(3)-retract Hessian, at @@ -2197,6 +2277,10 @@ def gen_integrator_hessian_device(self): gen_integrator_hessian_device_floating(self) self.gen_add_end_function() return + if self.robot.robot_has_spherical(): + _gen_integrator_hessian_spherical_body(self) + self.gen_add_end_function() + return # The four 2nd-order forward-dynamics blocks (fdsva_so output), each nv^3. self.gen_fdsva_so_device_function_call( scratch_in_smem_expr="SCRATCH_IN_SMEM", diff --git a/grid_codegen/algorithms/_inverse_dynamics.py b/grid_codegen/algorithms/_inverse_dynamics.py index 01f2eb14..ec240762 100644 --- a/grid_codegen/algorithms/_inverse_dynamics.py +++ b/grid_codegen/algorithms/_inverse_dynamics.py @@ -141,6 +141,8 @@ def gen_inverse_dynamics_joint_dynamics_bias(self): else: vs = self.robot.get_joint_index_v(jid) alpha = 1.0 + if alpha == 0.0: + continue # No local input or generalized-force ownership. qd = "s_qd[" + str(vs) + "]" terms = [] if b != 0.0: @@ -244,7 +246,14 @@ def _v_idx(jid): def _id_qd(jid_expr, qd_name="s_qd"): # Read body `jid_expr`'s joint velocity/accel, mimic-folded. if HAS_MIMIC: - return "s_mimic_alpha[" + str(jid_expr) + "] * " + qd_name + "[s_mimic_vslot[" + str(jid_expr) + "]]" + if isinstance(jid_expr, int) and self._alpha_for_jid(jid_expr) == 0: + return "static_cast(0)" + value = "s_mimic_alpha[" + str(jid_expr) + "] * " + qd_name + "[s_mimic_vslot[" + str(jid_expr) + "]]" + if not isinstance(jid_expr, int) and any(self._alpha_for_jid(j) == 0 for j in range(n)): + # Conditional evaluation avoids the placeholder read itself, + # including an empty v array and an unrelated NaN coordinate. + return "(s_mimic_alpha[" + str(jid_expr) + "] == static_cast(0) ? static_cast(0) : " + value + ")" + return value # Spherical-shifted v-slot for a compile-time-int body id (single-DoF). if HAS_SPHERICAL and isinstance(jid_expr, int): return qd_name + "[" + str(_v_idx(jid_expr)) + "]" @@ -725,7 +734,19 @@ def _id_qd(jid_expr, qd_name="s_qd"): "s_c[" + str(k) + "] += static_cast(" + repr(sgn) + ") * s_vaf[" + str(12*n + 6*jid + int(row)) + "];") continue + if self._alpha_for_jid(jid) == 0: + continue # Transmit this body's force, but do not project it. vs = self._v_slot_cpp(jid) + if not self.robot.S_is_cardinal_by_id(jid): + # Reduced-coordinate projection is alpha * S^T f, including + # every nonzero component of a skew motion subspace. + alpha = float(self._alpha_for_jid(jid)) + for row, value in enumerate(self.robot._get_flat_S_by_id(jid)): + if value != 0: + self.gen_add_code_line( + "s_c[" + str(vs) + "] += static_cast(" + repr(alpha * float(value)) + + ") * s_vaf[" + str(12*n + 6*jid + row) + "];") + continue s_ind = self.robot.get_S_index_by_id(jid) s_sign = self.robot.get_S_sign_by_id(jid) alpha = self._alpha_for_jid(jid) diff --git a/grid_codegen/algorithms/_inverse_dynamics_gradient.py b/grid_codegen/algorithms/_inverse_dynamics_gradient.py index ae8e443b..e0db92f0 100644 --- a/grid_codegen/algorithms/_inverse_dynamics_gradient.py +++ b/grid_codegen/algorithms/_inverse_dynamics_gradient.py @@ -62,6 +62,8 @@ def _idg_damping_diag_cpp(self, n): if HAS_MIMIC: vs = self._v_slot_cpp(jid) alpha = float(self._alpha_for_jid(jid)) + if alpha == 0.0: + continue # No reduced derivative row belongs to a constant joint. else: vs = self.robot.get_joint_index_v(jid) alpha = 1.0 @@ -1864,7 +1866,9 @@ def _gen_inverse_dynamics_gradient_mimic_inner(self, nv, NB): backward pass -> dc_dq, dc_dqd (nv x nv). Every joint-velocity read scales by the body's mimic multiplier alpha and every dc_du / df_du write accumulates (+=) into the body's reduced v-slot, - so a mimic body and its target fold together. Non-root bodies are single + so a mimic body and its target fold together. A constant body has no local + read or reduced-column contribution, but retains inherited motion and force + propagation. Non-root bodies are single -DoF (the floating root is the only multi-DoF joint and is never mimic); the floating-base root (jid 0) carries a 6-DoF motion subspace S = I_6 over v-slots 0..5, so its own-DoF contributions LOOP over the 6 root DoFs @@ -2020,28 +2024,29 @@ def cell(base, ind, c): self.gen_add_end_control_flow() self.gen_add_sync() # the X*parent cols feed the single-column += folds below - # dv_dq[:,idx,ind] += alpha * mxS(S, X*v_parent) = alpha*s_sign*mx_Sind(X v_parent) - # X*v_parent: dv contribution uses v[parent]. Single reduced-column (idx) - # fold -> ONE lane (cross-body v-slot accumulate stays unsplit, bit-exact). - # SPHERICAL: 3 single-column folds, one per v-block slot vblk[k], using - # mx (S col k = angular axis k, sign +1). Each is a single-lane write. - self.gen_add_serial_ops() - self.gen_add_code_line("// dv_dq[:,idx] += alpha*mxS(S, X*v_parent); dv_dqd[:,idx] += alpha*S") - self.gen_add_code_line("T s_mtmp[6];") - self.gen_add_code_line("for (int r = 0; r < 6; r++) { s_mtmp[r] = static_cast(0);") - self.gen_add_code_line(" for (int p = 0; p < 6; p++) s_mtmp[r] += s_XImats[" + str(Xoff) + " + r + 6*p] * s_vaf[" + str(6*parent) + " + p]; }") - # mx_peq_scaled into dv_dq[:,idx,ind]. mxS(S,.) carries the - # joint sign (S = s_sign*e_{s_ind}); mx_peq_scaled only applies - # the UNIT-axis column, so fold s_sign into the scale (alpha*s_sign). - if is_spherical: - for k in range(3): - self.gen_add_code_line("mx" + str(k) + "_peq_scaled(&s_temp[" + str(cell(off_dv_dq, ind, 0)) + " + 6*" + str(vblk[k]) + "], s_mtmp, static_cast(1.0));") - elif is_skew: - self.gen_add_code_line("{ const T S_skew[6] = " + _idg_Svec_cpp(S_vec) + "; mxS_general_peq_scaled(&s_temp[" + str(cell(off_dv_dq, ind, 0)) + " + 6*" + str(idx) + "], s_mtmp, S_skew, static_cast(" + repr(alpha) + ")); }") - else: - self.gen_add_code_line("mx" + str(s_ind) + "_peq_scaled(&s_temp[" + str(cell(off_dv_dq, ind, 0)) + " + 6*" + str(idx) + "], s_mtmp, static_cast(" + repr(alpha * s_sign) + "));") - self.gen_add_end_control_flow() - self.gen_add_sync() # dv_dq[:,idx] now visible to the da += mxS(dv) reader below + if alpha != 0.0: + # dv_dq[:,idx,ind] += alpha * mxS(S, X*v_parent) = alpha*s_sign*mx_Sind(X v_parent) + # X*v_parent: dv contribution uses v[parent]. Single reduced-column (idx) + # fold -> ONE lane (cross-body v-slot accumulate stays unsplit, bit-exact). + # SPHERICAL: 3 single-column folds, one per v-block slot vblk[k], using + # mx (S col k = angular axis k, sign +1). Each is a single-lane write. + self.gen_add_serial_ops() + self.gen_add_code_line("// dv_dq[:,idx] += alpha*mxS(S, X*v_parent); dv_dqd[:,idx] += alpha*S") + self.gen_add_code_line("T s_mtmp[6];") + self.gen_add_code_line("for (int r = 0; r < 6; r++) { s_mtmp[r] = static_cast(0);") + self.gen_add_code_line(" for (int p = 0; p < 6; p++) s_mtmp[r] += s_XImats[" + str(Xoff) + " + r + 6*p] * s_vaf[" + str(6*parent) + " + p]; }") + # mx_peq_scaled into dv_dq[:,idx,ind]. mxS(S,.) carries the + # joint sign (S = s_sign*e_{s_ind}); mx_peq_scaled only applies + # the UNIT-axis column, so fold s_sign into the scale (alpha*s_sign). + if is_spherical: + for k in range(3): + self.gen_add_code_line("mx" + str(k) + "_peq_scaled(&s_temp[" + str(cell(off_dv_dq, ind, 0)) + " + 6*" + str(vblk[k]) + "], s_mtmp, static_cast(1.0));") + elif is_skew: + self.gen_add_code_line("{ const T S_skew[6] = " + _idg_Svec_cpp(S_vec) + "; mxS_general_peq_scaled(&s_temp[" + str(cell(off_dv_dq, ind, 0)) + " + 6*" + str(idx) + "], s_mtmp, S_skew, static_cast(" + repr(alpha) + ")); }") + else: + self.gen_add_code_line("mx" + str(s_ind) + "_peq_scaled(&s_temp[" + str(cell(off_dv_dq, ind, 0)) + " + 6*" + str(idx) + "], s_mtmp, static_cast(" + repr(alpha * s_sign) + "));") + self.gen_add_end_control_flow() + self.gen_add_sync() # dv_dq[:,idx] now visible to the da += mxS(dv) reader below if is_float_root: # ===== FLOATING ROOT own-DoF terms (6-DoF motion subspace S) ===== @@ -2136,7 +2141,7 @@ def cell(base, ind, c): self.gen_add_code_line("mx" + str(k) + "_peq_scaled(&s_temp[" + str(cell(off_da_dqd, ind, 0)) + " + 6*" + str(vblk[k]) + "], &s_vaf[" + str(v_ind) + "], static_cast(1.0));") self.gen_add_end_control_flow() self.gen_add_sync() # all da cols (incl vblk cols) before df reads them - else: + elif alpha != 0.0: # dv_dqd[:,idx,ind] += alpha*S (S = s_sign*e_{s_ind}). NOTE: the oracle # adds this for EVERY body including the root (it sits OUTSIDE the # parent!=-1 guard in rnea_grad_fpass_dqd) — the joint's own velocity @@ -2328,36 +2333,38 @@ def cell(base, ind, c): # dc_dqd[idx, c] += alpha * s_sign * df_dqd[s_ind, c, ind] # WIN B: one lane per gradient column c. Each c writes a distinct dc_du # entry; the cross-body accumulate at row idx is serialized by the walk. - self.gen_add_parallel_loop("c", str(nv)) - if is_skew: - # dc[idx,c] += alpha * S^T df[:,c] = alpha * sum_r S[r]*df[r,c] - dq_terms = " + ".join("static_cast(" + repr(alpha * S_vec[r]) + ") * s_temp[" + str(cell(off_df_dq, ind, 0)) + " + 6*c + " + str(r) + "]" for r in range(6) if S_vec[r] != 0.0) - qd_terms = " + ".join("static_cast(" + repr(alpha * S_vec[r]) + ") * s_temp[" + str(cell(off_df_dqd, ind, 0)) + " + 6*c + " + str(r) + "]" for r in range(6) if S_vec[r] != 0.0) - self.gen_add_code_line("s_dc_du[c*" + str(nv) + " + " + str(idx) + "] += " + (dq_terms if dq_terms else "static_cast(0)") + ";") - self.gen_add_code_line("s_dc_du[" + str(nv*nv) + " + c*" + str(nv) + " + " + str(idx) + "] += " + (qd_terms if qd_terms else "static_cast(0)") + ";") - else: - coeff = alpha * s_sign - self.gen_add_code_line("s_dc_du[c*" + str(nv) + " + " + str(idx) + "] += static_cast(" + repr(coeff) + ") * s_temp[" + str(cell(off_df_dq, ind, 0)) + " + 6*c + " + str(s_ind) + "];") - self.gen_add_code_line("s_dc_du[" + str(nv*nv) + " + c*" + str(nv) + " + " + str(idx) + "] += static_cast(" + repr(coeff) + ") * s_temp[" + str(cell(off_df_dqd, ind, 0)) + " + 6*c + " + str(s_ind) + "];") - self.gen_add_end_control_flow() + if alpha != 0.0: + self.gen_add_parallel_loop("c", str(nv)) + if is_skew: + # dc[idx,c] += alpha * S^T df[:,c] = alpha * sum_r S[r]*df[r,c] + dq_terms = " + ".join("static_cast(" + repr(alpha * S_vec[r]) + ") * s_temp[" + str(cell(off_df_dq, ind, 0)) + " + 6*c + " + str(r) + "]" for r in range(6) if S_vec[r] != 0.0) + qd_terms = " + ".join("static_cast(" + repr(alpha * S_vec[r]) + ") * s_temp[" + str(cell(off_df_dqd, ind, 0)) + " + 6*c + " + str(r) + "]" for r in range(6) if S_vec[r] != 0.0) + self.gen_add_code_line("s_dc_du[c*" + str(nv) + " + " + str(idx) + "] += " + (dq_terms if dq_terms else "static_cast(0)") + ";") + self.gen_add_code_line("s_dc_du[" + str(nv*nv) + " + c*" + str(nv) + " + " + str(idx) + "] += " + (qd_terms if qd_terms else "static_cast(0)") + ";") + else: + coeff = alpha * s_sign + self.gen_add_code_line("s_dc_du[c*" + str(nv) + " + " + str(idx) + "] += static_cast(" + repr(coeff) + ") * s_temp[" + str(cell(off_df_dq, ind, 0)) + " + 6*c + " + str(s_ind) + "];") + self.gen_add_code_line("s_dc_du[" + str(nv*nv) + " + c*" + str(nv) + " + " + str(idx) + "] += static_cast(" + repr(coeff) + ") * s_temp[" + str(cell(off_df_dqd, ind, 0)) + " + 6*c + " + str(s_ind) + "];") + self.gen_add_end_control_flow() if parent != -1: self.gen_add_sync() # dc_du fold reads df[ind]; df[parent] += below must wait # df_dq[:,idx,parent] += alpha * X^T * fxS(S, f[ind]). Single reduced- # column (idx) fold on the PARENT -> ONE lane (bit-exact accumulate). # fxS(S, f) = Fx(S)*f = fx_times_v(S, f); S = s_sign*e_{s_ind} - self.gen_add_serial_ops() - self.gen_add_code_line("T s_fxs[6]; T s_xtfxs[6]; T s_Svec[6];") - if is_skew: - self.gen_add_code_line("{ const T S_skew[6] = " + _idg_Svec_cpp(S_vec) + "; for (int r = 0; r < 6; r++) s_Svec[r] = S_skew[r]; }") - else: - self.gen_add_code_line("for (int r = 0; r < 6; r++) s_Svec[r] = static_cast(0);") - self.gen_add_code_line("s_Svec[" + str(s_ind) + "] = static_cast(" + repr(s_sign) + ");") - self.gen_add_code_line("fx_times_v(s_fxs, s_Svec, &s_vaf[" + str(f_ind) + "]);") - # X^T * s_fxs : (X^T)[r,p] = X[p,r] = s_XImats[Xoff + p + 6*r] - self.gen_add_code_line("for (int r = 0; r < 6; r++) { s_xtfxs[r] = static_cast(0);") - self.gen_add_code_line(" for (int p = 0; p < 6; p++) s_xtfxs[r] += s_XImats[" + str(Xoff) + " + p + 6*r] * s_fxs[p]; }") - self.gen_add_code_line("for (int r = 0; r < 6; r++) s_temp[" + str(cell(off_df_dq, parent, 0)) + " + 6*" + str(idx) + " + r] += static_cast(" + repr(alpha) + ") * s_xtfxs[r];") - self.gen_add_end_control_flow() + if alpha != 0.0: + self.gen_add_serial_ops() + self.gen_add_code_line("T s_fxs[6]; T s_xtfxs[6]; T s_Svec[6];") + if is_skew: + self.gen_add_code_line("{ const T S_skew[6] = " + _idg_Svec_cpp(S_vec) + "; for (int r = 0; r < 6; r++) s_Svec[r] = S_skew[r]; }") + else: + self.gen_add_code_line("for (int r = 0; r < 6; r++) s_Svec[r] = static_cast(0);") + self.gen_add_code_line("s_Svec[" + str(s_ind) + "] = static_cast(" + repr(s_sign) + ");") + self.gen_add_code_line("fx_times_v(s_fxs, s_Svec, &s_vaf[" + str(f_ind) + "]);") + # X^T * s_fxs : (X^T)[r,p] = X[p,r] = s_XImats[Xoff + p + 6*r] + self.gen_add_code_line("for (int r = 0; r < 6; r++) { s_xtfxs[r] = static_cast(0);") + self.gen_add_code_line(" for (int p = 0; p < 6; p++) s_xtfxs[r] += s_XImats[" + str(Xoff) + " + p + 6*r] * s_fxs[p]; }") + self.gen_add_code_line("for (int r = 0; r < 6; r++) s_temp[" + str(cell(off_df_dq, parent, 0)) + " + 6*" + str(idx) + " + r] += static_cast(" + repr(alpha) + ") * s_xtfxs[r];") + self.gen_add_end_control_flow() # The single-column df_dq[:,idx,parent] += (above) must land BEFORE the # X^T propagation below touches that same parent column (preserves the # original H-then-I float accumulation order -> bit-exact). diff --git a/grid_codegen/algorithms/_plant.py b/grid_codegen/algorithms/_plant.py index ad2b1859..ee363a0f 100644 --- a/grid_codegen/algorithms/_plant.py +++ b/grid_codegen/algorithms/_plant.py @@ -2003,28 +2003,24 @@ def _plant_step_gradient_extra_t_buffers(self): gen_plant_step_gradient_kernel and the plant_step_gradient_arena carve struct so the two cannot drift.""" n = self.robot.get_num_vel() - fb = 1 if self.robot.floating_base else 0 - nx = self.robot.get_num_pos() + self.robot.get_num_vel() - from ._integrator import _max_stages_in_use - max_stages = _max_stages_in_use() - d_qdd_count = max_stages * n * 3 * n - vaf_cnt = 18 * (self.robot.get_num_joints() if self.robot_has_mimic_joints() else n) - return [ - ("s_x", nx), - ("s_u", n), - ("s_dAB", 2 * n * 3 * n), - ("s_df_du", n * 2 * n), - ("s_dc_du", n * 2 * n), - ("s_vaf", vaf_cnt), - ("s_Minv", n * n), - ("s_qdd", n), - ("s_q_orig", n + fb), - ("s_qd_orig", n), - ("s_stage_grad_qdd", max_stages * n), - ("s_D_qdd_stage", d_qdd_count), - ("s_dInt_q_6x6", 36), - ("s_dInt_v_6x6", 36), - ] + nq = self.robot.get_num_pos() + from ._integrator_gradient import _integrator_du_extra_t_buffers + # Only input packing differs: plant uses [q;qd] plus u, integrator uses + # three nq-wide slots. Share all scratch dimensions, including spherical + # q_orig and multi-joint SO(3) dIntegrate storage. + scratch = _integrator_du_extra_t_buffers( + self, nq, n, int(self.robot.floating_base), True, True, False)[1:] + return [("s_x", nq + n), ("s_u", n)] + scratch + + +def _plant_step_gradient_layout(self): + """Full-shared layout; never substitute the tier-selected integrator layout.""" + return self._resolve_arena_layout( + _plant_step_gradient_extra_t_buffers(self), + self.gen_integrator_gradient_inner_temp_mem_size(), + include_topology_helpers=self.gen_topology_helpers_size() > 0, + ximat_size=self.gen_get_XI_size(False, False), + apply_runtime_transform_band=getattr(self, "runtime_transform", False)) def gen_plant_step_gradient_arena_carve_struct(self): @@ -2032,27 +2028,30 @@ def gen_plant_step_gradient_arena_carve_struct(self): (GATO ASK6): the full-smem sub-buffer layout plant_step_gradient_kernel carves — the struct's members ARE plant_step_gradient's caller-placed buffer arguments, so an external kernel can allocate - grid::INTEGRATOR_DU_DYNAMIC_SHARED_MEM_BYTES() bytes (the - same reservation the kernel launches with), carve(), and forward. No exact - t-count tie exists for this surface (it reserves the du sizer, which may be - a spilled rung on big robots while this layout is always full-smem), so the - carve assert is the <= allocation-safety form only.""" - layout = self._resolve_arena_layout( - _plant_step_gradient_extra_t_buffers(self), - self.gen_integrator_gradient_inner_temp_mem_size(), - include_topology_helpers = (not self.robot.is_serial_chain() - or not self.robot.are_Ss_identical(list(range(self.robot.get_num_pos())))), - ximat_size = self.gen_get_XI_size(False, False), - apply_runtime_transform_band = getattr(self, "runtime_transform", False)) + PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES() bytes, carve(), and forward. + This surface does not spill. The tiered integrator kernel remains available + for models that cannot admit the full-shared plant layout.""" + layout = _plant_step_gradient_layout(self) + t_count = sum(count for _, count in layout.t_buffers) + layout.ximat_size + layout.temp_mem_size + self.gen_add_code_lines([ + "// Full-shared plant layout, NOT the tier-selected integrator allocation.", + "template __host__ __device__ constexpr size_t PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES() {", + " return grid::grid_shared_arena_bytes(" + str(t_count) + ", " + str(layout.topology_count) + ");", + "}", + "template __host__ inline cudaError_t plant_step_gradient_check_shared_memory() {", + ' return grid::grid_check_dynamic_shared_memory_bytes("plant_step_gradient (full shared; use tiered integrator_gradient if unsupported)", PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES());', + "}", + ]) self.gen_arena_carve_struct( "plant_step_gradient_arena", layout, - "grid::INTEGRATOR_DU_DYNAMIC_SHARED_MEM_BYTES()", - expected_t_count = None, - helper_ns = "grid::", # emitted in namespace grid_plant; the arena helpers live in grid:: + "PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES()", + expected_t_count = t_count, + helper_ns = "grid::", host_device=True, checked_bind=True, doc = "plant_step_gradient_arena: carve struct mirroring plant_step_gradient_kernel's " "full-smem scratch layout; members map 1:1 onto plant_step_gradient's caller-placed " - "buffer arguments. Allocate INTEGRATOR_DU_DYNAMIC_SHARED_MEM_BYTES() " - "bytes and call carve(base).") + "buffer arguments. Allocate PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES() " + "bytes and call carve(base). No spill support: check device admission before launch; " + "composed callers must also budget their own static/dynamic storage.") def gen_plant_step_gradient_kernel(self): @@ -2065,21 +2064,13 @@ def gen_plant_step_gradient_kernel(self): body (every band in smem: SCRATCH_IN_SMEM=true, no workspace/spill) — then calls grid_plant::plant_step_gradient (the thin pass-through). The emitted s_dAB is byte-identical to grid::integrator_gradient's. Inputs/outputs are - global; the launch reserves grid::INTEGRATOR_DU_DYNAMIC_SHARED_MEM_BYTES. + global; the launch reserves PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES. x = [q (NUM_POS); qd (NUM_VEL)] (NON-const: the multi-stage RK path mutates q/qd across stages in smem); u = control torque (NUM_VEL).""" n = self.robot.get_num_vel() - fb = 1 if self.robot.floating_base else 0 nx = self.robot.get_num_pos() + self.robot.get_num_vel() - from ._integrator import _max_stages_in_use - max_stages = _max_stages_in_use() - d_qdd_count = max_stages * n * 3 * n inner_temp_full = self.gen_integrator_gradient_inner_temp_mem_size() - # s_vaf is body-indexed (NB bodies, stride 6); size by NB for mimic robots - # (NB>nv) so the composed FD-grad ID sub-inner's 18*NB writes don't overflow - # the adjacent buffers (mirrors integrator_gradient_kernel's vaf sizing). - vaf_cnt = 18 * (self.robot.get_num_joints() if self.robot_has_mimic_joints() else n) self.gen_add_func_doc("plant_step_gradient kernel: [A|B] = integrator_gradient([q;qd], u, dt) per timestep " "(full-smem scratch arena; pass-through to grid::integrator_gradient_device)", [], @@ -2166,6 +2157,109 @@ def gen_plant_step_gradient_kernel(self): ] +def _plant_hessian_pool_size(self): + """Shared-tier scratch pool of the plant Hessian: the largest of the + idsva_so inner, the 4 nv^3 fdsva_so contraction and the inline fd gradient, + raised to the SE(3) and SO(3) floors on floating or spherical models.""" + inner = max(self.gen_idsva_so_body_frame_inner_temp_mem_size(), + self.gen_fdsva_so_contract_temp_mem_size(), + self.gen_fdsva_so_fd_gradient_inline_temp_mem_size()) + if self.robot.floating_base: + from ._integrator_gradient import FLOATING_HESSIAN_SE3_SCRATCH + inner = max(inner, FLOATING_HESSIAN_SE3_SCRATCH) + elif any(j.jtype == 'spherical' for j in self.robot.get_joints_ordered_by_id()): + from ._integrator_gradient import SPHERICAL_HESSIAN_SCRATCH + inner = max(inner, SPHERICAL_HESSIAN_SCRATCH) + return inner + + +def _plant_hessian_shared_slots(self, spill_d2ab, spill_tensors): + n = self.robot.get_num_vel() + slots = [("s_x", self.robot.get_num_pos() + n), ("s_u", n)] + if not spill_d2ab: + slots.append(("s_d2AB", 18 * n ** 3)) + if not spill_tensors: + slots.extend([("s_df2", 4 * n ** 3), ("s_idsva_so", 4 * n ** 3)]) + return slots + [("s_Minv", n * n), ("s_df_du", 2 * n * n), ("s_qdd", n)] + + +def _plant_hessian_layout(self, spill_d2ab, spill_tensors, spill_pool): + """Resolve the same reserve/floor/topology as the production kernel carve.""" + from ..helpers._topology_helpers import _helpers_sincos_temp_floor + temp = 0 if spill_pool else max(_plant_hessian_pool_size(self), _helpers_sincos_temp_floor(self)) + return self._resolve_arena_layout( + _plant_hessian_shared_slots(self, spill_d2ab, spill_tensors), temp, + include_topology_helpers=self.gen_topology_helpers_size() > 0, + ximat_size=self.gen_get_XI_size(), + apply_runtime_transform_band=getattr(self, "runtime_transform", False)) + + +def gen_plant_step_hessian_arena(self): + """Explicit full/deep composition; never infer storage from a resource tier.""" + n = self.robot.get_num_vel() + self.gen_add_code_line("// Plant Hessian workspace begins.") + supported = n > 0 and not self.robot.floating_base and self.robot.get_num_pos() == n + self.gen_add_code_line("#define GRID_PLANT_HAS_HESSIAN_ARENA " + str(int(supported))) + if not supported: + self.gen_add_code_line("// Composed Hessian views currently require fixed-base nq == nv > 0. Existing kernel support is unchanged.") + self.gen_add_code_line("// Plant Hessian workspace ends.") + return + self.gen_add_code_lines([ + "enum class plant_hessian_layout { full_shared, deep_spill };", + "template struct plant_step_hessian_arena;", + ]) + pool = _plant_hessian_pool_size(self) + for spill in (False, True): + tag = "deep_spill" if spill else "full_shared" + storage = "plant_hessian_" + tag + "_storage" + layout = _plant_hessian_layout(self, spill, spill, spill) + count = sum(c for _, c in layout.t_buffers) + layout.ximat_size + layout.temp_mem_size + sizer = f"grid::grid_shared_arena_bytes({count}, {layout.topology_count})" + self.gen_arena_carve_struct(storage, layout, sizer, + expected_t_count=count, helper_ns="grid::", host_device=True) + ws_count = 26 * n ** 3 + pool if spill else 0 + self.gen_add_code_lines([ + "template ", + f"struct plant_step_hessian_arena : {storage} {{", + ' static_assert(sizeof(T) == 4 || sizeof(T) == 8, "float or double storage required");', + " T *s_fdsva_pool;", + ] + ([" T *s_d2AB; T *s_df2; T *s_idsva_so;"] if spill else []) + [ + " static __host__ __device__ constexpr size_t alignment() { return 16; }", + f" static __host__ __device__ constexpr size_t shared_bytes() {{ return {sizer}; }}", + f" static __host__ __device__ constexpr size_t workspace_bytes() {{ return grid::grid_align_up(sizeof(T) * size_t({ws_count}), 16); }}", + " // Capacity and alignment only; enclosing-kernel resource admission is separate.", + " static __host__ __device__ bool bind(void *shared, size_t shared_capacity, void *workspace, size_t workspace_capacity, plant_step_hessian_arena &out) {", + " if (!shared || shared_capacity < shared_bytes() || reinterpret_cast(shared) % 16) return false;", + " if (workspace_bytes() && (!workspace || workspace_capacity < workspace_bytes() || reinterpret_cast(workspace) % 16)) return false;", + " plant_step_hessian_arena a;", + f" static_cast<{storage}&>(a) = {storage}::carve(shared);", + ]) + if spill: + self.gen_add_code_lines([ + " T *w = reinterpret_cast(workspace);", + " a.s_d2AB = w;", + f" a.s_df2 = w + {18*n**3};", + f" a.s_idsva_so = w + {22*n**3};", + f" a.s_fdsva_pool = w + {26*n**3};", + ]) + else: + self.gen_add_code_line(" a.s_fdsva_pool = nullptr;") + flag = "false" if spill else "true" + topology = "this->s_topology_helpers, " + self.gen_add_code_lines([ + " out = a; return true;", + " }", + " // Block collective. Caller initializes s_x/s_u and fences input/output reuse.", + " template ", + " __device__ void evaluate(const grid::robotModel *model, T gravity, T dt) const {", + ' static_assert(IT == grid::IntegratorType::EULER || IT == grid::IntegratorType::SEMI_IMPLICIT_EULER, "Hessian composition supports Euler and SI-Euler");', + f" plant_step_hessian(this->s_d2AB, this->s_x, this->s_u, this->s_df2, this->s_idsva_so, this->s_Minv, this->s_df_du, this->s_qdd, this->s_XImats, " + topology + "this->s_temp, this->s_fdsva_pool, nullptr, this->s_fdsva_pool, model, gravity, dt);", + " }", + "};", + ]) + self.gen_add_code_line("// Plant Hessian workspace ends.") + + def _emit_plant_step_hessian_kernel_body_for_flags(self, n, nx, d2ab_count, spill_d2AB, spill_tensors, spill_fdsva_pool): """Emit the plant_step_hessian_kernel body for one tier's spill flags. @@ -2175,28 +2269,11 @@ def _emit_plant_step_hessian_kernel_body_for_flags(self, n, nx, d2ab_count, for the spilled buffers, then call grid_plant::plant_step_hessian with the matching SCRATCH_IN_SMEM/CONTRACT_IN_SMEM flags, then (if d2AB is in smem) scatter it to the global output.""" - # SHARED-tier fdsva_so inner pool: max(idsva_so inner, 4*nv^3 contract, - # fd_grad inline) -- the same sizing GCG.py's _temp_full uses for fdsva_so. - inner_idsva = self.gen_idsva_so_body_frame_inner_temp_mem_size() - inner_temp_full = max(inner_idsva, - self.gen_fdsva_so_contract_temp_mem_size(), - self.gen_fdsva_so_fd_gradient_inline_temp_mem_size()) - if self.robot.floating_base: - from ._integrator_gradient import FLOATING_HESSIAN_SE3_SCRATCH - inner_temp_full = max(inner_temp_full, FLOATING_HESSIAN_SE3_SCRATCH) + inner_temp_full = _plant_hessian_pool_size(self) # When the pool spills, the smem s_temp slot is unused (size 0); the whole # pool (incl. the contraction scratch) routes to d_workspace. shared_temp_size = 0 if spill_fdsva_pool else inner_temp_full - extra_t_buffers = [("s_x", nx), ("s_u", n)] - if not spill_d2AB: - extra_t_buffers.append(("s_d2AB", d2ab_count)) - if not spill_tensors: - extra_t_buffers.append(("s_df2", 4 * n * n * n)) - extra_t_buffers.append(("s_idsva_so", 4 * n * n * n)) - # df_du / Minv / qdd are small + hot — always smem (surgical-spill rule). - extra_t_buffers.append(("s_Minv", n * n)) - extra_t_buffers.append(("s_df_du", 2 * n * n)) - extra_t_buffers.append(("s_qdd", n)) + extra_t_buffers = _plant_hessian_shared_slots(self, spill_d2AB, spill_tensors) self.gen_XImats_helpers_temp_shared_memory_code( shared_temp_size, extra_t_buffers=extra_t_buffers) # Floating mjx always needs d_workspace (the dedicated d_mjx_ws band), even at the @@ -2289,14 +2366,7 @@ def gen_plant_step_hessian_kernel(self): nx = self.robot.get_num_pos() + self.robot.get_num_vel() nz = 3 * n d2ab_count = 2 * n * nz * nz - xi_size = self.gen_get_XI_size() - inner_idsva = self.gen_idsva_so_body_frame_inner_temp_mem_size() - inner_temp_full = max(inner_idsva, - self.gen_fdsva_so_contract_temp_mem_size(), - self.gen_fdsva_so_fd_gradient_inline_temp_mem_size()) - if self.robot.floating_base: - from ._integrator_gradient import FLOATING_HESSIAN_SE3_SCRATCH - inner_temp_full = max(inner_temp_full, FLOATING_HESSIAN_SE3_SCRATCH) + inner_temp_full = _plant_hessian_pool_size(self) # Per-timestep workspace band for the spilled tiers: s_d2AB (18*nv^3) + # s_df2 + s_idsva_so (8*nv^3) + the fdsva_so pool/contraction scratch. Sized # for the MAX a tier might spill, so the allocation always covers any tier the @@ -2349,18 +2419,9 @@ def _emit_body(pick): # binding launch MUST size the dynamic smem with this macro AND raise the # per-kernel max via cudaFuncSetAttribute. The t_count mirrors each tier's # arena exactly (the in-smem extra_t_buffers + s_XImats + s_temp pool). - base_t = nx + n + n * n + 2 * n * n + n + xi_size - def _tier_t_count(pick): - spill_d2AB, spill_tensors, spill_fdsva_pool = _PLANT_HESSIAN_PICK_FLAGS[pick] - t = base_t - if not spill_d2AB: - t += d2ab_count - if not spill_tensors: - t += 8 * n * n * n - if not spill_fdsva_pool: - t += inner_temp_full - return t + layout = _plant_hessian_layout(self, *_PLANT_HESSIAN_PICK_FLAGS[pick]) + return sum(count for _, count in layout.t_buffers) + layout.ximat_size + layout.temp_mem_size t0 = _tier_t_count(picks[0]) t1 = _tier_t_count(picks[1]) @@ -2368,8 +2429,7 @@ def _tier_t_count(pick): self.gen_add_code_line( "template __host__ __device__ constexpr size_t " "INTEGRATOR_HESSIAN_DYNAMIC_SHARED_MEM_BYTES() { " - # ONE return (nested ternary): constexpr under the runners' -std=c++11 - # (same tier values as the if-constexpr chain it replaced, 2026-09-23). + # One return expression (a nested ternary) so the sizer is constexpr. "return (TIER == grid::TIER_SHARED) ? grid::grid_shared_arena_bytes(" + str(t0) + ", grid::TOPOLOGY_HELPERS_COUNT) " ": (TIER == grid::TIER_LITE) ? grid::grid_shared_arena_bytes(" + str(t1) + ", grid::TOPOLOGY_HELPERS_COUNT) " ": grid::grid_shared_arena_bytes(" + str(t2) + ", grid::TOPOLOGY_HELPERS_COUNT); " @@ -2672,6 +2732,7 @@ def gen_plant_kernels(self, algorithms): # fdsva_so TU): the kernel emits but `grid::IntegratorType` is undefined. if ("fdsva_so" in algorithms) and ("integrator" in algorithms): gen_plant_step_hessian_kernel(self) + gen_plant_step_hessian_arena(self) self.gen_add_code_line("#define GRID_PLANT_HAS_STEP_HESSIAN 1") if ("end_effector_pose" in algorithms) and ("end_effector_pose_gradient" in algorithms): gen_ee_pos_cost_kernel(self) diff --git a/grid_codegen/algorithms/_plant_menu.py b/grid_codegen/algorithms/_plant_menu.py index acb67ecf..6ce37331 100644 --- a/grid_codegen/algorithms/_plant_menu.py +++ b/grid_codegen/algorithms/_plant_menu.py @@ -803,6 +803,46 @@ def per_entry(which): self.gen_add_end_function() +def _gen_tracking_cost_menu_arena(self): + """Use the evaluator's exact bytes and the activation/feature row floor.""" + term_floor = 3 * self.robot.get_num_vel() + 72 + self.gen_add_code_lines([ + "// Cost menu workspace begins.", + "template ", + "struct tracking_cost_menu_arena {", + ' static_assert(MX >= 0 && MU >= 0, "feature row counts must be nonnegative");', + ' static_assert(sizeof(T) == 4 || sizeof(T) == 8, "float or double storage required");', + " T *s_frame; T *s_scratch;", + " static __host__ __device__ constexpr size_t alignment() { return 16; }", + " static __host__ __device__ constexpr size_t scratch_offset() {", + " return grid::grid_align_up(sizeof(T) * GRID_PLANT_FRAME_SCRATCH_COUNT, alignment());", + " }", + " static __host__ __device__ constexpr size_t scratch_bytes() {", + " constexpr size_t rows = MX > MU ? MX : MU;", + f" constexpr size_t terms = sizeof(T) * (rows > {term_floor} ? rows : {term_floor});", + " constexpr size_t frame = FRAME_EVAL_DYNAMIC_SHARED_MEM_BYTES();", + " return terms > frame ? terms : frame;", + " }", + " static __host__ __device__ constexpr size_t bytes() {", + " return grid::grid_align_up(scratch_offset() + scratch_bytes(), alignment());", + " }", + " // Views only: no allocation, initialization, barrier or device admission.", + " // Unchecked hot path: preparation must establish alignment and capacity.", + " static __host__ __device__ tracking_cost_menu_arena carve(void *base) {", + " tracking_cost_menu_arena a;", + " a.s_frame = reinterpret_cast(base);", + " a.s_scratch = reinterpret_cast(reinterpret_cast(base) + scratch_offset());", + " return a;", + " }", + " static __host__ __device__ bool bind(void *base, size_t capacity, tracking_cost_menu_arena &out) {", + " if (!base || capacity < bytes() || reinterpret_cast(base) % alignment()) return false;", + " out = carve(base); return true;", + " }", + "};", + "// Cost menu workspace ends.", + ]) + + def gen_tracking_cost_menu(self): """The menu preset. Fixed base: the state is x = [q; qd] (NUM_POS == NUM_VEL) and the input u is NUM_VEL wide. Floating base: the state term is the tangent @@ -819,6 +859,7 @@ def gen_tracking_cost_menu(self): self.gen_add_code_line("const int GRID_PLANT_MENU_TANGENT = " + ("1" if fb else "0") + "; // 1: the preset is in the tangent layout (gradient 2*NUM_VEL, Hessian 2*NUM_VEL square, input NUM_VEL - 6)") self.gen_add_code_line("const int GRID_PLANT_MENU_NU = " + NU + "; // the input width of the preset") self.gen_add_code_line("const int GRID_PLANT_MENU_NLX = " + NLX + "; // the variable width of the linear x feature ([q[7:]; qd] on a floating base, x on a fixed base)") + _gen_tracking_cost_menu_arena(self) self.gen_add_func_doc("cost_menu_params: the per-knot buffers of tracking_cost_menu (every pointer a runtime buffer; a term whose activation is GRID_PLANT_ACTIVATION_NONE is skipped)", ["x_des / u_des / Q / R and the barrier bound-and-mu triples as tracking_cost; ee_des / W are the cost-frame CF position target and weight.", "activation (GRID_PLANT_MENU_TERMS ints) and eps (same length) select and parameterize every term, order GRID_PLANT_MENU_*.", diff --git a/grid_codegen/algorithms/_regressor.py b/grid_codegen/algorithms/_regressor.py index 93d5a03e..7294bcd4 100644 --- a/grid_codegen/algorithms/_regressor.py +++ b/grid_codegen/algorithms/_regressor.py @@ -38,8 +38,7 @@ back into hd_data->h_Y (uniform `(hd_data, model, ...)` host signature). """ -from ._frame_jacobian import _emit_world_transform_chainup -from grid_codegen.helpers._code_generation_helpers import host_q_input_transfer_lines, _gen_mjx_build_R_lines, gen_workspace_repoint_line, gen_workspace_cast_expr, host_mode_flags, host_q_qd_input_transfer_lines, mangle_host_func_defs, wrap_host_single_call_timing +from grid_codegen.helpers._code_generation_helpers import _gen_mjx_build_R_lines, gen_workspace_repoint_line, host_mode_flags, mangle_host_func_defs, wrap_host_single_call_timing from grid_codegen.helpers._code_generation_helpers import gen_host_wrapper_head # The 10 basis spatial-inertia derivatives dI/dpi_k in GRiD [angular; linear] diff --git a/grid_codegen/algorithms/_spherize.py b/grid_codegen/algorithms/_spherize.py index 18e835ae..7d5d0f63 100644 --- a/grid_codegen/algorithms/_spherize.py +++ b/grid_codegen/algorithms/_spherize.py @@ -11,7 +11,9 @@ NO external mesh assets. go2 uses boxes/cylinders/spheres. The bundled iiwa14 model also references Drake meshes for links 6 and 7. * MESHES are voxel-filled via trimesh, or use the opt-in bounded-bulge fit. - Missing/unusable collision geometry is an error, never a partial model. + Missing/unusable collision geometry is an error, never a partial model. A failed + voxel fill and a model with no spheres are errors as well while `strict` holds + (the default); `strict=False` warns and keeps the bounding-box cover instead. `resolution` is the target sphere SPACING in meters: coarser -> fewer/larger spheres (broad tier), finer -> more/smaller spheres (fine tier). Primitive covers are analytic; @@ -137,10 +139,11 @@ def _load_mesh(mesh_elem, urdf_dir, link_name): raise ValueError(f"spherize: link '{link_name}' mesh '{filename}' unusable: {exc}") from exc -def _cover_mesh(mesh_elem, urdf_dir, spacing, link_name, mesh_mode='voxel'): +def _cover_mesh(mesh_elem, urdf_dir, spacing, link_name, mesh_mode='voxel', strict=True): """Voxel-fill a collision mesh -> one sphere per occupied interior voxel (radius = half the - voxel space-diagonal). Falls back to a bounding-box cover if voxel fill - fails. Unresolvable meshes fail before a model can be written.""" + voxel space-diagonal). A failed voxel fill is an error under `strict`; otherwise it + warns and falls back to a bounding-box cover. Unresolvable meshes fail before a + model can be written.""" try: import trimesh # local import: primitive-only robots never reach this except ImportError as e: # a declared base dependency, but name it for a hand-rolled env @@ -164,6 +167,9 @@ def _cover_mesh(mesh_elem, urdf_dir, spacing, link_name, mesh_mode='voxel'): sr = pitch * math.sqrt(3.0) / 2.0 return [(float(p[0]), float(p[1]), float(p[2]), sr) for p in pts] except Exception as exc: + if strict: + raise ValueError(f"spherize: link '{link_name}' voxelization failed ({exc}); a bounding-box " + "cover would replace the mesh (strict_collision=False accepts it with a warning)") from exc ext = np.asarray(mesh.bounding_box.extents, dtype=float) ctr = np.asarray(mesh.bounding_box.centroid, dtype=float) warnings.warn(f"spherize: link '{link_name}' voxelization failed ({exc}); using a bounding-box cover") @@ -189,7 +195,7 @@ def _collision_geometry(col_elem, link_name): return prim -def _cover_collision(col_elem, spacing, mesh_spacing, urdf_dir, link_name, mesh_mode='voxel'): +def _cover_collision(col_elem, spacing, mesh_spacing, urdf_dir, link_name, mesh_mode='voxel', strict=True): """All covering spheres for ONE , in the LINK frame (its applied).""" prim = _collision_geometry(col_elem, link_name) tag = prim.tag @@ -200,7 +206,7 @@ def _cover_collision(col_elem, spacing, mesh_spacing, urdf_dir, link_name, mesh_ elif tag == "box": local = _cover_box([float(v) for v in prim.get("size").split()], spacing) elif tag == "mesh": - local = _cover_mesh(prim, urdf_dir, mesh_spacing, link_name, mesh_mode) + local = _cover_mesh(prim, urdf_dir, mesh_spacing, link_name, mesh_mode, strict) else: raise ValueError(f"spherize: link '{link_name}' unsupported collision geometry <{tag}>") R, t = _origin_of(col_elem) @@ -212,11 +218,15 @@ def _cover_collision(col_elem, spacing, mesh_spacing, urdf_dir, link_name, mesh_ # --------------------------------------------------------------------------- public API -def spherize_urdf(urdf_path, resolution, out_path=None, mesh_resolution=None, *, mesh_mode='voxel'): +def spherize_urdf(urdf_path, resolution, out_path=None, mesh_resolution=None, *, mesh_mode='voxel', + strict=True): """Rewrite `urdf_path` with covering-sphere collisions and return the output path. `resolution` = sphere spacing (m) for primitives; `mesh_resolution` = voxel pitch for meshes (defaults to `resolution`). Links, joints, visuals and inertials are preserved; only `` geometry - is replaced. Output is a valid URDF in the foam spherized interchange format.""" + is replaced. Output is a valid URDF in the foam spherized interchange format. `strict` (the + default) raises where the spherizer would otherwise warn: a failed voxel fill replaced by a + bounding-box cover, and a model with no spheres at all. Either silently changes the + clearance rows a consumer bakes, so an opt-out has to be explicit.""" if mesh_resolution is None: mesh_resolution = resolution if any(not math.isfinite(x) or x <= 0 for x in (resolution, mesh_resolution)): @@ -233,7 +243,7 @@ def spherize_urdf(urdf_path, resolution, out_path=None, mesh_resolution=None, *, cols = link.findall("collision") spheres = [] for col in cols: - spheres.extend(_cover_collision(col, resolution, mesh_resolution, urdf_dir, name, mesh_mode)) + spheres.extend(_cover_collision(col, resolution, mesh_resolution, urdf_dir, name, mesh_mode, strict)) if any(not np.isfinite(s).all() or s[3] <= 0 for s in spheres): raise ValueError(f"spherize: link '{name}' has invalid collision spheres") for col in cols: @@ -253,6 +263,9 @@ def spherize_urdf(urdf_path, resolution, out_path=None, mesh_resolution=None, *, os.close(fd) tree.write(out_path, encoding="unicode", xml_declaration=False) if total == 0: - warnings.warn(f"spherize: '{urdf_path}' produced 0 collision spheres (no supported " - f"collision geometry resolved).") + message = (f"spherize: '{urdf_path}' produced 0 collision spheres (no supported " + f"collision geometry resolved).") + if strict: + raise ValueError(message + " strict_collision=False accepts an empty model with a warning.") + warnings.warn(message) return out_path diff --git a/grid_codegen/cli.py b/grid_codegen/cli.py index 08c7361e..f6075ef1 100644 --- a/grid_codegen/cli.py +++ b/grid_codegen/cli.py @@ -66,6 +66,9 @@ def parseInputs(NO_ARG_OPTION=False): "Comma-separate multiple densities for a broad->fine cascade, e.g. " "'0.10,0.05' (config_free uses coarsest to reject + finest to confirm). " "Default 0.05") + parser.add_argument("--no-strict-collision", dest="strict_collision", default=True, action="store_false", + help="Accept the spherizer's warnings (a voxel fill replaced by a bounding-box " + "cover, a model with no spheres) instead of stopping; the default stops") parser.add_argument("--collision-native", default=False, action="store_true", help="Use the URDF's own collision primitives as NATIVE capsule rows " "(sphere -> degenerate row; cylinder -> containing capsule, conservative; " @@ -221,12 +224,14 @@ def _collision_spec(args, robot): collision_spec = {key: tier[key] for key in ('anchor', 'offset', 'radius', 'self_cc_ranges')} elif args.collision_native: from grid_codegen.algorithms._collision import native_collision_spec_from_urdf - collision_spec = native_collision_spec_from_urdf(robot, args.urdf_path, args.collision_res[-1]) + collision_spec = native_collision_spec_from_urdf(robot, args.urdf_path, args.collision_res[-1], + strict=args.strict_collision) print(" collision rows = " + ", ".join( "%s:%d" % (t["name"], len(t["anchor"])) for t in collision_spec["tiers"])) elif args.collision: from grid_codegen.algorithms._collision import multi_tier_collision_spec_from_urdf - collision_spec = multi_tier_collision_spec_from_urdf(robot, args.urdf_path, args.collision_res) + collision_spec = multi_tier_collision_spec_from_urdf(robot, args.urdf_path, args.collision_res, + strict=args.strict_collision) if "tiers" in collision_spec: print(" collision spheres = " + ", ".join( "%s:%d" % (t["name"], len(t["anchor"])) for t in collision_spec["tiers"])) diff --git a/grid_codegen/collision/grid_collision_geometry.cuh b/grid_codegen/collision/grid_collision_geometry.cuh index a431f13f..f3bac988 100644 --- a/grid_codegen/collision/grid_collision_geometry.cuh +++ b/grid_codegen/collision/grid_collision_geometry.cuh @@ -15,6 +15,7 @@ #include #include // sqrtf/sqrt for the differentiable (true-distance + normal) path #include +#include #include // the host-side environment builders throw on bad geometry #include #include @@ -22,6 +23,32 @@ namespace grid_collision { +// Effective feasibility clearances, in metres. A scalar applies to both sides; +// self clearance inflates only ONE member of a pair. Device callers must supply +// finite, nonnegative values. The host factory below validates user input. +template +struct CollisionMargins { + using type = CollisionMargins; // non-deduced parameter preserves scalar calls + using value_type = T; + T environment_margin, self_margin; + __host__ __device__ constexpr CollisionMargins(T margin = T(0)) + : environment_margin(margin), self_margin(margin) {} + __host__ __device__ constexpr CollisionMargins(T environment, T self) + : environment_margin(environment), self_margin(self) {} +}; + +template +inline CollisionMargins make_collision_margins( + T margin = T(0), + std::optional::value_type> environment_margin = std::nullopt, + std::optional::value_type> self_margin = std::nullopt) { + const auto valid = [](T value) { return std::isfinite(value) && value >= T(0); }; + if (!valid(margin) || (environment_margin && !valid(*environment_margin)) || + (self_margin && !valid(*self_margin))) + throw std::invalid_argument("collision margins must be finite and nonnegative"); + return {environment_margin.value_or(margin), self_margin.value_or(margin)}; +} + // ------------------------------------------------------------------ shapes template struct Sphere { T x, y, z, r; }; @@ -512,15 +539,15 @@ __host__ __device__ bool grid_cc_config_free( const int *broad_sphere_link, const T *s_fine_pos, const T *s_fine_r, const int *fine_self_ranges, int n_fine_ranges, int n_fine, const int *fine_sphere_link, int *dbg_fine_rechecked = nullptr, - T margin = static_cast(0), bool check_self = true) { - // margin: a hit iff a raw clearance is below it (robot spheres tested at r + margin on both tiers, - // which keeps the covering argument); check_self = false skips both self-range scans (G7/G10). + typename CollisionMargins::type margins = {}, bool check_self = true) { + // Each side uses its own margin on BOTH tiers, preserving the covering argument. + // check_self = false skips both self-range scans, irrespective of self_margin. unsigned long long hit_mask = 0ull; // Broad self-collision: full scan (NO early-out — the mask needs every hit), flag BOTH endpoints' // links of each hit range (a self-pair's two spheres may sit on different links). if (check_self) for (int k = 0; k < n_broad_ranges; ++k) { int i = broad_self_ranges[3*k], j0 = broad_self_ranges[3*k+1], j1 = broad_self_ranges[3*k+2]; - T ix = s_broad_pos[3*i], iy = s_broad_pos[3*i+1], iz = s_broad_pos[3*i+2], ir = s_broad_r[i] + margin; + T ix = s_broad_pos[3*i], iy = s_broad_pos[3*i+1], iz = s_broad_pos[3*i+2], ir = s_broad_r[i] + margins.self_margin; for (int j = j0; j <= j1; ++j) if (grid_cc_sphere_sphere(ix, iy, iz, ir, s_broad_pos[3*j], s_broad_pos[3*j+1], s_broad_pos[3*j+2], s_broad_r[j]) < static_cast(0)) { @@ -530,7 +557,7 @@ __host__ __device__ bool grid_cc_config_free( } // Broad vs environment: full scan, flag each hit sphere's link. for (int i = 0; i < n_broad; ++i) - if (grid_cc_sphere_in_environment(env, s_broad_pos[3*i], s_broad_pos[3*i+1], s_broad_pos[3*i+2], s_broad_r[i] + margin)) + if (grid_cc_sphere_in_environment(env, s_broad_pos[3*i], s_broad_pos[3*i+1], s_broad_pos[3*i+2], s_broad_r[i] + margins.environment_margin)) hit_mask |= (1ull << broad_sphere_link[i]); // Narrowing measure (non-vacuous-gate hook): fine spheres surviving the mask. Written before the @@ -549,7 +576,7 @@ __host__ __device__ bool grid_cc_config_free( if (check_self) for (int k = 0; k < n_fine_ranges; ++k) { int i = fine_self_ranges[3*k], j0 = fine_self_ranges[3*k+1], j1 = fine_self_ranges[3*k+2]; if (((hit_mask >> fine_sphere_link[i]) & 1ull) == 0ull) continue; - T ix = s_fine_pos[3*i], iy = s_fine_pos[3*i+1], iz = s_fine_pos[3*i+2], ir = s_fine_r[i] + margin; + T ix = s_fine_pos[3*i], iy = s_fine_pos[3*i+1], iz = s_fine_pos[3*i+2], ir = s_fine_r[i] + margins.self_margin; for (int j = j0; j <= j1; ++j) if (grid_cc_sphere_sphere(ix, iy, iz, ir, s_fine_pos[3*j], s_fine_pos[3*j+1], s_fine_pos[3*j+2], s_fine_r[j]) < static_cast(0)) return false; @@ -557,7 +584,7 @@ __host__ __device__ bool grid_cc_config_free( // Fine vs environment: skip spheres on unflagged links. for (int i = 0; i < n_fine; ++i) { if (((hit_mask >> fine_sphere_link[i]) & 1ull) == 0ull) continue; - if (grid_cc_sphere_in_environment(env, s_fine_pos[3*i], s_fine_pos[3*i+1], s_fine_pos[3*i+2], s_fine_r[i] + margin)) + if (grid_cc_sphere_in_environment(env, s_fine_pos[3*i], s_fine_pos[3*i+1], s_fine_pos[3*i+2], s_fine_r[i] + margins.environment_margin)) return false; } return true; @@ -577,11 +604,11 @@ __host__ __device__ bool grid_cc_config_free_capsule( const int *broad_sphere_link, const T *s_fine_seg, const T *s_fine_r, const int *fine_self_ranges, int n_fine_ranges, int n_fine, const int *fine_row_link, int *dbg_fine_rechecked = nullptr, - T margin = static_cast(0), bool check_self = true) { + typename CollisionMargins::type margins = {}, bool check_self = true) { unsigned long long hit_mask = 0ull; if (check_self) for (int k = 0; k < n_broad_ranges; ++k) { int i = broad_self_ranges[3*k], j0 = broad_self_ranges[3*k+1], j1 = broad_self_ranges[3*k+2]; - T ix = s_broad_pos[3*i], iy = s_broad_pos[3*i+1], iz = s_broad_pos[3*i+2], ir = s_broad_r[i] + margin; + T ix = s_broad_pos[3*i], iy = s_broad_pos[3*i+1], iz = s_broad_pos[3*i+2], ir = s_broad_r[i] + margins.self_margin; for (int j = j0; j <= j1; ++j) if (grid_cc_sphere_sphere(ix, iy, iz, ir, s_broad_pos[3*j], s_broad_pos[3*j+1], s_broad_pos[3*j+2], s_broad_r[j]) < static_cast(0)) { @@ -590,7 +617,7 @@ __host__ __device__ bool grid_cc_config_free_capsule( } } for (int i = 0; i < n_broad; ++i) - if (grid_cc_sphere_in_environment(env, s_broad_pos[3*i], s_broad_pos[3*i+1], s_broad_pos[3*i+2], s_broad_r[i] + margin)) + if (grid_cc_sphere_in_environment(env, s_broad_pos[3*i], s_broad_pos[3*i+1], s_broad_pos[3*i+2], s_broad_r[i] + margins.environment_margin)) hit_mask |= (1ull << broad_sphere_link[i]); if (dbg_fine_rechecked != nullptr) { @@ -604,14 +631,14 @@ __host__ __device__ bool grid_cc_config_free_capsule( if (check_self) for (int k = 0; k < n_fine_ranges; ++k) { int i = fine_self_ranges[3*k], j0 = fine_self_ranges[3*k+1], j1 = fine_self_ranges[3*k+2]; if (((hit_mask >> fine_row_link[i]) & 1ull) == 0ull) continue; - Capsule ci = grid_cc_row_capsule(s_fine_seg, s_fine_r, i, margin); + Capsule ci = grid_cc_row_capsule(s_fine_seg, s_fine_r, i, margins.self_margin); for (int j = j0; j <= j1; ++j) if (grid_cc_capsule_capsule(ci, grid_cc_row_capsule(s_fine_seg, s_fine_r, j)) < static_cast(0)) return false; } for (int i = 0; i < n_fine; ++i) { if (((hit_mask >> fine_row_link[i]) & 1ull) == 0ull) continue; - if (grid_cc_capsule_in_environment(env, grid_cc_row_capsule(s_fine_seg, s_fine_r, i, margin))) + if (grid_cc_capsule_in_environment(env, grid_cc_row_capsule(s_fine_seg, s_fine_r, i, margins.environment_margin))) return false; } return true; diff --git a/grid_codegen/helpers/_code_generation_helpers.py b/grid_codegen/helpers/_code_generation_helpers.py index 11b6642a..8a6fceb8 100644 --- a/grid_codegen/helpers/_code_generation_helpers.py +++ b/grid_codegen/helpers/_code_generation_helpers.py @@ -1316,7 +1316,7 @@ def gen_arena_carve_struct(self, struct_name, layout, sizer_expr, expected_t_count = None, ximat_name = "s_XImats", temp_name = "s_temp", topology_name = "s_topology_helpers", doc = None, - helper_ns = ""): + helper_ns = "", host_device = False, checked_bind = False): """Emit a NAMESPACE-scope carve struct mirroring one kernel's shared-arena layout (TIER_SHARED shape only, no tier branch), so an external caller (e.g. GATO's BSQP) can allocate bytes and carve the exact @@ -1353,32 +1353,53 @@ def gen_arena_carve_struct(self, struct_name, layout, sizer_expr, self.gen_add_code_line("T *" + ximat_name + ";") self.gen_add_code_line("T *" + temp_name + ";") self.gen_add_code_line("int *" + topology_name + ";") - self.gen_add_code_line("static __device__ " + struct_name + " carve(void *base) {", True) + if checked_bind: + self.gen_add_code_line("// Checked arena interface begins.") + for name, count in layout.t_buffers: + self.gen_add_code_line(f"static constexpr size_t {name}_count = {count};") + self.gen_add_code_line(f"static constexpr size_t {temp_name}_count = {layout.temp_mem_size};") + self.gen_add_code_line(f"static constexpr size_t {ximat_name}_count = {layout.ximat_size};") + self.gen_add_code_line(f"static constexpr size_t {topology_name}_count = {layout.topology_count};") + self.gen_add_code_line("static __host__ __device__ constexpr size_t bytes() { return " + sizer_expr + "; }") + self.gen_add_code_line("static __host__ __device__ constexpr size_t alignment() { return 16; }") + qual = "__host__ __device__" if host_device else "__device__" + self.gen_add_code_line("static " + qual + " bool bind(void *base, size_t capacity, " + struct_name + " &out) {", True) + self.gen_add_code_line("if (!base || capacity < bytes() || reinterpret_cast(base) % alignment()) return false;") + self.gen_add_code_line("out = carve(base); return true;") + self.gen_add_end_control_flow() + self.gen_add_code_line("// Checked arena interface ends.") + qualifier = "__host__ __device__" if host_device else "__device__" + def pointer(kind): + if host_device: + return "reinterpret_cast<" + kind + " *>(s_arena + s_arena_offset)" + return helper_ns + "grid_arena_ptr<" + kind + ">(s_arena, s_arena_offset)" + self.gen_add_code_line("static " + qualifier + " " + struct_name + " carve(void *base) {", True) self.gen_add_code_line("unsigned char *s_arena = reinterpret_cast(base);") self.gen_add_code_line("size_t s_arena_offset = 0;") self.gen_add_code_line(struct_name + " a;") for name, count in layout.t_buffers: self.gen_add_code_line("s_arena_offset = " + helper_ns + "grid_align_up(s_arena_offset, alignof(T));") - self.gen_add_code_line("a." + name + " = " + helper_ns + "grid_arena_ptr(s_arena, s_arena_offset);") + self.gen_add_code_line("a." + name + " = " + pointer("T") + ";") self.gen_add_code_line("s_arena_offset += sizeof(T) * static_cast(" + str(count) + ");") if layout.ximat_size: self.gen_add_code_line("s_arena_offset = " + helper_ns + "grid_align_up(s_arena_offset, alignof(T));") - self.gen_add_code_line("a." + ximat_name + " = " + helper_ns + "grid_arena_ptr(s_arena, s_arena_offset);") + self.gen_add_code_line("a." + ximat_name + " = " + pointer("T") + ";") self.gen_add_code_line("s_arena_offset += sizeof(T) * static_cast(" + str(int(layout.ximat_size)) + ");") if int(layout.temp_mem_size or 0) != 0: self.gen_add_code_line("s_arena_offset = " + helper_ns + "grid_align_up(s_arena_offset, alignof(T));") - self.gen_add_code_line("a." + temp_name + " = " + helper_ns + "grid_arena_ptr(s_arena, s_arena_offset);") + self.gen_add_code_line("a." + temp_name + " = " + pointer("T") + ";") self.gen_add_code_line("s_arena_offset += sizeof(T) * static_cast(" + str(int(layout.temp_mem_size)) + ");") else: self.gen_add_code_line("a." + temp_name + " = nullptr;") if layout.topology_count > 0: self.gen_add_code_line("s_arena_offset = " + helper_ns + "grid_align_up(s_arena_offset, alignof(int));") - self.gen_add_code_line("a." + topology_name + " = " + helper_ns + "grid_arena_ptr(s_arena, s_arena_offset);") + self.gen_add_code_line("a." + topology_name + " = " + pointer("int") + ";") self.gen_add_code_line("s_arena_offset += sizeof(int) * static_cast(" + str(layout.topology_count) + ");") else: self.gen_add_code_line("a." + topology_name + " = nullptr;") self.gen_add_code_line("#ifdef GRID_CUDA_DEBUG_LAYOUT") - self.gen_add_code_line("assert(s_arena_offset <= " + sizer_expr + ");") + # Parenthesize template commas so the preprocessor sees one assert argument. + self.gen_add_code_line("assert((s_arena_offset <= " + sizer_expr + "));") self.gen_add_code_line("#endif") self.gen_add_code_line("(void)s_arena_offset;") self.gen_add_code_line("return a;") diff --git a/grid_codegen/helpers/_lin_alg_helpers.py b/grid_codegen/helpers/_lin_alg_helpers.py index 13ac0bb8..5db52903 100644 --- a/grid_codegen/helpers/_lin_alg_helpers.py +++ b/grid_codegen/helpers/_lin_alg_helpers.py @@ -1,4 +1,3 @@ -from pathlib import Path import re import subprocess diff --git a/grid_codegen/helpers/_topology_helpers.py b/grid_codegen/helpers/_topology_helpers.py index 10303c96..81911b56 100644 --- a/grid_codegen/helpers/_topology_helpers.py +++ b/grid_codegen/helpers/_topology_helpers.py @@ -40,6 +40,8 @@ def _global_hom_derivative_matrices_by_q(self): mats = [sp.zeros(4, 4) for _ in range(n)] owners = [None for _ in range(n)] for jid in range(self.robot.get_num_joints()): + if self._alpha_for_jid(jid) == 0.0: + continue # A constant body's dense placeholder owns no q slot. qinds = _qinds_to_list(self, self.robot.get_joint_index_q(jid)) for local_ind, qind in enumerate(qinds): mats[qind] = self.robot.get_dXmat_hom_local_by_id(jid, local_ind) @@ -76,6 +78,8 @@ def _global_hom_second_derivative_matrices(self): mats = [sp.zeros(4, 4) for _ in range(n*n)] owners = [None for _ in range(n*n)] for jid in range(self.robot.get_num_joints()): + if self._alpha_for_jid(jid) == 0.0: + continue # Do not overwrite another joint's quaternion/q block. qinds = _qinds_to_list(self, self.robot.get_joint_index_q(jid)) for local_i, qind_i in enumerate(qinds): for local_j, qind_j in enumerate(qinds): @@ -721,7 +725,7 @@ def gen_XImats_helpers_temp_shared_memory_code(self, temp_mem_size = 0, include_ # inside [0, inner+36*NB) since inner >= 2*num_pos. layout = self._resolve_arena_layout( extra_t_buffers, temp_mem_size, - include_topology_helpers = (not self.robot.is_serial_chain() or not self.robot.are_Ss_identical(list(range(n)))), + include_topology_helpers = self.gen_topology_helpers_size() > 0, ximat_size = XI_size, apply_runtime_transform_band = (getattr(self, "runtime_transform", False) and not include_homogenous_transforms)) self.gen_declare_shared_arena(layout.t_buffers, layout.temp_mem_size, @@ -763,9 +767,14 @@ def _emit_mimic_q_fold(self): if getattr(j, "is_mimic", False): mult = j.get_mimic_multiplier() off = j.get_mimic_offset() - expr = "static_cast(" + repr(mult) + ") * s_q[" + str(qslot) + "]" - if off != 0.0: - expr += " + static_cast(" + repr(off) + ")" + if j.get_mimic_target_id() == -1 or mult == 0.0: + # Constant mimics have no input coordinate, including when + # nq==0. Zero times an invalid/poisoned read is not safe. + expr = "static_cast(" + repr(off) + ")" + else: + expr = "static_cast(" + repr(mult) + ") * s_q[" + str(qslot) + "]" + if off != 0.0: + expr += " + static_cast(" + repr(off) + ")" self.gen_add_code_line("s_temp[" + str(ind) + "] = " + expr + ";") else: self.gen_add_code_line("s_temp[" + str(ind) + "] = s_q[" + str(qslot) + "];") @@ -977,7 +986,7 @@ def gen_load_update_XImats_helpers(self, include_base_inertia = False, include_h _emit_runtime_inertia_rebuild(self, n) if runtime_transform: _emit_runtime_transform_rebuild(self, n) - if not self.robot.is_serial_chain() or not self.robot.are_Ss_identical(list(range(n))): + if self.gen_topology_helpers_size() > 0: self.gen_add_parallel_loop("ind",str(self.gen_topology_helpers_size())) self.gen_add_code_line("s_topology_helpers[ind] = d_robotModel->d_topology_helpers[ind];") self.gen_add_end_control_flow() @@ -996,27 +1005,23 @@ def gen_load_update_XImats_helpers(self, include_base_inertia = False, include_h self.gen_add_sync() # else just load in XI from global to shared efficiently else: + # Block-wide typed element copy, the same loader as the trig branch. + self.gen_add_parallel_loop("ind", str(XI_size)) if runtime_inertia: - # RUNTIME_INERTIA: copy only the X-region [0,36*n) and any Xhom tail - # [36*2*n, XI_size); rebuild the I-region [36*n,36*2*n) from the - # param table. Default: one contiguous memcpy_async of the whole XI. - self.gen_add_code_line("if constexpr (RUNTIME_INERTIA) {", True) - self.gen_add_code_line("cgrps::memcpy_async(tgrp,s_XImats,d_robotModel->d_XImats," + str(i_region_lo) + ");") - if XI_size > i_region_hi: - self.gen_add_code_line("cgrps::memcpy_async(tgrp,s_XImats+" + str(i_region_hi) + ",d_robotModel->d_XImats+" + str(i_region_hi) + "," + str(XI_size - i_region_hi) + ");") - self.gen_add_end_control_flow() - self.gen_add_code_line("else {", True) - self.gen_add_code_line("cgrps::memcpy_async(tgrp,s_XImats,d_robotModel->d_XImats," + str(XI_size) + ");") + self.gen_add_code_line("if constexpr (RUNTIME_INERTIA) { if (ind >= " + str(i_region_lo) + " && ind < " + str(i_region_hi) + ") { continue; } }") + self.gen_add_code_line("s_XImats[ind] = d_robotModel->d_XImats[ind];") + self.gen_add_end_control_flow() + if self.gen_topology_helpers_size() > 0: + self.gen_add_parallel_loop("ind", str(self.gen_topology_helpers_size())) + self.gen_add_code_line("s_topology_helpers[ind] = d_robotModel->d_topology_helpers[ind];") self.gen_add_end_control_flow() - else: - self.gen_add_code_line("cgrps::memcpy_async(tgrp,s_XImats,d_robotModel->d_XImats," + str(XI_size) + ");") - if not self.robot.is_serial_chain() or not self.robot.are_Ss_identical(list(range(n))): - self.gen_add_code_line("cgrps::memcpy_async(tgrp,s_topology_helpers,d_robotModel->d_topology_helpers," + str(self.gen_topology_helpers_size()) + "*sizeof(int));") - self.gen_add_code_line("cgrps::wait(tgrp);") + self.gen_add_sync() if runtime_inertia: _emit_runtime_inertia_rebuild(self, n) if runtime_transform: _emit_runtime_transform_rebuild(self, n) + if self.robot_has_mimic_joints(): + _emit_mimic_q_fold(self) # loop through Xmats and update all non-constant values serially # runtime_transform: source the X cells from the symbolic-Xfixed transforms # (dense rpy pattern, origin carried as xf_* symbols) so the origin literals @@ -1253,7 +1258,9 @@ def gen_load_update_XmatsHom_helpers(self, include_base_inertia = False, include Xmats_hom = self.robot.get_Xmats_hom_ordered_by_id(include_fixed_joints = self.include_fixed_kinematic_targets) use_trig = False for mat in Xmats_hom: - if len(mat.atoms(sp.sin, sp.cos)) > 0: + # Movable transforms are symbolic; fixed tool transforms can be NumPy + # arrays. Normalize for the scan without changing their stored values. + if len(sp.Matrix(mat).atoms(sp.sin, sp.cos)) > 0: use_trig = True break # if trig is needed, compute sin/cos while loading XImats from global to shared @@ -1269,7 +1276,7 @@ def gen_load_update_XmatsHom_helpers(self, include_base_inertia = False, include self.gen_add_parallel_loop("ind",str(d2Xhom_size)) self.gen_add_code_line("s_d2XmatsHom[ind] = d_robotModel->d_XImats[ind+" + str(baseXI_size + Xhom_size + dXhom_size) + "];") self.gen_add_end_control_flow() - if not self.robot.is_serial_chain() or not self.robot.are_Ss_identical(list(range(n))): + if self.gen_topology_helpers_size() > 0: self.gen_add_parallel_loop("ind",str(self.gen_topology_helpers_size())) self.gen_add_code_line("s_topology_helpers[ind] = d_robotModel->d_topology_helpers[ind];") self.gen_add_end_control_flow() @@ -1299,11 +1306,13 @@ def gen_load_update_XmatsHom_helpers(self, include_base_inertia = False, include self.gen_add_parallel_loop("ind",str(d2Xhom_size)) self.gen_add_code_line("s_d2XmatsHom[ind] = d_robotModel->d_XImats[ind+" + str(baseXI_size + Xhom_size + dXhom_size) + "];") self.gen_add_end_control_flow() - if not self.robot.is_serial_chain() or not self.robot.are_Ss_identical(list(range(n))): + if self.gen_topology_helpers_size() > 0: self.gen_add_parallel_loop("ind",str(self.gen_topology_helpers_size())) self.gen_add_code_line("s_topology_helpers[ind] = d_robotModel->d_topology_helpers[ind];") self.gen_add_end_control_flow() self.gen_add_sync() + if self.robot_has_mimic_joints(): + _emit_mimic_q_fold(self) # loop through Xmats and update all non-constant values serially def replace_hom_config_symbols(str_val, ind): return _config_symbol_subst(self, str_val, ind, spherical = self.robot.joint_is_spherical(ind), ccode = True) @@ -1393,11 +1402,12 @@ def gen_topology_helpers_size(self): size = 0 if not self.robot.is_serial_chain(): size += 5*n + 1 - # Preserve the legacy are_Ss_identical query set (get_num_pos()) so the - # boolean — and thus whether the S_inds section exists at all — is unchanged - # for non-mimic robots (Gate A byte-identical). Only the allocated WIDTH (n) - # grows to cover the NJ-wide build for mimic models. - if not self.robot.are_Ss_identical(list(range(self.robot.get_num_pos()))): + # Subspaces belong to joints, not configuration coordinates: a spherical + # joint contributes four q entries but only one subspace. Match the builder's + # body set, including mimic/constant bodies. The allocation width above + # stays n. + query_count = self.robot.get_num_joints() + if not self.robot.are_Ss_identical(list(range(query_count))): size += n return size @@ -1510,19 +1520,19 @@ def gen_topology_helpers_pointers_for_cpp(self, inds = None, updated_var_names = var_names[key] = value n = self.robot.get_num_vel() NJ = self.robot.get_num_joints() - # FIXED-BASE MIMIC: the topology-helper sections are built NJ-wide, but the + # Fixed-base mimic/spherical: helper sections are built NJ-wide, but the # OFFSET=True section strides below use `n` (= get_num_vel()). For a mimic - # model NJ > nv, so the device would read parent/S-index at the wrong stride + # model NJ differs from nv, so the device would read parent/S-index at the wrong stride # at multi-joint BFS levels. Use NJ as the stride here (the algorithms also - # iterate over NJ bodies for mimic-fixed). Floating-base keeps the legacy - # nv stride (its sections + reads are already NJ-consistent via the - # floating-specific path, and a swap breaks the byte-identical Gate A). - if self.robot_has_mimic_joints() and not self.robot.floating_base: + # iterate over NJ bodies for mimic-fixed). Floating-base uses the nv + # stride; its sections and reads are NJ-consistent through the + # floating-specific path. + if (self.robot_has_mimic_joints() or self.robot.robot_has_spherical()) and not self.robot.floating_base: n = NJ if inds == None: - inds = list(range(n)) + inds = list(range(NJ)) IDENTICAL_S_FLAG_INDS = self.robot.are_Ss_identical(inds) - IDENTICAL_S_FLAG_GLOBAL = self.robot.are_Ss_identical(list(range(n))) + IDENTICAL_S_FLAG_GLOBAL = self.robot.are_Ss_identical(list(range(NJ))) # check for one ind if len(inds) == 1: diff --git a/grid_codegen/kernel_attrs.py b/grid_codegen/kernel_attrs.py index fe8a613f..307f6f9f 100644 --- a/grid_codegen/kernel_attrs.py +++ b/grid_codegen/kernel_attrs.py @@ -3,7 +3,7 @@ attr fns, init_grid/close_grid/streams). H4 move from GRiDCodeGenerator.py (2026-08-27, verbatim). The names are class-bound on GRiDCodeGenerator (tests read GRiDCodeGenerator.KERNEL_ATTR_MANIFEST etc.).""" -from .algo_registry import ALGO_DESCRIPTORS, descriptor_for +from .algo_registry import descriptor_for from .helpers._gpu_err import legacy_wrapper_lines from .launch_config import baked_launch_cfg diff --git a/grid_codegen/launch_config.py b/grid_codegen/launch_config.py index e9cafbd4..2b34463b 100644 --- a/grid_codegen/launch_config.py +++ b/grid_codegen/launch_config.py @@ -4,9 +4,8 @@ the external importers (bindings, autotune_ffi, parity goldens).""" import os import json -import warnings -from .algo_registry import ALGO_DESCRIPTORS, build_launch_config_algo_to_symbol, launch_config_descriptors +from .algo_registry import build_launch_config_algo_to_symbol, launch_config_descriptors # ─── A1b launch-config bake (single source of truth) ───────────────────────── # The autotuned per-(robot,base,algo) {tier,threads} live in diff --git a/grid_codegen/model_descriptor.py b/grid_codegen/model_descriptor.py new file mode 100644 index 00000000..33f775fb --- /dev/null +++ b/grid_codegen/model_descriptor.py @@ -0,0 +1,260 @@ +"""Normalized coordinate/frame metadata, independent of CUDA and artifact identity. + +The schema describes geometry and ownership, not inertia, actuation or supported +algorithms. Canonical identity is shared by JSON and generated C++ consumers. +Only extraction imports numpy; validation/prepared loading needs no parser. +""" +import hashlib +import json +import math +import struct + +SCHEMA_VERSION = 1 +CONVENTION = "grid-xyzw-local-v1" +_WIDTHS = {"revolute": (1, 1), "continuous": (1, 1), "prismatic": (1, 1), + "helical": (1, 1), "screw": (1, 1), "spherical": (4, 3), "floating": (7, 6)} +_TOP = {"schema_version", "model_name", "descriptor_id", "nq", "nv", "nb", "nj", + "floating_base", "runtime_transform", "coordinate_convention", "joints", + "bodies", "configuration_layout", "frames"} +_JOINT = {"id", "name", "kind", "parent_body", "child_body", "local_nq", "local_nv", + "q_start", "v_start", "owns_coordinates", "axis", "pitch", + "T_parent_joint_zero", "mimic", "limits"} +_FRAME = {"id", "name", "kind", "attached_body", "joint_id", "T_body_frame"} + + +def canonical_bytes(value): + """Schema-1 typed canonical encoding; preserve names, normalize float -0.""" + def encode(item): + if item is None: + return ["null"] + if type(item) is bool: + return ["bool", item] + if type(item) is int: + return ["int", str(item)] + if type(item) is float: + if not math.isfinite(item): + raise ValueError("descriptor numbers must be finite") + return ["f64", struct.pack('>d', 0.0 if item == 0 else item).hex()] + if type(item) is str: + item.encode('utf-8', errors='strict') + return ["str", item] + if type(item) is list: + return ["list", [encode(v) for v in item]] + if type(item) is dict and all(type(k) is str for k in item): + for k in item: + k.encode('utf-8', errors='strict') + return ["map", [[k, encode(item[k])] for k in sorted(item)]] + raise ValueError(f"unsupported descriptor value type: {type(item).__name__}") + return json.dumps(encode(value), ensure_ascii=False, allow_nan=False, + separators=(',', ':')).encode('utf-8') + + +def descriptor_id(descriptor): + return hashlib.sha256(canonical_bytes({k: v for k, v in descriptor.items() + if k != 'descriptor_id'})).hexdigest() + + +def _check(condition, message): + if not condition: + raise ValueError("invalid model descriptor: " + message) + + +def _keys(value, fields, name): + _check(type(value) is dict and set(value) == fields, name + " fields") + + +def _integer(value, minimum=0): + return type(value) is int and value >= minimum + + +def _number(value): + return type(value) is float and math.isfinite(value) + + +def _transform(value): + _check(type(value) is list and len(value) == 4 and + all(type(row) is list and len(row) == 4 and all(_number(x) for x in row) + for row in value), "transform must be finite binary64 4x4") + _check(value[3] == [0., 0., 0., 1.], "transform homogeneous last row") + r = [row[:3] for row in value[:3]] + _check(all(abs(sum(r[k][i]*r[k][j] for k in range(3)) - (i == j)) < 1e-10 + for i in range(3) for j in range(3)), "transform rotation orthogonality") + det = (r[0][0]*(r[1][1]*r[2][2]-r[1][2]*r[2][1]) + - r[0][1]*(r[1][0]*r[2][2]-r[1][2]*r[2][0]) + + r[0][2]*(r[1][0]*r[2][1]-r[1][1]*r[2][0])) + _check(abs(det-1) < 1e-10, "transform proper rotation") + + +def validate_descriptor(d): + """Reject inconsistent geometry/ownership even if its hash was recomputed.""" + _keys(d, _TOP, "top-level") + _check(type(d['schema_version']) is int and d['schema_version'] == SCHEMA_VERSION, + "unsupported schema version") + _check(d['coordinate_convention'] == CONVENTION, "coordinate convention") + _check(type(d['model_name']) is str, "model name") + _check(type(d['floating_base']) is bool and type(d['runtime_transform']) is bool, + "model flags") + _check(all(_integer(d[k]) for k in ('nq', 'nv', 'nb', 'nj')), "counts") + for key in ('joints', 'bodies', 'frames', 'configuration_layout'): + _check(type(d[key]) is list, key + " must be a list") + joints = d['joints'] + _check(len(joints) == d['nj'] == d['nb'] == len(d['bodies']), "body/joint counts") + _check(not d['floating_base'] or bool(joints), "missing floating root") + # Validate records before any forward mimic-leader lookup. + for j in joints: + _keys(j, _JOINT, "joint") + blocks, qend, vend, names = [], 0, 0, set() + for i, j in enumerate(joints): + _check(type(j['id']) is int and j['id'] == i and type(j['child_body']) is int + and j['child_body'] == i, "joint/body ordering") + _check(_integer(j['parent_body'], -1) and j['parent_body'] < i, "joint parent") + _check(type(j['name']) is str and j['name'] not in names, "joint name") + names.add(j['name']) + kind = j['kind'] + _check(type(kind) is str and kind in _WIDTHS, "joint kind") + nq, nv = _WIDTHS[kind] + _check(type(j['local_nq']) is int and type(j['local_nv']) is int + and (j['local_nq'], j['local_nv']) == (nq, nv), "local widths") + _check((kind == 'floating') == (i == 0 and d['floating_base']), "floating root") + axis = j['axis'] + if nq > 1: + _check(axis is None, "multi-coordinate axis") + else: + _check(type(axis) is list and len(axis) == 3 and all(_number(x) for x in axis), "axis") + _check(abs(sum(x*x for x in axis)-1.) < 1e-10, "unit axis") + _check(_number(j['pitch']) if kind in ('helical', 'screw') else j['pitch'] is None, + "pitch") + _transform(j['T_parent_joint_zero']) + m = j['mimic'] + _check(type(j['owns_coordinates']) is bool and j['owns_coordinates'] == (m is None), + "coordinate ownership flag") + if m is None: + _check(type(j['q_start']) is int and type(j['v_start']) is int and + (j['q_start'], j['v_start']) == (qend, vend), "configuration layout") + blocks.append(dict(kind=kind if nq > 1 else 'euclidean', q_start=qend, + v_start=vend, nq=nq, nv=nv)) + qend, vend = qend+nq, vend+nv + else: + _check(nq == nv == 1 and j['q_start'] is j['v_start'] is None, "dependent slices") + _keys(m, {'leader_joint', 'multiplier', 'offset'}, "mimic") + _check(_number(m['multiplier']) and _number(m['offset']), "mimic coefficients") + leader = m['leader_joint'] + if leader is None: + _check(m['multiplier'] == 0., "constant mimic multiplier") + else: + _check(_integer(leader) and leader < len(joints), "mimic leader") + owner = joints[leader] + _check(owner.get('mimic') is None and owner.get('local_nq') == 1, + "mimic leader must own one coordinate") + _keys(j['limits'], {'position', 'velocity', 'effort'}, "limits") + pos = j['limits']['position'] + _check(pos is None or (nq == 1 and type(pos) is list and len(pos) == 2 + and all(x is None or _number(x) for x in pos)), "position limits") + if pos is not None and all(x is not None for x in pos): + _check(pos[0] <= pos[1], "ordered position limits") + _check(kind != 'continuous' or pos is None, "continuous position limits") + for key in ('velocity', 'effort'): + _check(j['limits'][key] is None or _number(j['limits'][key]), "limit magnitude") + b = d['bodies'][i] + _keys(b, {'id', 'name', 'parent_body', 'joint_id'}, "body") + _check(all(type(b[k]) is int for k in ('id', 'parent_body', 'joint_id')) and + type(b['name']) is str and b == dict(id=i, name=b['name'], + parent_body=j['parent_body'], joint_id=i), "body mapping") + _check((qend, vend) == (d['nq'], d['nv']), "coordinate totals") + # Type-aware comparison rejects bool/int/float aliases in redundant views. + _check(canonical_bytes(d['configuration_layout']) == canonical_bytes(blocks), + "configuration layout projection") + frame_names = set() + _check(len(d['frames']) >= 1+len(joints), "missing root/joint frames") + identity = [[float(i == j) for j in range(4)] for i in range(4)] + for i, f in enumerate(d['frames']): + _keys(f, _FRAME, "frame") + _check(type(f['id']) is int and f['id'] == i, "frame ordering") + _check(type(f['name']) is str and type(f['kind']) is str, "frame name/kind") + _check((f['kind'], f['name']) not in frame_names, "duplicate typed frame name") + frame_names.add((f['kind'], f['name'])) + _check(_integer(f['attached_body'], -1) and f['attached_body'] < len(joints), "frame attachment") + _transform(f['T_body_frame']) + if i == 0: + _check(f['kind'] == 'root' and f['attached_body'] == -1 and + f['joint_id'] is None and f['T_body_frame'] == identity, "root frame") + elif i <= len(joints): + _check(f['kind'] == 'joint' and f['name'] == joints[i-1]['name'] and + type(f['joint_id']) is int and f['joint_id'] == f['attached_body'] == i-1 + and f['T_body_frame'] == identity, "joint frame projection") + else: + _check(f['kind'] == 'fixed_joint' and f['joint_id'] is None, "fixed frame") + _check(type(d['descriptor_id']) is str and d['descriptor_id'] == descriptor_id(d), "identity digest") + return d + + +def descriptor_from_robot(robot, *, runtime_transform=False): + """Extract effective placements and the configuration layout from one parsed model.""" + import numpy as np + if not robot.using_quaternion: + raise ValueError("schema 1 requires quaternion coordinates") + if type(runtime_transform) is not bool: + raise TypeError("runtime_transform must be bool") + def finite_limit(value): + if value is None or math.isinf(float(value)): + return None + value = float(value) + if not math.isfinite(value): + raise ValueError("NaN model limit") + return value + joints, bodies, blocks = [], [], [] + for joint in robot.get_joints_ordered_by_id(): + kind = joint.jtype + if kind not in _WIDTHS: + raise ValueError(f"unsupported descriptor joint kind {kind!r}") + nq, nv = _WIDTHS[kind] + neutral = np.zeros(nq) + if nq > 1: + neutral[-1] = 1. + zero = np.asarray(joint.get_transformation_matrix_hom_function()( + neutral if nq > 1 else 0.), dtype=float).tolist() + motion = np.asarray(joint.S, dtype=float) + axis = None if nq > 1 else (motion[3:] if kind == 'prismatic' else motion[:3]).reshape(3).tolist() + pitch = float(np.dot(motion[3:].reshape(3), axis)) if kind in ('helical', 'screw') else None + mimic = None + if joint.is_mimic: + mimic = dict(leader_joint=None if joint.mimic_target_id in (None, -1) + else int(joint.mimic_target_id), multiplier=float(joint.get_mimic_multiplier()), + offset=float(joint.get_mimic_offset())) + qi = None if mimic else int(np.asarray(robot.get_joint_index_q(joint.jid)).reshape(-1)[0]) + vi = None if mimic else int(np.asarray(robot.get_joint_index_v(joint.jid)).reshape(-1)[0]) + limits = robot.get_joint_limits_by_id(joint.jid) or [] + position = ([finite_limit(x) for x in limits] if nq == 1 and + kind != 'continuous' and len(limits) == 2 else None) + parent = int(robot.get_parent_id(joint.jid)) + joints.append(dict(id=int(joint.jid), name=joint.name, kind=kind, + parent_body=parent, child_body=int(joint.jid), local_nq=nq, + local_nv=nv, q_start=qi, v_start=vi, owns_coordinates=mimic is None, + axis=axis, pitch=pitch, T_parent_joint_zero=zero, mimic=mimic, + limits=dict(position=position, + velocity=finite_limit(robot.get_velocity_limit_by_id(joint.jid)), + effort=finite_limit(robot.get_effort_limit_by_id(joint.jid))))) + bodies.append(dict(id=int(joint.jid), name=joint.get_child(), parent_body=parent, + joint_id=int(joint.jid))) + if mimic is None: + blocks.append(dict(kind=kind if nq > 1 else 'euclidean', q_start=qi, v_start=vi, + nq=nq, nv=nv)) + identity = np.eye(4).tolist() + frames = [dict(id=0, name='root', kind='root', attached_body=-1, joint_id=None, + T_body_frame=identity)] + for j in joints: + frames.append(dict(id=len(frames), name=j['name'], kind='joint', attached_body=j['id'], + joint_id=j['id'], T_body_frame=np.eye(4).tolist())) + for f in robot.fixed_joints: + parent = robot.get_joint_by_name(f.get_parent()) + frames.append(dict(id=len(frames), name=f.get_name(), kind='fixed_joint', + attached_body=-1 if parent is None else int(parent.jid), joint_id=None, + T_body_frame=np.asarray(f.get_transformation_matrix_hom(), dtype=float).tolist())) + result = dict(schema_version=SCHEMA_VERSION, model_name=robot.get_name(), descriptor_id='', + nq=int(robot.get_num_pos()), nv=int(robot.get_num_vel()), nb=len(bodies), + nj=len(joints), floating_base=bool(robot.floating_base), + runtime_transform=runtime_transform, coordinate_convention=CONVENTION, + joints=joints, bodies=bodies, configuration_layout=sorted(blocks, key=lambda b:b['q_start']), + frames=frames) + result['descriptor_id'] = descriptor_id(result) + return validate_descriptor(result) diff --git a/grid_codegen/model_metadata_cpp.py b/grid_codegen/model_metadata_cpp.py new file mode 100644 index 00000000..85d706e3 --- /dev/null +++ b/grid_codegen/model_metadata_cpp.py @@ -0,0 +1,96 @@ +"""C++ coordinate metadata emitted only from the validated model descriptor.""" +import json +import re + +from .model_descriptor import validate_descriptor + +JOINT_KINDS = ('revolute', 'continuous', 'prismatic', 'helical', 'screw', 'spherical', 'floating') +FRAME_KINDS = ('root', 'joint', 'fixed_joint') + + +def emit_model_metadata(descriptor, namespace='grid'): + """Host constexpr and runtime-indexed device values; no host-array device reads.""" + d = validate_descriptor(descriptor) + if not re.fullmatch(r'[A-Za-z_][A-Za-z_0-9]*', namespace): + raise ValueError('model metadata namespace must be a C++ identifier') + def string(value): + # UTF-8 bytes with fixed-width octal escapes avoid C++ hex escape greed + # and universal-character restrictions for control characters. + return '"' + ''.join(chr(b) if 32 <= b < 127 and b not in (34, 92) + else '\\%03o' % b for b in value.encode('utf-8')) + '"' + def number(x): + return repr(0.0 if x == 0 else x) + def array(values): + return '{' + ','.join(number(x) for x in values) + '}' + def flat(matrix): + return [matrix[r][c] for c in range(4) for r in range(4)] + def optional(value): + return str(-1 if value is None else value) + lines = ['// Model-coordinate metadata; not a complete physical-model identity.', + f'namespace {namespace} {{ namespace model {{', + 'enum class joint_kind { ' + ', '.join(JOINT_KINDS) + ' };', + 'enum class frame_kind { ' + ', '.join(FRAME_KINDS) + ' };', + f'inline constexpr int schema_version = {d["schema_version"]};', + f'inline constexpr int nq = {d["nq"]}, nv = {d["nv"]}, nb = {d["nb"]}, nj = {d["nj"]};', + f'inline constexpr int frame_count = {len(d["frames"])};', + f'inline constexpr bool runtime_transform = {str(d["runtime_transform"]).lower()};', + f'inline constexpr char descriptor_id[] = {string(d["descriptor_id"])};', + f'inline constexpr char descriptor_json[] = {string(json.dumps(d, ensure_ascii=False, allow_nan=False, separators=(",", ":")))};', + 'struct joint_record {', + ' int id, parent_body, child_body, q_start, v_start, local_nq, local_nv;', + ' joint_kind kind; bool owns_coordinates, has_axis, has_pitch, has_mimic;', + ' int leader_joint; double multiplier, offset, axis[3], pitch, T_parent_joint_zero[16];', + ' bool has_position_limit[2], has_velocity_limit, has_effort_limit;', + ' double position_limit[2], velocity_limit, effort_limit;', + '};', + 'struct frame_record {', + ' int id, attached_body, joint_id; frame_kind kind; double T_body_frame[16];', + '};', + '__host__ __device__ constexpr bool joint(int id, joint_record &out) {', + ' switch(id) {'] + for j in d['joints']: + mimic = j['mimic'] + values = [str(j[k]) for k in ('id', 'parent_body', 'child_body')] + values += [optional(j[k]) for k in ('q_start', 'v_start')] + values += [str(j[k]) for k in ('local_nq', 'local_nv')] + values += ['joint_kind::'+j['kind'], str(j['owns_coordinates']).lower(), + str(j['axis'] is not None).lower(), str(j['pitch'] is not None).lower(), + str(mimic is not None).lower(), optional(None if mimic is None else mimic['leader_joint']), + number(1.0 if mimic is None else mimic['multiplier']), + number(0.0 if mimic is None else mimic['offset']), array(j['axis'] or [0., 0., 0.]), + number(j['pitch'] or 0.0), array(flat(j['T_parent_joint_zero']))] + pos = j['limits']['position'] or [None, None] + vel, effort = j['limits']['velocity'], j['limits']['effort'] + values += ['{' + ','.join(str(x is not None).lower() for x in pos) + '}', + str(vel is not None).lower(), str(effort is not None).lower(), + array([x if x is not None else 0.0 for x in pos]), + number(vel if vel is not None else 0.0), number(effort if effort is not None else 0.0)] + lines.append(f' case {j["id"]}: out = joint_record{{'+','.join(values)+'}; return true;') + lines += [' default: return false;', ' }', '}', + '__host__ __device__ constexpr bool frame(int id, frame_record &out) {', ' switch(id) {'] + for f in d['frames']: + values = [str(f['id']), str(f['attached_body']), optional(f['joint_id']), + 'frame_kind::'+f['kind'], array(flat(f['T_body_frame']))] + lines.append(f' case {f["id"]}: out = frame_record{{'+','.join(values)+'}; return true;') + lines += [' default: return false;', ' }', '}'] + for kind, records in [('joint', d['joints']), ('frame', d['frames'])]: + lines += [f'__host__ constexpr const char *{kind}_name(int id) {{', ' switch(id) {'] + for r in records: + lines.append(f' case {r["id"]}: return {string(r["name"])};') + lines += [' default: return nullptr;', ' }', '}'] + lines += ['// Thread-scope composition; distinct 16-element input/output, no implicit fences.', + '// Failure leaves output untouched. Root attachment expects the root world pose.', + 'template __host__ __device__ inline bool compose_frame_from_body(', + ' int frame_id, int body_id, const T *world_body, T *world_frame) {', + ' frame_record f{};', + ' if (runtime_transform || !world_body || !world_frame || world_body == world_frame', + ' || !frame(frame_id, f) || f.attached_body != body_id) return false;', + ' for (int c=0;c<4;++c) for (int r=0;r<4;++r) {', + ' T value = T(0);', + ' for (int k=0;k<4;++k) value += world_body[r+4*k]*T(f.T_body_frame[k+4*c]);', + ' world_frame[r+4*c] = value;', + ' }', + ' return true;', + '}', + '} } // namespace model', ''] + return '\n'.join(lines) diff --git a/grid_codegen/model_primitives.py b/grid_codegen/model_primitives.py new file mode 100644 index 00000000..81e545a0 --- /dev/null +++ b/grid_codegen/model_primitives.py @@ -0,0 +1,150 @@ +"""Thread-local model operations; symbolic transforms and descriptor ownership. + +No traversal, arena, solver, mutable table or independently parsed model. +""" +import sympy as sp + +from .algorithms._eepose_gradient_hessian import ( + _ee_pose_inner_angle_expr, _ee_pose_root_fb_subst) +from .helpers._topology_helpers import _xi_spherical_quat_subst + + +def emit_joint_transforms(gen): + """joint_transform reads s_q; joint_transform_scalar takes one coordinate value. + Both emit the parser's homogeneous joint matrices.""" + lines = [f'namespace {gen.file_namespace} {{ namespace model {{', + '// Thread scope, all 16 column-major cells written; disjoint output.', + '// Invalid IDs/modes leave output untouched. Unit xyzw inputs required.'] + mats = gen.robot.get_Xmats_hom_ordered_by_id(include_fixed_joints=False) + for scalar in (False, True): + name = 'joint_transform_scalar' if scalar else 'joint_transform' + arg = 'T coordinate' if scalar else 'const T *s_q' + lines += ['template __device__ __forceinline__ bool '+name+'(', + f' int joint_id, {arg}, T *out) {{', + ' if (runtime_transform) return false;', ' switch(joint_id) {'] + for j in gen.model_descriptor['joints']: + jid = j['id'] + if scalar and (not j['owns_coordinates'] or j['kind'] not in + ('revolute', 'continuous', 'prismatic')): + continue + lines.append(f' case {jid}: {{') + if j['local_nq'] == 1: + angle = 'coordinate' if scalar else _ee_pose_inner_angle_expr(gen, jid) + lines += [f' const T theta={angle};', + ' const T s=sin(theta), c=cos(theta);', ' (void)s; (void)c;'] + for col in range(4): + for row in range(4): + expression = sp.ccode(mats[jid][row, col]) + if j['kind'] == 'floating': + expression = _ee_pose_root_fb_subst(expression) + elif j['kind'] == 'spherical': + expression = _xi_spherical_quat_subst(gen, expression, jid, ccode=True) + else: + expression = expression.replace('sin(theta)', 's').replace('cos(theta)', 'c') + lines.append(f' out[{row+4*col}]=static_cast({expression});') + lines += [' break;', ' }'] + lines += [' default: return false;', ' }', ' return true;', '}'] + lines += ['} } // namespace model', ''] + return '\n'.join(lines) + + +def emit_configuration_primitives(descriptor, namespace): + """Compact/dense forms share GLASS Lie algebra and one coordinate partition. + + Double internal small blocks avoid float cancellation near the identity. + Matrix temporaries are at most 6x6, independent of model dimensions. + """ + blocks = descriptor['configuration_layout'] + lines = [f'namespace {namespace} {{', 'namespace model {', + 'enum class coordinate_kind { euclidean, spherical, floating };', + 'struct coordinate_block_record { coordinate_kind kind; int q_start, v_start, nq, nv; };', + f'inline constexpr int coordinate_block_count={len(blocks)};', + '__host__ __device__ constexpr bool coordinate_block(int id, coordinate_block_record &out) {', + ' switch(id) {'] + for index, b in enumerate(blocks): + fields = ','.join(str(b[k]) for k in ('q_start', 'v_start', 'nq', 'nv')) + lines.append(f' case {index}: out=coordinate_block_record{{coordinate_kind::{b["kind"]},{fields}}}; return true;') + lines += [' default: return false;', ' }', '}', '} // namespace model'] + lines += r''' +namespace configuration_detail { +// Reference-first differences; independent quaternion sign representatives. +template __device__ inline void block_difference( + const model::coordinate_block_record &b, const T *from, const T *to, double *e) { + if(b.kind==model::coordinate_kind::euclidean) { e[0]=double(to[b.q_start])-double(from[b.q_start]); return; } + double a[7],z[7]; + for(int k=0;k(a,z,e,e+3); + } else { + double conjugate[4],relative[4]; + glass::thread::quat_conj(a,conjugate); + glass::thread::quat_mul(conjugate,z,relative); + glass::thread::quat_log(relative,e); + } +} +// One nonlinear evaluation body prevents caller-dependent FMA contraction +// from making dense and compact representations disagree in their last bits. +template __device__ __noinline__ void nonlinear_block_jacobians( + const model::coordinate_block_record &b, const T *from, const T *to, T *J1, T *J2) { + double e[6]; block_difference(b,from,to,e); + if(b.kind==model::coordinate_kind::spherical) { + double A[9],B[9]; + glass::thread::so3_left_jacobian_inv(e,A); + glass::thread::so3_right_jacobian_inv(e,B); + for(int k=0;k<9;++k) { J1[k]=T(-A[k]); J2[k]=T(B[k]); } + return; + } + // Invert the upper-triangular SE(3) Jr: [A,Q;0,A]^-1. + double A[9],Q[9],AQ[9],C[9],inverse[36],base[36]; + glass::thread::so3_right_jacobian_inv(e+3,A); + glass::thread::se3_Q_block(e,e+3,Q); + glass::thread::gemm(1.,A,Q,0.,AQ); + glass::thread::gemm(-1.,AQ,A,0.,C); + for(int c=0;c<6;++c) for(int r=0;r<6;++r) + inverse[c*6+r]=(r<3 && c<3)?A[c*3+r]: + ((r>=3 && c>=3)?A[(c-3)*3+r-3]:((r<3 && c>=3)?C[(c-3)*3+r]:0.)); + glass::thread::se3_retract_jacobian_q(e,e+3,base); + for(int c=0;c<6;++c) for(int r=0;r<6;++r) { + double v=0.; for(int k=0;k<6;++k) v-=inverse[k*6+r]*base[c*6+k]; + J1[c*6+r]=T(v); J2[c*6+r]=T(inverse[c*6+r]); + } +} +template __device__ inline void block_jacobians( + const model::coordinate_block_record &b, const T *from, const T *to, T *J1, T *J2) { + if(b.kind==model::coordinate_kind::euclidean) { J1[0]=T(-1); J2[0]=T(1); return; } + nonlinear_block_jacobians(b,from,to,J1,J2); +} +} // namespace configuration_detail +// Capacity is the number of T elements in EACH disjoint output, not bytes. +// False leaves both outputs untouched. Inputs may coincide; valid unit quaternions required. +template __device__ inline bool configuration_difference_jacobian_block( + int block_id, const T *from, const T *to, T *J_from, T *J_to, size_t capacity) { + model::coordinate_block_record b{}; + if(!model::coordinate_block(block_id,b) || capacity __device__ inline void configuration_difference( + const T *from, const T *to, T *delta) { + for(int k=0;k __device__ inline void configuration_difference_jacobians( + const T *from, const T *to, T *J_from, T *J_to) { + for(int k=0;k list[str]: " GRID_RBD_CTX_LOCALS(ctx);", *op["launcher_decls"], " dim3 grid_dim = grid_rbd_grid_for(g_ctx, batch);", - f" const size_t smem = grid::{smem}();", + f" const size_t smem = {op.get('smem_ns', 'grid')}::{smem}();", f" cudaFuncSetAttribute(grid_plant::{kern}, cudaFuncAttributeMaxDynamicSharedMemorySize, (int)smem);", f" dim3 thr = grid_clamp_threads_for(grid_plant::{kern}, grid_rbd_launch_threads_n(g_ctx, batch));", f" grid_plant::{kern}<< list[str]: ] if op["out_size"] == "dab": L.append(" const int dab = 2 * nv * 3 * nv;") + L.append(' if (grid_plant::plant_step_gradient_check_shared_memory() != cudaSuccess) return ffi::Error::Internal("plant_step_gradient full-shared arena exceeds device capacity; use integrator_gradient");') L += [ " auto dims = x.dimensions();", " if (dims.size() != 2 || (int)dims[1] != nx)", @@ -392,7 +394,7 @@ def _torch_step_block(key: str) -> list[str]: L.append(" const int nx = grid::NUM_POS + grid::NUM_VEL, nv = grid::NUM_VEL;") L += [ " dim3 grid_dim = grid_rbd_grid_for(g_ctx, batch);", - f" const size_t smem = grid::{smem}();", + f" const size_t smem = {op.get('smem_ns', 'grid')}::{smem}();", f" cudaFuncSetAttribute(grid_plant::{kern}, cudaFuncAttributeMaxDynamicSharedMemorySize, (int)smem);", f" dim3 thr = grid_clamp_threads_for(grid_plant::{kern}, grid_rbd_launch_threads_n(g_ctx, batch));", f" grid_plant::{kern}<< list[str]: ] if op["out_size"] == "dab": L.append(" const int dab = 2 * nv * 3 * nv;") + L.append(' TORCH_CHECK(grid_plant::plant_step_gradient_check_shared_memory() == cudaSuccess, "plant_step_gradient full-shared arena exceeds device capacity; use integrator_gradient");') L += [ f' grid_torch_check_n(x, "{name}: x", nx);', f' grid_torch_check_n(u, "{name}: u", nv);', diff --git a/install/docker/Dockerfile b/install/docker/Dockerfile index f2eff9da..5b0e0af7 100644 --- a/install/docker/Dockerfile +++ b/install/docker/Dockerfile @@ -2,9 +2,9 @@ # and PyTorch CUDA builds, and the generated iiwa14 header baked in. # # Build from the repository root (the build context supplies the sample URDF): -# docker build -f install/docker/Dockerfile -t grid:0.5.1 . +# docker build -f install/docker/Dockerfile -t grid:0.7.0 . # Run with the NVIDIA Container Toolkit: -# docker run --rm -it --gpus all -v grid-cache:/var/cache/grid-rbd grid:0.5.1 +# docker run --rm -it --gpus all -v grid-cache:/var/cache/grid-rbd grid:0.7.0 # # The image's cache store carries one compiled iiwa14 library: a fat binary for # the compute capabilities in GRID_ARCHS (default sm_86, sm_89, sm_90 and @@ -26,7 +26,7 @@ ARG CUDA_IMAGE=nvidia/cuda:13.2.0-devel-ubuntu24.04 FROM ${CUDA_IMAGE} -ARG GRID_RBD_VERSION=0.5.1 +ARG GRID_RBD_VERSION=0.7.0 ARG TORCH_INDEX_URL=https://download.pytorch.org/whl/cu128 # Compute capabilities the precompiled iiwa14 library embeds (GRID_RBD_CUDA_ARCHS). ARG GRID_ARCHS=86,89,90,120 diff --git a/install/environment.yml b/install/environment.yml index 0222719e..7115b0ef 100644 --- a/install/environment.yml +++ b/install/environment.yml @@ -19,6 +19,6 @@ dependencies: - pip - pip: - --extra-index-url https://download.pytorch.org/whl/cu128 - - grid-rbd[jax,torch]==0.5.1 + - grid-rbd[jax,torch]==0.7.0 - jax[cuda12] - torch diff --git a/install/requirements-dev.txt b/install/requirements-dev.txt index c5f919f1..9f1a4210 100644 --- a/install/requirements-dev.txt +++ b/install/requirements-dev.txt @@ -1,6 +1,6 @@ pytest setuptools>=77 # packaging-hook unit tests run outside the isolated build env -pytest-gpu-proof==0.4.1 # pinned 2026-10-13 (re-pin batch; CI installs the same pin) — signed GPU-equivalence receipts (test/run_gpu_proof.sh); was the test/pytest-gpu-proof submodule, now on PyPI +pytest-gpu-proof==0.5.0 # pinned 2026-10-29; CI uses this pin (Python 3.10 support and signed nvcc metadata) nbval # execute example notebooks as smoke tests (pytest --nbval-lax examples/notebooks/) # Pinocchio oracle (CPU): the peers' LOAD-BEARING pins — plain `pin` resolves 4.x, # breaks `import pinocchio` and the pin_so_ext build (external/URDFParser CI note). diff --git a/pyproject.toml b/pyproject.toml index 1e7a3b5e..ba6ff70b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta" # the wrapper hard-imports the codegen tree at register_robot time, so they were # always one distribution — now folded into one editable install. name = "grid-rbd" -version = "0.6.0" +version = "0.7.0" description = "GPU robot dynamics, kinematics and collisions with analytical derivatives" readme = "README.md" # 3.10 floor: the runtime wrapper + its pybind11 _core ext require it; codegen @@ -142,11 +142,13 @@ fingerprint_paths = [ "external/GLASS", "external/RBDReference", "external/URDFParser", "grid_assets", "pyproject.toml", "setup.py", "_build_resources.py", "MANIFEST.in", "test/cuda_equivalents", "test/python_wrappers", "test/conftest.py", - "test/helpers.py", "test/run_gpu_proof.sh", "test/run_split_suite.py", + "test/helpers.py", "test/test_model_descriptor.py", "test/test_manifold_conventions.py", + "test/ee_constant_slot_warnings.py", "test/run_gpu_proof.sh", "test/run_split_suite.py", "test/compile_sched.py", "test/prewarm_cuda_flagship.py", "test/codegen_neutrality.py", "test/header_key_replay.py", "test/receipt_integrity.py", "test/gpu-proof-tests.txt", "test/run_validation.py", "tools/check_installed_package.py", "tools/check_installed_numerics.py", + "bindings/examples/consumer_artifact_migration.py", # 2026-10-14 audit: executed by every GPU import / consulted by the verifier. "test/__init__.py", "test/gpu-proof-policy.yaml", "test/gpu-proof-policy-release.yaml", "test/gpu-proof-expected-skips.txt", diff --git a/test/_consumer_model_fixture.py b/test/_consumer_model_fixture.py new file mode 100644 index 00000000..d043104d --- /dev/null +++ b/test/_consumer_model_fixture.py @@ -0,0 +1,26 @@ +"""Minimal real descriptor emission for CPU-only mocked consumer builds.""" +import json + + +def generate_metadata_header(urdf, options, out): + from grid_rbd import describe_model + from grid_codegen.model_metadata_cpp import emit_model_metadata + descriptor = describe_model(urdf, floating_base=options.get('floating_base', False), + runtime_transform=options.get('runtime_transform', False)) + out.write_text(emit_model_metadata(descriptor)) + (out.parent/'model_descriptor.json').write_text(json.dumps(descriptor)) + + +class FakeToolchain: + """A resolved consumer toolchain for CPU tests: no host compiler is probed.""" + identity = {"host": "fake-host", "family": "fake"} + prepend = [] + + def compile_flags(self): + return [] + + def environment(self): + return None + + def link_command(self, nvcc, out, objects): + return [nvcc, "--shared", "-o", str(out), *objects] diff --git a/test/compile_sched.py b/test/compile_sched.py index 4c0f851d..04ec9fbf 100644 --- a/test/compile_sched.py +++ b/test/compile_sched.py @@ -37,7 +37,7 @@ import subprocess import threading import time -from dataclasses import dataclass, field +from dataclasses import dataclass from pathlib import Path _TIME_BIN = "/usr/bin/time" @@ -131,7 +131,15 @@ def __init__(self, path: Path): self.dropped_legacy = len(raw) def predict_kb(self, key: str, default_kb: int, margin: float) -> int: - base = self.data.get(key, {}).get("peak_kb", default_kb) + # A warm variant may have measured only a cheap relink. Other variants + # of the same robot provide a lower bound for a later cold object build. + # Keep the default for unseen variants; do not group prewarm job keys. + with self._lock: + base = self.data.get(key, {}).get("peak_kb", default_kb) + if key.startswith("warm:"): + robot_key = key.split(",", 1)[0] + base = max(base, *(v["peak_kb"] for k, v in self.data.items() + if k == robot_key or k.startswith(robot_key + ",")), 0) return int(base * margin) def predict_secs(self, key: str, default_secs: float = DEFAULT_SECS) -> float: diff --git a/test/cuda_equivalents/constant_model.py b/test/cuda_equivalents/constant_model.py new file mode 100644 index 00000000..0643414d --- /dev/null +++ b/test/cuda_equivalents/constant_model.py @@ -0,0 +1,31 @@ +"""The constant-joint fixture robot shared by the constant-joint and descriptor suites. + +A helper module, not a test module, so the split driver's helper-import rule +fingerprints it into every shard that imports it. +""" +from URDFParser import URDFParser + + +def constant_model(directory, zero=False, locked=False, branched=False): + inertial = ('' + '') + links = ''.join(f'{inertial}' + for name in ('base', 'mount', 'arm', 'follower')) + drive = ('' + '' if zero else + '' + '' + '') + follower = ('' + '' if locked else + '' + '' + '' + '') + if branched: + follower = follower.replace('', '') + path = directory / f'model-{zero}-{locked}-{branched}.urdf' + path.write_text(f'{links}' + '' + f'{drive}{follower}') + return URDFParser().parse(str(path)) diff --git a/test/cuda_equivalents/consumer_acrobot.LICENSE b/test/cuda_equivalents/consumer_acrobot.LICENSE new file mode 100644 index 00000000..302cfcdf --- /dev/null +++ b/test/cuda_equivalents/consumer_acrobot.LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 A2R Lab (Accessible & Accelerated Robotics Lab) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/test/cuda_equivalents/consumer_acrobot.PROVENANCE b/test/cuda_equivalents/consumer_acrobot.PROVENANCE new file mode 100644 index 00000000..356f0ccb --- /dev/null +++ b/test/cuda_equivalents/consumer_acrobot.PROVENANCE @@ -0,0 +1,5 @@ +Source: A2R-Lab Parallel-DDP-unified robot_assets/acrobot.urdf +Commit: a89455b84b7dc11d95333d532347fec8a35bad67 +SHA256: 76dfcd91bf5515d7ba6200177d71f4c7e5fe7b216cc8c729f21eb3643327ff67 +Copied byte-for-byte for the agreed Package 3 consumer model/precision matrix. +This fixture is fully actuated; no acrobot underactuation contract is implied. diff --git a/test/cuda_equivalents/consumer_acrobot.urdf b/test/cuda_equivalents/consumer_acrobot.urdf new file mode 100644 index 00000000..962a4293 --- /dev/null +++ b/test/cuda_equivalents/consumer_acrobot.urdf @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/cuda_equivalents/consumer_indy7.LICENSE b/test/cuda_equivalents/consumer_indy7.LICENSE new file mode 100644 index 00000000..21661126 --- /dev/null +++ b/test/cuda_equivalents/consumer_indy7.LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 The Accessible and Accelerated Robotics Lab (A²R Lab) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/test/cuda_equivalents/consumer_indy7.PROVENANCE b/test/cuda_equivalents/consumer_indy7.PROVENANCE new file mode 100644 index 00000000..2580d2e6 --- /dev/null +++ b/test/cuda_equivalents/consumer_indy7.PROVENANCE @@ -0,0 +1,5 @@ +Test fixture copied from A2R-Lab/GATO commit +a5eb91f0520e9e30e54e9009fef52a310f3b7a3f, examples/indy7_description/indy7.urdf. +Preserved for reproducible Package 3 menu composition validation; not a new +bundled-model API. Source license is preserved as consumer_indy7.LICENSE. +Mesh references are unused by these numerical tests. diff --git a/test/cuda_equivalents/consumer_indy7.urdf b/test/cuda_equivalents/consumer_indy7.urdf new file mode 100644 index 00000000..2efb0fae --- /dev/null +++ b/test/cuda_equivalents/consumer_indy7.urdf @@ -0,0 +1,378 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fake_components/GenericSystem + + 0.0 + + + + {-2*pi} + {2*pi} + + + -3.15 + 3.15 + + + + 0.0 + + + + 0.0 + + + + + + {-2*pi} + {2*pi} + + + -3.15 + 3.15 + + + + 0.0 + + + + 0.0 + + + + + + {-2*pi} + {2*pi} + + + -3.15 + 3.15 + + + + -1.57 + + + + 0.0 + + + + + + {-2*pi} + {2*pi} + + + -3.15 + 3.15 + + + + 0.0 + + + + 0.0 + + + + + + {-2*pi} + {2*pi} + + + -3.15 + 3.15 + + + + -1.57 + + + + 0.0 + + + + + + {-2*pi} + {2*pi} + + + -3.15 + 3.15 + + + + 0.0 + + + + 0.0 + + + + + + + diff --git a/test/cuda_equivalents/cuda_collision_environment_runner.cu b/test/cuda_equivalents/cuda_collision_environment_runner.cu index ba94a12a..ae9dd7fa 100644 --- a/test/cuda_equivalents/cuda_collision_environment_runner.cu +++ b/test/cuda_equivalents/cuda_collision_environment_runner.cu @@ -13,8 +13,11 @@ // config_free verdicts and collision_distance outputs on random configurations. // host grid_cc_sphere_in_environment on the host view equals the device evaluation on the // owned view at the same points. -// lifetime free device memory after destruction equals the value before construction; a moved -// from object is empty and the destination keeps the lists. +// lifetime the owned environment's four device lists are live device allocations before its +// destruction and unregistered addresses after it (cudaPointerGetAttributes, which is +// process-local; device-global free memory is not compared because a sibling process +// sharing the GPU moves it between two readings); a moved from object is empty and +// the destination keeps the lists. // argv[1] = block threads (default 64), argv[2] = configs (default 100), argv[3] = seed. #define GRID_HEADER #include "grid.cuh" @@ -116,8 +119,15 @@ int main(int argc, char **argv) { for (int i = 0; i < 2; ++i) { cap.insert(cap.end(), { (urand()-0.5f)*1.2f, (urand()-0.5f)*1.2f, urand()*1.0f, (urand()-0.5f)*1.2f, (urand()-0.5f)*1.2f, urand()*1.0f, 0.04f + 0.06f*urand() }); } for (int i = 0; i < 2; ++i) { float a = urand() * 3.14159f; cub.insert(cub.end(), { (urand()-0.5f)*1.2f, (urand()-0.5f)*1.2f, urand()*1.0f, cosf(a/2), 0, 0, sinf(a/2), 0.1f + 0.2f*urand(), 0.1f + 0.2f*urand(), 0.1f + 0.2f*urand() }); } pla.insert(pla.end(), { 0, 0, 1, -0.05f }); // floor just below the base - size_t free_before = 0, total = 0; - CK(cudaDeviceSynchronize()); CK(cudaMemGetInfo(&free_before, &total)); + // Process-local lifetime probe: a freed allocation reports cudaMemoryTypeUnregistered, queried + // right after the release and before anything can reuse the address. + auto memory_type = [](const void *p) -> int { + cudaPointerAttributes a{}; + if (cudaPointerGetAttributes(&a, p) != cudaSuccess) { cudaGetLastError(); return -1; } + return (int)a.type; + }; + const void *lists[4] = { nullptr, nullptr, nullptr, nullptr }; + int live_before = 0; { gc::HostEnvironment host = gc::make_host_environment(sph.data(), 3, cap.data(), 2, cub.data(), 2, pla.data(), 1); gc::DeviceEnvironment owned(host); @@ -176,13 +186,16 @@ int main(int argc, char **argv) { eval_kernel<<<1, threads, smem>>>(d_q, d_m, assigned.view(), d_free[0], d_dist[0], d_normal[0]); CK(cudaDeviceSynchronize()); report("move", move_ok); + lists[0] = assigned.view().spheres; lists[1] = assigned.view().capsules; + lists[2] = assigned.view().cuboids; lists[3] = assigned.view().planes; + for (const void *p : lists) live_before += (p != nullptr && memory_type(p) == (int)cudaMemoryTypeDevice); CK(cudaFree(d_s)); CK(cudaFree(d_c)); CK(cudaFree(d_b)); CK(cudaFree(d_p)); CK(cudaFree(d_q)); CK(cudaFree(d_pts)); CK(cudaFree(d_hit)); for (int k = 0; k < 2; ++k) { CK(cudaFree(d_dist[k])); CK(cudaFree(d_normal[k])); CK(cudaFree(d_free[k])); } } // `assigned` releases here - size_t free_after = 0; - CK(cudaDeviceSynchronize()); CK(cudaMemGetInfo(&free_after, &total)); - printf("LIFETIME free_before=%zu free_after=%zu\n", free_before, free_after); - report("lifetime", free_after == free_before); + int live_after = 0; + for (const void *p : lists) live_after += (memory_type(p) != (int)cudaMemoryTypeUnregistered); + printf("LIFETIME lists=4 live_before=%d live_after=%d\n", live_before, live_after); + report("lifetime", live_before == 4 && live_after == 0); printf("RESULT %s\n", all_ok ? "PASS" : "FAIL"); return all_ok ? 0 : 1; } diff --git a/test/cuda_equivalents/cuda_collision_margins_runner.cu b/test/cuda_equivalents/cuda_collision_margins_runner.cu new file mode 100644 index 00000000..0c9575a8 --- /dev/null +++ b/test/cuda_equivalents/cuda_collision_margins_runner.cu @@ -0,0 +1,81 @@ +// Exactly representable independent thresholds for the static sphere/capsule cascades. +#include "grid_collision_geometry.cuh" +#include +#include +#include +namespace gc = grid_collision; + +template +__host__ __device__ bool thresholds() { + const T p[] = {0, 0, 1, 1, 0, 1}, r[] = {T(.25), T(.25)}; + const T seg[] = {0, T(-.125), 1, 0, T(.125), 1, + 1, T(-.125), 1, 1, T(.125), 1}; + const T cover[] = {T(.375), T(.375)}; + const int links[] = {0, 1}, pairs[] = {0, 1, 1}; + const gc::Plane plane{0, 0, 1, 0}; + gc::Environment env{}; + env.planes = &plane; env.n_planes = 1; + const T step = T(1.0 / 1048576); + for (int e = -1; e <= 1; ++e) for (int s = -1; s <= 1; ++s) { + gc::CollisionMargins margins(T(.75) + T(e)*step, T(.5) + T(s)*step); + for (int check_self = 0; check_self <= 1; ++check_self) { + const bool expected = e <= 0 && (!check_self || s <= 0); + // Deliberately infer T: the non-deduced margin parameter must accept both + // the value object and a scalar without disrupting template deduction. + const bool sphere = gc::grid_cc_config_free(env, p, r, pairs, 1, 2, links, + p, r, pairs, 1, 2, links, nullptr, margins, check_self); + const bool capsule = gc::grid_cc_config_free_capsule(env, p, cover, pairs, 1, 2, links, + seg, r, pairs, 1, 2, links, nullptr, margins, check_self); + if (sphere != expected || capsule != expected) return false; + } + } + const T common[] = {T(0), T(.5), T(.5)+step, T(.75), T(1)}; + for (T margin : common) { + const bool expected = margin <= T(.5); + if (gc::grid_cc_config_free(env, p, r, pairs, 1, 2, links, + p, r, pairs, 1, 2, links, nullptr, margin) != expected) return false; + if (gc::grid_cc_config_free_capsule(env, p, cover, pairs, 1, 2, links, + seg, r, pairs, 1, 2, links, nullptr, margin) != expected) return false; + } + return true; +} + +__global__ void check(int *out) { out[threadIdx.x] = thresholds() && thresholds(); } + +template bool factory() { + auto zero = gc::make_collision_margins(); + auto common = gc::make_collision_margins(T(.1)); + auto env = gc::make_collision_margins(T(.1), T(0)); + auto self = gc::make_collision_margins(T(.1), std::nullopt, T(0)); + auto both = gc::make_collision_margins(T(.1), T(.2), T(.3)); + if (zero.self_margin != 0 || zero.environment_margin != 0 || + common.environment_margin != T(.1) || common.self_margin != T(.1) || + env.environment_margin != 0 || env.self_margin != T(.1) || + self.environment_margin != T(.1) || self.self_margin != 0 || + both.environment_margin != T(.2) || both.self_margin != T(.3)) return false; + for (T bad : {T(-1), std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN()}) { + for (int side = 0; side < 3; ++side) { + try { + (void)gc::make_collision_margins(side == 0 ? bad : T(0), + side == 1 ? bad : T(0), side == 2 ? bad : T(0)); + return false; + } catch (const std::invalid_argument &) {} + } + } + return true; +} + +int main() { + if (!factory() || !factory() || !thresholds() || !thresholds()) return 1; + int *out; int host[128]; + if (cudaMalloc(&out, sizeof(host)) != cudaSuccess) return 2; + for (int threads : {1, 32, 128}) { + check<<<1, threads>>>(out); + if (cudaDeviceSynchronize() != cudaSuccess || + cudaMemcpy(host, out, threads*sizeof(int), cudaMemcpyDeviceToHost) != cudaSuccess) return 2; + for (int i = 0; i < threads; ++i) if (host[i] != 1) return 1; + } + cudaFree(out); + puts("MARGINS_FACTORY_BOUNDARY PASS"); + puts("RESULT: PASS"); +} diff --git a/test/cuda_equivalents/cuda_collision_native_margins_runner.cu b/test/cuda_equivalents/cuda_collision_native_margins_runner.cu new file mode 100644 index 00000000..1e296919 --- /dev/null +++ b/test/cuda_equivalents/cuda_collision_native_margins_runner.cu @@ -0,0 +1,86 @@ +// Native capsule margins: generated cascade/single-tier vs independent fine-only +// checks and the consumer's two scalar calls. No timing, one block per configuration. +#define GRID_HEADER +#include "grid.cuh" +#include "margin_config.h" +#include +#include +namespace gc = grid_collision; +using T = double; +constexpr int NR = gc::NUM_COLLISION_ROWS; +constexpr int NQ = grid::NUM_POS; +#define CK(x) do { if ((x) != cudaSuccess) { printf("CUDA failure line %d\n", __LINE__); return 2; } } while (0) + +__global__ void check(const T *q, const grid::robotModel *model, + gc::Sphere obstacle, gc::CollisionMargins margins, + bool self_on, int *out) { + extern __shared__ T scratch[]; + __shared__ T pos[6*NR], radii[NR]; + gc::Environment env{}; env.spheres = &obstacle; env.n_spheres = 1; + gc::Environment empty{}; +#if TEST_NATIVE_TWO_TIERS + __shared__ T broad[3*gc::NUM_COLLISION_SPHERES_BROAD], br[gc::NUM_COLLISION_SPHERES_BROAD]; + #define CONFIG(e, mg, self) gc::config_free(q, model, e, broad, br, pos, radii, nullptr, nullptr, mg, self) +#else + #define CONFIG(e, mg, self) gc::config_free(q, model, e, pos, radii, nullptr, mg, self) +#endif + const bool combined = CONFIG(env, margins, self_on); + __syncthreads(); + bool fine = true; + if (self_on && gc::grid_cc_self_collision_capsules(pos, radii, + gc::g_collision_self_cc_ranges, gc::NUM_COLLISION_SELF_CC_RANGES, margins.self_margin)) fine = false; + for (int i = 0; i < NR; ++i) + if (gc::grid_cc_capsule_in_environment(env, + gc::grid_cc_row_capsule(pos, radii, i, margins.environment_margin))) fine = false; + __syncthreads(); + const bool environment = CONFIG(env, margins.environment_margin, false); + __syncthreads(); + const bool self = CONFIG(empty, margins.self_margin, self_on); + __syncthreads(); + out[threadIdx.x] = combined == fine && combined == (environment && self) ? int(combined) : -1; + #undef CONFIG +} + +static unsigned long long state = 771; +static T random_value() { state = state * 6364136223846793005ull + 1; return T((state >> 33) & 0xffffff)/T(0x1000000); } + +int main(int argc, char **argv) { + const int cases = argc > 1 ? 12 : 200; // compact sanitizer variant retains every margin/thread combination + const auto *model = grid::init_robotModel(); + T *q; int *out; CK(cudaMalloc(&q, NQ*sizeof(T))); CK(cudaMalloc(&out, 128*sizeof(int))); + size_t smem = grid::MULTI_TARGET_POSITION_DYNAMIC_SHARED_MEM_BYTES(); +#if TEST_NATIVE_TWO_TIERS + smem = std::max(smem, grid::MULTI_TARGET_POSITION_BROAD_DYNAMIC_SHARED_MEM_BYTES()); +#endif + CK(cudaFuncSetAttribute(check, cudaFuncAttributeMaxDynamicSharedMemorySize, int(smem))); + int hits = 0, free = 0, env_changes = 0, self_changes = 0; + std::vector hostq(NQ); int host[128]; + for (int k = 0; k < cases; ++k) { + for (auto &v : hostq) v = (random_value()*2-1)*T(3.14); + CK(cudaMemcpy(q, hostq.data(), NQ*sizeof(T), cudaMemcpyHostToDevice)); + gc::Sphere obs{(random_value()-.5)*1.6, (random_value()-.5)*1.6, random_value()*1.2, .1}; + int verdict[2][4]; + const gc::CollisionMargins margins[] = {{0,0}, {.1,0}, {0,.2}, {.1,.2}}; + for (int self = 0; self <= 1; ++self) for (int mg = 0; mg < 4; ++mg) { + int baseline = -1; + for (int threads : {1, 32, 128}) { + check<<<1, threads, smem>>>(q, model, obs, margins[mg], bool(self), out); + CK(cudaDeviceSynchronize()); + CK(cudaMemcpy(host, out, threads*sizeof(int), cudaMemcpyDeviceToHost)); + if (threads == 1) baseline = host[0]; + for (int t = 0; t < threads; ++t) if (host[t] < 0 || host[t] != baseline) { + printf("mismatch config=%d self=%d margin=%d thread=%d\n", k, self, mg, t); return 1; + } + } + verdict[self][mg] = baseline; + if (baseline) ++free; else ++hits; + } + env_changes += verdict[0][0] != verdict[0][1]; + self_changes += verdict[1][0] != verdict[1][2]; + if (verdict[0][0] != verdict[0][2] || verdict[0][1] != verdict[0][3]) return 1; + } + printf("hits=%d free=%d env_changes=%d self_changes=%d\n", hits, free, env_changes, self_changes); + if (!hits || !free || (argc == 1 && (!env_changes || !self_changes))) return 1; + CK(cudaFree(q)); CK(cudaFree(out)); + puts("RESULT: PASS"); +} diff --git a/test/cuda_equivalents/cuda_collision_warp_runner.cu b/test/cuda_equivalents/cuda_collision_warp_runner.cu index 87be7a7a..560a036b 100644 --- a/test/cuda_equivalents/cuda_collision_warp_runner.cu +++ b/test/cuda_equivalents/cuda_collision_warp_runner.cu @@ -37,7 +37,7 @@ constexpr int NX = grid::NUM_JOINTS; __global__ void cmp_kernel(const T *d_q, const grid::robotModel *m, const gc::Sphere *d_sph, int n_sph, const gc::Capsule *d_cap, int n_cap, - const gc::Cuboid *d_cub, int n_cub, float margin, int check_self, + const gc::Cuboid *d_cub, int n_cub, gc::CollisionMargins margins, int check_self, int *d_block_free, int *d_warp_free, T *d_block_dist, float *d_warp_dist, T *d_block_normal, float *d_warp_normal, T *d_self_dist) { extern __shared__ T s_dyn[]; @@ -59,10 +59,20 @@ __global__ void cmp_kernel(const T *d_q, const grid::robotModel *m, #if GRID_COLLISION_NUM_TIERS > 1 __shared__ T s_bpos[3*gc::NUM_COLLISION_SPHERES_BROAD]; __shared__ T s_br[gc::NUM_COLLISION_SPHERES_BROAD]; - bool is_free = gc::config_free(d_q, m, env, s_bpos, s_br, s_pos, s_r, nullptr, nullptr, static_cast(margin), self_on); + #define BLOCK_CHECK(e, mg, self) gc::config_free(d_q, m, e, s_bpos, s_br, s_pos, s_r, nullptr, nullptr, mg, self) #else - bool is_free = gc::config_free(d_q, m, env, s_pos, s_r, nullptr, static_cast(margin), self_on); + #define BLOCK_CHECK(e, mg, self) gc::config_free(d_q, m, e, s_pos, s_r, nullptr, mg, self) #endif + const bool is_free = BLOCK_CHECK(env, margins, self_on); + __syncthreads(); + // Consumer migration referee: old scalar environment-only + empty-env self call. + gc::Environment empty{}; + const bool env_free = BLOCK_CHECK(env, margins.environment_margin, false); + __syncthreads(); + const bool self_free = BLOCK_CHECK(empty, margins.self_margin, self_on); + __syncthreads(); + const bool parity = is_free == (env_free && self_free); + #undef BLOCK_CHECK __syncthreads(); gc::collision_distance(s_dist, s_normal, d_q, m, env, s_pos, s_r, nullptr); __syncthreads(); @@ -76,12 +86,15 @@ __global__ void cmp_kernel(const T *d_q, const grid::robotModel *m, if (threadIdx.x < 32) { grid::ee_pose_inner_warp(s_jointX, s_XmatsHom, d_q, 0); __syncwarp(); - w_free = gc::warp::config_free(s_jointX, env, w_scratch, margin, self_on) ? 1 : 0; + w_free = gc::warp::config_free(s_jointX, env, w_scratch, margins, self_on) ? 1 : 0; + const bool we = gc::warp::config_free(s_jointX, env, w_scratch, margins.environment_margin, false); + const bool ws = gc::warp::config_free(s_jointX, empty, w_scratch, margins.self_margin, self_on); + if (w_free != int(we && ws)) w_free = -2; gc::warp::collision_distance(s_jointX, env, w_dist, w_normal, w_scratch); } __syncthreads(); if (threadIdx.x == 0) { - *d_block_free = is_free ? 1 : 0; + *d_block_free = parity ? int(is_free) : -2; *d_warp_free = w_free; for (int i = 0; i < NS; ++i) { d_block_dist[i] = s_dist[i]; d_warp_dist[i] = w_dist[i]; d_self_dist[i] = s_sdist[i]; } for (int i = 0; i < 3*NS; ++i) { d_block_normal[i] = s_normal[i]; d_warp_normal[i] = w_normal[i]; } @@ -121,6 +134,10 @@ int main(int argc, char **argv){ const float margin = argc > 6 ? (float)atof(argv[6]) : 0.f; const int check_self = argc > 7 ? atoi(argv[7]) : 1; const int boundary = argc > 8 ? atoi(argv[8]) : 0; + // Optional explicit overrides; zero is a real override, not an inheritance sentinel. + const auto overrides = gc::make_collision_margins(margin, + argc > 9 ? std::optional(atof(argv[9])) : std::nullopt, + argc > 10 ? std::optional(atof(argv[10])) : std::nullopt); const float eps = 1e-5f; // the rounding band between squared-gap tests and square-root distances const float step = 1e-4f; // the boundary sweep's offset on either side of the threshold const bool exact_ok = exact_boundary_rule(); @@ -150,7 +167,8 @@ int main(int argc, char **argv){ int n_cap = urand() < 0.6f ? 1 : 0, n_cub = urand() < 0.6f ? 1 : 0; // one launch at a given margin; returns the two verdicts and fills the distance vectors auto launch = [&](float mg, int &bf, int &wf) -> int { - cmp_kernel<<<1, threads, smem>>>(d_q, d_m, d_sph, n_sph, d_cap, n_cap, d_cub, n_cub, mg, check_self, d_bf, d_wf, d_bd, d_wd, d_bn, d_wn, d_sd); + const gc::CollisionMargins effective = argc > 9 ? overrides : gc::CollisionMargins(mg); + cmp_kernel<<<1, threads, smem>>>(d_q, d_m, d_sph, n_sph, d_cap, n_cap, d_cub, n_cub, effective, check_self, d_bf, d_wf, d_bd, d_wd, d_bn, d_wn, d_sd); CK(cudaDeviceSynchronize()); CK(cudaMemcpy(&bf, d_bf, sizeof(int), cudaMemcpyDeviceToHost)); CK(cudaMemcpy(&wf, d_wf, sizeof(int), cudaMemcpyDeviceToHost)); CK(cudaMemcpy(bd.data(), d_bd, NS*sizeof(T), cudaMemcpyDeviceToHost)); CK(cudaMemcpy(wd.data(), d_wd, NS*sizeof(float), cudaMemcpyDeviceToHost)); @@ -166,8 +184,13 @@ int main(int argc, char **argv){ // reference clearance: environment on every sphere, self pairs only when they are checked float c = FLT_MAX; for (int i = 0; i < NS; ++i) { c = fminf(c, (float)bd[i]); if (check_self) c = fminf(c, (float)sd[i]); } + float residual = FLT_MAX; + for (int i = 0; i < NS; ++i) { + residual = fminf(residual, bd[i] - overrides.environment_margin); + if (check_self) residual = fminf(residual, sd[i] - overrides.self_margin); + } int ref = -1; // -1 = inside the rounding band - if (c > margin + eps) ref = 1; else if (c < margin - eps) ref = 0; + if (residual > eps) ref = 1; else if (residual < -eps) ref = 0; if (bf != wf) ++n_mismatch; if (ref < 0) ++n_band; else if (bf != ref) ++n_ref_mismatch; if (!bf) ++n_hit; diff --git a/test/cuda_equivalents/cuda_constant_derivatives_runner.cu b/test/cuda_equivalents/cuda_constant_derivatives_runner.cu new file mode 100644 index 00000000..96917f57 --- /dev/null +++ b/test/cuda_equivalents/cuda_constant_derivatives_runner.cu @@ -0,0 +1,63 @@ +#include "grid.cuh" +#include +#include +#include +using T = DTYPE; +constexpr int NV = grid::NUM_VEL; +constexpr int NB = grid::NUM_BODIES; +#define CK(x) do { auto e=(x); if(e!=cudaSuccess) { fprintf(stderr,"%s: %s\n",#x,cudaGetErrorString(e)); return 2; } } while(0) + +__global__ void gradient(T *out, const T *in, const grid::robotModel *model, bool internal) { + __shared__ T vaf[18*NB], xi[72*NB], scratch[ID_TEMP]; + __shared__ int topology[grid::TOPOLOGY_HELPERS_COUNT ? grid::TOPOLOGY_HELPERS_COUNT : 1]; + __shared__ T guarded[2*NV*NV+2]; + for (int i=threadIdx.x;i(guarded+1,in,v,vaf,a,xi,topology,scratch, + nullptr,nullptr,model,nullptr,T(-9.81)); + __syncthreads(); + if(internal) { + if(threadIdx.x==0) {out[0]=123;out[36*NV*NB+1]=456;} + for(int i=threadIdx.x;i<36*NV*NB;i+=blockDim.x) out[i+1]=scratch[i]; + } else for(int i=threadIdx.x;i<2*NV*NV+2;i+=blockDim.x) out[i]=guarded[i]; +} + +__global__ void frame(T *out, const T *in, const grid::robotModel *model, int mode) { + __shared__ T guarded[6*NV+2]; + for(int i=threadIdx.x;i<6*NV+2;i+=blockDim.x) guarded[i]=T(NAN); + if(threadIdx.x==0) { guarded[0]=123; guarded[6*NV+1]=456; } + __syncthreads(); + if(mode==1) grid::frame_jacobian_device(guarded+1,NB-1,2,in,model); + else grid::frame_jacobian_dot_device(guarded+1,NB-1,2,in,NV ? in+NV : nullptr,model); + __syncthreads(); + for(int i=threadIdx.x;i<6*NV+2;i+=blockDim.x) out[i]=guarded[i]; +} + +int main(int argc,char **argv) { + if(argc!=3 && argc!=4) return 1; + int threads=atoi(argv[1]), mode=atoi(argv[2]); + T *in=nullptr,*out=nullptr; + grid::robotModel *model=nullptr; CK(grid::init_robotModel_checked(&model)); + if constexpr(NV>0) { + T h[3]={T(.35),T(.23),T(-.31)}; + if(argc==4) h[1]=h[2]=T(NAN); + CK(cudaMalloc(&in,sizeof(h))); CK(cudaMemcpy(in,h,sizeof(h),cudaMemcpyHostToDevice)); + } + int count=(mode==3 ? 36*NV*NB : mode==0 ? 2*NV*NV : 6*NV)+2; + CK(cudaMalloc(&out,count*sizeof(T))); + if(mode==0 || mode==3) gradient<<<1,threads>>>(out,in,model,mode==3); + else { + // Existing public kernel sizer safely covers the nested device carve. + size_t bytes=grid::FRAME_JACOBIAN_DOT_DYNAMIC_SHARED_MEM_BYTES(); + CK(cudaFuncSetAttribute(frame,cudaFuncAttributeMaxDynamicSharedMemorySize,int(bytes))); + frame<<<1,threads,bytes>>>(out,in,model,mode); + } + CK(cudaGetLastError()); CK(cudaDeviceSynchronize()); + std::vector host(count); CK(cudaMemcpy(host.data(),out,count*sizeof(T),cudaMemcpyDeviceToHost)); + for(T x:host) printf("%.17g ",double(x)); puts(""); + CK(cudaFree(in)); CK(cudaFree(out)); CK(grid::free_robotModel_checked(model)); +} diff --git a/test/cuda_equivalents/cuda_constant_dynamics_audit_runner.cu b/test/cuda_equivalents/cuda_constant_dynamics_audit_runner.cu new file mode 100644 index 00000000..6f80b3ff --- /dev/null +++ b/test/cuda_equivalents/cuda_constant_dynamics_audit_runner.cu @@ -0,0 +1,54 @@ +#include "grid.cuh" +#include +#include +#include +using T=DTYPE; +constexpr int NQ=grid::NUM_POS, NV=grid::NUM_VEL, NB=grid::NUM_BODIES; +#define CK(x) do{auto e=(x);if(e!=cudaSuccess){fprintf(stderr,"%s: %s\n",#x,cudaGetErrorString(e));return 2;}}while(0) +namespace grid { +template __device__ void audit_public_body_layout(T *s_idsva_so){ +BODY_OUTPUT_REPAIR +} +} +// The SO inners explicitly permit global output (their normal spill path). +// Use private per-launch output, with guards and NaN-initialized scratch. +template __global__ void probe(T *out,const T *input,T *workspace,const grid::robotModel *model){ + constexpr int TEMP=MODE==0?CRBA_TEMP:MODE==1?ID_TEMP:MODE==2?BODY_TEMP:WORLD_TEMP; + __shared__ T xi[72*NB],vaf[18*NB],a[NV]; + extern __shared__ T scratch[]; + __shared__ int topology[grid::TOPOLOGY_HELPERS_COUNT?grid::TOPOLOGY_HELPERS_COUNT:1]; + const T *q=input,*v=input+NQ; + for(int i=threadIdx.x;i(xi,q,topology,model,scratch); + grid::crba_inner(out+1,q,v,xi,topology,scratch,nullptr,T(-9.81)); + } + if constexpr(MODE==1) grid::inverse_dynamics_gradient_device_qdd(out+1,q,v,vaf,a,xi,topology,scratch,nullptr,nullptr,model,nullptr,T(-9.81)); + if constexpr(MODE==2){ + grid::idsva_so_body_frame_inner(out+1,q,v,a,xi,topology,scratch,workspace,model,T(-9.81)); + grid::audit_public_body_layout(out+1); + } + if constexpr(MODE==3) grid::idsva_so_world_frame_inner(out+1,q,v,a,xi,topology,scratch,nullptr,model,T(-9.81)); +} +int main(int argc,char **argv){ + if(argc!=4)return 1; + int threads=atoi(argv[1]),mode=atoi(argv[2]); + std::vector input(NQ+2*NV); + FILE *f=fopen(argv[3],"r");if(!f)return 3; + for(auto &x:input){double v;if(fscanf(f,"%lf",&v)!=1)return 4;x=T(v);}fclose(f); + int count=(mode==0?NV*NV:mode==1?2*NV*NV:4*NV*NV*NV)+2; + std::vector host(count,T(NAN));host.front()=123;host.back()=456; + T *in,*out,*workspace=nullptr;grid::robotModel *model=nullptr;CK(grid::init_robotModel_checked(&model)); + if constexpr(GRAV_TEMP>0){CK(cudaMalloc(&workspace,GRAV_TEMP*sizeof(T)));CK(cudaMemset(workspace,0xff,GRAV_TEMP*sizeof(T)));} + CK(cudaMalloc(&in,input.size()*sizeof(T)));CK(cudaMemcpy(in,input.data(),input.size()*sizeof(T),cudaMemcpyHostToDevice)); + CK(cudaMalloc(&out,count*sizeof(T)));CK(cudaMemcpy(out,host.data(),count*sizeof(T),cudaMemcpyHostToDevice)); + if(mode==0){CK(cudaFuncSetAttribute(probe<0>,cudaFuncAttributeMaxDynamicSharedMemorySize,CRBA_TEMP*sizeof(T)));probe<0><<<1,threads,CRBA_TEMP*sizeof(T)>>>(out,in,workspace,model);} + if(mode==1){CK(cudaFuncSetAttribute(probe<1>,cudaFuncAttributeMaxDynamicSharedMemorySize,ID_TEMP*sizeof(T)));probe<1><<<1,threads,ID_TEMP*sizeof(T)>>>(out,in,workspace,model);} + if(mode==2){CK(cudaFuncSetAttribute(probe<2>,cudaFuncAttributeMaxDynamicSharedMemorySize,BODY_TEMP*sizeof(T)));probe<2><<<1,threads,BODY_TEMP*sizeof(T)>>>(out,in,workspace,model);} + if(mode==3){CK(cudaFuncSetAttribute(probe<3>,cudaFuncAttributeMaxDynamicSharedMemorySize,WORLD_TEMP*sizeof(T)));probe<3><<<1,threads,WORLD_TEMP*sizeof(T)>>>(out,in,workspace,model);} + CK(cudaGetLastError());CK(cudaDeviceSynchronize());CK(cudaMemcpy(host.data(),out,count*sizeof(T),cudaMemcpyDeviceToHost)); + for(auto x:host)printf("%.17g ",double(x));puts(""); + CK(cudaFree(in));CK(cudaFree(out));CK(cudaFree(workspace));CK(grid::free_robotModel_checked(model)); +} diff --git a/test/cuda_equivalents/cuda_constant_second_order_runner.cu b/test/cuda_equivalents/cuda_constant_second_order_runner.cu new file mode 100644 index 00000000..091dd737 --- /dev/null +++ b/test/cuda_equivalents/cuda_constant_second_order_runner.cu @@ -0,0 +1,47 @@ +#include "grid.cuh" +#include +#include +#include +using T = DTYPE; +constexpr int NV = grid::NUM_VEL; +#define CK(x) do { auto e=(x); if(e!=cudaSuccess) { fprintf(stderr,"%s: %s\n",#x,cudaGetErrorString(e)); return 2; } } while(0) +// Compose the existing inners without the generated kernels' 64-thread launch +// bound. This tests the device contract at 256 threads, not an invalid launch. +__global__ void probe(T *out, const T *in, const grid::robotModel *model, int mode) { + __shared__ T xi[72*grid::NUM_BODIES], scratch[BODY_TEMP > WORLD_TEMP ? BODY_TEMP : WORLD_TEMP]; + __shared__ T qdd[NV ? NV : 1]; + __shared__ int topology[grid::TOPOLOGY_HELPERS_COUNT ? grid::TOPOLOGY_HELPERS_COUNT : 1]; + __shared__ T result[4*NV*NV*NV+NV*NV+2]; + int n=mode==2 ? NV*NV : 4*NV*NV*NV; + for(int i=threadIdx.x;i(result+1,in,v,NV ? qdd : nullptr,xi,topology,scratch,nullptr,model,T(-9.81)); + else if(mode==1) grid::idsva_so_world_frame_inner(result+1,in,v,NV ? qdd : nullptr,xi,topology,scratch,nullptr,model,T(-9.81)); + else grid::coriolis_matrix_device(result+1,in,v,model,T(-9.81)); + __syncthreads(); + for(int i=threadIdx.x;i *model=nullptr; CK(grid::init_robotModel_checked(&model)); + if constexpr(NV>0) { + T h[3]={T(.35),T(.23),T(-.31)}; + CK(cudaMalloc(&in,sizeof(h))); CK(cudaMemcpy(in,h,sizeof(h),cudaMemcpyHostToDevice)); + } + int count=(mode==2 ? NV*NV : 4*NV*NV*NV)+2; + std::vector host(count,T(NAN)); host.front()=123; host.back()=456; + CK(cudaMalloc(&out,count*sizeof(T))); + CK(cudaMemcpy(out,host.data(),count*sizeof(T),cudaMemcpyHostToDevice)); + size_t bytes=grid::CORIOLIS_MATRIX_DYNAMIC_SHARED_MEM_BYTES(); + CK(cudaFuncSetAttribute(probe,cudaFuncAttributeMaxDynamicSharedMemorySize,int(bytes))); + probe<<<1,threads,bytes>>>(out,in,model,mode); + CK(cudaGetLastError()); CK(cudaDeviceSynchronize()); + CK(cudaMemcpy(host.data(),out,count*sizeof(T),cudaMemcpyDeviceToHost)); + for(T x:host) printf("%.17g ",double(x)); puts(""); + CK(cudaFree(in)); CK(cudaFree(out)); CK(grid::free_robotModel_checked(model)); +} diff --git a/test/cuda_equivalents/cuda_glass_handoff_runner.cu b/test/cuda_equivalents/cuda_glass_handoff_runner.cu new file mode 100644 index 00000000..016fdc03 --- /dev/null +++ b/test/cuda_equivalents/cuda_glass_handoff_runner.cu @@ -0,0 +1,77 @@ +// Restricted cooperative callers of the GLASS bodies embedded by GRiD. +// Each block owns its arena. No timing, aggregate registration, or aliasing. +#define GRID_HEADER +#include "grid.cuh" +#include +#include +#include +#include + +using T = TEST_SCALAR; +constexpr int N = 7, NN = N*N; +constexpr int SCRATCH = 2*NN + 2*N + 4; +constexpr int ARENA = 2*NN + N + SCRATCH + 2; +constexpr int INPUT = 3*NN + N, OUTPUT = N + 2*NN + 3; + +__device__ __forceinline__ void operate(T* __restrict__ arena, + const T* __restrict__ in, + T* __restrict__ out) { + T *a = arena + 1, *b = a + 2*NN, *scratch = b + N; + for (int op = 0; op < 3; ++op) { + for (int i = threadIdx.x; i < ARENA; i += blockDim.x) arena[i] = T(NAN); + if (threadIdx.x == 0) arena[0] = arena[ARENA-1] = T(1234); + __syncthreads(); + for (int i = threadIdx.x; i < NN; i += blockDim.x) { + a[i] = in[op*NN+i]; + if (op == 2) a[NN+i] = T(i/N == i%N); + } + for (int i = threadIdx.x; i < N; i += blockDim.x) b[i] = in[3*NN+i]; + __syncthreads(); + if (op == 0) grid::glass::posv(a, b); + if (op == 1) grid::glass::eig_clamp(a, T(0.125), scratch); + if (op == 2) grid::glass::inv_pivoted(a, scratch); + __syncthreads(); + const int count = op == 0 ? N : NN; + const int offset = op == 0 ? 0 : N + (op-1)*NN; + for (int i = threadIdx.x; i < count; i += blockDim.x) + out[offset+i] = op == 0 ? b[i] : a[(op == 2 ? NN : 0)+i]; + if (threadIdx.x == 0) + out[N+2*NN+op] = (arena[0] == T(1234) && arena[ARENA-1] == T(1234)) ? T(1234) : T(NAN); + __syncthreads(); + } +} + +template +__global__ void run(T* __restrict__ global, const T* __restrict__ in, + T* __restrict__ out) { + __shared__ T shared[ARENA]; + operate(GLOBAL ? global + blockIdx.x*ARENA : shared, + in + blockIdx.x*INPUT, out + blockIdx.x*OUTPUT); +} + +#define CK(call) do { auto e=(call); if(e != cudaSuccess) { \ + fprintf(stderr, "%s: %s\n", #call, cudaGetErrorString(e)); return 2; } } while(0) + +int main(int argc, char** argv) { + if (argc != 3) return 2; + const int threads = atoi(argv[1]); + const bool global = atoi(argv[2]); + if (threads < 1 || threads > 256) return 2; + std::vector in(2*INPUT), out(2*OUTPUT); + for (auto& x : in) { double value; if (scanf("%lf", &value) != 1) return 2; x=T(value); } + T *di, *dw, *dout; + CK(cudaMalloc(&di, in.size()*sizeof(T))); + CK(cudaMalloc(&dw, 2*ARENA*sizeof(T))); + CK(cudaMalloc(&dout, out.size()*sizeof(T))); + CK(cudaMemcpy(di, in.data(), in.size()*sizeof(T), cudaMemcpyHostToDevice)); + for (int repeat=0; repeat<3; ++repeat) { + CK(cudaMemset(dout, 0xff, out.size()*sizeof(T))); + if (global) run<<<2,threads>>>(dw,di,dout); + else run<<<2,threads>>>(dw,di,dout); + CK(cudaGetLastError()); CK(cudaDeviceSynchronize()); + CK(cudaMemcpy(out.data(), dout, out.size()*sizeof(T), cudaMemcpyDeviceToHost)); + for (auto x : out) printf("%.17g ", double(x)); + printf("\n"); + } + CK(cudaFree(di)); CK(cudaFree(dw)); CK(cudaFree(dout)); +} diff --git a/test/cuda_equivalents/cuda_harness.py b/test/cuda_equivalents/cuda_harness.py index e37bb2ef..9ce61e32 100644 --- a/test/cuda_equivalents/cuda_harness.py +++ b/test/cuda_equivalents/cuda_harness.py @@ -502,7 +502,7 @@ def _linalg_backend_compile_flags(arch: str) -> tuple[str, list[str], str]: function keeps the (cxx_standard, flags, note) tuple shape so callers don't need updating.""" del arch # unused; sm/arch is handled by the outer compile harness - return "-std=c++11", ["-DGRID_CUDA_LINALG_BACKEND=GRID_LINALG_GLASS"], "glass" + return "-std=c++17", ["-DGRID_CUDA_LINALG_BACKEND=GRID_LINALG_GLASS"], "glass" def _parse_const_ints(header_path: Path) -> dict[str, int]: diff --git a/test/cuda_equivalents/cuda_joint_transform_suffix_runner.cu b/test/cuda_equivalents/cuda_joint_transform_suffix_runner.cu new file mode 100644 index 00000000..9a7847a9 --- /dev/null +++ b/test/cuda_equivalents/cuda_joint_transform_suffix_runner.cu @@ -0,0 +1,58 @@ +// Two independent warp anchors, divergent scalar overrides and suffix reuse. +#include "grid.cuh" +#include +#include +#include +#include +using T=TEST_SCALAR; +constexpr int N=grid::model::nj, FRAME=TEST_FRAME; +#define CK(x) do {auto e=(x);if(e!=cudaSuccess){fprintf(stderr,"%s\n",cudaGetErrorString(e));exit(2);}}while(0) +__device__ __forceinline__ void suffix(T*out,const T*world,const T*local,int changed,T value) { + T W[16], next[16], joint[16]; + for(int k=0;k<16;++k) W[k]=changed?world[16*(changed-1)+k]:T(k%5==0); + for(int j=changed;j(joint,local,j,value); +#else + if(!grid::model::joint_transform_scalar(j,value,joint)) asm("trap;"); +#endif + transform=joint; + } else transform=local+16*j; + grid::glass::thread::gemm(T(1),W,transform,next); + for(int k=0;k<16;++k) W[k]=next[k]; + } + if(!grid::model::compose_frame_from_body(FRAME,N-1,W,out)) asm("trap;"); +} +__global__ void probe(const T*input,T*output) { + __shared__ T anchors[2*32*N]; + const int lane=threadIdx.x%32,warp=threadIdx.x/32; + T* a=anchors+warp*32*N; + for(int repeat=0;repeat<2;++repeat) { + if(lane==0)for(int k=0;k<32*N;++k)a[k]=input[warp*32*N+k]; + __syncwarp(); + if(lane input(2*32*N),out(2*64*16,789); + FILE*f=fopen(argv[1],"rb");if(!f||fread(input.data(),sizeof(T),input.size(),f)!=input.size())return 3;fclose(f); + T*a,*b;CK(cudaMalloc(&a,input.size()*sizeof(T)));CK(cudaMalloc(&b,out.size()*sizeof(T))); + CK(cudaMemcpy(a,input.data(),input.size()*sizeof(T),cudaMemcpyHostToDevice)); + CK(cudaMemcpy(b,out.data(),out.size()*sizeof(T),cudaMemcpyHostToDevice)); + probe<<<1,64>>>(a,b);CK(cudaGetLastError());CK(cudaDeviceSynchronize()); + CK(cudaMemcpy(out.data(),b,out.size()*sizeof(T),cudaMemcpyDeviceToHost)); + cudaFuncAttributes attr{};CK(cudaFuncGetAttributes(&attr,probe)); + printf("registers=%d local_bytes=%zu shared_bytes=%zu\n",attr.numRegs,attr.localSizeBytes,attr.sharedSizeBytes); + f=fopen(argv[2],"wb");if(!f||fwrite(out.data(),sizeof(T),out.size(),f)!=out.size())return 4;fclose(f); + CK(cudaFree(a));CK(cudaFree(b)); +} diff --git a/test/cuda_equivalents/cuda_menu_workspace_runner.cu b/test/cuda_equivalents/cuda_menu_workspace_runner.cu new file mode 100644 index 00000000..506ce4be --- /dev/null +++ b/test/cuda_equivalents/cuda_menu_workspace_runner.cu @@ -0,0 +1,100 @@ +#include "grid.cuh" +#include +#include +#include +#include +#include +using T=TEST_SCALAR; +constexpr int NQ=grid::NUM_POS,NV=grid::NUM_VEL,NX=NQ+NV,TN=2*NV; +constexpr int NU=grid_plant::GRID_PLANT_MENU_NU,NLX=grid_plant::GRID_PLANT_MENU_NLX; +constexpr int COUNT=1+TN+NU+TN*TN+NU*NU,B=2; +#define CK(x) do {auto e=(x);if(e!=cudaSuccess){fprintf(stderr,"%s: %s\n",#x,cudaGetErrorString(e));exit(2);}}while(0) +template using Arena=grid_plant::tracking_cost_menu_arena; +template +__global__ void probe(T *out,int *bad,const T *data,const grid::robotModel*model,int poison) { + using A=Arena; + extern __shared__ __align__(16) unsigned char raw[]; + __shared__ T x[NX],u[NU]; + __shared__ T lower[NX],upper[NX]; + __shared__ int activation[grid_plant::GRID_PLANT_MENU_TERMS]; + __shared__ T eps[grid_plant::GRID_PLANT_MENU_TERMS]; + int tid=threadIdx.x,nt=blockDim.x,b=blockIdx.x; + for(int i=tid;i<64;i+=nt) raw[i]=raw[64+A::bytes()+i]=0x5a; + for(int i=tid;i(raw+64)[i]=poison?T(NAN):0; + for(int i=tid;i(raw); + if(A::bind(raw+65,A::bytes(),a) || A::bind(raw+64,A::bytes()-1,a) || + A::bind(nullptr,A::bytes(),a) || a.s_frame!=reinterpret_cast(raw)) {if(tid==0) atomicAdd(bad,1);return;} + if(!A::bind(raw+64,A::bytes(),a)) {if(tid==0) atomicAdd(bad,1);return;} + const A unchecked=A::carve(raw+64); + if(a.s_frame!=unchecked.s_frame || a.s_scratch!=unchecked.s_scratch) {if(tid==0) atomicAdd(bad,1);return;} + grid_plant::cost_menu_params p{}; + p.activation=activation;p.eps=eps; + p.q_lower=p.qd_lower=p.u_lower=lower;p.q_upper=p.qd_upper=p.u_upper=upper; + p.Ax=data;p.bx=p.Ax+MX*NLX;p.tx=p.bx+MX;p.Wx=p.tx+MX; + p.Au=p.Wx+MX;p.bu=p.Au+MU*NU;p.tu=p.bu+MU;p.Wu=p.tu+MU; + T *v=out+b*COUNT,*gx=v+1,*gu=gx+TN,*hx=gu+NU,*hu=hx+TN*TN; + grid_plant::tracking_cost_menu(v,x,u,p,a.s_frame,a.s_scratch,model); + __syncthreads(); + grid_plant::tracking_cost_menu_gradient(gx,gu,x,u,p,a.s_frame,a.s_scratch,model); + __syncthreads(); + grid_plant::tracking_cost_menu_hessian(hx,hu,x,u,p,a.s_frame,a.s_scratch,model); + __syncthreads(); + // Reuse with ACCUMULATE=true: result must be exactly twice the first value. + grid_plant::tracking_cost_menu(v,x,u,p,a.s_frame,a.s_scratch,model); + __syncthreads(); + grid_plant::tracking_cost_menu_gradient(gx,gu,x,u,p,a.s_frame,a.s_scratch,model); + __syncthreads(); + grid_plant::tracking_cost_menu_hessian(hx,hu,x,u,p,a.s_frame,a.s_scratch,model); + __syncthreads(); + for(int i=tid;i<64;i+=nt) if(raw[i]!=0x5a || raw[64+A::bytes()+i]!=0x5a) atomicAdd(bad,1); +} +template int run(const char*input,const char*output,int threads) { + using A=Arena; + constexpr size_t count=MX*(NLX+3)+MU*(NU+3); + std::vector data(count),result(B*COUNT),first; + FILE*f=fopen(input,"rb");if(!f || fread(data.data(),sizeof(T),count,f)!=count)return 3;fclose(f); + T *d,*out;int *bad; + CK(cudaMalloc(&d,(count?count:1)*sizeof(T)));CK(cudaMalloc(&out,result.size()*sizeof(T)));CK(cudaMalloc(&bad,sizeof(int))); + if(count) CK(cudaMemcpy(d,data.data(),count*sizeof(T),cudaMemcpyHostToDevice)); + grid::robotModel*model=nullptr;CK(grid::init_robotModel_checked(&model)); + constexpr size_t bytes=A::bytes()+128; + cudaFuncAttributes attr;CK(cudaFuncGetAttributes(&attr,probe)); + int cap;CK(cudaDeviceGetAttribute(&cap,cudaDevAttrMaxSharedMemoryPerBlockOptin,0)); + if(bytes+attr.sharedSizeBytes>size_t(cap)) return 4; + CK(cudaFuncSetAttribute(probe,cudaFuncAttributeMaxDynamicSharedMemorySize,bytes)); + printf("menu=%zu scratch=%zu offset=%zu static=%zu registers=%d local=%zu\n",A::bytes(),A::scratch_bytes(),A::scratch_offset(),attr.sharedSizeBytes,attr.numRegs,attr.localSizeBytes); + alignas(16) unsigned char dummy[16]; A view{};view.s_frame=reinterpret_cast(dummy); + if(A::bind(dummy+1,A::bytes(),view) || A::bind(dummy,A::bytes()-1,view) || A::bind(nullptr,A::bytes(),view) || view.s_frame!=reinterpret_cast(dummy)) return 5; + for(int repeat=0;repeat<3;repeat++) { + CK(cudaMemset(out,0xff,result.size()*sizeof(T)));CK(cudaMemset(bad,0,sizeof(int))); + probe<<>>(out,bad,d,model,repeat!=0);CK(cudaGetLastError());CK(cudaDeviceSynchronize()); + int errors;CK(cudaMemcpy(&errors,bad,sizeof(int),cudaMemcpyDeviceToHost));if(errors)return 6; + CK(cudaMemcpy(result.data(),out,result.size()*sizeof(T),cudaMemcpyDeviceToHost)); + for(T v:result)if(!std::isfinite(v))return 7; + if(repeat && result!=first)return 8;first=result; + } + f=fopen(output,"wb");if(!f||fwrite(result.data(),sizeof(T),result.size(),f)!=result.size())return 9;fclose(f); + CK(cudaFree(d));CK(cudaFree(out));CK(cudaFree(bad));CK(grid::free_robotModel_checked(model));return 0; +} +int main(int argc,char**argv) { + if(argc!=5)return 1; + switch(atoi(argv[4])) { + case 0:return run<0,0>(argv[1],argv[2],atoi(argv[3])); + case 1:return run<2*NV-6*(NQ!=NV),grid_plant::GRID_PLANT_MENU_NU>(argv[1],argv[2],atoi(argv[3])); + case 2:return run<4097,0>(argv[1],argv[2],atoi(argv[3])); + case 3:return run<0,4097>(argv[1],argv[2],atoi(argv[3])); + default:return 10; + } +} diff --git a/test/cuda_equivalents/cuda_model_descriptor_runner.cu b/test/cuda_equivalents/cuda_model_descriptor_runner.cu new file mode 100644 index 00000000..30e0db86 --- /dev/null +++ b/test/cuda_equivalents/cuda_model_descriptor_runner.cu @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include "metadata.cuh" + +// Different lanes request different records. Only one lane composes each frame; +// there is no whole-block participation contract in this thread-scope interface. +template __global__ void probe(T *result) { + const int lane = blockIdx.x * blockDim.x + threadIdx.x; + if (lane < grid::model::nj) { + grid::model::joint_record j{}; + if (!grid::model::joint(lane, j)) return; + T *out = result + lane * 45; + out[0]=j.id; out[1]=j.parent_body; out[2]=j.child_body; + out[3]=j.q_start; out[4]=j.v_start; out[5]=j.local_nq; out[6]=j.local_nv; + out[7]=static_cast(j.kind); out[8]=j.owns_coordinates; + out[9]=j.has_axis; out[10]=j.has_pitch; out[11]=j.has_mimic; + out[12]=j.leader_joint; out[13]=j.multiplier; out[14]=j.offset; + for(int k=0;k<3;++k) out[15+k]=j.axis[k]; + out[18]=j.pitch; + for(int k=0;k<16;++k) out[19+k]=j.T_parent_joint_zero[k]; + out[35]=j.has_position_limit[0]; out[36]=j.has_position_limit[1]; + out[37]=j.has_velocity_limit; out[38]=j.has_effort_limit; + out[39]=j.position_limit[0]; out[40]=j.position_limit[1]; + out[41]=j.velocity_limit; out[42]=j.effort_limit; + grid::model::joint_record sentinel{}; sentinel.id=71; + out[43]=grid::model::joint(-1, sentinel); + out[44]=sentinel.id; + } + if (lane < grid::model::frame_count) { + grid::model::frame_record f{}; + if (!grid::model::frame(lane, f)) return; + T *out=result+45*grid::model::nj+lane*38; + out[0]=f.id; out[1]=f.attached_body; out[2]=f.joint_id; + out[3]=static_cast(f.kind); + for(int k=0;k<16;++k) out[4+k]=f.T_body_frame[k]; + T body[16]={0,1,0,0,-1,0,0,0,0,0,1,0,T(.3),T(-.4),T(.2),1}; + T composed[16]; for (int k=0;k<16;++k) composed[k]=T(91); + out[20]=grid::model::compose_frame_from_body(lane,f.attached_body,body,composed); + for(int k=0;k<16;++k) out[21+k]=composed[k]; + // A wrong body must not overwrite output or run hidden synchronization. + out[37]=grid::model::compose_frame_from_body(lane,f.attached_body+1,body,composed); + for(int k=0;k<16;++k) if(out[21+k]!=composed[k]) out[37]=T(99); + } +} + +template int run(int threads) { + const size_t n=45*grid::model::nj+38*grid::model::frame_count; + T *allocation=nullptr; + if(cudaMalloc(&allocation,(n+2)*sizeof(T))!=cudaSuccess) return 2; + std::vector values(n+2,T(-123)); + if(cudaMemcpy(allocation,values.data(),values.size()*sizeof(T),cudaMemcpyHostToDevice)!=cudaSuccess) return 3; + int records=grid::model::nj>grid::model::frame_count?grid::model::nj:grid::model::frame_count; + probe<<<(records+threads-1)/threads,threads>>>(allocation+1); + if(cudaGetLastError()!=cudaSuccess || cudaDeviceSynchronize()!=cudaSuccess) return 4; + if(cudaMemcpy(values.data(),allocation,values.size()*sizeof(T),cudaMemcpyDeviceToHost)!=cudaSuccess) return 5; + if(cudaFree(allocation)!=cudaSuccess || values.front()!=T(-123) || values.back()!=T(-123)) return 6; + for(size_t k=1;k<=n;++k) std::printf("%.17g\n",double(values[k])); + return 0; +} +int main(int argc,char **argv) { + if(argc!=3) return 1; + return std::atoi(argv[2])?run(std::atoi(argv[1])):run(std::atoi(argv[1])); +} diff --git a/test/cuda_equivalents/cuda_model_primitives_runner.cu b/test/cuda_equivalents/cuda_model_primitives_runner.cu new file mode 100644 index 00000000..7b854b97 --- /dev/null +++ b/test/cuda_equivalents/cuda_model_primitives_runner.cu @@ -0,0 +1,72 @@ +#include "grid.cuh" +#include +#include +#include +#include +using T=TEST_SCALAR; +constexpr int Q=grid::model::nq,N=grid::model::nv,J=grid::model::nj; +constexpr int WIDTH=N+2*N*N+32*J, SAMPLES=35; +#define CK(x) do { auto e=(x); if(e!=cudaSuccess){fprintf(stderr,"%s: %s\n",#x,cudaGetErrorString(e));exit(2);} } while(0) +__global__ void probe(const T *input,T *output,int *errors) { + // Divergent threads, non-warp-aligned batch, separate blocks/output slots. + int sample=blockIdx.x*blockDim.x+threadIdx.x; + if(sample>=SAMPLES) return; + const T *a=Q?input+sample*2*Q:nullptr,*b=Q?a+Q:nullptr; + T *out=output+sample*(WIDTH+2)+1; + grid::configuration_difference(a,b,out); + grid::configuration_difference_jacobians(a,b,out+N,out+N+N*N); + for(int k=0;krec.nv*rec.nv)&&(A[i]!=T(321)||B[i]!=T(321))) { if(sample==2) printf("overrun %d %d\n",k,i); atomicAdd(errors,1); } + } + T invalid[16]; for(int i=0;i<16;++i) invalid[i]=T(321); + if(grid::configuration_difference_jacobian_block(-1,a,b,invalid,invalid+8,0) || + grid::model::joint_transform(-1,a,invalid) || + grid::model::joint_transform_scalar(J,T(.2),invalid)) atomicAdd(errors,1); + for(int i=0;i<16;++i) if(invalid[i]!=T(321)) { if(sample==2) printf("invalid write %d\n",i); atomicAdd(errors,1); } + for(int slot=0;slot input(SAMPLES*2*Q),out(SAMPLES*(WIDTH+2)); + FILE*f=fopen(argv[1],"rb"); if(!f||fread(input.data(),sizeof(T),input.size(),f)!=input.size()) return 3;fclose(f); + T *din,*dout;int *bad; + CK(cudaMalloc(&din,std::max(size_t(1),input.size())*sizeof(T))); CK(cudaMalloc(&dout,out.size()*sizeof(T)));CK(cudaMalloc(&bad,sizeof(int))); + CK(cudaMemcpy(din,input.data(),input.size()*sizeof(T),cudaMemcpyHostToDevice)); + std::vector first; + for(int repeat=0;repeat<2;++repeat) { + std::fill(out.begin(),out.end(),T(789));CK(cudaMemcpy(dout,out.data(),out.size()*sizeof(T),cudaMemcpyHostToDevice));CK(cudaMemset(bad,0,sizeof(int))); + probe<<<(SAMPLES+threads-1)/threads,threads>>>(din,dout,bad);CK(cudaGetLastError());CK(cudaDeviceSynchronize()); + int nbad; CK(cudaMemcpy(&nbad,bad,sizeof(int),cudaMemcpyDeviceToHost));if(nbad){fprintf(stderr,"contract errors: %d\n",nbad);return 4;} + CK(cudaMemcpy(out.data(),dout,out.size()*sizeof(T),cudaMemcpyDeviceToHost)); + for(int s=0;s menu_arena; + if (!decltype(menu_arena)::bind(menu_storage, decltype(menu_arena)::bytes(), menu_arena)) return; + T *s_frame = menu_arena.s_frame, *s_arena = menu_arena.s_scratch; +#else __shared__ T s_frame[grid_plant::GRID_PLANT_FRAME_SCRATCH_COUNT]; extern __shared__ __align__(16) T s_arena[]; +#endif __shared__ T s_out[1], s_grad[NX], s_hess[NX * NX]; const int tid = threadIdx.x + threadIdx.y * blockDim.x; const int nth = blockDim.x * blockDim.y; @@ -931,6 +938,12 @@ void run() { if (grid_plant::FRAME_EVAL_DYNAMIC_SHARED_MEM_BYTES() > menu_dyn) menu_dyn = grid_plant::FRAME_EVAL_DYNAMIC_SHARED_MEM_BYTES(); #if defined(GRID_PLANT_HAS_GRAVITY_TORQUE) if (grid_plant::GRAVITY_TORQUE_DYNAMIC_SHARED_MEM_BYTES() > menu_dyn) menu_dyn = grid_plant::GRAVITY_TORQUE_DYNAMIC_SHARED_MEM_BYTES(); +#endif +#if defined(GRID_PLANT_HAS_COST_MENU) + // Other raw terms in this diagnostic kernel reuse only the scratch region. + menu_dyn += grid_plant::tracking_cost_menu_arena::scratch_offset(); + if (grid_plant::tracking_cost_menu_arena::bytes() > menu_dyn) + menu_dyn = grid_plant::tracking_cost_menu_arena::bytes(); #endif cudaFuncSetAttribute(cost_menu_kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, (int)menu_dyn); cost_menu_kernel<<<1, nthreads, menu_dyn>>>(g_q, g_qd, g_u, d_robotModel, gravity, o_mas, o_mai, o_mae, o_mlx, o_mlu, o_mq, o_mm, o_mg, diff --git a/test/cuda_equivalents/cuda_plant_workspace_runner.cu b/test/cuda_equivalents/cuda_plant_workspace_runner.cu new file mode 100644 index 00000000..2a729ed9 --- /dev/null +++ b/test/cuda_equivalents/cuda_plant_workspace_runner.cu @@ -0,0 +1,187 @@ +// Composed Hessian and gradient, two invocations, live outer storage, no timing. +#include "grid.cuh" +#include +#include +#include +#include +#include +#include +using T = TEST_SCALAR; +constexpr int N=grid::NUM_VEL, NX=grid::NUM_POS+N, NH=18*N*N*N, NJ=6*N*N, B=2; +#ifndef TEST_INTEGRATOR +#define TEST_INTEGRATOR EULER +#endif +#ifndef TEST_RUNTIME_TRANSFORM +#define TEST_RUNTIME_TRANSFORM 0 +#endif +#ifndef TEST_CROSS_KERNEL_ROUNDOFF +#define TEST_CROSS_KERNEL_ROUNDOFF 0 +#endif +// Brian approved this narrow cross-kernel criterion on 2026-10-30. It does +// not alter the independent oracle, repeatability or thread-count checks. +bool stock_matches(T a,T b) { + if(!std::isfinite(a)||!std::isfinite(b)) return false; + if(a==b) return true; + if constexpr(TEST_CROSS_KERNEL_ROUNDOFF && sizeof(T)==sizeof(double)) + return std::abs(a-b)<=T(64)*std::numeric_limits::epsilon()*std::max(std::abs(a),std::abs(b)); + return false; +} +constexpr auto IT=grid::IntegratorType::TEST_INTEGRATOR; +using L=grid_plant::plant_hessian_layout; +template using Arena=grid_plant::plant_step_hessian_arena; +constexpr size_t GB=grid_plant::PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES(); +template constexpr size_t SH=64+(Arena::shared_bytes()>GB?Arena::shared_bytes():GB); +template constexpr size_t SLOT=Arena::workspace_bytes()+128; +#define CK(x) do { auto e=(x); if(e!=cudaSuccess) {fprintf(stderr,"%s: %s\n",#x,cudaGetErrorString(e));exit(2);} } while(0) +template +__global__ void composed(T *out, unsigned char *workspace, const T *z, + const grid::robotModel*model, int *bad, int poison) { + extern __shared__ __align__(16) unsigned char raw[]; + __shared__ T xi[grid::DYNAMICS_XI_T_COUNT]; + __shared__ T temp[grid::FD_INNER_SMEM_BYTES()/sizeof(T)]; + __shared__ T qdd[N]; + volatile T *vx=xi,*vt=temp,*vq=qdd; + const int tid=threadIdx.x, nt=blockDim.x, b=blockIdx.x; + const T *x=z+b*(NX+N), *u=x+NX; + for(int i=tid;i-64)/sizeof(T);i+=nt) reinterpret_cast(raw+64)[i]=poison?T(NAN):0; + __syncthreads(); + Arena a{}; + if(!Arena::bind(raw+64,SH-64,workspace+b*SLOT+64,Arena::workspace_bytes(),a)) { + if(tid==0) atomicAdd(bad,1); return; + } + for(int i=tid;i(model,T(-9.81),T(.01)); + __syncthreads(); + for(int i=tid;i(raw+64)[i]=poison?T(NAN):0; + __syncthreads(); + grid_plant::plant_step_gradient_arena g; + if(!decltype(g)::bind(raw+64,GB,g)) {if(tid==0) atomicAdd(bad,1); return;} + for(int i=tid;i(g.s_dAB,g.s_x,g.s_u,g.s_df_du,g.s_dc_du,g.s_vaf,g.s_Minv,g.s_qdd, + g.s_q_orig,g.s_qd_orig,g.s_stage_grad_qdd,g.s_D_qdd_stage,g.s_dInt_q_6x6,g.s_dInt_v_6x6, + g.s_XImats,g.s_topology_helpers,g.s_temp,nullptr,nullptr,model,T(-9.81),T(.01)); + __syncthreads(); + for(int i=tid;i(xi,x,g.s_topology_helpers,model,temp); +#else + grid::load_update_XImats_helpers(xi,x,g.s_topology_helpers,model,temp); +#endif + __syncthreads(); + grid::forward_dynamics_inner(qdd,x,x+N,u,xi,g.s_topology_helpers,temp,nullptr,nullptr,T(-9.81)); + __syncthreads(); + for(int i=tid;i int run(const char *input,const char *output,int threads) { + using A=Arena; + if(!stock_matches(T(1),T(1)) || stock_matches(T(NAN),T(NAN)) || + stock_matches(T(INFINITY),T(INFINITY)) || stock_matches(T(0),T(1e-20)) || + stock_matches(T(1),T(1.001))) return 15; + if constexpr(TEST_CROSS_KERNEL_ROUNDOFF && sizeof(T)==sizeof(double)) { + if(!stock_matches(T(1),std::nextafter(T(1),T(2))) || stock_matches(T(1),T(1)+T(128)*std::numeric_limits::epsilon())) return 15; + } + std::vector z(B*(NX+N)),values(B*(NH+NJ+NX)),first; + FILE*f=fopen(input,"rb"); if(!f||fread(z.data(),sizeof(T),z.size(),f)!=z.size()) return 3; fclose(f); + T *dz,*out,*stock,*stockx,*stocku; unsigned char *ws,*stockws; int *bad; + CK(cudaMalloc(&dz,z.size()*sizeof(T)));CK(cudaMalloc(&out,values.size()*sizeof(T))); + CK(cudaMalloc(&ws,B*SLOT));CK(cudaMalloc(&bad,sizeof(int))); + CK(cudaMemcpy(dz,z.data(),z.size()*sizeof(T),cudaMemcpyHostToDevice)); + CK(cudaMalloc(&stock,B*NH*sizeof(T))); CK(cudaMalloc(&stockx,B*NX*sizeof(T))); CK(cudaMalloc(&stocku,B*N*sizeof(T))); + constexpr size_t stock_stride=grid_plant::PLANT_HESSIAN_WORKSPACE_BYTES_PER_TIMESTEP(); + constexpr size_t stock_sh=grid_plant::INTEGRATOR_HESSIAN_DYNAMIC_SHARED_MEM_BYTES(); + CK(cudaMalloc(&stockws,B*stock_stride)); + for(int b=0;b*model=nullptr;CK(grid::init_robotModel_checked(&model)); + // Pure host views: exercise refusal without ever launching invalid storage. + alignas(16) unsigned char host[16]; + A a{}; a.s_x=reinterpret_cast(host); A original=a; + if(A::bind(nullptr,A::shared_bytes(),ws,A::workspace_bytes(),a) || + A::bind(host,A::shared_bytes()-1,ws,A::workspace_bytes(),a) || + A::bind(host+1,A::shared_bytes(),ws,A::workspace_bytes(),a) || a.s_x!=original.s_x) return 4; + if constexpr(layout==L::deep_spill) { + if(A::bind(host,A::shared_bytes(),nullptr,A::workspace_bytes(),a) || + A::bind(host,A::shared_bytes(),ws,A::workspace_bytes()-1,a) || + A::bind(host,A::shared_bytes(),ws+1,A::workspace_bytes(),a)) return 5; + } + // Backed host storage checks the complete pointer extent (no fake allocation). + void *host_arena=nullptr; if(posix_memalign(&host_arena,16,A::shared_bytes())) return 6; + if(!A::bind(host_arena,A::shared_bytes(),ws,A::workspace_bytes(),a)) return 7; + free(host_arena); + cudaFuncAttributes attr;CK(cudaFuncGetAttributes(&attr,composed)); + int limit;CK(cudaDeviceGetAttribute(&limit,cudaDevAttrMaxSharedMemoryPerBlockOptin,0)); + if(SH+attr.sharedSizeBytes>size_t(limit)) return 8; + CK(cudaFuncSetAttribute(composed,cudaFuncAttributeMaxDynamicSharedMemorySize,SH)); + CK(cudaFuncSetAttribute(grid_plant::plant_step_hessian_kernel,cudaFuncAttributeMaxDynamicSharedMemorySize,stock_sh)); + printf("shared=%zu workspace=%zu outer_dynamic=64 static=%zu regs=%d local=%zu limit=%d\n", + SH,A::workspace_bytes(),attr.sharedSizeBytes,attr.numRegs,attr.localSizeBytes,limit); + for(int repeat=0;repeat<3;repeat++) { + if(repeat<2) { + std::vector bytes(B*SLOT,0xa5); + std::vector poison(A::workspace_bytes()/sizeof(T),repeat?T(NAN):T(0)); + for(int b=0;b+64,poison.data(),A::workspace_bytes()); + CK(cudaMemcpy(ws,bytes.data(),bytes.size(),cudaMemcpyHostToDevice)); + } + CK(cudaMemset(bad,0,sizeof(int)));CK(cudaMemset(out,0xff,values.size()*sizeof(T))); + composed<<)>>>(out,ws,dz,model,bad,repeat!=0); + CK(cudaGetLastError());CK(cudaDeviceSynchronize()); + int errors;CK(cudaMemcpy(&errors,bad,sizeof(int),cudaMemcpyDeviceToHost));if(errors) return 9; + std::vector bytes(B*SLOT);CK(cudaMemcpy(bytes.data(),ws,bytes.size(),cudaMemcpyDeviceToHost)); + for(int b=0;b+i]!=0xa5 || bytes[b*SLOT+64+A::workspace_bytes()+i]!=0xa5) return 10; + CK(cudaMemcpy(values.data(),out,values.size()*sizeof(T),cudaMemcpyDeviceToHost)); + // The stock small-robot kernel is launch-bounded (acrobot: 64). + // The composed caller still executes every requested size, including 256. + const int stock_threads=threads<<>>(stock,stockws,stockx,stocku,NX,N,model,T(-9.81),T(.01),B); + CK(cudaGetLastError());CK(cudaDeviceSynchronize()); + std::vector original(B*NH);CK(cudaMemcpy(original.data(),stock,original.size()*sizeof(T),cudaMemcpyDeviceToHost)); + size_t different=0,rejected=0;double max_difference=0; + for(T v:original) if(!std::isfinite(v)) return 11; + for(T v:values) if(!std::isfinite(v)) return 11; + for(int b=0;b(model)); + CK(cudaFree(stock));CK(cudaFree(stockx));CK(cudaFree(stocku));CK(cudaFree(stockws)); + return 0; +} +int main(int argc,char**argv) { + if(argc!=5) return 1; + return atoi(argv[4]) ? run(argv[1],argv[2],atoi(argv[3])) : run(argv[1],argv[2],atoi(argv[3])); +} diff --git a/test/cuda_equivalents/cuda_prismatic_fixed_target_runner.cu b/test/cuda_equivalents/cuda_prismatic_fixed_target_runner.cu new file mode 100644 index 00000000..c9c112cb --- /dev/null +++ b/test/cuda_equivalents/cuda_prismatic_fixed_target_runner.cu @@ -0,0 +1,65 @@ +// G13: direct block pose/Jacobian and thread/warp FK for the slider fixture. +#include "grid.cuh" +#include +#include +#include + +#ifdef USE_DOUBLE +using Scalar = double; +#else +using Scalar = float; +#endif + +#define CHECK(expr) do { cudaError_t e = (expr); if (e != cudaSuccess) { \ + fprintf(stderr, "%s: %s\n", #expr, cudaGetErrorString(e)); return 2; } } while (0) + +__global__ void evaluate(const Scalar *q, const grid::robotModel *model, Scalar *out) { + __shared__ Scalar sq[grid::NUM_POS]; + __shared__ Scalar pose[6 * grid::NUM_EES]; + __shared__ Scalar jac[6 * grid::NUM_EES * grid::NUM_VEL]; + for (int i = threadIdx.x; i < grid::NUM_POS; i += blockDim.x) sq[i] = q[i]; + __syncthreads(); + grid::end_effector_pose_device_tool(pose, sq, model); + __syncthreads(); + grid::end_effector_pose_gradient_device_tool(jac, sq, model); + __syncthreads(); + if (threadIdx.x == 0) { + for (int i = 0; i < 6; ++i) out[i] = pose[i]; + for (int i = 0; i < 6 * grid::NUM_VEL; ++i) out[6 + i] = jac[i]; + } +} + +int main(int argc, char **argv) { + if (argc != 3) return 1; + const int threads = atoi(argv[1]); + const Scalar q = static_cast(strtod(argv[2], nullptr)); + static_assert(grid::NUM_POS == 1 && grid::NUM_VEL == 1 && grid::NUM_EES == 1); + grid::robotModel *model = nullptr; + CHECK(grid::init_robotModel_checked(&model)); + Scalar *dq = nullptr, *out = nullptr; + CHECK(cudaMalloc(&dq, sizeof(Scalar))); + CHECK(cudaMalloc(&out, 26 * sizeof(Scalar))); + CHECK(cudaMemcpy(dq, &q, sizeof(Scalar), cudaMemcpyHostToDevice)); + CHECK(cudaMemset(out, 0xff, 26 * sizeof(Scalar))); + size_t smem = std::max(grid::END_EFFECTOR_POSE_DYNAMIC_SHARED_MEM_BYTES(), + grid::END_EFFECTOR_POSE_GRADIENT_DYNAMIC_SHARED_MEM_BYTES()); + CHECK(cudaFuncSetAttribute(evaluate, cudaFuncAttributeMaxDynamicSharedMemorySize, int(smem))); + evaluate<<<1, threads, smem>>>(dq, model, out); + CHECK(cudaGetLastError()); + // The last articulated frame is also evaluated through both FK primitives. + grid::ee_pose_fk_batched_kernel<<<1, threads>>>(out + 12, dq, 1, model, 1, grid::NUM_BODIES - 1); + CHECK(cudaGetLastError()); + if (threads >= 32) { + grid::ee_pose_fk_batched_kernel<<<1, threads>>>(out + 19, dq, 1, model, 1, grid::NUM_BODIES - 1); + CHECK(cudaGetLastError()); + } + CHECK(cudaDeviceSynchronize()); + Scalar host[26]; + CHECK(cudaMemcpy(host, out, sizeof(host), cudaMemcpyDeviceToHost)); + for (int i = 0; i < (threads >= 32 ? 26 : 19); ++i) printf("%.17g ", double(host[i])); + puts(""); + CHECK(cudaFree(dq)); + CHECK(cudaFree(out)); + CHECK(grid::free_robotModel_checked(model)); + return 0; +} diff --git a/test/cuda_equivalents/cuda_spherical_plant_hessian_runner.cu b/test/cuda_equivalents/cuda_spherical_plant_hessian_runner.cu new file mode 100644 index 00000000..4323ff8a --- /dev/null +++ b/test/cuda_equivalents/cuda_spherical_plant_hessian_runner.cu @@ -0,0 +1,41 @@ +// Existing public stock kernel; no new workspace-binder support implied. +#include "grid.cuh" +#include +#include +#include +#include +#include +using T=TEST_SCALAR; +constexpr int N=grid::NUM_VEL, NX=grid::NUM_POS+N, NH=18*N*N*N; +constexpr auto IT=grid::IntegratorType::TEST_INTEGRATOR; +constexpr int TIER=TEST_TIER; +#define CK(x) do { auto e=(x); if(e!=cudaSuccess) {fprintf(stderr,"%s: %s\n",#x,cudaGetErrorString(e));exit(2);} } while(0) +int main(int argc,char**argv) { + if(argc!=4) return 1; + int threads=atoi(argv[3]); + std::vector z(NX+N),out(NH+2); + FILE*f=fopen(argv[1],"rb"); if(!f||fread(z.data(),sizeof(T),z.size(),f)!=z.size()) return 3; fclose(f); + T *input,*output; unsigned char *workspace; + CK(cudaMalloc(&input,z.size()*sizeof(T))); CK(cudaMalloc(&output,out.size()*sizeof(T))); + CK(cudaMalloc(&workspace,grid_plant::PLANT_HESSIAN_WORKSPACE_BYTES_PER_TIMESTEP())); + CK(cudaMemcpy(input,z.data(),z.size()*sizeof(T),cudaMemcpyHostToDevice)); + grid::robotModel*model=nullptr; CK(grid::init_robotModel_checked(&model)); + constexpr size_t sh=grid_plant::INTEGRATOR_HESSIAN_DYNAMIC_SHARED_MEM_BYTES(); + auto kernel=grid_plant::plant_step_hessian_kernel; + cudaFuncAttributes attributes{}; CK(cudaFuncGetAttributes(&attributes,kernel)); + if(threads>attributes.maxThreadsPerBlock) {printf("ADMISSION_LIMIT=%d\n",attributes.maxThreadsPerBlock);return 7;} + CK(cudaFuncSetAttribute(kernel,cudaFuncAttributeMaxDynamicSharedMemorySize,sh)); + for(int repeat=0;repeat<2;++repeat) { + std::fill(out.begin(),out.end(),T(123+repeat)); + CK(cudaMemcpy(output,out.data(),out.size()*sizeof(T),cudaMemcpyHostToDevice)); + CK(cudaMemset(workspace,0xff,grid_plant::PLANT_HESSIAN_WORKSPACE_BYTES_PER_TIMESTEP())); + grid_plant::plant_step_hessian_kernel<<<1,threads,sh>>>(output+1,workspace,input,input+NX,NX,N,model,T(-9.81),T(.02),1); + CK(cudaGetLastError()); CK(cudaDeviceSynchronize()); + CK(cudaMemcpy(out.data(),output,out.size()*sizeof(T),cudaMemcpyDeviceToHost)); + if(out.front()!=T(123+repeat)||out.back()!=T(123+repeat)) return 4; + f=fopen((std::string(argv[2])+std::to_string(repeat)).c_str(),"wb"); + if(!f||fwrite(out.data()+1,sizeof(T),NH,f)!=NH) return 5; fclose(f); + } + CK(cudaFree(input)); CK(cudaFree(output)); CK(cudaFree(workspace)); + CK(grid::free_robotModel_checked(model)); +} diff --git a/test/cuda_equivalents/descriptor_oracle.py b/test/cuda_equivalents/descriptor_oracle.py new file mode 100644 index 00000000..9bcb0804 --- /dev/null +++ b/test/cuda_equivalents/descriptor_oracle.py @@ -0,0 +1,55 @@ +"""Descriptor-only forward kinematics: fields, elementary trig and q, no parser objects. + +A helper module shared by the CPU descriptor tests and the GPU metadata and +joint-transform suites; the split driver fingerprints it into their shards. +""" +import numpy as np +from RBDReference import RBDReference + + +def descriptor_fk(d, q): + """No parser objects or transform helpers: fields, elementary trig and q only.""" + world = [] + for j in d['joints']: + local = q[j['q_start']:j['q_start']+j['local_nq']] if j['mimic'] is None else None + if j['mimic'] is not None: + m = j['mimic'] + value = m['offset'] + if m['leader_joint'] is not None: + value += m['multiplier']*q[d['joints'][m['leader_joint']]['q_start']] + local = np.array([value]) + motion = np.eye(4) + if j['kind'] in ('floating', 'spherical'): + x, y, z, w = local[-4:] + motion[:3, :3] = [[1-2*(y*y+z*z), 2*(x*y-z*w), 2*(x*z+y*w)], + [2*(x*y+z*w), 1-2*(x*x+z*z), 2*(y*z-x*w)], + [2*(x*z-y*w), 2*(y*z+x*w), 1-2*(x*x+y*y)]] + if j['kind'] == 'floating': + motion[:3, 3] = local[:3] + else: + axis = np.array(j['axis']) + angle = local[0] + if j['kind'] == 'prismatic': + motion[:3, 3] = axis*angle + else: + x, y, z = axis + cross = np.array([[0., -z, y], [z, 0., -x], [-y, x, 0.]]) + motion[:3, :3] = np.eye(3)+np.sin(angle)*cross+(1-np.cos(angle))*(cross@cross) + if j['pitch'] is not None: + motion[:3, 3] = axis*angle*j['pitch'] + parent = np.eye(4) if j['parent_body'] == -1 else world[j['parent_body']] + world.append(parent@np.array(j['T_parent_joint_zero'])@motion) + return [(np.eye(4) if f['attached_body'] == -1 else world[f['attached_body']]) + @np.array(f['T_body_frame']) for f in d['frames']] + + +def assert_reference(d, robot, q): + ref = RBDReference(robot) + world, _ = ref._frame_world_placement_and_chain(q) + for f, actual in zip(d['frames'], descriptor_fk(d, q)): + if f['kind'] == 'root': + expected = np.eye(4) + else: + jid, offset = ref._resolve_frame_joint(f['name']) + expected = (np.eye(4) if jid == -1 else world[jid])@offset + np.testing.assert_allclose(actual, expected, atol=1e-11, rtol=1e-11) diff --git a/test/cuda_equivalents/manifold_oracle.py b/test/cuda_equivalents/manifold_oracle.py new file mode 100644 index 00000000..c43e0f6e --- /dev/null +++ b/test/cuda_equivalents/manifold_oracle.py @@ -0,0 +1,35 @@ +"""Reference models, admitted states and the fourth-order stencil of the manifold tests. + +A helper module shared by the CPU manifold tests and the GPU primitive and +spherical plant Hessian suites; the split driver fingerprints it into their shards. +""" +from pathlib import Path + +import numpy as np +from RBDReference import RBDReference +from URDFParser import URDFParser + +ROOT = Path(__file__).resolve().parents[2] +FIX = ROOT / 'external/URDFParser/tests/fixtures' + + +def reference(name='mixed_spherical_arm', floating=False): + return RBDReference(URDFParser().parse(str(FIX / (name + '.urdf')), floating_base=floating)) + + +def state(ref, seed=430): + rng = np.random.default_rng(seed) + q = rng.uniform(-.5, .5, ref.robot.get_num_pos()) + for _, iq, _ in ref._joint_retract_specs(): + q[iq[-4:]] /= np.linalg.norm(q[iq[-4:]]) + return q, rng.uniform(-.6, .6, ref.robot.get_num_vel()) + + +def derivative(f, width, h=2e-4): + if width == 0: + return np.empty(np.shape(f(np.empty(0))) + (0,)) + columns = [] + for k in range(width): + e = np.eye(width)[k] * h + columns.append((8*(f(e)-f(-e)) - (f(2*e)-f(-2*e)))/(12*h)) + return np.stack(columns, axis=-1) diff --git a/test/cuda_equivalents/revolute_mimic_mechanics.py b/test/cuda_equivalents/revolute_mimic_mechanics.py new file mode 100644 index 00000000..746edfeb --- /dev/null +++ b/test/cuda_equivalents/revolute_mimic_mechanics.py @@ -0,0 +1,47 @@ +"""Closed-form mechanics of the skew revolute mimic fixture, independent of every spatial-algebra library. + +The fixture ``revolute_mimic_fixed_tool`` has two bodies on one skew axis line: the +rotor at angle q and the follower, a revolute mimic with multiplier -0.5 and offset +0.17, at absolute angle 0.5 q + 0.17. Both inertia tensors are 0.01 times the +identity about their centres of mass, so each body's moment about the axis is +0.01 plus mass times the squared distance of its centre of mass from the axis. +The generalized mass is constant, so the single coordinate has no velocity +terms; inverse dynamics is M qdd plus the gravity term, the derivative of the +potential with respect to q. Everything below uses only numpy and scipy. +""" +import numpy as np +from scipy.spatial.transform import Rotation + +AXIS = np.array([.6, 0., .8]) +ORIGIN_R = Rotation.from_euler("xyz", [.3, .2, .1]).as_matrix() +BODIES = ( # (mass, centre of mass in the shared joint frame, angle slope, angle offset) + (1.0, np.array([.03, .02, -.01]), 1.0, 0.0), + (0.7, np.array([.05, -.02, .03]), 0.5, 0.17), +) +GRAVITY = 9.81 + + +def generalized_mass(): + total = 0. + for mass, com, slope, _ in BODIES: + total += slope ** 2 * (0.01 + mass * np.sum(np.cross(com, AXIS) ** 2)) + return total + + +def gravity_torque(q): + """d/dq of the potential sum of m g z, in joint units.""" + total = 0. + for mass, com, slope, offset in BODIES: + theta = slope * q + offset + rotated = Rotation.from_rotvec(AXIS * theta).apply(com) + d_rotated = np.cross(AXIS, rotated) # d/dtheta of Rot(axis, theta) com + total += mass * GRAVITY * (ORIGIN_R @ d_rotated)[2] * slope + return total + + +def inverse_dynamics(q, qdd): + return generalized_mass() * qdd + gravity_torque(q) + + +def forward_dynamics(q, tau): + return (tau - gravity_torque(q)) / generalized_mass() diff --git a/test/cuda_equivalents/test_cuda_branching_skew_equivalence.py b/test/cuda_equivalents/test_cuda_branching_skew_equivalence.py index e0d5450d..3051df8c 100644 --- a/test/cuda_equivalents/test_cuda_branching_skew_equivalence.py +++ b/test/cuda_equivalents/test_cuda_branching_skew_equivalence.py @@ -75,7 +75,7 @@ def _compile_runner(build_dir): arch = _detect_cuda_arch() exe = build_dir / "cuda_branching_skew_runner.exe" cmd = [ - nvcc, "-std=c++11", "-O0", + nvcc, "-std=c++17", "-O0", "-DGRID_CUDA_FLOATING_BASE=0", "-DGRID_RUNNER_SKIP_GRADIENTS=1", "-DGRID_CUDA_LINALG_BACKEND=GRID_LINALG_GLASS", diff --git a/test/cuda_equivalents/test_cuda_codegen_layout.py b/test/cuda_equivalents/test_cuda_codegen_layout.py index 864414cd..5abe5fe3 100644 --- a/test/cuda_equivalents/test_cuda_codegen_layout.py +++ b/test/cuda_equivalents/test_cuda_codegen_layout.py @@ -152,7 +152,7 @@ def _compile_header_consumer( source: str, label: str, *, - cxx_standard: str = "-std=c++11", + cxx_standard: str = "-std=c++17", extra_flags: list[str] | None = None, expect_success: bool = True, expected_error: str | None = None, @@ -657,10 +657,10 @@ def test_linalg_backend_controls_and_helpers_are_generated(tmp_path): @pytest.mark.cuda_equivalence @pytest.mark.developer_only -def test_linalg_backend_default_cxx11_compiles_without_mathdx(tmp_path): +def test_linalg_backend_default_cxx17_compiles_without_mathdx(tmp_path): header = _generate_header(tmp_path, "fr3", "fixed", codegen_profile="dynamics-core") # v2.0: cuBLASDx/mathDx was removed; the default header is SIMT-only GLASS and - # must compile under -std=c++11 with no mathDx headers present; referencing + # must compile under -std=c++17 with no mathDx headers present; referencing # the arena sizer proves the SIMT-only arena path compiles clean. source = r''' #include "grid.cuh" @@ -680,7 +680,7 @@ def test_linalg_backend_default_cxx11_compiles_without_mathdx(tmp_path): tmp_path, header, source, - "linalg_backend_default_cxx11", + "linalg_backend_default_cxx17", ) diff --git a/test/cuda_equivalents/test_cuda_collision_environment.py b/test/cuda_equivalents/test_cuda_collision_environment.py index 4ec6efd3..948eff8d 100644 --- a/test/cuda_equivalents/test_cuda_collision_environment.py +++ b/test/cuda_equivalents/test_cuda_collision_environment.py @@ -6,7 +6,8 @@ records it, frees on destruction and moves but never copies. The runner checks the row layouts and the quaternion box, the rejections, bit-identical verdicts and clearances against a hand upload of the same lists, host-versus-device agreement of the primitives, move semantics and -that destruction returns the device memory (cuda_collision_environment_runner.cu). +that destruction releases the four device lists, probed per pointer so a sibling process on +the same GPU cannot disturb the verdict (cuda_collision_environment_runner.cu). """ from __future__ import annotations import re diff --git a/test/cuda_equivalents/test_cuda_collision_margins.py b/test/cuda_equivalents/test_cuda_collision_margins.py new file mode 100644 index 00000000..49495435 --- /dev/null +++ b/test/cuda_equivalents/test_cuda_collision_margins.py @@ -0,0 +1,26 @@ +"""Separate capsule margins through both generated signatures and all thread counts.""" +from pathlib import Path +import pytest + +from test.cuda_equivalents.test_cuda_collision_config_free import _robot, _gen_header, _compile_and_run +from test.cuda_equivalents.test_cuda_collision_warp import _mixed_spec, _two_tier_spec + + +@pytest.mark.cuda_equivalence +@pytest.mark.developer_only +@pytest.mark.robot_smoke +@pytest.mark.parametrize("two_tier", [False, True]) +def test_native_separate_margins(tmp_path, two_tier): + robot = _robot() + if two_tier: + spec = _two_tier_spec(robot) + fine = spec["tiers"][-1] + # Original covering spheres enclose the old endpoints; +3cm also encloses + # the new endpoints shifted 2cm along z, with the original safety slack. + spec["tiers"][0]["radius"] = [r + .03 for r in spec["tiers"][0]["radius"]] + else: + fine = spec = _mixed_spec(robot) + fine["pb"] = [v + (.02 if i % 3 == 2 else 0) for i, v in enumerate(fine["offset"])] + _gen_header(robot, tmp_path, spec) + (tmp_path / "margin_config.h").write_text(f"#define TEST_NATIVE_TWO_TIERS {int(two_tier)}\n") + _compile_and_run(tmp_path, Path(__file__).with_name("cuda_collision_native_margins_runner.cu")) diff --git a/test/cuda_equivalents/test_cuda_collision_native.py b/test/cuda_equivalents/test_cuda_collision_native.py index 715f5e50..223ec710 100644 --- a/test/cuda_equivalents/test_cuda_collision_native.py +++ b/test/cuda_equivalents/test_cuda_collision_native.py @@ -23,7 +23,6 @@ import numpy as np import pytest -from grid_codegen import GRiDCodeGenerator from grid_codegen.algorithms._collision import ( build_self_cc_ranges, native_collision_spec_from_urdf, parse_native_urdf) from test.cuda_equivalents.test_cuda_collision_config_free import ( @@ -37,6 +36,14 @@ SELFCC_RUNNER = Path(__file__).with_name("cuda_collision_native_selfcc_runner.cu") +@pytest.mark.cuda_equivalence +@pytest.mark.developer_only +@pytest.mark.robot_smoke +def test_static_separate_margin_thresholds(tmp_path): + out = _compile_and_run(tmp_path, Path(__file__).with_name("cuda_collision_margins_runner.cu")) + assert "MARGINS_FACTORY_BOUNDARY PASS" in out + + def _iiwa_native_spec(): urdf = robot_urdf("iiwa14") if not urdf.exists(): diff --git a/test/cuda_equivalents/test_cuda_collision_warp.py b/test/cuda_equivalents/test_cuda_collision_warp.py index e312602b..a3453916 100644 --- a/test/cuda_equivalents/test_cuda_collision_warp.py +++ b/test/cuda_equivalents/test_cuda_collision_warp.py @@ -20,8 +20,6 @@ environment clearance alone. """ from __future__ import annotations -import contextlib -import os import re import shutil import subprocess @@ -30,14 +28,12 @@ import pytest -from grid_codegen import GRiDCodeGenerator from grid_codegen.algorithms._collision import build_self_cc_ranges from test.cuda_equivalents.cuda_harness import _detect_cuda_arch from test.cuda_equivalents.test_cuda_collision_config_free import _robot, _gen_header REPO_ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(REPO_ROOT)) -from grid_assets import robot_urdf # noqa: E402 COLLISION_INCLUDE = REPO_ROOT / "grid_codegen" / "collision" RUNNER_SOURCE = Path(__file__).with_name("cuda_collision_warp_runner.cu") @@ -87,10 +83,15 @@ def _compile(build_dir): return exe -def _run(exe, threads, n_cfg, seed, amplitude=None, margin=0.0, check_self=True, boundary=False): +def _run(exe, threads, n_cfg, seed, amplitude=None, margin=0.0, check_self=True, boundary=False, + environment_margin=None, self_margin=None): args = [str(exe), str(threads), str(n_cfg), str(seed), str(amplitude if amplitude is not None else 3.14159265), "0", repr(float(margin)), "1" if check_self else "0", "1" if boundary else "0"] + if environment_margin is not None or self_margin is not None: + assert not boundary # old common-margin boundary sweep has separate coverage + args += [str(margin if environment_margin is None else environment_margin), + str(margin if self_margin is None else self_margin)] run = subprocess.run(args, capture_output=True, text=True) assert run.returncode == 0, f"warp runner FAILED (threads={threads}, margin={margin}, check_self={check_self}):\nstdout:\n{run.stdout[-3000:]}\nstderr:\n{run.stderr}" summary = [l for l in run.stdout.splitlines() if l.startswith("SUMMARY")][0] @@ -175,6 +176,22 @@ def test_warp_matches_block_two_tier_cascade(tmp_path, threads): MARGINS = (0.0, 0.005, 0.02) +@pytest.mark.cuda_equivalence +@pytest.mark.developer_only +@pytest.mark.robot_smoke +@pytest.mark.parametrize("two_tier", [False, True]) +def test_separate_environment_self_margins(tmp_path, two_tier): + robot = _robot() + build_dir = tmp_path / "warp_separate" + _gen_header(robot, build_dir, _two_tier_spec(robot) if two_tier else _mixed_spec(robot)) + exe = _compile(build_dir) + for threads in (32, 64, 128): + for environment, self_margin in ((0, 0.02), (0.02, 0), (0.005, 0.02), (0.02, 0.005)): + for check_self in (False, True): + _run(exe, threads, 200, 7, margin=0.01, check_self=check_self, + environment_margin=environment, self_margin=self_margin) + + @pytest.mark.cuda_equivalence @pytest.mark.developer_only @pytest.mark.robot_smoke diff --git a/test/cuda_equivalents/test_cuda_constant_branch_mimic_scale.py b/test/cuda_equivalents/test_cuda_constant_branch_mimic_scale.py new file mode 100644 index 00000000..46c56035 --- /dev/null +++ b/test/cuda_equivalents/test_cuda_constant_branch_mimic_scale.py @@ -0,0 +1,78 @@ +"""Removing a constant column must preserve a remaining mimic's multiplier.""" +import subprocess +import numpy as np +import pytest +from URDFParser import URDFParser +from RBDReference import RBDReference +from grid_codegen import GRiDCodeGenerator +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable +from test.cuda_equivalents.constant_model import constant_model + +pytestmark = [pytest.mark.cuda_equivalence, pytest.mark.robot_smoke] + + +@pytest.mark.parametrize('precision', ['float', pytest.param('double', marks=pytest.mark.fp64_alpha)]) +def test_constant_branch_keeps_nonunit_mimic_scale(tmp_path, precision): + constant_model(tmp_path, branched=True) + path = tmp_path / 'model-False-False-True.urdf' + text = path.read_text().replace('', ''' + + + + + + + + ''') + path.write_text(text) + robot = URDFParser().parse(str(path)) + gen = GRiDCodeGenerator(robot, dtype=precision) + gen.gen_all_code(output_path=str(tmp_path / 'grid.cuh'), emit_plant=False, + algorithm_list=['end_effector_pose_gradient'], + enable_mujoco_kernels=False) + source = r''' +#include "grid.cuh" +#include +#include +using T=DTYPE; +static_assert(grid::NUM_VEL==1 && grid::NUM_POS==1 && grid::NUM_EES==2); +#define CK(x) do{auto e=(x);if(e!=cudaSuccess){fprintf(stderr,"%s: %s\n",#x,cudaGetErrorString(e));return 2;}}while(0) +__global__ void probe(T *out,const grid::robotModel *model){ + __shared__ T q[1],jac[12]; + if(threadIdx.x==0)q[0]=T(.35);__syncthreads(); + grid::end_effector_pose_gradient_device(jac,q,model);__syncthreads(); + for(int i=threadIdx.x;i<12;i+=blockDim.x)out[i]=jac[i]; +} +int main(int argc,char **argv){ + if(argc!=2)return 1; + grid::robotModel *model=nullptr;CK(grid::init_robotModel_checked(&model)); + T *out;T h[12];CK(cudaMalloc(&out,sizeof(h)));CK(cudaMemset(out,0xff,sizeof(h))); + size_t bytes=grid::END_EFFECTOR_POSE_GRADIENT_DYNAMIC_SHARED_MEM_BYTES(); + CK(cudaFuncSetAttribute(probe,cudaFuncAttributeMaxDynamicSharedMemorySize,int(bytes))); + probe<<<1,atoi(argv[1]),bytes>>>(out,model);CK(cudaGetLastError());CK(cudaDeviceSynchronize()); + CK(cudaMemcpy(h,out,sizeof(h),cudaMemcpyDeviceToHost)); + for(auto x:h)printf("%.17g ",double(x));puts(""); + CK(cudaFree(out));CK(grid::free_robotModel_checked(model)); +} +'''.replace('DTYPE', precision) + runner = tmp_path / 'probe.cu' + runner.write_text(source) + exe, _ = cached_nvcc_executable([runner], ['-std=c++17', '-O2', '-lineinfo', f'-arch=sm_{_detect_cuda_arch()}'], + exe_name='probe', fallback_dir=tmp_path, include_dirs=[tmp_path], + what='constant branch mimic scale probe') + expected = np.asarray(RBDReference(robot).end_effector_pose_gradient(np.array([.35]))).ravel() + # Independent yaw derivative of the tip branch is exactly the mimic scale; + # the other branch's angular velocity is around y and has zero yaw rate. + np.testing.assert_array_equal(np.sort(expected.reshape(2, 6)[:, 5]), [-.5, 0.]) + baseline = None + for threads in (1, 17, 32, 64, 256): + ran = subprocess.run([str(exe), str(threads)], capture_output=True, text=True) + assert ran.returncode == 0, ran.stderr + actual = np.fromstring(ran.stdout, sep=' ') + assert actual.size == 12 + tol = 3e-5 if precision == 'float' else 1e-10 + np.testing.assert_allclose(actual, expected, atol=tol, rtol=tol) + if baseline is not None: + np.testing.assert_array_equal(actual, baseline) + baseline = actual diff --git a/test/cuda_equivalents/test_cuda_constant_derivatives.py b/test/cuda_equivalents/test_cuda_constant_derivatives.py new file mode 100644 index 00000000..7e24726e --- /dev/null +++ b/test/cuda_equivalents/test_cuda_constant_derivatives.py @@ -0,0 +1,82 @@ +"""Constant-joint derivatives, including genuinely empty coordinate buffers.""" +from pathlib import Path +import subprocess + +import numpy as np +import pytest +from URDFParser import URDFParser +from RBDReference import RBDReference +from grid_codegen import GRiDCodeGenerator +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.constant_model import constant_model +from test.cuda_equivalents.executable_cache import cached_nvcc_executable + +pytestmark = [pytest.mark.cuda_equivalence, pytest.mark.robot_smoke] + + +@pytest.fixture(scope='module', params=[(z, p) for z in ('chain', 'zero', 'branch') for p in ('float', 'double')]) +def derivative_probe(request, tmp_path_factory): + layout, precision = request.param + zero = layout == 'zero' + path = tmp_path_factory.mktemp(f'constant-derivatives-{layout}-{precision}') + robot = constant_model(path, zero=zero, branched=layout == 'branch') + gen = GRiDCodeGenerator(robot, dtype=precision) + gen.gen_all_code(output_path=str(path / 'grid.cuh'), emit_plant=False, + algorithm_list=['inverse_dynamics_gradient', 'frame_jacobian', 'frame_jacobian_dot'], + include_homogenous_transforms=True, enable_mujoco_kernels=False) + source = Path(__file__).with_name('cuda_constant_derivatives_runner.cu') + exe, _ = cached_nvcc_executable([source], ['-std=c++17', '-O2', f'-arch=sm_{_detect_cuda_arch()}', + f'-DDTYPE={precision}', + f'-DID_TEMP={gen.gen_inverse_dynamics_gradient_inner_temp_mem_size()}'], + exe_name='probe', fallback_dir=path, include_dirs=[path], + what='constant derivatives runner') + return robot, RBDReference(robot), path, exe, precision, layout + + +@pytest.mark.parametrize('mode', [0, 1, 2], ids=['id-gradient', 'frame-jacobian', 'frame-jacobian-dot']) +def test_constant_derivatives(derivative_probe, mode): + robot, ref, path, exe, precision, layout = derivative_probe + zero = layout == 'zero' + q = np.full(robot.get_num_pos(), .35) + v, a = np.full(robot.get_num_vel(), .23), np.full(robot.get_num_vel(), -.31) + if mode == 0: + jac = ref.inverse_dynamics_gradient(q, v, a) + expected = np.r_[jac[:, :len(v)].ravel(order='F'), jac[:, len(v):].ravel(order='F')] + # A separately parsed rigidly locked model also checks force transmission. + if not zero: + locked = RBDReference(constant_model(path, locked=True, branched=layout == 'branch')) + np.testing.assert_allclose(jac, locked.inverse_dynamics_gradient(q, v, a), atol=1e-11, rtol=1e-10) + else: + method = ref.frame_jacobian if mode == 1 else ref.frame_jacobian_dot + args = (q,) if mode == 1 else (q, v) + expected = method(*args, 'constant', reference_frame='LOCAL_WORLD_ALIGNED').ravel(order='F') + tol = 3e-5 if precision == 'float' else 1e-11 + baseline = None + for threads in (1, 17, 32, 64, 256): + result = subprocess.run([str(exe), str(threads), str(mode)], capture_output=True, text=True) + assert result.returncode == 0, result.stderr + actual = np.fromstring(result.stdout, sep=' ') + assert actual.size == expected.size + 2 + np.testing.assert_array_equal(actual[[0, -1]], [123, 456]) + np.testing.assert_allclose(actual[1:-1], expected, atol=tol, rtol=tol) + if baseline is not None: + np.testing.assert_array_equal(actual, baseline) + baseline = actual + if layout == 'branch': + for threads in (1, 17, 32, 64, 256): + # Internal gradient bands of a different, constant root branch must + # stay exactly zero when the independent branch's qd/qdd are NaN. + poison_mode = 3 if mode == 0 else mode + result = subprocess.run([str(exe), str(threads), str(poison_mode), 'poison'], + capture_output=True, text=True) + assert result.returncode == 0, result.stderr + values = np.fromstring(result.stdout, sep=' ') + np.testing.assert_array_equal(values[[0, -1]], [123, 456]) + if mode == 0: + bands = values[1:-1].reshape(6, robot.get_num_bodies(), 6) + constant = robot.get_joint_by_name('constant').jid + np.testing.assert_array_equal(bands[:, constant], 0) + drive = robot.get_joint_by_name('drive').jid + assert np.isnan(bands[:, drive]).any(), 'Do not sanitize real NaN inputs' + else: + np.testing.assert_array_equal(values[1:-1], 0) diff --git a/test/cuda_equivalents/test_cuda_constant_dynamics_audit.py b/test/cuda_equivalents/test_cuda_constant_dynamics_audit.py new file mode 100644 index 00000000..8b51adc0 --- /dev/null +++ b/test/cuda_equivalents/test_cuda_constant_dynamics_audit.py @@ -0,0 +1,93 @@ +"""Constant-body transport and floating-coordinate ownership against locked models.""" +from pathlib import Path +import subprocess + +import numpy as np +import pytest +from URDFParser import URDFParser +from RBDReference import RBDReference +from grid_codegen import GRiDCodeGenerator +from grid_codegen.algorithms._idsva_so import gen_floating_gravity_d2tau_dq_spill_count +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable +from test.cuda_equivalents.constant_model import constant_model + +pytestmark = [pytest.mark.cuda_equivalence, pytest.mark.robot_smoke] + + +def audit_model(directory, *, floating, descendant, locked=False): + constant_model(directory, locked=locked) + source = directory / f'model-False-{locked}-False.urdf' + text = source.read_text() + if descendant: + text = text.replace('', ''' + + + + + + + ''') + source.write_text(text) + return URDFParser().parse(str(source), floating_base=floating) + + +@pytest.fixture(scope='module', params=[(f, d, p) for f, d in + [(False, True), (True, False), (True, True)] for p in ('float', 'double')]) +def dynamics_probe(request, tmp_path_factory): + floating, descendant, precision = request.param + path = tmp_path_factory.mktemp(f'constant-dynamics-{floating}-{descendant}-{precision}') + robot = audit_model(path, floating=floating, descendant=descendant) + locked = audit_model(path, floating=floating, descendant=descendant, locked=True) + gen = GRiDCodeGenerator(robot, dtype=precision) + gen.gen_all_code(output_path=str(path / 'grid.cuh'), emit_plant=False, + algorithm_list=['crba', 'inverse_dynamics_gradient', + 'idsva_so_body_frame', 'idsva_so_world_frame'], + enable_idsva_so_body_frame=True, enable_idsva_so_world_frame=True, + enable_mujoco_kernels=False) + # Use the same public-output epilogue as the generated body-frame kernel; + # its fixed-base inner intentionally has a different mixed-axis layout. + gen.code_str = '' + gen.gen_idsva_so_body_frame_public_dvdq_layout_repair() + runner = path / 'probe.cu' + runner.write_text(Path(__file__).with_name('cuda_constant_dynamics_audit_runner.cu') + .read_text().replace('BODY_OUTPUT_REPAIR', gen.code_str)) + exe, _ = cached_nvcc_executable([runner], [ + '-std=c++17', '-O2', '-lineinfo', f'-arch=sm_{_detect_cuda_arch()}', f'-DDTYPE={precision}', + f'-DID_TEMP={gen.gen_inverse_dynamics_gradient_inner_temp_mem_size()}', + f'-DCRBA_TEMP={gen.gen_crba_inner_temp_mem_size()}', + f'-DBODY_TEMP={gen.gen_idsva_so_body_frame_inner_temp_mem_size()}', + f'-DWORLD_TEMP={gen.gen_idsva_so_world_frame_temp_mem_size()}', + f'-DGRAV_TEMP={gen_floating_gravity_d2tau_dq_spill_count(gen)}'], + exe_name='probe', fallback_dir=path, include_dirs=[path], what='constant dynamics audit runner') + q = np.linspace(.13, .37, robot.get_num_pos()) + if floating: + q[3:7] = np.array([.1, -.2, .3, .9]) / np.linalg.norm([.1, -.2, .3, .9]) + v = np.linspace(-.23, .19, robot.get_num_vel()) + a = np.linspace(.31, -.11, robot.get_num_vel()) + np.savetxt(path / 'inputs.txt', np.r_[q, v, a], fmt='%.17g') + return robot, locked, path, exe, precision, q, v, a + + +@pytest.mark.parametrize('mode', [0, 1, 2, 3], ids=['crba', 'id-gradient', 'body-so', 'world-so']) +def test_constant_dynamics_audit(dynamics_probe, mode): + robot, locked, path, exe, precision, q, v, a = dynamics_probe + method = ['crba', 'inverse_dynamics_gradient', 'idsva_so_body_frame', 'idsva_so_world_frame'][mode] + args = (q,) if mode == 0 else (q, v, a) + reference = np.asarray(getattr(RBDReference(robot), method)(*args)) + independent = np.asarray(getattr(RBDReference(locked), method)(*args)) + np.testing.assert_allclose(reference, independent, atol=2e-10, rtol=2e-10) + expected = reference.ravel(order='F' if mode < 2 else 'C') + tol = 5e-5 if precision == 'float' else 2e-10 + baseline = None + for threads in (1, 17, 32, 64, 256): + ran = subprocess.run([str(exe), str(threads), str(mode), str(path / 'inputs.txt')], + capture_output=True, text=True) + assert ran.returncode == 0, ran.stderr + actual = np.fromstring(ran.stdout, sep=' ') + assert actual.size == expected.size + 2 + np.testing.assert_array_equal(actual[[0, -1]], [123, 456]) + np.testing.assert_allclose(actual[1:-1], expected, atol=tol, rtol=tol) + if baseline is not None: + np.testing.assert_array_equal(actual, baseline) + baseline = actual diff --git a/test/cuda_equivalents/test_cuda_constant_frame_velocity.py b/test/cuda_equivalents/test_cuda_constant_frame_velocity.py new file mode 100644 index 00000000..90d0967a --- /dev/null +++ b/test/cuda_equivalents/test_cuda_constant_frame_velocity.py @@ -0,0 +1,75 @@ +"""The cost-menu frame path shares constant-joint column ownership.""" +import subprocess +import numpy as np +import pytest +from RBDReference import RBDReference +from grid_codegen import GRiDCodeGenerator +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable +from test.cuda_equivalents.constant_model import constant_model + +pytestmark = [pytest.mark.cuda_equivalence, pytest.mark.robot_smoke] + + +@pytest.mark.parametrize('zero', [False, True]) +@pytest.mark.parametrize('precision', ['float', pytest.param('double', marks=pytest.mark.fp64_alpha)]) +def test_constant_frame_velocity(tmp_path, zero, precision): + robot = constant_model(tmp_path, zero=zero) + gen = GRiDCodeGenerator(robot, dtype=precision) + gen.gen_all_code(output_path=str(tmp_path / 'grid.cuh'), + algorithm_list=['end_effector_pose', 'frame_jacobian'], + cost_frames=['constant'], enable_mujoco_kernels=False) + source = r''' +#include "grid.cuh" +#include +#include +using T=DTYPE; +constexpr int NV=grid::NUM_VEL; +#define CK(x) do {auto e=(x);if(e!=cudaSuccess){fprintf(stderr,"%s: %s\n",#x,cudaGetErrorString(e));return 2;}}while(0) +__global__ void probe(T *out,const T *in,const grid::robotModel *model) { + __shared__ T frame[grid_plant::GRID_PLANT_FRAME_SCRATCH_COUNT]; + extern __shared__ __align__(16) unsigned char arena[]; + grid_plant::frame_velocity_eval(0,in,NV ? in+NV : nullptr,frame,reinterpret_cast(arena),model); + __syncthreads(); + for(int i=threadIdx.x;i<6;i+=blockDim.x) out[i]=frame[i]; + for(int i=threadIdx.x;i<12*NV;i+=blockDim.x) out[i+6]=frame[16+i]; +} +int main(int argc,char **argv) { + if(argc!=2)return 1; + T *in=nullptr,*out;T host[6+12*NV]; + grid::robotModel *model=nullptr;CK(grid::init_robotModel_checked(&model)); + if constexpr(NV>0){T h[2]={T(.35),T(.23)};CK(cudaMalloc(&in,sizeof(h)));CK(cudaMemcpy(in,h,sizeof(h),cudaMemcpyHostToDevice));} + CK(cudaMalloc(&out,sizeof(host)));CK(cudaMemset(out,0xff,sizeof(host))); + size_t bytes=grid_plant::FRAME_EVAL_DYNAMIC_SHARED_MEM_BYTES(); + CK(cudaFuncSetAttribute(probe,cudaFuncAttributeMaxDynamicSharedMemorySize,int(bytes))); + probe<<<1,atoi(argv[1]),bytes>>>(out,in,model); + CK(cudaGetLastError());CK(cudaDeviceSynchronize());CK(cudaMemcpy(host,out,sizeof(host),cudaMemcpyDeviceToHost)); + for(auto x:host)printf("%.17g ",double(x));puts(""); + CK(cudaFree(in));CK(cudaFree(out));CK(grid::free_robotModel_checked(model)); +} +'''.replace('DTYPE', precision) + runner = tmp_path / 'probe.cu' + runner.write_text(source) + exe, _ = cached_nvcc_executable([runner], ['-std=c++17', '-O2', f'-arch=sm_{_detect_cuda_arch()}'], + exe_name='probe', fallback_dir=tmp_path, include_dirs=[tmp_path], + what='constant frame velocity probe') + ref = RBDReference(robot) + q, v = np.full(robot.get_num_pos(), .35), np.full(robot.get_num_vel(), .23) + J = ref.frame_jacobian(q, 'constant', reference_frame='LOCAL_WORLD_ALIGNED') + if zero: + dJv = np.empty((6, 0)) + else: + h = 1e-6 + dJv = ((ref.frame_jacobian(q+h, 'constant', reference_frame='LOCAL_WORLD_ALIGNED') + - ref.frame_jacobian(q-h, 'constant', reference_frame='LOCAL_WORLD_ALIGNED')) @ v / (2*h)).reshape(6, 1) + expected = np.r_[J @ v, J.ravel(order='F'), dJv.ravel(order='F')] + baseline = None + for threads in (1, 17, 32, 64, 256): + ran = subprocess.run([str(exe), str(threads)], capture_output=True, text=True) + assert ran.returncode == 0, ran.stderr + actual = np.fromstring(ran.stdout, sep=' ') + tol = 3e-5 if precision == 'float' else 1e-9 + np.testing.assert_allclose(actual, expected, atol=tol, rtol=tol) + if baseline is not None: + np.testing.assert_array_equal(actual, baseline) + baseline = actual diff --git a/test/cuda_equivalents/test_cuda_constant_mimic.py b/test/cuda_equivalents/test_cuda_constant_mimic.py new file mode 100644 index 00000000..bcd875b7 --- /dev/null +++ b/test/cuda_equivalents/test_cuda_constant_mimic.py @@ -0,0 +1,71 @@ +"""Constant follower FK/Jacobian: block, thread and warp, both precisions.""" +from pathlib import Path +import subprocess +import numpy as np +import pytest +from scipy.spatial.transform import Rotation +from URDFParser import URDFParser +from grid_assets import robot_urdf +from grid_codegen import GRiDCodeGenerator +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable + +pytestmark = [pytest.mark.cuda_equivalence, pytest.mark.robot_smoke] + + +@pytest.mark.parametrize('kind', ['prismatic', 'revolute']) +@pytest.mark.parametrize('precision', ['float', pytest.param('double', marks=pytest.mark.fp64_alpha)]) +def test_constant_mimic_kinematics(tmp_path, kind, precision): + urdf = tmp_path / 'constant.urdf' + urdf.write_text(robot_urdf('slider_mimic_fixed_tool').read_text() + .replace('type="prismatic"', f'type="{kind}"') + .replace('mimic joint="slide"', 'mimic joint="tool"')) + robot = URDFParser().parse(str(urdf)) + gen = GRiDCodeGenerator(robot, False, False, 'grid') + gen.gen_all_code(output_path=str(tmp_path / 'grid.cuh'), + algorithm_list=['end_effector_pose', 'end_effector_pose_gradient'], + include_homogenous_transforms=True, fixed_target_name='tool', + enable_mujoco_kernels=False) + runner = Path(__file__).with_name('cuda_prismatic_fixed_target_runner.cu') + flags = ['-std=c++17', '-O2', f'-arch=sm_{_detect_cuda_arch()}'] + (['-DUSE_DOUBLE'] if precision == 'double' else []) + exe, _ = cached_nvcc_executable([runner], flags, exe_name='constant.exe', fallback_dir=tmp_path, + include_dirs=[tmp_path], what='constant mimic runner') + axis = np.array([.6, 0., .8]) + origin = np.eye(4) + origin[:3, :3] = Rotation.from_euler('xyz', [.3, .2, .1]).as_matrix() + origin[:3, 3] = [.04, .01, .02] + + def motion(angle): + T = np.eye(4) + if kind == 'prismatic': + T[:3, 3] = axis * angle + else: + T[:3, :3] = Rotation.from_rotvec(axis * angle).as_matrix() + return T + + def expected(q): + body = origin @ motion(q) @ motion(.17) + tool = body @ origin + pose = np.r_[tool[:3, 3], Rotation.from_matrix(tool[:3, :3]).as_euler('xyz')] + quat = Rotation.from_matrix(body[:3, :3]).as_quat() + return pose, np.r_[body[:3, 3], quat[3], quat[:3]] + + tol = 2e-5 if precision == 'float' else 1e-9 + for q in (0., .13, .5): + pose, frame = expected(q) + # Independent transform finite difference, not the reference/generator. + h = 1e-6 + jac = (expected(q+h)[0] - expected(q-h)[0])/(2*h) + baseline = None + for threads in (1, 17, 32, 64, 256): + result = subprocess.run([str(exe), str(threads), str(q)], capture_output=True, text=True) + assert result.returncode == 0, result.stdout + result.stderr + values = np.fromstring(result.stdout.strip().splitlines()[-1], sep=' ') + assert values.size == (26 if threads >= 32 else 19) + np.testing.assert_allclose(values[:12], np.r_[pose, jac], atol=tol, rtol=tol) + np.testing.assert_allclose(values[12:19], frame, atol=tol, rtol=tol) + if threads >= 32: + np.testing.assert_allclose(values[19:26], frame, atol=tol, rtol=tol) + if baseline is not None: + np.testing.assert_array_equal(values[:19], baseline) + baseline = values[:19] diff --git a/test/cuda_equivalents/test_cuda_constant_second_order.py b/test/cuda_equivalents/test_cuda_constant_second_order.py new file mode 100644 index 00000000..30c2f29b --- /dev/null +++ b/test/cuda_equivalents/test_cuda_constant_second_order.py @@ -0,0 +1,57 @@ +"""Constant-body second-order folds and Coriolis projection, including nv=0.""" +from pathlib import Path +import subprocess + +import numpy as np +import pytest +from RBDReference import RBDReference +from grid_codegen import GRiDCodeGenerator +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable +from test.cuda_equivalents.constant_model import constant_model + +pytestmark = [pytest.mark.cuda_equivalence, pytest.mark.robot_smoke] + + +@pytest.fixture(scope='module', params=[(z, p) for z in (False, True) for p in ('float', 'double')]) +def second_probe(request, tmp_path_factory): + zero, precision = request.param + path = tmp_path_factory.mktemp(f'constant-second-{zero}-{precision}') + robot = constant_model(path, zero=zero) + gen = GRiDCodeGenerator(robot, dtype=precision) + gen.gen_all_code(output_path=str(path / 'grid.cuh'), emit_plant=False, + algorithm_list=['idsva_so_body_frame', 'idsva_so_world_frame', 'coriolis_matrix'], + enable_idsva_so_world_frame=True, enable_idsva_so_body_frame=True, + enable_mujoco_kernels=False) + exe, _ = cached_nvcc_executable([Path(__file__).with_name('cuda_constant_second_order_runner.cu')], [ + '-std=c++17', '-O2', f'-arch=sm_{_detect_cuda_arch()}', f'-DDTYPE={precision}', + f'-DBODY_TEMP={gen.gen_idsva_so_body_frame_inner_temp_mem_size()}', + f'-DWORLD_TEMP={gen.gen_idsva_so_world_frame_temp_mem_size()}'], + exe_name='probe', fallback_dir=path, include_dirs=[path], what='constant second order runner') + return robot, path, exe, precision, zero + + +@pytest.mark.parametrize('mode', [0, 1, 2], ids=['body', 'world', 'coriolis']) +def test_constant_second_order(second_probe, mode): + robot, path, exe, precision, zero = second_probe + ref = RBDReference(robot) + q, v, a = [np.full(robot.get_num_vel(), x) for x in (.35, .23, -.31)] + method = ['idsva_so_body_frame', 'idsva_so_world_frame', 'coriolis_matrix'][mode] + args = (q, v) if mode == 2 else (q, v, a) + parts = getattr(ref, method)(*args) + expected = np.asarray(parts).ravel() + if not zero: + locked = RBDReference(constant_model(path, locked=True)) + np.testing.assert_allclose(parts, getattr(locked, method)(*args), atol=1e-11, rtol=1e-10) + tol = 5e-5 if precision == 'float' else 1e-10 + baseline = None + for threads in (1, 17, 32, 64, 256): + result = subprocess.run([str(exe), str(threads), str(mode)], capture_output=True, text=True) + assert result.returncode == 0, result.stderr + actual = np.fromstring(result.stdout, sep=' ') + assert actual.size == expected.size+2 + np.testing.assert_array_equal(actual[[0, -1]], [123, 456]) + np.testing.assert_allclose(actual[1:-1], expected, atol=tol, rtol=tol) + if baseline is not None: + np.testing.assert_array_equal(actual, baseline) + baseline = actual diff --git a/test/cuda_equivalents/test_cuda_constant_velocity.py b/test/cuda_equivalents/test_cuda_constant_velocity.py new file mode 100644 index 00000000..b79a9491 --- /dev/null +++ b/test/cuda_equivalents/test_cuda_constant_velocity.py @@ -0,0 +1,90 @@ +"""A constant branch must not read another branch's velocity placeholder.""" +import subprocess + +import numpy as np +import pytest +from URDFParser import URDFParser +from RBDReference import RBDReference +from grid_codegen import GRiDCodeGenerator +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable + +pytestmark = [pytest.mark.cuda_equivalence, pytest.mark.robot_smoke] + + +@pytest.mark.parametrize('precision', ['float', pytest.param('double', marks=pytest.mark.fp64_alpha)]) +def test_constant_branch_velocity_isolation(tmp_path, precision): + inertial = ('' + '') + links = ''.join(f'{inertial}' + for name in ('base', 'mount', 'drive', 'constant')) + urdf = tmp_path / 'branches.urdf' + urdf.write_text(f'{links}' + '' + '' + '' + '' + '' + '' + '') + robot = URDFParser().parse(str(urdf)) + gen = GRiDCodeGenerator(robot, dtype=precision) + gen.gen_all_code(algorithm_list=['inverse_dynamics'], emit_plant=False, + enable_mujoco_kernels=False, output_path=str(tmp_path / 'grid.cuh')) + source = r''' +#include "grid.cuh" +#include +#include +#include +using T = DTYPE; +#define CK(x) do {auto e=(x);if(e!=cudaSuccess){fprintf(stderr,"%s: %s\n",#x,cudaGetErrorString(e));return 2;}}while(0) +__global__ void probe(T *out, const T *in, grid::robotModel *model) { + __shared__ T vaf[18*grid::NUM_BODIES]; + for(int i=threadIdx.x;i<18*grid::NUM_BODIES;i+=blockDim.x) vaf[i]=T(NAN); + __syncthreads(); + grid::inverse_dynamics_vaf_device(vaf,in,in+1,in+2,model,nullptr,T(-9.81)); + __syncthreads(); + for(int i=threadIdx.x;i<18*grid::NUM_BODIES;i+=blockDim.x) out[i]=vaf[i]; +} +int main(int argc,char **argv) { + if(argc!=3)return 1; + static_assert(grid::NUM_POS==1 && grid::NUM_VEL==1); + T in[3]={T(.2),T(0),T(0)}; + if(atoi(argv[2]))in[1]=in[2]=std::numeric_limits::quiet_NaN(); + T *d_in,*out,host[18*grid::NUM_BODIES]; + grid::robotModel *model=nullptr; CK(grid::init_robotModel_checked(&model)); + CK(cudaMalloc(&d_in,sizeof(in)));CK(cudaMalloc(&out,sizeof(host))); + CK(cudaMemcpy(d_in,in,sizeof(in),cudaMemcpyHostToDevice)); + const size_t bytes=grid::INVERSE_DYNAMICS_DEVICE_DYNAMIC_SHARED_MEM_BYTES(); + CK(cudaFuncSetAttribute(probe,cudaFuncAttributeMaxDynamicSharedMemorySize,int(bytes))); + probe<<<1,atoi(argv[1]),bytes>>>(out,d_in,model); + CK(cudaGetLastError());CK(cudaDeviceSynchronize()); + CK(cudaMemcpy(host,out,sizeof(host),cudaMemcpyDeviceToHost)); + for(auto x:host)printf("%.17g ",double(x));puts(""); + CK(cudaFree(d_in));CK(cudaFree(out));CK(grid::free_robotModel_checked(model)); +} +'''.replace('DTYPE', precision) + runner = tmp_path / 'probe.cu' + runner.write_text(source) + exe, _ = cached_nvcc_executable([runner], ['-std=c++17', '-O2', f'-arch=sm_{_detect_cuda_arch()}'], + exe_name='probe', fallback_dir=tmp_path, include_dirs=[tmp_path], + what='constant velocity probe') + ref = RBDReference(robot) + expected = np.asarray(ref.inverse_dynamics_fpass(np.array([.2]), np.zeros(1), np.zeros(1))) + const = robot.get_joint_by_name('constant').jid + drive = robot.get_joint_by_name('drive').jid + baseline = None + tol = 2e-5 if precision == 'float' else 1e-12 + for threads in (1, 17, 32, 64, 256): + for poison in (0, 1): + ran = subprocess.run([str(exe), str(threads), str(poison)], capture_output=True, text=True) + assert ran.returncode == 0, ran.stderr + actual = np.fromstring(ran.stdout, sep=' ').reshape(3, robot.get_num_bodies(), 6).transpose(0, 2, 1) + if poison: + np.testing.assert_array_equal(actual[:, :, const], baseline[:, :, const]) + assert np.isnan(actual[0, :, drive]).any(), 'Actual NaN inputs must not be sanitized' + else: + np.testing.assert_allclose(actual, expected, atol=tol, rtol=tol) + if baseline is not None: + np.testing.assert_array_equal(actual, baseline) + baseline = actual diff --git a/test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py b/test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py index f6a028d4..79b525dd 100644 --- a/test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py +++ b/test/cuda_equivalents/test_cuda_continuous_joint_equivalence.py @@ -140,7 +140,7 @@ def _compile_runner(build_dir, floating=False): arch = _detect_cuda_arch() exe = build_dir / "cuda_continuous_joint_runner.exe" cmd = [ - nvcc, "-std=c++11", "-O0", + nvcc, "-std=c++17", "-O0", f"-DGRID_CUDA_FLOATING_BASE={1 if floating else 0}", # gen3 exercises id / crba / ee_pose only; skip the gradient and # ee-pose-gradient runner sections so we don't need the heavy gradient / diff --git a/test/cuda_equivalents/test_cuda_coriolis.py b/test/cuda_equivalents/test_cuda_coriolis.py index b1f9b667..c10443f9 100644 --- a/test/cuda_equivalents/test_cuda_coriolis.py +++ b/test/cuda_equivalents/test_cuda_coriolis.py @@ -106,7 +106,7 @@ def _compile_runner(build_dir: Path, floating_base: bool): arch = _detect_cuda_arch() executable = build_dir / "cuda_coriolis_runner.exe" cmd = [ - nvcc, "-std=c++11", "-O0", + nvcc, "-std=c++17", "-O0", f"-DGRID_CUDA_FLOATING_BASE={1 if floating_base else 0}", "-DGRID_CUDA_LINALG_BACKEND=GRID_LINALG_GLASS", "-gencode", f"arch=compute_{arch},code=sm_{arch}", diff --git a/test/cuda_equivalents/test_cuda_dccrba.py b/test/cuda_equivalents/test_cuda_dccrba.py index 98a63e56..a87ade5c 100644 --- a/test/cuda_equivalents/test_cuda_dccrba.py +++ b/test/cuda_equivalents/test_cuda_dccrba.py @@ -128,7 +128,7 @@ def _compile_runner(build_dir: Path, floating_base: bool): arch = _detect_cuda_arch() executable = build_dir / "cuda_dccrba_runner.exe" cmd = [ - nvcc, "-std=c++11", "-O0", + nvcc, "-std=c++17", "-O0", f"-DGRID_CUDA_FLOATING_BASE={1 if floating_base else 0}", "-DGRID_CUDA_LINALG_BACKEND=GRID_LINALG_GLASS", "-gencode", f"arch=compute_{arch},code=sm_{arch}", diff --git a/test/cuda_equivalents/test_cuda_ee_workspace_queries.py b/test/cuda_equivalents/test_cuda_ee_workspace_queries.py new file mode 100644 index 00000000..ea61e2fe --- /dev/null +++ b/test/cuda_equivalents/test_cuda_ee_workspace_queries.py @@ -0,0 +1,92 @@ +"""Exact target scratch: guarded allocations, independent pose/Jacobian oracle.""" +import subprocess +import numpy as np +import pytest +from URDFParser import URDFParser +from RBDReference import RBDReference +from grid_assets import robot_urdf +from grid_codegen import GRiDCodeGenerator +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable + +pytestmark=[pytest.mark.cuda_equivalence,pytest.mark.robot_smoke] + +SOURCE=r''' +#include "grid.cuh" +#include +#include +#include +#include +using T=TEST_SCALAR; +constexpr int P=grid::EE_TARGET_POSE_INNER_SCRATCH_BYTES()/sizeof(T); +constexpr int G=grid::EE_TARGET_GRAD_INNER_SCRATCH_BYTES()/sizeof(T); +constexpr int OUT=6*grid::NUM_TARGET_EES*(1+grid::NUM_VEL); +#define CK(x) do{auto e=(x);if(e!=cudaSuccess){fprintf(stderr,"%s\n",cudaGetErrorString(e));return 2;}}while(0) +__global__ void probe(T *out,const T *q,const grid::robotModel*model,int*bad){ + __shared__ T xh[grid::XHOM_T_COUNT],dxh[grid::DXHOM_T_COUNT]; + __shared__ T loader[32*grid::NUM_POS+100],ptmp[P+2],gtmp[G+2]; + __shared__ int topo[grid::TOPOLOGY_HELPERS_COUNT ? grid::TOPOLOGY_HELPERS_COUNT:1]; + const int tid=threadIdx.x,nt=blockDim.x; + for(int repeat=0;repeat<2;repeat++){ + for(int i=tid;i(xh,dxh,topo,q,model,loader); + __syncthreads(); + grid::end_effector_pose_target_inner(out,q,xh,topo,ptmp+1,nullptr); + __syncthreads(); + grid::end_effector_pose_gradient_target_inner(out+6*grid::NUM_TARGET_EES,q,xh,dxh,topo,gtmp+1,nullptr); + __syncthreads(); + if(tid==0 && (ptmp[0]!=123 || ptmp[P+1]!=456 || gtmp[0]!=123 || gtmp[G+1]!=456))atomicAdd(bad,1); + __syncthreads(); + } +} +int main(int argc,char**argv){ + if(argc!=4)return 1; + std::vector q(grid::NUM_POS),out(OUT); + FILE*f=fopen(argv[1],"rb");if(!f||fread(q.data(),sizeof(T),q.size(),f)!=q.size())return 3;fclose(f); + T*dq,*dout;int*bad;grid::robotModel*model=nullptr; + CK(grid::init_robotModel_checked(&model));CK(cudaMalloc(&dq,q.size()*sizeof(T))); + CK(cudaMalloc(&dout,OUT*sizeof(T)));CK(cudaMalloc(&bad,sizeof(int)));CK(cudaMemset(bad,0,sizeof(int))); + CK(cudaMemcpy(dq,q.data(),q.size()*sizeof(T),cudaMemcpyHostToDevice));CK(cudaMemset(dout,0xff,OUT*sizeof(T))); + probe<<<1,atoi(argv[3])>>>(dout,dq,model,bad);CK(cudaGetLastError());CK(cudaDeviceSynchronize()); + int errors;CK(cudaMemcpy(&errors,bad,sizeof(int),cudaMemcpyDeviceToHost));if(errors)return 4; + CK(cudaMemcpy(out.data(),dout,OUT*sizeof(T),cudaMemcpyDeviceToHost)); + f=fopen(argv[2],"wb");if(!f||fwrite(out.data(),sizeof(T),OUT,f)!=OUT)return 5;fclose(f); + CK(cudaFree(dq));CK(cudaFree(dout));CK(cudaFree(bad));CK(grid::free_robotModel_checked(model)); +} +''' + + +@pytest.mark.parametrize('name,target',[('iiwa14','iiwa_joint_ee'),('go2','FR_foot_joint'),('go2','')]) +@pytest.mark.parametrize('scalar',['float',pytest.param('double', marks=pytest.mark.fp64_alpha)]) +def test_exact_target_scratch(tmp_path,name,target,scalar): + robot=URDFParser().parse(str(robot_urdf(name)),floating_base=name=='go2') + gen=GRiDCodeGenerator(robot,dtype=scalar) + gen.gen_all_code(algorithm_list=['end_effector_pose','end_effector_pose_gradient'], + fixed_target_name=target,include_homogenous_transforms=True, + enable_mujoco_kernels=False,output_path=str(tmp_path/'grid.cuh')) + (tmp_path/'probe.cu').write_text(SOURCE) + exe,_=cached_nvcc_executable([tmp_path/'probe.cu'], + ['-std=c++17','-O2','-lineinfo','-arch=sm_'+_detect_cuda_arch(),'-DTEST_SCALAR='+scalar], + exe_name='probe',fallback_dir=tmp_path,include_dirs=[tmp_path],what='exact target scratch probe') + dtype=np.float32 if scalar=='float' else np.float64 + q=np.linspace(.07,.23,robot.get_num_pos()).astype(dtype) + if name=='go2':q[3:7]=[0,0,0,1] + q.tofile(tmp_path/'input.bin') + ref=RBDReference(robot);names=[target] if target else None + expected=np.r_[np.asarray(ref.end_effector_pose(q.astype(float),ee_joint_names=names)).ravel(), + np.concatenate([j.ravel(order='F') for j in ref.end_effector_pose_gradient(q.astype(float),ee_joint_names=names)])] + baseline=None + for threads in (1,17,32,64,256): + output=tmp_path/f'out-{threads}.bin' + result=subprocess.run([str(exe),str(tmp_path/'input.bin'),str(output),str(threads)],capture_output=True,text=True) + (tmp_path/f'run-{threads}.log').write_text(result.stdout+result.stderr) + assert result.returncode==0,result.stderr + actual=np.fromfile(output,dtype) + np.testing.assert_allclose(actual,expected,rtol=2e-5 if scalar=='float' else 1e-9, + atol=2e-5 if scalar=='float' else 1e-9) + if baseline is not None:np.testing.assert_array_equal(actual,baseline) + baseline=actual diff --git a/test/cuda_equivalents/test_cuda_energy_regressor.py b/test/cuda_equivalents/test_cuda_energy_regressor.py index 6bb3d6b5..3bcbb610 100644 --- a/test/cuda_equivalents/test_cuda_energy_regressor.py +++ b/test/cuda_equivalents/test_cuda_energy_regressor.py @@ -104,7 +104,7 @@ def _compile_runner(build_dir: Path, floating_base: bool): executable = build_dir / "cuda_energy_regressor_runner.exe" thread_count = _random_thread_count() cmd = [ - nvcc, "-std=c++11", "-O0", + nvcc, "-std=c++17", "-O0", f"-DGRID_CUDA_FLOATING_BASE={1 if floating_base else 0}", "-DGRID_CUDA_LINALG_BACKEND=GRID_LINALG_GLASS", f"-DGRID_CUDA_REGRESSOR_TEST_THREADS={thread_count}", diff --git a/test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py b/test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py index 298b1395..dadfdcd0 100644 --- a/test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py +++ b/test/cuda_equivalents/test_cuda_f_ext_gradient_equivalence.py @@ -114,7 +114,7 @@ def _gen_and_compile(proj, build_dir, floating_base, codegen_profile="all"): shutil.copyfile(RUNNER_SOURCE, runner_copy) exe = build_dir / "cuda_f_ext_gradient_runner.exe" cmd = [ - nvcc, "-std=c++11", "-O0", + nvcc, "-std=c++17", "-O0", f"-DGRID_CUDA_FLOATING_BASE={1 if floating_base else 0}", "-DGRID_CUDA_LINALG_BACKEND=GRID_LINALG_GLASS", "-I", str(Path(__file__).resolve().parents[2]), diff --git a/test/cuda_equivalents/test_cuda_fd_parameter_gradient.py b/test/cuda_equivalents/test_cuda_fd_parameter_gradient.py index 8a3921ec..3867b981 100644 --- a/test/cuda_equivalents/test_cuda_fd_parameter_gradient.py +++ b/test/cuda_equivalents/test_cuda_fd_parameter_gradient.py @@ -126,7 +126,7 @@ def _compile_runner(build_dir: Path, floating_base: bool): executable = build_dir / "cuda_fpg_runner.exe" thread_count = _random_thread_count() cmd = [ - nvcc, "-std=c++11", "-O0", + nvcc, "-std=c++17", "-O0", f"-DGRID_CUDA_FLOATING_BASE={1 if floating_base else 0}", "-DGRID_CUDA_LINALG_BACKEND=GRID_LINALG_GLASS", f"-DGRID_CUDA_FPG_TEST_THREADS={thread_count}", diff --git a/test/cuda_equivalents/test_cuda_fext_equivalence.py b/test/cuda_equivalents/test_cuda_fext_equivalence.py index acb0c925..7f17f5e5 100644 --- a/test/cuda_equivalents/test_cuda_fext_equivalence.py +++ b/test/cuda_equivalents/test_cuda_fext_equivalence.py @@ -105,7 +105,7 @@ def _gen_and_compile(proj, build_dir, floating_base): build_dir / "grid_runner_select.cuh") exe = build_dir / "cuda_fext_runner.exe" cmd = [ - nvcc, "-std=c++11", "-O0", + nvcc, "-std=c++17", "-O0", f"-DGRID_CUDA_FLOATING_BASE={1 if floating_base else 0}", "-DGRID_CUDA_LINALG_BACKEND=GRID_LINALG_GLASS", "-gencode", f"arch=compute_{arch},code=sm_{arch}", diff --git a/test/cuda_equivalents/test_cuda_glass_handoff.py b/test/cuda_equivalents/test_cuda_glass_handoff.py new file mode 100644 index 00000000..aae68f1e --- /dev/null +++ b/test/cuda_equivalents/test_cuda_glass_handoff.py @@ -0,0 +1,59 @@ +"""Independent numerical checks of embedded cooperative handoffs, not timing.""" +import subprocess +from pathlib import Path + +import numpy as np +import pytest + +from grid_assets import robot_urdf +from grid_codegen import GRiDCodeGenerator +from URDFParser import URDFParser +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable + + +@pytest.mark.cuda_equivalence +@pytest.mark.parametrize("scalar,atol,rtol", [("float", 3e-5, 3e-5), pytest.param("double", 2e-11, 2e-11, marks=pytest.mark.fp64_alpha)]) +def test_embedded_glass_restricted_handoffs(tmp_path, scalar, atol, rtol): + robot = URDFParser().parse(str(robot_urdf("iiwa14"))) + GRiDCodeGenerator(robot, FILE_NAMESPACE="grid").gen_all_code( + codegen_profile="kinematics", output_path=str(tmp_path / "grid.cuh")) + exe, _ = cached_nvcc_executable([Path(__file__).with_name("cuda_glass_handoff_runner.cu")], + ["-std=c++17", "-O3", "-Xptxas=-v", f"-DTEST_SCALAR={scalar}", + f"-arch=sm_{_detect_cuda_arch()}"], + exe_name="handoff.exe", fallback_dir=tmp_path, include_dirs=[tmp_path], + what="GLASS handoff runner") + rng = np.random.default_rng(729) + inputs, expected = [], [] + for _ in range(2): + m = rng.normal(size=(7, 7)) + spd = m @ m.T + 2*np.eye(7) + indefinite = (m + m.T)/2 + # Force pivot selection away from the original diagonal. + pivoted = np.roll(spd, 1, axis=0) + rhs = rng.normal(size=7) + dtype = np.float32 if scalar == "float" else np.float64 + spd, indefinite, pivoted, rhs = [a.astype(dtype).astype(np.float64) + for a in (spd, indefinite, pivoted, rhs)] + w, v = np.linalg.eigh(indefinite) + projected = (v * np.maximum(w, .125)) @ v.T + inputs.extend(np.r_[spd.ravel(order="F"), indefinite.ravel(order="F"), + pivoted.ravel(order="F"), rhs]) + expected.extend(np.r_[np.linalg.solve(spd, rhs), projected.ravel(order="F"), + np.linalg.inv(pivoted).ravel(order="F"), [1234]*3]) + stdin = " ".join(format(x, ".17g") for x in inputs) + "\n" + (tmp_path / "input.txt").write_text(stdin) + for storage in (0, 1): + baseline = None + for threads in (1, 17, 32, 64, 128, 256): + result = subprocess.run([str(exe), str(threads), str(storage)], input=stdin, + capture_output=True, text=True, timeout=60) + assert result.returncode == 0, result.stdout + result.stderr + values = np.array([np.fromstring(line, sep=" ") for line in result.stdout.splitlines()]) + assert values.shape == (3, len(expected)), result.stdout + np.testing.assert_allclose(values, np.broadcast_to(expected, values.shape), atol=atol, rtol=rtol) + np.testing.assert_array_equal(values[0], values[1]) + np.testing.assert_array_equal(values[0], values[2]) + if baseline is not None: + np.testing.assert_array_equal(values[0], baseline) + baseline = values[0] diff --git a/test/cuda_equivalents/test_cuda_joint_transform_suffix.py b/test/cuda_equivalents/test_cuda_joint_transform_suffix.py new file mode 100644 index 00000000..a01cdcbc --- /dev/null +++ b/test/cuda_equivalents/test_cuda_joint_transform_suffix.py @@ -0,0 +1,58 @@ +"""Scalar transforms in a composed divergent suffix, not only in isolation.""" +from pathlib import Path +import subprocess +import numpy as np +import pytest +from URDFParser import URDFParser +from grid_codegen import GRiDCodeGenerator +from grid_codegen.model_descriptor import descriptor_from_robot +from test.cuda_equivalents.descriptor_oracle import descriptor_fk +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable + +pytestmark = [pytest.mark.cuda_equivalence, pytest.mark.robot_smoke] + + +@pytest.mark.parametrize('n,general', [(7,False),(7,True),(32,True)]) +@pytest.mark.parametrize('precision', ['float', pytest.param('double', marks=pytest.mark.fp64_alpha)]) +def test_scalar_transform_composed_suffix(tmp_path,n,general,precision): + inertial='' + xml=''+''.join(f'{inertial}' for i in range(n))+'' + for i in range(n): + axis='0 0 1' if not general else ['0 1 0','0 0 -1','1 0 0'][i%3] + kind='prismatic' if general and i%5==2 else 'revolute' + parent='base' if i==0 else f'b{i-1}' + xml+=f'' + xml+=f'' + urdf=tmp_path/'robot.urdf';urdf.write_text(xml) + robot=URDFParser().parse(str(urdf));d=descriptor_from_robot(robot) + GRiDCodeGenerator(robot).gen_all_code(output_path=str(tmp_path/'grid.cuh'), + codegen_profile='kinematics',emit_plant=False,include_homogenous_transforms=True, + enable_mujoco_kernels=False) + frame=next(f['id'] for f in d['frames'] if f['name']=='tip' and f['kind']=='fixed_joint') + inputs=[];expected=[] + for warp in range(2): + q=np.linspace(-.15,.2,n);q[warp]+=.1 + poses=descriptor_fk(d,q) + world={f['joint_id']:p for f,p in zip(d['frames'],poses) if f['kind']=='joint'} + local=[world[j] if j==0 else np.linalg.solve(world[j-1],world[j]) for j in range(n)] + inputs.extend(p.ravel(order='F') for p in [*(world[j] for j in range(n)),*local]) + for j in range(32): + if j>=n:expected.append(np.full(16,789.));continue + changed=q.copy();changed[j]+=.025*(j+1) + expected.append(descriptor_fk(d,changed)[frame].ravel(order='F')) + dtype=np.float32 if precision=='float' else np.float64 + np.asarray(inputs,dtype).tofile(tmp_path/'input.bin') + for legacy in (True,False): + name='legacy' if legacy else 'probe' + flags=['-O3','-std=c++17','-lineinfo','-Xptxas=-v','-arch=sm_'+_detect_cuda_arch(), + '-DTEST_SCALAR='+precision,'-DTEST_FRAME='+str(frame)]+(['-DTEST_LEGACY'] if legacy else []) + exe,_=cached_nvcc_executable([Path(__file__).with_name('cuda_joint_transform_suffix_runner.cu')],flags, + exe_name=name,fallback_dir=tmp_path,include_dirs=[tmp_path],what='joint transform suffix runner') + run=subprocess.run([str(exe),str(tmp_path/'input.bin'),str(tmp_path/(name+'.bin'))],capture_output=True,text=True) + (tmp_path/(name+'-run.log')).write_text(run.stdout+run.stderr) + assert run.returncode==0,run.stderr + actual=np.fromfile(tmp_path/(name+'.bin'),dtype).reshape(2,64,16) + tol=2e-5 if precision=='float' else 2e-12 + np.testing.assert_allclose(actual[0],expected,atol=tol,rtol=tol) + np.testing.assert_array_equal(actual[0],actual[1]) diff --git a/test/cuda_equivalents/test_cuda_menu_workspace.py b/test/cuda_equivalents/test_cuda_menu_workspace.py new file mode 100644 index 00000000..a373a8a9 --- /dev/null +++ b/test/cuda_equivalents/test_cuda_menu_workspace.py @@ -0,0 +1,64 @@ +"""Menu dimensions, alignment, poison and repeated B=2 composition.""" +from pathlib import Path +import subprocess +import numpy as np +import pytest +from URDFParser import URDFParser +from grid_assets import robot_urdf +from grid_codegen import GRiDCodeGenerator +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable + +pytestmark=[pytest.mark.cuda_equivalence,pytest.mark.robot_smoke] + + +@pytest.fixture(scope='module',params=[('iiwa14',False,'float'),('go2',True,'float'), + pytest.param(('iiwa14',False,'double'),marks=pytest.mark.fp64_alpha),('indy7',False,'float')], + ids=['iiwa14-float','go2-float','iiwa14-double','indy7-float']) +def menu_case(request,tmp_path_factory): + name,floating,scalar=request.param + path=tmp_path_factory.mktemp('menu_workspace_'+name+'_'+scalar) + urdf=Path(__file__).with_name('consumer_indy7.urdf') if name=='indy7' else robot_urdf(name) + robot=URDFParser().parse(str(urdf),floating_base=floating) + gen=GRiDCodeGenerator(robot,dtype=scalar) + gen.gen_all_code(algorithm_list=['integrator','frame_jacobian'],emit_plant=True, + enable_mujoco_kernels=False,output_path=str(path/'grid.cuh')) + exe,_=cached_nvcc_executable([Path(__file__).with_name('cuda_menu_workspace_runner.cu')], + ['-std=c++17','-O2','-lineinfo','-arch=sm_'+_detect_cuda_arch(),'-Xptxas=-v','-DTEST_SCALAR='+scalar], + exe_name='probe',fallback_dir=path,include_dirs=[path],what='menu workspace runner') + return path,exe,robot,np.float32 if scalar=='float' else np.float64 + + +@pytest.mark.parametrize('kind',[0,1,2,3],ids=['zero','default','wide-x','wide-u']) +def test_menu_workspace(menu_case,kind): + path,exe,robot,dtype=menu_case + nv,nq=robot.get_num_vel(),robot.get_num_pos();fb=robot.floating_base + nx=nq+nv;tn=2*nv;nu=nv-6 if fb else nv;nlx=tn-6 if fb else nx + mx,mu=[(0,0),(nlx,nu),(4097,0),(0,4097)][kind] + rng=np.random.default_rng(7203) + ax=rng.uniform(-.03,.03,(mx,nlx)).astype(dtype);au=rng.uniform(-.03,.03,(mu,nu)).astype(dtype) + bx=rng.uniform(-.1,.1,mx).astype(dtype);bu=rng.uniform(-.1,.1,mu).astype(dtype) + tx=np.zeros(mx,dtype);tu=np.zeros(mu,dtype) + wx=rng.uniform(.1,1,mx).astype(dtype);wu=rng.uniform(.1,1,mu).astype(dtype) + inp=path/f'input-{kind}.bin';np.concatenate([ax.ravel(order='F'),bx,tx,wx,au.ravel(order='F'),bu,tu,wu]).tofile(inp) + expected=[] + for b in range(2): + x=(.01*(np.arange(nx)+1)+.03*b).astype(dtype);u=(-.02*(np.arange(nu)+1)+.04*b).astype(dtype) + if fb:x[3:7]=[0,0,0,1] + lx=x[7:] if fb else x + rx=ax.astype(float)@lx+bx;ru=au.astype(float)@u+bu + gx=np.zeros(tn);hx=np.zeros((tn,tn));offset=6 if fb else 0 + gx[offset:]=ax.T@(wx*rx);hx[offset:,offset:]=ax.T@(wx[:,None]*ax) + gu=au.T@(wu*ru);hu=au.T@(wu[:,None]*au) + expected.append(2*np.r_[.5*(np.dot(wx,rx*rx)+np.dot(wu,ru*ru)),gx,gu,hx.ravel(order='F'),hu.ravel(order='F')]) + expected=np.asarray(expected);baseline=None + for threads in (1,17,32,64,256): + output=path/f'out-{kind}-{threads}.bin' + result=subprocess.run([str(exe),str(inp),str(output),str(threads),str(kind)],capture_output=True,text=True) + (path/f'run-{kind}-{threads}.log').write_text(result.stdout+result.stderr) + assert result.returncode==0,result.stdout+result.stderr+f' exit={result.returncode}' + actual=np.fromfile(output,dtype).reshape(expected.shape) + np.testing.assert_allclose(actual,expected,rtol=1e-4 if dtype==np.float32 else 2e-12, + atol=1e-6 if dtype==np.float32 else 2e-12) + if baseline is not None:np.testing.assert_array_equal(actual,baseline) + baseline=actual diff --git a/test/cuda_equivalents/test_cuda_model_descriptor.py b/test/cuda_equivalents/test_cuda_model_descriptor.py new file mode 100644 index 00000000..16b73e3e --- /dev/null +++ b/test/cuda_equivalents/test_cuda_model_descriptor.py @@ -0,0 +1,76 @@ +"""Public metadata device access and single-pose frame composition, no timing.""" +from pathlib import Path +import subprocess + +import numpy as np +import pytest +from URDFParser import URDFParser +from grid_codegen.model_descriptor import descriptor_from_robot +from grid_codegen.model_metadata_cpp import emit_model_metadata, JOINT_KINDS, FRAME_KINDS +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable +from test.cuda_equivalents.constant_model import constant_model + +pytestmark = [pytest.mark.cuda_equivalence, pytest.mark.robot_smoke] +ROOT = Path(__file__).resolve().parents[2] + + +@pytest.fixture(scope='module', params=['constant', 'floating', 'spherical', 'branch', 'empty', 'runtime']) +def case(request, tmp_path_factory): + path=tmp_path_factory.mktemp('descriptor_'+request.param) + if request.param in ('spherical', 'branch'): + name='mixed_spherical_arm' if request.param=='spherical' else 'branching_skew_arm' + robot=URDFParser().parse(str(ROOT/'external/URDFParser/tests/fixtures'/f'{name}.urdf')) + else: + robot=constant_model(path, zero=request.param=='empty') + if request.param=='floating': + robot=URDFParser().parse(str(path/'model-False-False-False.urdf'),floating_base=True) + descriptor=descriptor_from_robot(robot, runtime_transform=request.param=='runtime') + (path/'metadata.cuh').write_text(emit_model_metadata(descriptor)) + exe,_=cached_nvcc_executable([Path(__file__).with_name('cuda_model_descriptor_runner.cu')], + ['-std=c++17','-O2','-arch=sm_'+_detect_cuda_arch()], + exe_name='probe',fallback_dir=path,include_dirs=[path],what='model descriptor runner') + return descriptor,exe + + +def expected(d): + values=[] + for j in d['joints']: + m=j['mimic'] + pos=j['limits']['position'] or [None,None] + vel,effort=j['limits']['velocity'],j['limits']['effort'] + values.extend([j['id'],j['parent_body'],j['child_body'], + -1 if j['q_start'] is None else j['q_start'], + -1 if j['v_start'] is None else j['v_start'],j['local_nq'],j['local_nv'], + JOINT_KINDS.index(j['kind']),j['owns_coordinates'],j['axis'] is not None, + j['pitch'] is not None,m is not None,-1 if not m or m['leader_joint'] is None else m['leader_joint'], + m['multiplier'] if m else 1.,m['offset'] if m else 0.]) + values.extend(j['axis'] or [0.,0.,0.]) + values.append(j['pitch'] or 0.) + values.extend(np.asarray(j['T_parent_joint_zero']).ravel(order='F')) + values.extend([pos[0] is not None,pos[1] is not None,vel is not None,effort is not None, + pos[0] or 0.,pos[1] or 0.,vel or 0.,effort or 0.,0,71]) + body=np.array([[0.,-1,0,.3],[1,0,0,-.4],[0,0,1,.2],[0,0,0,1]]) + for f in d['frames']: + values.extend([f['id'],f['attached_body'],-1 if f['joint_id'] is None else f['joint_id'], + FRAME_KINDS.index(f['kind'])]) + values.extend(np.asarray(f['T_body_frame']).ravel(order='F')) + values.append(not d['runtime_transform']) + values.extend([91]*16 if d['runtime_transform'] else (body@f['T_body_frame']).ravel(order='F')) + values.append(0) + return values + + +@pytest.mark.parametrize('precision',[0,pytest.param(1, marks=pytest.mark.fp64_alpha)],ids=['fp32','fp64']) +def test_device_metadata_and_single_pose_composition(case,precision): + descriptor,exe=case + oracle=np.array(expected(descriptor),dtype=np.float64 if precision else np.float32) + baseline=None + for threads in (1,17,32,64,256): + result=subprocess.run([str(exe),str(threads),str(precision)],capture_output=True,text=True) + assert result.returncode==0,result.stderr + actual=np.fromstring(result.stdout,sep='\n') + np.testing.assert_allclose(actual,oracle,atol=2e-7 if not precision else 2e-15,rtol=0) + if baseline is not None: + np.testing.assert_array_equal(actual,baseline) + baseline=actual diff --git a/test/cuda_equivalents/test_cuda_model_primitives.py b/test/cuda_equivalents/test_cuda_model_primitives.py new file mode 100644 index 00000000..0498ce13 --- /dev/null +++ b/test/cuda_equivalents/test_cuda_model_primitives.py @@ -0,0 +1,81 @@ +"""Thread-scope transforms and compact/dense manifold differences.""" +from pathlib import Path +import subprocess + +import numpy as np +import pytest +from URDFParser import URDFParser +from RBDReference import RBDReference +from grid_codegen import GRiDCodeGenerator +from grid_codegen.model_descriptor import descriptor_from_robot +from test.cuda_equivalents.descriptor_oracle import descriptor_fk +from test.cuda_equivalents.manifold_oracle import derivative, state +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable +from test.cuda_equivalents.constant_model import constant_model +from grid_assets import robot_urdf + +pytestmark=[pytest.mark.cuda_equivalence,pytest.mark.robot_smoke] +ROOT=Path(__file__).resolve().parents[2] + + +@pytest.mark.parametrize('name,floating,runtime',[ + ('mixed_spherical_arm',False,False), ('rotated_inertial_arm',True,False), + ('branching_skew_arm',False,False), ('helical_arm',False,False), + ('rotated_inertial_arm',False,True), ('constant',False,False), + ('zero',False,False), ('slider_mimic_fixed_tool',False,False), + ('fr3',False,False)]) +@pytest.mark.parametrize('precision',['float',pytest.param('double', marks=pytest.mark.fp64_alpha)]) +def test_model_primitives(tmp_path,name,floating,runtime,precision): + if name in ('constant','zero'): + robot=constant_model(tmp_path,zero=name=='zero') + else: + path = robot_urdf(name) if name in ('slider_mimic_fixed_tool','fr3') else ROOT/'external/URDFParser/tests/fixtures'/f'{name}.urdf' + robot=URDFParser().parse(str(path),floating_base=floating) + ref=RBDReference(robot);d=descriptor_from_robot(robot,runtime_transform=runtime) + gen=GRiDCodeGenerator(robot,dtype=precision) + gen.gen_all_code(algorithm_list=['integrator'],emit_plant=False,runtime_transform=runtime, + include_homogenous_transforms=True,enable_mujoco_kernels=False,output_path=str(tmp_path/'grid.cuh')) + exe,_=cached_nvcc_executable([Path(__file__).with_name('cuda_model_primitives_runner.cu')], + ['-std=c++17','-O2','-lineinfo','-Xptxas=-v','-arch=sm_'+_detect_cuda_arch(),'-DTEST_SCALAR='+precision], + exe_name='probe',fallback_dir=tmp_path,include_dirs=[tmp_path],what='model primitives runner') + dtype=np.float32 if precision=='float' else np.float64 + inputs=[];expected=[];N=d['nv'];Q=d['nq'] + for s in range(35): + a,w=state(ref,430+s) + if s in (0,1): w*=0 if s==0 else 1e-7 + if s in (3,4): + for _,_,iv in ref._joint_retract_specs(): + axis=np.array([1.,-2.,3.])/np.sqrt(14.) + w[iv[-3:]]=axis*(np.pi+(-1 if s==3 else 1)*.02) + b=ref.integrate(a,w) + if s%2: + for _,iq,_ in ref._joint_retract_specs(): b[iq[-4:]]*=-1 + a,b=a.astype(dtype),b.astype(dtype);inputs.append(np.r_[a,b]) + a,b=a.astype(float),b.astype(float) + out=[ref.difference(a,b),ref.dDifference(a,b,'from').ravel(order='F'),ref.dDifference(a,b,'to').ravel(order='F')] + if s==2: + for arg in ('from','to'): + fd=derivative(lambda e: ref.difference(ref.integrate(a,e),b) if arg=='from' else ref.difference(a,ref.integrate(b,e)),N) + # FD assumes unit input; float-rounding perturbs norms slightly. + if precision=='double': np.testing.assert_allclose(fd,ref.dDifference(a,b,arg),atol=2e-8,rtol=2e-8) + frames=descriptor_fk(d,a) + worlds={f['joint_id']:pose for f,pose in zip(d['frames'],frames) if f['kind']=='joint'} + for j in d['joints']: + local=np.linalg.solve(worlds[j['parent_body']],worlds[j['id']]) if j['parent_body']!=-1 else worlds[j['id']] + out.append(np.full(16,789.) if runtime else local.ravel(order='F')) + admitted=j['owns_coordinates'] and j['kind'] in ('revolute','continuous','prismatic') and not runtime + out.append(local.ravel(order='F') if admitted else np.full(16,789.)) + expected.append(np.r_[789.,np.concatenate(out),789.]) + np.asarray(inputs,dtype=dtype).tofile(tmp_path/'input.bin') + baseline=None + for threads in (1,17,32,64,128): + output=tmp_path/f'out-{threads}.bin' + ran=subprocess.run([str(exe),str(tmp_path/'input.bin'),str(output),str(threads)],capture_output=True,text=True) + (tmp_path/f'run-{threads}.log').write_text(ran.stdout+ran.stderr) + assert ran.returncode==0,ran.stdout+ran.stderr + actual=np.fromfile(output,dtype=dtype).reshape(np.shape(expected)) + tol=2e-5 if precision=='float' else 1e-10 + np.testing.assert_allclose(actual,expected,atol=tol,rtol=tol) + if baseline is not None: np.testing.assert_array_equal(actual,baseline) + baseline=actual diff --git a/test/cuda_equivalents/test_cuda_planar_translation_equivalence.py b/test/cuda_equivalents/test_cuda_planar_translation_equivalence.py index 755687ca..10119c24 100644 --- a/test/cuda_equivalents/test_cuda_planar_translation_equivalence.py +++ b/test/cuda_equivalents/test_cuda_planar_translation_equivalence.py @@ -63,7 +63,7 @@ def _compile_runner(build_dir): arch = _detect_cuda_arch() exe = build_dir / "cuda_planar_translation_runner.exe" cmd = [ - nvcc, "-std=c++11", "-O0", + nvcc, "-std=c++17", "-O0", "-DGRID_CUDA_FLOATING_BASE=0", "-DGRID_RUNNER_SKIP_GRADIENTS=1", "-DGRID_CUDA_LINALG_BACKEND=GRID_LINALG_GLASS", diff --git a/test/cuda_equivalents/test_cuda_plant_gradient_arena.py b/test/cuda_equivalents/test_cuda_plant_gradient_arena.py new file mode 100644 index 00000000..fd2fac16 --- /dev/null +++ b/test/cuda_equivalents/test_cuda_plant_gradient_arena.py @@ -0,0 +1,124 @@ +"""Full-shared plant allocation must not borrow a spilled integrator sizer.""" +import json +import subprocess + +import numpy as np +import pytest +from URDFParser import URDFParser +from grid_assets import robot_urdf +from grid_codegen import GRiDCodeGenerator +from grid_codegen.algorithms._plant import _plant_step_gradient_extra_t_buffers +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable + +pytestmark = [pytest.mark.cuda_equivalence, pytest.mark.robot_smoke] + + +@pytest.mark.parametrize('name,floating,dtype,runtime', [ + ('iiwa14', False, 'float', False), + pytest.param('iiwa14', False, 'double', False, marks=pytest.mark.fp64_alpha), + ('go2', True, 'float', False), + ('go2', True, 'float', True), + ('g1', False, 'float', False), +]) +def test_full_shared_plant_gradient_bounds(tmp_path, name, floating, dtype, runtime): + robot = URDFParser().parse(str(robot_urdf(name)), floating_base=floating) + gen = GRiDCodeGenerator(robot, dtype=dtype) + gen.gen_all_code(algorithm_list=['integrator_gradient'], emit_plant=True, + enable_mujoco_kernels=False, runtime_transform=runtime, + output_path=str(tmp_path / 'grid.cuh')) + # The real kernel-emission helper, not the binder's own size expression. + layout = gen.gen_XImats_helpers_temp_shared_memory_code( + gen.gen_integrator_gradient_inner_temp_mem_size(), + extra_t_buffers=_plant_step_gradient_extra_t_buffers(gen)) + fields = list(layout.t_buffers) + [('s_XImats', layout.ximat_size), + ('s_temp', layout.temp_mem_size)] + if layout.topology_count: + fields.append(('s_topology_helpers', layout.topology_count)) + writes = '\n'.join( + f'ends[{i}] = reinterpret_cast(a.{field} + {count}) - base;\n' + f'if ({count}) a.{field}[{count}-1] = 0;' + for i, (field, count) in enumerate(fields)) + source = r''' +#include "grid.cuh" +#include +#include +#include +#include +using T = DTYPE; +#define CK(x) do { auto e=(x); if(e!=cudaSuccess) {fprintf(stderr,"%s: %s\n",#x,cudaGetErrorString(e));exit(2);} } while(0) +__global__ void inspect(unsigned char *base, size_t *ends) { + auto a = grid_plant::plant_step_gradient_arena::carve(base); + WRITES +} +int main() { + const size_t bytes = grid_plant::PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES(); + const size_t legacy = grid::INTEGRATOR_DU_DYNAMIC_SHARED_MEM_BYTES(); + unsigned char *base; size_t *ends, host[NFIELDS]; + CK(cudaMalloc(&base, bytes + 32)); CK(cudaMemset(base, 0xA5, bytes + 32)); + CK(cudaMalloc(&ends, sizeof(host))); + inspect<<<1,1>>>(base, ends); CK(cudaGetLastError()); CK(cudaDeviceSynchronize()); + CK(cudaMemcpy(host, ends, sizeof(host), cudaMemcpyDeviceToHost)); + unsigned char guard[32]; CK(cudaMemcpy(guard, base+bytes, 32, cudaMemcpyDeviceToHost)); + for(auto x:guard) if(x!=0xA5) return 3; + size_t end=0; for(auto x:host) end=std::max(end,x); + if(end>bytes || bytes-end>=16) return 4; + size_t cap; CK(grid::grid_get_max_dynamic_shared_memory_bytes(&cap)); + auto admission=grid_plant::plant_step_gradient_check_shared_memory(); + if(admission!=(bytes>cap ? cudaErrorInvalidConfiguration : cudaSuccess)) return 5; + printf("{\"bytes\":%zu,\"legacy\":%zu,\"end\":%zu,\"capacity\":%zu,\"admitted\":%s}\n", + bytes, legacy, end, cap, admission==cudaSuccess ? "true":"false"); + CK(cudaFree(ends)); CK(cudaFree(base)); +} +'''.replace('DTYPE', dtype).replace('WRITES', writes).replace('NFIELDS', str(len(fields))) + runner = tmp_path / 'probe.cu' + runner.write_text(source) + exe, _ = cached_nvcc_executable([runner], ['-std=c++17', '-O2', '-DGRID_CUDA_DEBUG_LAYOUT', + f'-arch=sm_{_detect_cuda_arch()}'], + exe_name='probe', fallback_dir=tmp_path, include_dirs=[tmp_path], + what='plant gradient arena probe') + ran = subprocess.run([str(exe)], capture_output=True, text=True) + (tmp_path / 'run.log').write_text(ran.stdout + ran.stderr) + assert ran.returncode == 0, ran.stdout + ran.stderr + measured = json.loads(ran.stdout.strip().splitlines()[-1]) + if name == 'g1': + # Negative control: the old allocation really was smaller than the carve. + assert measured['end'] > measured['legacy'] + assert measured['admitted'] == (measured['bytes'] <= measured['capacity']) + if name in ('iiwa14', 'g1'): + import grid_rbd + from RBDReference import RBDReference + handle = grid_rbd.register_robot( + name=f'plant_arena_{name}', urdf_path=str(robot_urdf(name)), + cache_dir=tmp_path / 'cache', max_batch_size=2, + algorithm_list=['integrator_gradient'], enable_mujoco_kernels=False, + dtype='float64' if dtype == 'double' else 'float32') + scalar = np.float64 if dtype == 'double' else np.float32 + rng = np.random.default_rng(91) + q = rng.uniform(-.1, .1, (2, robot.get_num_pos())).astype(scalar) + qd = rng.uniform(-.1, .1, (2, robot.get_num_vel())).astype(scalar) + u = rng.uniform(-.1, .1, qd.shape).astype(scalar) + x = np.concatenate([q, qd], axis=1) + # Rejection must not poison subsequent use of the supported tiered path. + if not measured['admitted']: + with pytest.raises(RuntimeError): + handle.plant_step_gradient(x, u, .01) + tiered = handle.integrator_gradient(q, qd, u, .01) + reference = RBDReference(robot) + for b in range(2): + expected = reference.plant_step_gradient(q[b], qd[b], u[b], .01) + error = np.max(np.abs(tiered[b] - expected) / (np.abs(expected) + 1e-3)) + assert error < (5e-8 if dtype == 'double' else 5e-3) + if measured['admitted']: + actual = handle.plant_step_gradient(x, u, .01) + np.testing.assert_array_equal(actual, tiered) + # Step and gradient share the precision-dependent C ABI scalar + # contract. Non-default gravity and dt catch accidental constants. + for dt, gravity in ((.01, -9.81), (.025, -3.7)): + step = handle.plant_step(x, u, dt, gravity=gravity) + for b in range(2): + bias = reference.inverse_dynamics(q[b], qd[b], GRAVITY=gravity)[0] + acceleration = np.linalg.solve(reference.crba(q[b]), u[b] - bias) + expected = np.concatenate([q[b] + dt * qd[b], qd[b] + dt * acceleration]) + error = np.max(np.abs(step[b] - expected) / (np.abs(expected) + 1e-3)) + assert error < (5e-8 if dtype == 'double' else 5e-3) diff --git a/test/cuda_equivalents/test_cuda_plant_workspace.py b/test/cuda_equivalents/test_cuda_plant_workspace.py new file mode 100644 index 00000000..351a3e56 --- /dev/null +++ b/test/cuda_equivalents/test_cuda_plant_workspace.py @@ -0,0 +1,119 @@ +"""Bounded public binder acceptance, not timing or downstream acceptance.""" +from pathlib import Path +import subprocess + +import numpy as np +import pytest +from URDFParser import URDFParser +from RBDReference import RBDReference +from grid_assets import robot_urdf +from grid_codegen import GRiDCodeGenerator +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable + +pytestmark = [pytest.mark.cuda_equivalence, pytest.mark.robot_smoke] + + +@pytest.fixture(scope='module', params=[('acrobot','float',False,'euler'), + pytest.param(('acrobot','double',False,'euler'),marks=pytest.mark.fp64_alpha), + ('iiwa14','float',False,'euler'), + pytest.param(('iiwa14','double',False,'euler'),marks=pytest.mark.fp64_alpha), + ('acrobot','float',True,'semi_implicit_euler'), + pytest.param(('acrobot','double',True,'semi_implicit_euler'),marks=pytest.mark.fp64_alpha), + ('branching','double',True,'euler')], + ids=['acrobot-float','acrobot-double','iiwa14-float','iiwa14-double','acrobot-semi-runtime-float', + 'acrobot-semi-runtime-double','branching-runtime-double']) +def workspace_case(request,tmp_path_factory): + name,scalar,runtime,integrator=request.param + path=tmp_path_factory.mktemp('workspace_'+name+'_'+scalar) + if name=='branching': + urdf=Path(__file__).resolve().parents[2]/'external/URDFParser/tests/fixtures/branching_skew_arm.urdf' + else: + urdf = Path(__file__).with_name('consumer_acrobot.urdf') if name=='acrobot' else robot_urdf(name) + robot=URDFParser().parse(str(urdf)) + gen=GRiDCodeGenerator(robot,dtype=scalar) + gen.gen_all_code(algorithm_list=['integrator_hessian','integrator_gradient'],emit_plant=True, + enable_mujoco_kernels=False,fixed_target_name='iiwa_joint_ee' if name=='iiwa14' else '', + runtime_transform=runtime,output_path=str(path/'grid.cuh')) + exe,_=cached_nvcc_executable([Path(__file__).with_name('cuda_plant_workspace_runner.cu')], + ['-std=c++17','-O3','-lineinfo','-arch=sm_'+_detect_cuda_arch(),'-Xptxas=-v', + '-DTEST_INTEGRATOR='+integrator.upper(),'-DTEST_RUNTIME_TRANSFORM='+str(int(runtime)), + '-DTEST_CROSS_KERNEL_ROUNDOFF='+str(int(name=='branching' and scalar=='double' and runtime)), + '-DTEST_SCALAR='+scalar], + exe_name='probe',fallback_dir=path,include_dirs=[path],what='plant workspace runner') + n=robot.get_num_vel() + dtype=np.float32 if scalar=='float' else np.float64 + rng=np.random.default_rng(29103) + z=np.stack([np.r_[rng.uniform(-.6,.6,n),rng.uniform(-.4,.4,n),rng.uniform(-2,2,n)] for _ in range(2)]).astype(dtype) + z.tofile(path/'input.bin') + ref=RBDReference(robot) + expected=[] + for row in z.astype(float): + q,v,u=row[:n],row[n:2*n],row[2*n:] + dt=float(dtype(.01)) + expected.append(np.r_[ref.plant_step_hessian(q,v,u,dt,integrator).ravel(), + ref.plant_step_gradient(q,v,u,dt,integrator).ravel(order='F'), + ref.plant_step(q,v,u,dt,integrator)]) + np.savez(path/'oracle.npz', inputs=z, expected=np.asarray(expected)) + return path,exe,dtype,np.asarray(expected),robot,z,integrator + + +@pytest.mark.parametrize('spill',[False,True],ids=['full','spill']) +def test_composed_workspace(workspace_case,spill): + path,exe,dtype,expected,robot,z,integrator=workspace_case + n=robot.get_num_vel();nh=18*n**3;nj=6*n*n;nx=2*n + # Full FP64 is intentionally beyond the physical limit: test its admission + # separately, never launch it as a positive row. FP32 exercises both shapes. + if n==7 and dtype==np.float64 and not spill: + result=subprocess.run([str(exe),str(path/'input.bin'),str(path/'not-written.bin'),'17','0'],capture_output=True,text=True) + assert result.returncode==8, result.stdout+result.stderr + return + baseline=None + for threads in (1,17,32,64,256): + output=path/f'out-{spill}-{threads}.bin' + result=subprocess.run([str(exe),str(path/'input.bin'),str(output),str(threads),str(int(spill))],capture_output=True,text=True) + (path/f'run-{spill}-{threads}.log').write_text(result.stdout+result.stderr) + assert result.returncode==0,result.stdout+result.stderr+f' exit={result.returncode}' + actual=np.fromfile(output,dtype=dtype).reshape(expected.shape) + if path.name.startswith('workspace_branching_'): + # The approved roundoff allowance is between separately compiled + # stock/composed kernels, not between our storage layouts. + other=path/f'cross-storage-{spill}-{threads}.bin' + compared=subprocess.run([str(exe),str(path/'input.bin'),str(other),str(threads),str(int(not spill))],capture_output=True,text=True) + (path/f'cross-storage-{spill}-{threads}.log').write_text(compared.stdout+compared.stderr) + assert compared.returncode==0,compared.stdout+compared.stderr + np.testing.assert_array_equal(actual,np.fromfile(other,dtype=dtype).reshape(expected.shape)) + (path/f'difference-{spill}-{threads}.txt').write_text('\n'.join( + f'{tuple(i)}: {actual[tuple(i)]:.17g} vs {expected[tuple(i)]:.17g}' + for i in np.argwhere(abs(actual-expected)>(2e-5+2e-3*abs(expected)))[:50])) + if dtype==np.float64: + np.testing.assert_allclose(actual,expected,rtol=1e-9,atol=1e-9) + else: + # Preserve the AGREED consumer policy, pinned DDP a89455b8 + # test_grid_adapter.py: step/J entrywise, Hessian tensor-norm error. + # The extra entrywise diagnostic above is deliberately retained: + # iiwa near-zero cancellation exceeds it in the stock kernel too. + np.testing.assert_allclose(actual[:,nh+nj:],expected[:,nh+nj:],rtol=3e-5,atol=3e-6) + np.testing.assert_allclose(actual[:,nh:nh+nj],expected[:,nh:nh+nj],rtol=3e-4,atol=3e-6) + for b in range(2): + h=actual[b,:nh].reshape(nx,3*n,3*n) + href=expected[b,:nh].reshape(h.shape) + budget=(3e-4*max(1.,np.max(abs(href)))) if dtype==np.float32 else 1e-9 + assert np.max(abs(h-href)) < budget + np.testing.assert_allclose(h,h.swapaxes(1,2),rtol=1e-4 if dtype==np.float32 else 1e-9, + atol=1e-5 if dtype==np.float32 else 1e-9) + np.testing.assert_array_equal(h[:,nx:,nx:],0) + if threads==1: + direction=np.random.default_rng(992+b).normal(size=3*n);direction/=np.linalg.norm(direction) + ref=RBDReference(robot);errors=[] + for eps in (1e-3,1e-4,1e-5): + plus=z[b].astype(float)+eps*direction;minus=z[b].astype(float)-eps*direction + def jac(v):return ref.plant_step_gradient(v[:n],v[n:nx],v[nx:],float(dtype(.01)),integrator) + fd=(jac(plus)-jac(minus))/(2*eps) + errors.append(np.max(abs(fd-np.einsum('oab,a->ob',h,direction)))) + # The finite difference has a truncation/cancellation floor; + # analytic FP64 agreement above remains 1e-9 entrywise. + fd_budget=budget if dtype==np.float32 else 1e-7*max(1.,np.max(abs(href))) + assert min(errors)= 32 else 19) + np.testing.assert_allclose(values[:12], expected, atol=2e-5, rtol=2e-5) + np.testing.assert_allclose(values[12:19], frame, atol=2e-5, rtol=2e-5) + if threads >= 32: + np.testing.assert_allclose(values[19:26], frame, atol=2e-5, rtol=2e-5) + if baseline is not None: + np.testing.assert_array_equal(values[:19], baseline) + baseline = values[:19] + + +@pytest.mark.parametrize("mimic", [False, True, "constant"]) +@pytest.mark.parametrize("axis", ["cardinal", "skew"]) +def test_prismatic_dynamics_against_independent_mechanics(tmp_path, mimic, axis, monkeypatch): + """ID/FD/ABA/M/Minv and cardinal first derivatives, not only FK. + + Pure translation gives a constant scalar mass. The follower's absolute + displacement is q/2 + offset, so its generalized mass is .7/4 and its + gravity contribution is .7/2. These expectations do not use either generator + or RBDReference spatial algebra. Dense-axis derivative coverage is separate. + """ + from RBDReference import RBDReference + from test.cuda_equivalents.cuda_harness import _compile_runner, _parse_runner_output, _run_runner + + source = robot_urdf("slider_mimic_fixed_tool" if mimic else "slider_fixed_tool") + urdf = tmp_path / "slider.urdf" + urdf.write_text(source.read_text().replace("0.6 0 0.8", "0 0 1") + if axis == "cardinal" else source.read_text()) + if mimic == "constant": + urdf.write_text(urdf.read_text().replace('mimic joint="slide"', 'mimic joint="tool"')) + robot = URDFParser().parse(str(urdf)) + algos = ["inverse_dynamics", "forward_dynamics", "aba", "crba", "minv"] + if axis == "cardinal": + algos += ["inverse_dynamics_gradient", "forward_dynamics_gradient"] + gen = GRiDCodeGenerator(robot, False, True, "grid") + gen.gen_all_code(output_path=str(tmp_path / "grid.cuh"), algorithm_list=algos, + enable_mujoco_kernels=False) + monkeypatch.setenv("GRID_CUDA_CACHE_DIR", str(tmp_path / "executables")) + exe, command = _compile_runner(tmp_path, run_tokens={"RUN_" + a.upper() for a in algos}, + skip_gradients=axis != "cardinal", skip_eepose_gradients=True) + direction = np.array([0., 0., 1.]) if axis == "cardinal" else np.array([.6, 0., .8]) + world_axis = Rotation.from_euler("xyz", [.3, .2, .1]).apply(direction) + # A constant follower still inherits the ancestor's full translation. + ratio = 1. if mimic == "constant" else .5 + mass = 1. + (.7 * ratio**2 if mimic else 0.) + bias = (1. + (.7 * ratio if mimic else 0.)) * 9.81 * world_axis[2] + reference = RBDReference(robot) + for q, qd, torque in ((0., 0., 0.), (.13, -.4, 2.), (.5, .7, -3.)): + expected = {"inverse_dynamics": bias, "forward_dynamics": (torque - bias) / mass, + "aba": (torque - bias) / mass, "crba": mass, "minv": 1. / mass} + np.testing.assert_allclose(reference.crba(np.array([q])), [[mass]], atol=1e-12, rtol=1e-12) + np.testing.assert_allclose(reference.inverse_dynamics(np.array([q]), np.array([qd]), np.zeros(1))[0], + [bias], atol=1e-12, rtol=1e-12) + if axis == "cardinal": + expected.update({name + suffix: 0. for name in + ("inverse_dynamics_gradient", "forward_dynamics_gradient") + for suffix in ("_q", "_qd")}) + baseline = None + for threads in (1, 32, 64, 128): + stdout = _run_runner(exe, f"{q}\n{qd}\n{torque}\n", command, num_threads=threads) + outputs = _parse_runner_output(stdout) + actual = [] + for name, value in expected.items(): + assert name in outputs, (name, stdout) + result = np.asarray(outputs[name]).reshape(-1) + assert result.size == 1, (name, result) + np.testing.assert_allclose(result, [value], atol=2e-4, rtol=2e-4, err_msg=name) + actual.extend(result) + if baseline is not None: + np.testing.assert_array_equal(actual, baseline) + baseline = actual diff --git a/test/cuda_equivalents/test_cuda_regressor.py b/test/cuda_equivalents/test_cuda_regressor.py index b0927ca6..4d162ae1 100644 --- a/test/cuda_equivalents/test_cuda_regressor.py +++ b/test/cuda_equivalents/test_cuda_regressor.py @@ -122,7 +122,7 @@ def _compile_runner(build_dir: Path, floating_base: bool): executable = build_dir / "cuda_regressor_runner.exe" thread_count = _random_thread_count() cmd = [ - nvcc, "-std=c++11", "-O0", + nvcc, "-std=c++17", "-O0", f"-DGRID_CUDA_FLOATING_BASE={1 if floating_base else 0}", "-DGRID_CUDA_LINALG_BACKEND=GRID_LINALG_GLASS", f"-DGRID_CUDA_REGRESSOR_TEST_THREADS={thread_count}", diff --git a/test/cuda_equivalents/test_cuda_regressor_gradient.py b/test/cuda_equivalents/test_cuda_regressor_gradient.py index 6f325b65..0d032b12 100644 --- a/test/cuda_equivalents/test_cuda_regressor_gradient.py +++ b/test/cuda_equivalents/test_cuda_regressor_gradient.py @@ -34,8 +34,7 @@ _sample_to_stdin, GPU_UNAVAILABLE_PATTERNS, ) -from RBDReference.tests import MANIFEST_PATH -from RBDReference.tests.model_sources import iter_robot_cases, resolve_robot_spec +from RBDReference.tests.model_sources import resolve_robot_spec from RBDReference.equivalents.reference_backend import build_project_adapter from RBDReference.tests.tolerances import get_tolerance @@ -75,7 +74,7 @@ def _compile_runner(build_dir: Path, floating_base: bool): executable = build_dir / "cuda_regressor_gradient_runner.exe" thread_count = _random_thread_count() cmd = [ - nvcc, "-std=c++11", "-O0", + nvcc, "-std=c++17", "-O0", f"-DGRID_CUDA_FLOATING_BASE={1 if floating_base else 0}", "-DGRID_CUDA_LINALG_BACKEND=GRID_LINALG_GLASS", f"-DGRID_CUDA_REGRESSOR_GRADIENT_TEST_THREADS={thread_count}", diff --git a/test/cuda_equivalents/test_cuda_revolute_mimic_mechanics.py b/test/cuda_equivalents/test_cuda_revolute_mimic_mechanics.py new file mode 100644 index 00000000..6d191518 --- /dev/null +++ b/test/cuda_equivalents/test_cuda_revolute_mimic_mechanics.py @@ -0,0 +1,55 @@ +"""Skew revolute mimic dynamics on the GPU against closed-form mechanics. + +The revolute twin of ``test_prismatic_dynamics_against_independent_mechanics``: +two bodies on one skew axis, the follower a revolute mimic with multiplier -0.5 +and offset 0.17. Expectations come from ``test/cuda_equivalents/revolute_mimic_mechanics.py``, +which uses neither generator nor RBDReference; the numpy oracle is pinned to +the same closed form by ``test/test_revolute_mimic_mechanics.py``. Outputs must +be thread-count invariant. +""" +import numpy as np +import pytest +from grid_assets import robot_urdf +from grid_codegen import GRiDCodeGenerator +from URDFParser import URDFParser +from test.cuda_equivalents import revolute_mimic_mechanics as mech + +pytestmark = [pytest.mark.cuda_equivalence, pytest.mark.robot_smoke] + +ALGOS = ["inverse_dynamics", "forward_dynamics", "aba", "crba", "minv"] + + +def test_revolute_mimic_dynamics_against_independent_mechanics(tmp_path, monkeypatch): + from RBDReference import RBDReference + from test.cuda_equivalents.cuda_harness import _compile_runner, _parse_runner_output, _run_runner + + robot = URDFParser().parse(str(robot_urdf("revolute_mimic_fixed_tool"))) + gen = GRiDCodeGenerator(robot, False, True, "grid") + gen.gen_all_code(output_path=str(tmp_path / "grid.cuh"), algorithm_list=ALGOS, + enable_mujoco_kernels=False) + monkeypatch.setenv("GRID_CUDA_CACHE_DIR", str(tmp_path / "executables")) + exe, command = _compile_runner(tmp_path, run_tokens={"RUN_" + a.upper() for a in ALGOS}, + skip_gradients=True, skip_eepose_gradients=True) + mass = mech.generalized_mass() + reference = RBDReference(robot) + for q, qd, torque in ((0., 0., 0.), (.13, -.4, 2.), (.5, .7, -3.), (-1.2, .25, .4)): + bias = mech.gravity_torque(q) + expected = {"inverse_dynamics": bias, "forward_dynamics": (torque - bias) / mass, + "aba": (torque - bias) / mass, "crba": mass, "minv": 1. / mass} + np.testing.assert_allclose(reference.crba(np.array([q])), [[mass]], atol=1e-12, rtol=1e-12) + np.testing.assert_allclose(reference.inverse_dynamics(np.array([q]), np.array([qd]), np.zeros(1))[0], + [bias], atol=1e-12, rtol=1e-12) + baseline = None + for threads in (1, 32, 64, 128): + stdout = _run_runner(exe, f"{q}\n{qd}\n{torque}\n", command, num_threads=threads) + outputs = _parse_runner_output(stdout) + actual = [] + for name, value in expected.items(): + assert name in outputs, (name, stdout) + result = np.asarray(outputs[name]).reshape(-1) + assert result.size == 1, (name, result) + np.testing.assert_allclose(result, [value], atol=2e-4, rtol=2e-4, err_msg=name) + actual.extend(result) + if baseline is not None: + np.testing.assert_array_equal(actual, baseline) + baseline = actual diff --git a/test/cuda_equivalents/test_cuda_second_order_fallback.py b/test/cuda_equivalents/test_cuda_second_order_fallback.py index 6b5acc52..0472b3ca 100644 --- a/test/cuda_equivalents/test_cuda_second_order_fallback.py +++ b/test/cuda_equivalents/test_cuda_second_order_fallback.py @@ -1,7 +1,5 @@ import contextlib import os -import shutil -import subprocess from pathlib import Path import numpy as np @@ -180,7 +178,7 @@ def _second_order_thread_count() -> int: def _compile_second_order_runner(build_dir: Path, *, enable_fdsva=True, inspect_only=False): arch = _detect_cuda_arch() flags = [ - "-std=c++11", "-O0", + "-std=c++17", "-O0", "-gencode", f"arch=compute_{arch},code=sm_{arch}", "-gencode", f"arch=compute_{arch},code=compute_{arch}", f"-DGRID_CUDA_SECOND_ORDER_ENABLE_FDSVA={int(enable_fdsva)}", diff --git a/test/cuda_equivalents/test_cuda_spherical_plant_hessian.py b/test/cuda_equivalents/test_cuda_spherical_plant_hessian.py new file mode 100644 index 00000000..2c429a90 --- /dev/null +++ b/test/cuda_equivalents/test_cuda_spherical_plant_hessian.py @@ -0,0 +1,74 @@ +"""Repair audit of already-emitted fixed-spherical plant second derivatives.""" +from pathlib import Path +import subprocess + +import numpy as np +import pytest +from grid_codegen import GRiDCodeGenerator +from URDFParser import URDFParser +from RBDReference import RBDReference +import xml.etree.ElementTree as ET +from test.cuda_equivalents.manifold_oracle import reference, state, derivative +from test.cuda_equivalents.cuda_harness import _detect_cuda_arch +from test.cuda_equivalents.executable_cache import cached_nvcc_executable + +pytestmark=[pytest.mark.cuda_equivalence,pytest.mark.robot_smoke] + + +@pytest.mark.parametrize('name',['spherical_arm','mixed_spherical_arm','single_spherical','two_spherical']) +@pytest.mark.parametrize('precision',['float',pytest.param('double', marks=pytest.mark.fp64_alpha)]) +@pytest.mark.parametrize('scheme',['euler','semi_implicit_euler']) +@pytest.mark.parametrize('tier',[0,2],ids=['shared','minimal']) +def test_spherical_plant_hessian(tmp_path,name,precision,scheme,tier): + if name in ('single_spherical','two_spherical'): + source=Path(__file__).resolve().parents[2]/'external/URDFParser/tests/fixtures/spherical_arm.urdf' + tree=ET.parse(source);root=tree.getroot() + if name=='single_spherical': + root.remove(root.find("joint[@name='joint_2']")) + root.remove(root.find("link[@name='link2']")) + else: + joint=root.find("joint[@name='joint_2']") + joint.set('type','spherical') + axis=joint.find('axis') + if axis is not None:joint.remove(axis) + tree.write(tmp_path/'single.urdf') + ref=RBDReference(URDFParser().parse(str(tmp_path/'single.urdf'))) + else: + ref=reference(name) + robot=ref.robot; nv=robot.get_num_vel() + gen=GRiDCodeGenerator(robot,dtype=precision) + gen.gen_all_code(algorithm_list=['fdsva_so','integrator_gradient'],emit_plant=True, + enable_mujoco_kernels=False,output_path=str(tmp_path/'grid.cuh')) + exe,_=cached_nvcc_executable([Path(__file__).with_name('cuda_spherical_plant_hessian_runner.cu')], + ['-std=c++17','-O2','-lineinfo','-Xptxas=-v','-arch=sm_'+_detect_cuda_arch(),'-DTEST_SCALAR='+precision, + '-DTEST_INTEGRATOR='+scheme.upper(),'-DTEST_TIER='+str(tier)], + exe_name='probe',fallback_dir=tmp_path,include_dirs=[tmp_path],what='spherical plant Hessian runner') + dtype=np.float32 if precision=='float' else np.float64 + q,v=state(ref);u=np.linspace(-.4,.7,nv) + z=np.r_[q,v,u].astype(dtype);z.tofile(tmp_path/'input.bin') + nq=len(q); q,v,u=z[:nq].astype(float),z[nq:nq+nv].astype(float),z[nq+nv:].astype(float) + dt=float(dtype(.02)) + oracle=ref.plant_step_hessian(q,v,u,dt,integrator_type=scheme) + fd=derivative(lambda d: ref.plant_step_gradient(ref.integrate(q,d[:nv]), + v+d[nv:2*nv],u+d[2*nv:],dt,integrator_type=scheme),3*nv).transpose(0,2,1) + np.testing.assert_allclose(oracle,fd,atol=2e-7,rtol=2e-7) + baseline=None + # Test the stock shared-tier admission, plus the minimal tier's larger + # supported blocks. An inadmissible request must refuse before launch. + for threads in (1,17,32,64,128,256): + prefix=tmp_path/f'out-{threads}-' + ran=subprocess.run([str(exe),str(tmp_path/'input.bin'),str(prefix),str(threads)],capture_output=True,text=True) + (tmp_path/f'run-{threads}.log').write_text(ran.stdout+ran.stderr) + if ran.returncode==7: + assert tier==0 and ran.stdout.startswith('ADMISSION_LIMIT=') + # The n=3 single-spherical stock shared kernel admits one warp; + # larger tested models admit two. Minimal tier exercises all sizes. + assert 32<=int(ran.stdout.split('=')[1]) *model) { + grid::end_effector_pose_hessian_device(out+1,out+1+6*NV*NV,q,model); +} +''' + launch = 'probe<<<1,threads,bytes>>>(out,q,model);' + resource = 'grid::END_EFFECTOR_POSE_HESSIAN_DYNAMIC_SHARED_MEM_BYTES()' + attribute = 'probe' + threads = (1, 17, 32, 64, 256) + else: + kernel = '' + launch = ('grid_plant::plant_step_gradient_kernel' + '<<<1,threads,bytes>>>(out+1,q,NV ? q+2*NV : nullptr,2*NV,NV,model,T(-9.81),T(.01),1);') + resource = 'grid_plant::PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES()' + attribute = 'grid_plant::plant_step_gradient_kernel' + # The generated zero-DoF plant kernel has a 32-thread launch bound. + threads = (1, 17, 32) + source = r''' +#include "grid.cuh" +#include +#include +using T=DTYPE; +constexpr int NV=grid::NUM_VEL; +constexpr int NOUT=OUTPUT_COUNT; +static_assert(grid::NUM_POS==NV && NV<=1); +#define CK(x) do{auto e=(x);if(e!=cudaSuccess){fprintf(stderr,"%s: %s\n",#x,cudaGetErrorString(e));return 2;}}while(0) +KERNEL +int main(int argc,char **argv){ + if(argc!=2)return 1; + int threads=atoi(argv[1]); + grid::robotModel *model=nullptr;CK(grid::init_robotModel_checked(&model)); + T *out,*q=nullptr;T h[NOUT+2]; + for(auto &x:h)x=T(NAN);h[0]=123;h[NOUT+1]=456; + if constexpr(NV>0){T hq[3]={T(.35),T(.23),T(-.31)};CK(cudaMalloc(&q,sizeof(hq)));CK(cudaMemcpy(q,hq,sizeof(hq),cudaMemcpyHostToDevice));} + CK(cudaMalloc(&out,sizeof(h)));CK(cudaMemcpy(out,h,sizeof(h),cudaMemcpyHostToDevice)); + size_t bytes=RESOURCE; + CK((cudaFuncSetAttribute(ATTRIBUTE,cudaFuncAttributeMaxDynamicSharedMemorySize,int(bytes)))); + LAUNCH + CK(cudaGetLastError());CK(cudaDeviceSynchronize());CK(cudaMemcpy(h,out,sizeof(h),cudaMemcpyDeviceToHost)); + for(auto x:h)printf("%.17g ",double(x));puts(""); + CK(cudaFree(q));CK(cudaFree(out));CK(grid::free_robotModel_checked(model)); +} +''' + for old, new in [('DTYPE', precision), ('KERNEL', kernel), ('RESOURCE', resource), + ('ATTRIBUTE', attribute), ('LAUNCH', launch), + ('OUTPUT_COUNT', '6*NV*NV+6*NV' if surface == 'pose-hessian' else '6*NV*NV')]: + source = source.replace(old, new) + runner = tmp_path / 'probe.cu' + runner.write_text(source) + exe, _ = cached_nvcc_executable([runner], ['-std=c++17', '-O2', '-lineinfo', f'-arch=sm_{_detect_cuda_arch()}'], + exe_name='probe', fallback_dir=tmp_path, include_dirs=[tmp_path], + what='zero dof surfaces probe') + expected = np.array([123, 456]) + if not zero: + ref = RBDReference(robot) + q = np.array([.35]) + if surface == 'pose-hessian': + hessian = np.asarray(ref.end_effector_pose_hessian_analytic(q)).ravel() + jacobian = np.asarray(ref.end_effector_pose_gradient(q)).ravel() + # One coordinate and one EE: both flattenings agree with device layout. + expected = np.r_[123, hessian, jacobian, 456] + step = 1e-5 + finite = (np.asarray(ref.end_effector_pose_gradient(q+step)) + - np.asarray(ref.end_effector_pose_gradient(q-step))) / (2*step) + np.testing.assert_allclose(hessian, finite.ravel(), atol=1e-8, rtol=1e-8) + else: + args = (q, np.array([.23]), np.array([-.31]), .01) + gradient = ref.plant_step_gradient(*args) + locked = RBDReference(constant_model(tmp_path, locked=True)) + np.testing.assert_allclose(gradient, locked.plant_step_gradient(*args), atol=1e-11, rtol=1e-10) + expected = np.r_[123, gradient.ravel(order='F'), 456] + baseline = None + for count in threads: + ran = subprocess.run([str(exe), str(count)], capture_output=True, text=True) + assert ran.returncode == 0, ran.stderr + actual = np.fromstring(ran.stdout, sep=' ') + assert actual.size == expected.size + np.testing.assert_array_equal(actual[[0, -1]], [123, 456]) + tol = 3e-5 if precision == 'float' else 1e-10 + np.testing.assert_allclose(actual, expected, atol=tol, rtol=tol) + if baseline is not None: + np.testing.assert_array_equal(actual, baseline) + baseline = actual diff --git a/test/ee_constant_slot_warnings.py b/test/ee_constant_slot_warnings.py new file mode 100644 index 00000000..f45bc72c --- /dev/null +++ b/test/ee_constant_slot_warnings.py @@ -0,0 +1,49 @@ +"""Fail-closed classification of the diagnosed Package 3 EE constant-slot warning. + +Not a general racecheck suppression. Applies only to the three reviewed FP32 +fixtures with --racecheck-report all, source audit and separate numerical and +repeatability gates. All raw logs remain evidence; disposition is KNOWN_WARNING, +never zero-hazard PASS. Brian approved this disposition on 2026-10-30. +""" +import re + +CASES = { + 'test_exact_target_scratch[float-iiwa14-iiwa_joint_ee]': ('end_effector_pose_inner_iiwa_joint_ee', 24, 3), + 'test_exact_target_scratch[float-go2-FR_foot_joint]': ('end_effector_pose_inner_FR_foot_joint', 16, 2), + 'test_exact_target_scratch[float-go2-]': ('end_effector_pose_inner', 8, 1), +} + + +def known_constant_slot_warning(text, case): + if case not in CASES or re.search(r'^========= (Error:|ERROR|Fatal|Invalid)', text, re.M): + return False + target, accesses, groups = CASES[case] + summary = re.findall(r'RACECHECK SUMMARY: (\d+) hazards displayed \((\d+) errors, (\d+) warnings\)', text) + total = accesses + groups + if summary != [(str(total), '0', str(total))]: + return False + warnings = re.findall(r'^========= Warning: (.*?)(?=^========= Warning:|^========= RACECHECK SUMMARY:|\Z)', text, re.M | re.S) + if len(warnings) != total: + return False + details = analyses = 0 + for warning in warnings: + if 'grid::glass::dot_strided *d_ } } -extern "C" int consumer_num_pos() { return grid::NUM_POS; } -extern "C" int consumer_num_ees() { return grid::NUM_EES; } -extern "C" int consumer_build_tag() { return CONSUMER_BUILD_TAG; } +extern "C" __attribute__((visibility("default"))) int consumer_num_pos() { return grid::NUM_POS; } +extern "C" __attribute__((visibility("default"))) int consumer_num_ees() { return grid::NUM_EES; } +extern "C" __attribute__((visibility("default"))) int consumer_build_tag() { return CONSUMER_BUILD_TAG; } // Evaluates the pose (6 per end effector) of `q` on the device; returns 0 on success, // writes the collision verdict (1 free, 0 hit, -1 when the header has no collision namespace). -extern "C" int consumer_pose(const float *q, float *pose, int *is_free) { +extern "C" __attribute__((visibility("default"))) int consumer_pose(const float *q, float *pose, int *is_free) { static const grid::robotModel *d_model = nullptr; if (d_model == nullptr) d_model = grid::init_robotModel(); T *d_q = nullptr, *d_pose = nullptr; int *d_free = nullptr; diff --git a/test/python_wrappers/test_consumer_artifact.py b/test/python_wrappers/test_consumer_artifact.py index d867ac22..101b2fe2 100644 --- a/test/python_wrappers/test_consumer_artifact.py +++ b/test/python_wrappers/test_consumer_artifact.py @@ -5,9 +5,9 @@ belongs to the consumer. This suite builds `consumer_artifact_kernel.cu` for iiwa14 in a fresh cache directory and checks, in order: the artifact loads through ctypes and its end-effector pose equals the RBDReference oracle; the second build of the same request is a pointer hit -(no generation, no nvcc); a changed define re-keys the artifact and recompiles; a second unit +(no generation or compilation; dependency preprocessing only); a changed define re-keys the artifact and recompiles; a second unit that does not change reuses its object; a tampered identity record is refused and the -content key re-hits without nvcc; `load_artifact` works with nvcc off the PATH and refuses an +content key re-hits without compilation; `load_artifact` works with nvcc off the PATH and refuses an artifact recorded for another device; the collision options emit the `grid_collision` namespace and the sphere-model validator runs; a kinematics-only artifact carries no plant block unless asked. @@ -18,9 +18,13 @@ from __future__ import annotations import ctypes +from concurrent.futures import ThreadPoolExecutor import json import shutil import sys +import sysconfig +import importlib.util +import subprocess from pathlib import Path import numpy as np @@ -40,6 +44,165 @@ _KINEMATICS = ["end_effector_pose"] +def test_descriptor_matches_exported_cpp_and_source_free_prepared_load(tmp_path, monkeypatch): + from test.cuda_equivalents.cuda_harness import _detect_cuda_arch + arch=int(_detect_cuda_arch()) + urdf=tmp_path/'model.urdf' + shutil.copyfile(_URDF,urdf) + source=tmp_path/'descriptor.cu' + source.write_text('#include "grid.cuh"\n' + 'extern "C" __attribute__((visibility("default"))) const char* model_json() ' + '{ return grid::model::descriptor_json; }\n') + model=_grid_rbd.describe_model(urdf) + opts=dict(sources=[source],cache_dir=tmp_path/'cache',cuda_arch=arch, + algorithm_list=_KINEMATICS,enable_mujoco_kernels=False, + expected_descriptor_id=model['descriptor_id']) + first=_grid_rbd.build_artifact('model','probe',urdf,**opts) + lib=ctypes.CDLL(str(first.path)) + lib.model_json.restype=ctypes.c_char_p + assert json.loads(lib.model_json().decode())==model==first.model_descriptor + assert not _grid_rbd.build_artifact('model','probe',urdf,**opts).compiled + urdf.unlink(); source.unlink() + from grid_rbd import _artifacts, _model + monkeypatch.setattr(_model,'parse_model',lambda *a,**k: pytest.fail('prepared parsed source')) + monkeypatch.setattr(_artifacts,'find_nvcc',lambda *a: pytest.fail('prepared looked for nvcc')) + loaded=_grid_rbd.load_artifact('model','probe',cache_dir=tmp_path/'cache',cuda_arch=arch, + expected_descriptor_id=model['descriptor_id']) + assert loaded.model_descriptor==model + # Public access returns a fresh value, not a mutable alias to cached metadata. + loaded.model_descriptor['joints'][0]['name']='caller mutation' + assert loaded.model_descriptor==model + + +def test_compiler_discovered_headers_rekey_native_values(tmp_path): + """Transitive edits and a new earlier include win without explicit headers=.""" + from test.cuda_equivalents.cuda_harness import _detect_cuda_arch + early, late = tmp_path / "early", tmp_path / "late headers" + early.mkdir(); late.mkdir() + (late / "config.h").write_text('#include "detail space.h"\n') + detail = late / "detail space.h" + detail.write_text("#define VALUE 13\n") + device_header = late / "device.h" + device_header.write_text("#define DEVICE_VALUE 17\n") + source = tmp_path / "deps.cu" + source.write_text('#include "grid.cuh"\n#include "config.h"\n' + '#ifdef __CUDA_ARCH__\n#include "device.h"\n#endif\n' + 'extern "C" __attribute__((visibility("default")))\n' + 'int dependency_value() { return VALUE; }\n') + def build(): + return _grid_rbd.build_artifact("dependencies", "robot", _URDF, + sources=[source], include_dirs=[early, late], cache_dir=tmp_path / "cache", + cuda_arch=_detect_cuda_arch(), algorithm_list=_KINEMATICS, + enable_mujoco_kernels=False) + def value(artifact): + return ctypes.CDLL(str(artifact.path)).dependency_value() + first = build() + assert str(device_header) in { + row["path"] for row in first.meta["dependencies"]["units"][str(source)]} + assert value(first) == 13 + assert not build().compiled + detail.write_text("#define VALUE 29\n") + second = build() + assert second.content_key != first.content_key and value(second) == 29 + (early / "config.h").write_text("#define VALUE 47\n") + third = build() + assert third.content_key != second.content_key and value(third) == 47 + device_header.write_text("#define DEVICE_VALUE 23\n") + fourth = build() + assert fourth.content_key != third.content_key and fourth.compiled + + +def test_consumer_migration_example(tmp_path): + from test.cuda_equivalents.cuda_harness import _detect_cuda_arch + script = _REPO_ROOT / "bindings/examples/consumer_artifact_migration.py" + result = subprocess.run([sys.executable, str(script), "--urdf", str(_URDF), + "--work", str(tmp_path / "migration"), + "--cuda-arch", str(_detect_cuda_arch())], + cwd=tmp_path, capture_output=True, text=True) + assert result.returncode == 0, result.stdout + result.stderr + report = json.loads(result.stdout.strip().splitlines()[-1]) + assert report["num_pos"] == 7 + assert report["artifacts"]["native"]["python_extension"] is False + assert report["artifacts"]["python"]["python_extension"] is True + assert report["prepared_phase"] == "ok" + + +def test_shadowing_symlink_preserves_header_file_macro_identity(tmp_path): + early, late = tmp_path / "early", tmp_path / "late" + early.mkdir(); late.mkdir() + header = late / "selected.h" + header.write_text('inline const char* selected_path() { return __FILE__; }\n') + source = tmp_path / "path.cpp" + source.write_text('#include "selected.h"\n' + 'extern "C" __attribute__((visibility("default"))) const char* header_path() { return selected_path(); }\n') + opts = dict(sources=[source], include_dirs=[early, late]) + first = _build(tmp_path, name="header_path", **opts) + def path(art): + lib = ctypes.CDLL(str(art.path)) + lib.header_path.restype = ctypes.c_char_p + return lib.header_path().decode() + assert path(first) == str(header) + alias = early / "selected.h" + alias.symlink_to(header) + second = _build(tmp_path, name="header_path", **opts) + assert second.content_key != first.content_key + assert path(second) == str(alias) + + +@pytest.mark.parametrize("header_name", ["space name.h", "hash#name.h", "cash$name.h", "cash$$name.h"]) +def test_real_compiler_dependency_filename_escaping(tmp_path, header_name): + from grid_rbd._consumer_dependencies import discover_dependencies + header = tmp_path / header_name + header.write_text("constexpr int dependency_probe = 7;\n") + source = tmp_path / "escaping.cpp" + source.write_text(f'#include "{header_name}"\n') + # Direct compiler discovery is enough here; executable builds above cover + # its connection to object keys and the returned numerical artifact. + result = discover_dependencies(tmp_path / "generated", [source], [shutil.which("nvcc")]) + assert str(header) in {row["path"] for row in result["units"][str(source)]} + + +def test_real_compiler_unreadable_dependency_fails_closed(tmp_path): + from grid_rbd._consumer_dependencies import discover_dependencies + # nvcc emits back/slash.h for this legal POSIX name. Do not silently omit + # the dependency or adopt an old cached artifact when its report is unusable. + (tmp_path / r"back\slash.h").write_text("constexpr int probe = 7;\n") + source = tmp_path / "backslash.cpp" + source.write_text('#include "back\\slash.h"\n') + with pytest.raises(RuntimeError, match="dependency cannot be read"): + discover_dependencies(tmp_path / "generated", [source], [shutil.which("nvcc")]) + + +def test_concurrent_consumer_builds_publish_loadable_winner(tmp_path): + """Real separately imported builders share a private store, not a mock lock.""" + from test.cuda_equivalents.cuda_harness import _detect_cuda_arch + source = tmp_path / "shared.cu" + source.write_text('#include "grid.cuh"\n' + 'extern "C" __attribute__((visibility("default")))\n' + 'int concurrent_num_pos() { return grid::NUM_POS; }\n') + cache = tmp_path / "cache" + script = '''import ctypes, json, sys +from grid_rbd import build_artifact, load_artifact +art = build_artifact("concurrent", "robot", sys.argv[1], sources=[sys.argv[2]], + cache_dir=sys.argv[3], cuda_arch=int(sys.argv[4]), + algorithm_list=["end_effector_pose"], enable_mujoco_kernels=False) +loaded = load_artifact("concurrent", "robot", cache_dir=sys.argv[3], cuda_arch=int(sys.argv[4])) +assert loaded.content_key == art.content_key +lib = ctypes.CDLL(str(loaded.path)) +assert lib.concurrent_num_pos() == 7 +print(json.dumps({"key": loaded.content_key, "path": str(loaded.path)})) +''' + argv = [sys.executable, "-c", script, str(_URDF), str(source), str(cache), str(_detect_cuda_arch())] + def build(_): + result = subprocess.run(argv, cwd=tmp_path, text=True, capture_output=True, timeout=120) + assert result.returncode == 0, result.stdout + result.stderr + return json.loads(result.stdout.strip().splitlines()[-1]) + with ThreadPoolExecutor(max_workers=2) as workers: + first, second = list(workers.map(build, range(2))) + assert first == second + assert Path(first["path"]).is_file() + + def _build(cache_dir, name="iiwa14", tag=1, **kw): opts = dict(sources=[_KERNEL], defines={"CONSUMER_BUILD_TAG": tag}, cache_dir=cache_dir, algorithm_list=_KINEMATICS, enable_mujoco_kernels=False) @@ -103,11 +266,11 @@ def test_second_build_is_a_pointer_hit_and_a_define_rekeys(tmp_path): def test_unchanged_unit_reuses_its_object(tmp_path): cache = tmp_path / "cache" other = tmp_path / "other_unit.cu" - other.write_text('#include "grid.cuh"\nextern "C" int consumer_other() { return grid::NUM_POS * 10; }\n') + other.write_text('#include "grid.cuh"\nextern "C" __attribute__((visibility("default"))) int consumer_other() { return grid::NUM_POS * 10; }\n') first = _build(cache, sources=[_KERNEL, other]) units = json.loads((first.store_dir / "meta.json").read_text())["build"]["units"] assert units[_KERNEL.name]["compiled"] and units[other.name]["compiled"] - other.write_text('#include "grid.cuh"\nextern "C" int consumer_other() { return grid::NUM_POS * 20; }\n') + other.write_text('#include "grid.cuh"\nextern "C" __attribute__((visibility("default"))) int consumer_other() { return grid::NUM_POS * 20; }\n') second = _build(cache, sources=[_KERNEL, other]) units = json.loads((second.store_dir / "meta.json").read_text())["build"]["units"] assert second.content_key != first.content_key @@ -165,3 +328,96 @@ def test_plant_block_follows_the_request(tmp_path): assert "namespace grid_plant {" not in without.header.read_text() forced = _build(cache, name="iiwa14_plant", emit_plant=True) assert "namespace grid_plant {" in forced.header.read_text() and forced.content_key != without.content_key + + +def test_declared_python_extension_import_and_prepared_load(tmp_path, monkeypatch): + import pybind11 + # Only a private copy is changed; installed/peer headers stay untouched. + pybind_include = tmp_path / "pybind-include" + shutil.copytree(pybind11.get_include(), pybind_include) + source = tmp_path / "abi_probe.cpp" + source.write_text('#include \nextern "C" int abi_num_pos();\n' + 'PYBIND11_MODULE(grid_abi_probe, m) { m.def("num_pos", &abi_num_pos); }\n') + cuda_source = tmp_path / "abi_probe.cu" + cuda_source.write_text('#include "grid.cuh"\nextern "C" int abi_num_pos() { return grid::NUM_POS; }\n') + opts = dict(sources=[source, cuda_source], include_dirs=[pybind_include, sysconfig.get_path("include")], + artifact_name="grid_abi_probe.so", python_extension=True) + art = _build(tmp_path, name="extension", **opts) + assert art.meta["python_abi"]["soabi"] == sysconfig.get_config_var("SOABI") + spec = importlib.util.spec_from_file_location("grid_abi_probe", art.path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + assert module.num_pos() == 7 + assert not _build(tmp_path, name="extension", **opts).compiled + common = pybind_include / "pybind11" / "detail" / "common.h" + common.write_text(common.read_text() + "\n// private in-place header upgrade probe\n") + upgraded = _build(tmp_path, name="extension", **opts) + assert upgraded.content_key != art.content_key + assert upgraded.meta["build"]["units"][source.name]["compiled"] + assert not upgraded.meta["build"]["units"][cuda_source.name]["compiled"] + spec = importlib.util.spec_from_file_location("grid_abi_probe", upgraded.path) + upgraded_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(upgraded_module) + assert upgraded_module.num_pos() == 7 + monkeypatch.setenv("PATH", "/nonexistent") + loaded = _grid_rbd.load_artifact("gate", "extension", cache_dir=tmp_path, cuda_arch=art.cuda_arch) + assert loaded.path == upgraded.path + + +def test_undeclared_python_extension_is_not_published(tmp_path): + source = tmp_path / "undeclared.cpp" + source.write_text('extern "C" __attribute__((visibility("default"))) void* PyInit_undeclared() { return nullptr; }\n') + with pytest.raises(ValueError, match="python_extension"): + _build(tmp_path, name="undeclared", sources=[source]) + assert not _grid_rbd.list_artifacts("gate", cache_dir=tmp_path) + + +def test_source_path_macro_survives_cache_reuse(tmp_path): + keys = [] + for folder in ("first", "second"): + source = tmp_path / folder / "same.cpp" + source.parent.mkdir() + source.write_text('extern "C" __attribute__((visibility("default"))) const char* source_path() { return __FILE__; }\n') + art = _build(tmp_path / "cache", name=folder, sources=[source]) + lib = ctypes.CDLL(str(art.path)) + lib.source_path.restype = ctypes.c_char_p + assert lib.source_path().decode() == str(source) + keys.append(art.content_key) + assert len(set(keys)) == 2 + + +def test_two_robot_consumer_statics_are_isolated_in_both_load_orders(tmp_path): + source = tmp_path / "state.cu" + source.write_text('''#include "grid.cuh" +inline int& consumer_state() { static int value = grid::NUM_POS; return value; } +extern "C" __attribute__((visibility("default"))) int state_get() { return consumer_state(); } +extern "C" __attribute__((visibility("default"))) void state_set(int n) { consumer_state() = n; } +extern "C" __attribute__((visibility("default"))) void* state_address() { return &consumer_state(); } +''') + artifacts = [] + for name, urdf, n in (("arm", _URDF, 7), ("slider", robot_urdf("slider_fixed_tool"), 1)): + art = _grid_rbd.build_artifact("isolation", name, urdf, sources=[source], cache_dir=tmp_path, + algorithm_list=["end_effector_pose"], enable_mujoco_kernels=False) + symbols = subprocess.run(["nm", "-D", "--defined-only", str(art.path)], + capture_output=True, text=True, check=True).stdout + assert not any(line.split()[1] == "u" for line in symbols.splitlines() if len(line.split()) >= 3) + assert "consumer_state" not in symbols + for export in ("state_get", "state_set", "state_address"): + assert any(line.split()[-1] == export for line in symbols.splitlines()) + artifacts.append((str(art.path), n)) + # New interpreter for each order: unloading a CUDA shared library is not a + # supported reset mechanism, and previous imports would mask order dependence. + program = '''import ctypes, json, sys +pairs = json.loads(sys.argv[1]) +libs = [ctypes.CDLL(path) for path, n in pairs] +for lib, (_, n) in zip(libs, pairs): + lib.state_address.restype = ctypes.c_void_p + assert lib.state_get() == n +assert libs[0].state_address() != libs[1].state_address() +libs[0].state_set(101) +assert libs[0].state_get() == 101 and libs[1].state_get() == pairs[1][1] +libs[1].state_set(202) +assert libs[0].state_get() == 101 and libs[1].state_get() == 202 +''' + for ordered in (artifacts, artifacts[::-1]): + subprocess.run([sys.executable, "-c", program, json.dumps(ordered)], check=True) diff --git a/test/run_split_suite.py b/test/run_split_suite.py index 391d7230..74c639bc 100644 --- a/test/run_split_suite.py +++ b/test/run_split_suite.py @@ -949,6 +949,7 @@ def aggregate_header_keys(rdir: Path, results: list[dict], STATE_PATH = REPO_ROOT / "test" / ".split-suite-state.json" WRAPPER_HELPER_PATHS = { "test_release_numerics": ["tools/check_installed_numerics.py"], + "test_consumer_artifact": ["bindings/examples/consumer_artifact_migration.py"], } diff --git a/test/test_binding_codegen_logging.py b/test/test_binding_codegen_logging.py new file mode 100644 index 00000000..0728bb21 --- /dev/null +++ b/test/test_binding_codegen_logging.py @@ -0,0 +1,15 @@ +"""Library code generation is quiet on stdout, diagnostic via the package logger.""" +import logging + +from grid_assets import robot_urdf +from grid_rbd._compile import generate_grid_cuh + + +def test_codegen_reports_joint_order_only_to_logger(tmp_path, capsys, caplog): + with caplog.at_level(logging.INFO, logger="grid_rbd"): + generate_grid_cuh(robot_urdf("slider_fixed_tool"), + {"algorithm_list": ["end_effector_pose"], + "ee_joint_names": ["tool"], "enable_mujoco_kernels": False}, + tmp_path / "grid.cuh") + assert capsys.readouterr().out == "" + assert "joint order: slide (nq=1, nv=1)" in caplog.text diff --git a/test/test_cache_sidecar_publication.py b/test/test_cache_sidecar_publication.py new file mode 100644 index 00000000..1f2c1a1d --- /dev/null +++ b/test/test_cache_sidecar_publication.py @@ -0,0 +1,58 @@ +"""Shared pointer/identity publication must also be safe between threads.""" +from concurrent.futures import ThreadPoolExecutor +import json +from pathlib import Path +import threading + +import pytest + +from grid_rbd import _cache + + +@pytest.mark.parametrize("kind", ["pointer", "identity"]) +def test_same_process_publication_uses_private_temporary(tmp_path, monkeypatch, kind): + target = tmp_path / "bykey" / "key" if kind == "pointer" else tmp_path / "build_inputs.json" + original = Path.replace + barrier = threading.Barrier(2) + temporary_paths = [] + + def replace(path, destination): + if Path(destination) == target: + temporary_paths.append(path) + barrier.wait(timeout=10) + return original(path, destination) + + monkeypatch.setattr(Path, "replace", replace) + + def publish(_): + if kind == "pointer": + _cache.keymap_record(tmp_path, "key", "content") + else: + _cache.write_build_inputs(tmp_path, {"key_schema": 2}) + + with ThreadPoolExecutor(2) as pool: + list(pool.map(publish, range(2))) + assert len(set(temporary_paths)) == 2 + assert target.read_text().strip() == ("content" if kind == "pointer" else '{\n "key_schema": 2\n}') + assert not list(target.parent.glob("*.tmp")) + + +@pytest.mark.parametrize("value", [[], ["key_schema"], 1, "identity", None]) +def test_identity_record_requires_an_object(tmp_path, value): + (tmp_path / "build_inputs.json").write_text(json.dumps(value)) + assert _cache.read_build_inputs(tmp_path) is None + assert _cache.stale_hit_reasons(tmp_path, {"key_schema": 2}) + assert _cache.load_incompat_reasons(tmp_path, {"key_schema": 2}) + + +@pytest.mark.parametrize("kind", ["pointer", "identity"]) +def test_permission_failure_is_not_treated_as_a_missing_record(tmp_path, monkeypatch, kind): + def fail(*args, **kwargs): + raise PermissionError("injected read refusal") + + monkeypatch.setattr(Path, "read_text", fail) + with pytest.raises(PermissionError, match="injected"): + if kind == "identity": + _cache.read_build_inputs(tmp_path) + else: + _cache.keymap_lookup(tmp_path, "key") diff --git a/test/test_collision_margins.py b/test/test_collision_margins.py new file mode 100644 index 00000000..5edf41c8 --- /dev/null +++ b/test/test_collision_margins.py @@ -0,0 +1,27 @@ +"""CPU code-generation contract for feasibility-only separate margins.""" +import pytest + +from test.cuda_equivalents.test_cuda_collision_config_free import _robot, _gen_header +from test.cuda_equivalents.test_cuda_collision_warp import _mixed_spec, _two_tier_spec +from test.cuda_equivalents.test_cuda_collision_native import _two_row_spec, _iiwa_native_spec + + +@pytest.mark.parametrize("kind", ["sphere", "sphere_two", "capsule", "capsule_two"]) +def test_separate_margins_emission(tmp_path, kind): + robot = _robot() + specs = {"sphere": lambda: _mixed_spec(robot), "sphere_two": lambda: _two_tier_spec(robot), + "capsule": lambda: _two_row_spec(robot, 0, 2, 0.05), + "capsule_two": lambda: _iiwa_native_spec()[1]} + code = _gen_header(robot, tmp_path / kind, specs[kind]()).read_text() + assert "typename CollisionMargins::type margins = {}, bool check_self = true" in code + assert "T margin = static_cast(0), bool check_self" not in code + if kind == "capsule_two": + assert "dbg_fine_rechecked, margins, check_self" in code + else: + assert "margins.environment_margin" in code + assert "margins.self_margin" in code + if kind.startswith("sphere"): + assert "CollisionMargins margins = {}, bool check_self = true" in code + # Feasibility inputs do not replace the independent environment hinge margin. + assert "const Environment &env, T margin, T weight," in code + assert "T viol = margin -" in code diff --git a/test/test_collision_spherize.py b/test/test_collision_spherize.py index 7aed771c..7af4604a 100644 --- a/test/test_collision_spherize.py +++ b/test/test_collision_spherize.py @@ -112,3 +112,37 @@ def test_spherize_urdf_roundtrip_go2(): # joint tree preserved (go2 is a quadruped with many joints) joints_in = urdf_joint_tree(urdf) assert set(joints_out) == set(joints_in), "spherize must preserve the URDF joint tree" + + +def test_strict_spherize_rejects_an_empty_model(tmp_path): + """A URDF with no collision geometry is an error by default: an empty sphere model + would bake zero clearance rows without a word. The opt-out keeps the warning.""" + urdf = tmp_path / "empty.urdf" + urdf.write_text('') + with pytest.raises(ValueError, match="0 collision spheres"): + spherize_urdf(str(urdf), 0.05, out_path=str(tmp_path / "strict.urdf")) + with pytest.warns(UserWarning, match="0 collision spheres"): + out = spherize_urdf(str(urdf), 0.05, out_path=str(tmp_path / "lenient.urdf"), strict=False) + assert parse_spherized_urdf(out) == {} + + +def test_strict_spherize_rejects_a_voxel_fill_fallback(tmp_path, monkeypatch): + """A failed voxel fill is an error by default; strict=False warns and keeps the + bounding-box cover, which still covers the mesh surface.""" + trimesh = pytest.importorskip("trimesh") + import xml.etree.ElementTree as ET + mesh = trimesh.creation.box(extents=[0.2, 0.12, 0.3]) + obj = tmp_path / "boxmesh.obj" + mesh.export(str(obj)) + elem = ET.fromstring(f'') + + def broken(self, pitch): + raise RuntimeError("probe: voxel fill unavailable") + monkeypatch.setattr(trimesh.Trimesh, "voxelized", broken) + with pytest.raises(ValueError, match="voxelization failed .*probe"): + _cover_mesh(elem, str(tmp_path), spacing=0.05, link_name="boxmesh") + with pytest.warns(UserWarning, match="bounding-box cover"): + spheres = _cover_mesh(elem, str(tmp_path), spacing=0.05, link_name="boxmesh", strict=False) + assert spheres + gap = _max_uncovered_gap([tuple(p) for p in mesh.sample(2000)], spheres) + assert gap <= 1e-6, f"bounding-box cover left the mesh surface uncovered (worst gap {gap:.2e})" diff --git a/test/test_compile_sched_robot_floor.py b/test/test_compile_sched_robot_floor.py new file mode 100644 index 00000000..6e3c7bf1 --- /dev/null +++ b/test/test_compile_sched_robot_floor.py @@ -0,0 +1,72 @@ +"""A cached relink must not hide a sibling variant's measured compile peak.""" +import sys + +import pytest + +sys.path.insert(0, "test") +import compile_sched + + +@pytest.fixture +def ledger(tmp_path): + result = compile_sched.Ledger(tmp_path / "rss.json") + result.record("warm:iiwa14,mb=64", 700_000) + result.record("warm:iiwa14,mb=1024", 14_000_000) + result.record("warm:iiwa140,mb=64", 30_000_000) + result.record("prewarm:iiwa14", 40_000_000) + return result + + +@pytest.mark.parametrize("key", ["warm:iiwa14,mb=64", "warm:iiwa14,mb=4096", "warm:iiwa14"]) +def test_warm_prediction_uses_robot_peak(ledger, key): + assert ledger.predict_kb(key, 8_000_000, 1.3) == 18_200_000 + + +def test_unmeasured_variant_keeps_larger_default(ledger): + assert ledger.predict_kb("warm:iiwa14,new=1", 20_000_000, 1.3) == 26_000_000 + + +def test_other_robots_and_non_warm_keys_are_not_grouped(ledger): + assert ledger.predict_kb("warm:other,mb=64", 8_000_000, 1) == 8_000_000 + assert ledger.predict_kb("prewarm:iiwa14", 8_000_000, 1) == 40_000_000 + assert ledger.predict_kb("prewarm:iiwa14,new=1", 8_000_000, 1) == 8_000_000 + + +def test_floor_survives_reload_and_small_relink(ledger): + ledger.record("warm:iiwa14,mb=1024", 1) + reloaded = compile_sched.Ledger(ledger.path) + assert reloaded.predict_kb("warm:iiwa14,mb=64", 8_000_000, 1) == 14_000_000 + + +def test_first_job_admission_is_still_an_explicit_exception(ledger, monkeypatch): + # Preserve the existing progress policy: an empty pool admits one job even + # if its estimate exceeds the available budget. The new floor is NOT an OOM + # guarantee; the operator still needs a feasible single-job compile profile. + monkeypatch.setattr(compile_sched, "mem_available_kb", lambda: 1_000_000) + scheduler = compile_sched.RamScheduler(ledger, floor_kb=1_000_000, default_peak_kb=1_000_000) + observed = [] + + def launch(job, predicted, running, groups): + observed.append(predicted) + scheduler.stop() + return False + + monkeypatch.setattr(scheduler, "_launch", launch) + monkeypatch.setattr(compile_sched.time, "sleep", lambda _: None) + job = compile_sched.Job("first", ["not-executed"], "warm:iiwa14,mb=64") + scheduler.run([job]) + assert observed == [18_200_000] + assert scheduler.results["first"].rc == -1 + + +def test_historical_small_relinks_cannot_all_fit_the_jetson_budget(ledger): + # 61 GiB total is not the available budget: reserve the 10 GiB floor + # against a plausible 56 GiB MemAvailable snapshot. Three iiwa14 builds + # previously looked like 14 + .7 + .7 GiB, plus go2. + gib = 1024 ** 2 + ledger.record("warm:iiwa14,mb=8", 14 * gib) + ledger.record("warm:go2,mb=8", 14 * gib) + budget = (56 - 10) * gib + keys = ["warm:iiwa14,mb=8", "warm:go2,mb=8", "warm:iiwa14,mb=64", "warm:iiwa14,mb=256"] + peaks = [ledger.predict_kb(key, 8 * gib, 1.3) for key in keys] + assert sum(peaks[:2]) <= budget < sum(peaks[:3]) diff --git a/test/test_constant_derivative_codegen.py b/test/test_constant_derivative_codegen.py new file mode 100644 index 00000000..be2bc1b7 --- /dev/null +++ b/test/test_constant_derivative_codegen.py @@ -0,0 +1,140 @@ +"""CPU ownership/fold guards complement the CUDA empty-buffer regressions.""" +import itertools +import numpy as np +import pytest +from URDFParser import URDFParser +from grid_codegen import GRiDCodeGenerator +from grid_codegen.algorithms._idsva_so import _idsva_so_fold_jobs +from grid_codegen.algorithms._frame_jacobian import frame_jacobian_jobs +from grid_codegen.algorithms._coriolis import _coriolis_metadata +from grid_codegen.helpers._topology_helpers import ( + _global_hom_derivative_matrices_by_q, _global_hom_second_derivative_matrices, +) +from grid_codegen.algorithms._eepose_gradient_hessian import ( + _eepose_grad_chain_metadata, _eepose_hessian_chain_metadata, group_jacobian_jobs, +) +from test.cuda_equivalents.constant_model import constant_model + + +def test_constant_tensor_fold_never_reads_zero_weight_cells(): + # Slot 1 belongs to a constant body. Its placeholder is slot zero, but the + # projection must not load its tensor cells even with a zero multiplier. + starts, sources, weights, values = _idsva_so_fold_jobs({0: [0, 1], 1: [2]}, [1., 0., -.5], 2, 3) + expected_sources = [i*9+j*3+k for i, j, k in itertools.product((0, 2), repeat=3)] + assert sources == expected_sources + assert starts == list(range(9)) + internal = np.full(27, np.nan) + internal[sources] = np.arange(8)+1 + projected = np.array([values[weights[j]] * internal[sources[j]] for j in range(8)]) + expected = np.array([a*b*c for a, b, c in itertools.product((1., -.5), repeat=3)]) * (np.arange(8)+1) + np.testing.assert_array_equal(projected, expected) + + +def test_empty_tensor_fold_has_no_jobs(): + assert _idsva_so_fold_jobs({}, [0., 0.], 0, 2) == ([0], [], [], []) + + +@pytest.mark.parametrize('alpha', [1., -.5, 2.]) +def test_lone_mimic_column_keeps_its_scale(alpha): + from types import SimpleNamespace + gen = SimpleNamespace(robot_has_mimic_joints=lambda: True, + _alpha_for_jid=lambda jid: alpha) + job = {'j': 1, 'vi': 0} + direct, weighted, mimic = group_jacobian_jobs(gen, [[job]], [1]) + assert mimic + if alpha == 1.: + assert direct == [(0, 1, job)] and weighted == [] + else: + assert direct == [] and weighted == [[(0, 1, job)]] + + +@pytest.mark.parametrize('zero', [False, True]) +def test_constant_columns_are_not_coordinate_owners(tmp_path, zero): + robot = constant_model(tmp_path, zero=zero) + gen = GRiDCodeGenerator(robot) + constant = robot.get_joint_by_name('constant').jid + jobs = frame_jacobian_jobs(gen) + assert all(row[1] != constant and row[-1] != 0 for row in jobs) + md = _coriolis_metadata(gen) + assert constant not in md['col_body'] + assert md['body_col_start'][constant] == md['body_col_start'][constant+1] + chains, _, ee_jobs = _eepose_grad_chain_metadata(gen, [constant]) + assert constant in chains[0] + assert all(job['j'] != constant for job in ee_jobs[0]) + chains, _, ee_dofs, _ = _eepose_hessian_chain_metadata(gen, [constant]) + assert constant in chains[0] + assert all(job['joint_jid'] != constant for job in ee_dofs[0]) + gen.gen_all_code(output_path=str(tmp_path / 'grid.cuh'), emit_plant=False, + algorithm_list=['inverse_dynamics_gradient', 'frame_jacobian_dot', 'crba'], + include_homogenous_transforms=True, enable_mujoco_kernels=False) + code = (tmp_path / 'grid.cuh').read_text() + body = code.split(f'// --- body {constant} (v-slot', 1)[1].split('// --- bpass', 1)[0] + assert 'T qd_a =' not in body + assert '// dv_dqd[:,idx] += alpha*S' not in body + assert 'if (fjd_alpha[t] == static_cast(0)) continue;' in code + assert f'// body {constant} -> v-slot 0 (alpha=0.0)' not in code + if zero: + assert jobs == [] + + +@pytest.mark.parametrize('zero', [False, True]) +def test_constant_hom_derivative_does_not_own_placeholder(tmp_path, zero): + robot = constant_model(tmp_path, zero=zero) + gen = GRiDCodeGenerator(robot) + matrices, owners = _global_hom_derivative_matrices_by_q(gen) + if zero: + assert matrices == owners == [] + else: + drive = robot.get_joint_by_name('drive').jid + assert owners == [drive] + assert matrices == [robot.get_dXmat_hom_local_by_id(drive, 0)] + + +def test_constant_hom_derivative_preserves_floating_base_owner(tmp_path): + constant_model(tmp_path) + robot = URDFParser().parse(str(tmp_path / 'model-False-False-False.urdf'), + floating_base=True) + gen = GRiDCodeGenerator(robot) + first, owners = _global_hom_derivative_matrices_by_q(gen) + second, second_owners = _global_hom_second_derivative_matrices(gen) + nq = robot.get_num_pos() + constant = robot.get_joint_by_name('constant').jid + assert constant not in owners and constant not in second_owners + for i in range(7): + assert owners[i] == 0 + assert first[i] == robot.get_dXmat_hom_local_by_id(0, i) + for j in range(7): + assert second_owners[i*nq+j] == 0 + assert second[i*nq+j] == robot.get_d2Xmat_hom_local_by_id(0, i, j) + + +def test_floating_constant_crba_has_no_placeholder_projection(tmp_path): + from test.cuda_equivalents.test_cuda_constant_dynamics_audit import audit_model + robot = audit_model(tmp_path, floating=True, descendant=True) + gen = GRiDCodeGenerator(robot) + gen.gen_all_code(output_path=str(tmp_path / 'grid.cuh'), emit_plant=False, + algorithm_list=['crba'], enable_mujoco_kernels=False) + code = (tmp_path / 'grid.cuh').read_text() + phase = code.split('// === mimic-aware floating CRBA Phase 2', 1)[1] + phase = phase.split('// floating-base root block', 1)[0] + constant = robot.get_joint_by_name('constant').jid + assert f'// body {constant} ->' not in phase + assert 'static_cast(0.0) *' not in phase + # The constant intermediary's transform must still carry a descendant's + # inertia to the active ancestor and then to the floating base. + assert f's_XImats[{36*constant} + p + 6*r]' in phase + + +@pytest.mark.parametrize('algorithm', [ + 'inverse_dynamics', 'minv', 'forward_dynamics', 'aba', 'crba', + 'forward_dynamics_gradient', 'integrator_gradient', 'fdsva_so', + 'end_effector_pose_gradient', 'end_effector_pose_hessian', +]) +@pytest.mark.parametrize('plant', [False, True]) +def test_zero_dof_generation(tmp_path, algorithm, plant): + """Generation is necessary, not sufficient, for zero-DoF device support.""" + robot = constant_model(tmp_path, zero=True) + gen = GRiDCodeGenerator(robot) + gen.gen_all_code(output_path=str(tmp_path / 'grid.cuh'), + algorithm_list=[algorithm], emit_plant=plant, + enable_mujoco_kernels=False) diff --git a/test/test_constant_mimic_codegen.py b/test/test_constant_mimic_codegen.py new file mode 100644 index 00000000..77ccfa48 --- /dev/null +++ b/test/test_constant_mimic_codegen.py @@ -0,0 +1,29 @@ +"""Constant mimic expressions must not read a missing or unrelated q slot.""" +import pytest +from URDFParser import URDFParser +from grid_assets import robot_urdf +from grid_codegen import GRiDCodeGenerator +from grid_codegen.helpers._topology_helpers import _emit_mimic_q_fold +from grid_codegen.algorithms._eepose_gradient_hessian import _ee_pose_inner_angle_expr + + +@pytest.mark.parametrize('kind', ['prismatic', 'revolute']) +@pytest.mark.parametrize('constant_only', [False, True]) +def test_constant_fold_has_no_coordinate_read(tmp_path, kind, constant_only): + text = robot_urdf('slider_mimic_fixed_tool').read_text() + text = text.replace('type="prismatic"', f'type="{kind}"') + text = text.replace('mimic joint="slide"', 'mimic joint="tool"') + if constant_only: + text = text.replace(f'', '') + path = tmp_path / 'constant.urdf' + path.write_text(text) + robot = URDFParser().parse(str(path)) + joint = robot.get_joint_by_name('follower') + gen = GRiDCodeGenerator(robot, False, False, 'grid') + expression = _ee_pose_inner_angle_expr(gen, joint.jid) + assert expression == 'static_cast(0.17)' + _emit_mimic_q_fold(gen) + emitted = gen.code_str + assert f's_temp[{joint.jid}] = static_cast(0.17);' in emitted + if constant_only: + assert 's_q[' not in emitted diff --git a/test/test_consumer_cache_cpu.py b/test/test_consumer_cache_cpu.py new file mode 100644 index 00000000..214432f6 --- /dev/null +++ b/test/test_consumer_cache_cpu.py @@ -0,0 +1,300 @@ +"""Consumer artifact cache recovery and identity without CUDA or a compiler.""" +import json +from pathlib import Path +from types import SimpleNamespace + +import pytest + +from grid_rbd import _artifacts as artifacts +from test._consumer_model_fixture import FakeToolchain, generate_metadata_header + + +@pytest.fixture +def consumer(tmp_path, monkeypatch): + urdf = tmp_path / "robot.urdf" + urdf.write_text('') + source = tmp_path / "kernel.cu" + source.write_text('// consumer\n') + state = SimpleNamespace(generated=0, compiled=0, urdf=urdf, source=source) + monkeypatch.setattr(artifacts, "artifact_identity", lambda arch: { + "key_schema": artifacts._KEY_SCHEMA, "artifact_schema": artifacts.ARTIFACT_SCHEMA, "cuda_arch": arch}) + monkeypatch.setattr(artifacts, "_compile_command", lambda gen, *a: ["fake-nvcc", f"-I{gen}"]) + monkeypatch.setattr(artifacts, "_toolchain_tag", lambda: "fake-toolchain") + monkeypatch.setattr(artifacts, "_validate_python_exports", lambda *a: None) + monkeypatch.setattr(artifacts, "resolve_consumer_toolchain", lambda *a: FakeToolchain()) + monkeypatch.setattr(artifacts, "discover_dependencies", lambda *a: { + "schema": 1, "mode": "nvcc-MM", "units": {str(source): []}}) + + def generate(urdf, opts, out): + state.generated += 1 + generate_metadata_header(urdf, opts, out) + + def build(gen, sources, headers, cmd, flags, out, objects, reuse_objects, **kw): + state.compiled += 1 + out.write_bytes(b"artifact") + return {"compiled_units": 1, "units": {}} + + monkeypatch.setattr(artifacts, "generate_grid_cuh", generate) + monkeypatch.setattr(artifacts, "_build", build) + state.build = lambda **kw: artifacts.build_artifact( + "cpu-probe", "robot", urdf, sources=[source], cuda_arch=120, + cache_dir=tmp_path / "cache", **kw) + return state + + +@pytest.mark.parametrize("missing", ["artifact.so", "meta.json", "grid.cuh", "model_descriptor.json"]) +def test_missing_entry_file_is_repaired(consumer, missing): + first = consumer.build() + (first.store_dir / missing).unlink() + second = consumer.build() + assert second.content_key == first.content_key + assert (consumer.generated, consumer.compiled) == (2, 2) + assert (second.store_dir / missing).is_file() + consumer.build() + assert (consumer.generated, consumer.compiled) == (2, 2) + + +def test_bad_metadata_is_repaired(consumer): + first = consumer.build() + (first.store_dir / "meta.json").write_text("not json") + second = consumer.build() + assert second.content_key == first.content_key and consumer.compiled == 2 + assert json.loads((second.store_dir / "meta.json").read_text())["consumer"] == "cpu-probe" + + +@pytest.mark.parametrize('damage', ['descriptor', 'rehash', 'header', 'binary', 'association']) +def test_model_association_rejects_and_repairs_swapped_files(consumer, damage): + from grid_codegen.model_descriptor import descriptor_id + first=consumer.build() + original=first.model_descriptor + if damage in ('descriptor', 'rehash'): + path=first.store_dir/'model_descriptor.json' + d=json.loads(path.read_text()) + d['model_name']='different coordinates' + if damage=='rehash': + d['descriptor_id']=descriptor_id(d) + path.write_text(json.dumps(d)) + elif damage=='association': + path=first.store_dir/'meta.json' + meta=json.loads(path.read_text()); meta.pop('model_association') + path.write_text(json.dumps(meta)) + else: + path=first.header if damage=='header' else first.path + path.write_bytes(path.read_bytes()+b'changed') + with pytest.raises(artifacts.ArtifactIncompatibleError, match='model descriptor'): + artifacts.load_artifact('cpu-probe', 'robot', cache_dir=consumer.urdf.parent/'cache', cuda_arch=120) + assert not consumer.build(plan_only=True)['cached'] + repaired=consumer.build() + assert repaired.model_descriptor==original + assert consumer.compiled==2 + assert not consumer.build().compiled + + +@pytest.mark.parametrize('sidecar',['meta.json','build_inputs.json']) +@pytest.mark.parametrize('value',[1,True,2.0]) +def test_schema_damage_cannot_hide_in_content_reuse(consumer,sidecar,value): + first=consumer.build() + path=first.store_dir/sidecar + data=json.loads(path.read_text()); data['artifact_schema']=value + path.write_text(json.dumps(data)) + with pytest.raises(artifacts.ArtifactIncompatibleError,match='artifact_schema'): + artifacts.load_artifact('cpu-probe','robot',cache_dir=first.store_dir.parents[3],cuda_arch=120) + rebuilt=consumer.build() + assert rebuilt.content_key==first.content_key + assert artifacts.load_artifact('cpu-probe','robot',cache_dir=first.store_dir.parents[3], + cuda_arch=120).model_descriptor==first.model_descriptor + assert not consumer.build().compiled + + +def test_expected_descriptor_checked_on_cold_warm_content_and_prepared_paths(consumer, monkeypatch): + from grid_rbd import describe_model + expected=describe_model(consumer.urdf)['descriptor_id'] + with pytest.raises(ValueError,match='expected_descriptor_id'): + consumer.build(expected_descriptor_id='0'*64) + assert consumer.compiled==0 + first=consumer.build(expected_descriptor_id=expected) + assert not consumer.build(expected_descriptor_id=expected).compiled + # Remove just the disposable test pointer to exercise a content-store reuse. + monkeypatch.setattr(artifacts,'keymap_lookup',lambda *a: None) + assert not consumer.build(expected_descriptor_id=expected).compiled + consumer.urdf.unlink(); consumer.source.unlink() + monkeypatch.setattr(artifacts,'find_nvcc',lambda: pytest.fail('prepared load used compiler')) + from grid_codegen import model_metadata_cpp + monkeypatch.setattr(model_metadata_cpp,'emit_model_metadata',lambda *a: pytest.fail('prepared regenerated metadata')) + loaded=artifacts.load_artifact('cpu-probe','robot',cache_dir=first.store_dir.parents[3], + cuda_arch=120,expected_descriptor_id=expected) + assert loaded.model_descriptor['descriptor_id']==expected + with pytest.raises(artifacts.ArtifactIncompatibleError,match='expected_descriptor_id'): + artifacts.load_artifact('cpu-probe','robot',cache_dir=first.store_dir.parents[3], + cuda_arch=120,expected_descriptor_id='0'*64) + + +@pytest.mark.parametrize('literal', ['"duplicate";\ninline constexpr char descriptor_id[] = "second"', + '"bad\\x41"', '"bad\\777"', '"unterminated']) +def test_bad_header_literals_rejected(consumer, literal): + import re + first=consumer.build() + text=first.header.read_text() + text=re.sub(r'inline constexpr char descriptor_id\[\] = .*?;', + lambda _: 'inline constexpr char descriptor_id[] = '+literal+';', text) + first.header.write_text(text) + with pytest.raises(artifacts.ArtifactIncompatibleError,match='model descriptor'): + artifacts.load_artifact('cpu-probe','robot',cache_dir=first.store_dir.parents[3],cuda_arch=120) + + +def test_same_stem_sources_produce_distinct_objects_and_links(tmp_path, monkeypatch): + gen = tmp_path / "generated" + gen.mkdir() + (gen / "grid.cuh").write_text("// header") + sources = [tmp_path / "kernel.cu", tmp_path / "kernel.cpp"] + for p in sources: + p.write_text("// identical bytes, different source languages") + monkeypatch.setattr(artifacts, "_toolchain_tag", lambda: "fake") + monkeypatch.setattr(artifacts, "_build_jobs", lambda: 2) + paths = [] + linked = [] + + def nvcc(cmd, **kw): + out = Path(cmd[cmd.index("-o") + 1]) + if "-c" in cmd: + source = Path(cmd[cmd.index("-c") + 1]) + paths.append(out) + out.write_bytes(source.name.encode()) + else: + linked.extend(Path(a).read_bytes() for a in cmd if a.endswith(".o")) + out.write_bytes(b"shared") + return SimpleNamespace(returncode=0) + + monkeypatch.setattr(artifacts.subprocess, "run", nvcc) + artifacts._build(gen, sources, [], ["fake"], [], gen / "artifact.so", tmp_path / "objects", True, + validate_unit=lambda unit: None, python_abi=None, toolchain=FakeToolchain(), + dependencies={"units": {str(p): [] for p in sources}}) + assert len(set(paths)) == 2 + assert linked == [b"kernel.cu", b"kernel.cpp"] + + +def test_source_language_participates_in_object_key(tmp_path, monkeypatch): + (tmp_path / "grid.cuh").write_text("// header") + sources = [tmp_path / "kernel.cu", tmp_path / "kernel.cpp"] + for source in sources: + source.write_text("same") + monkeypatch.setattr(artifacts, "_toolchain_tag", lambda: "fake") + keys = [artifacts._unit_object_key(p, tmp_path, [], ["nvcc"], python_abi=None, toolchain=None, + dependencies=None) for p in sources] + assert keys[0] != keys[1] + + +def test_source_location_participates_in_all_reuse_keys(tmp_path, monkeypatch): + # nvcc sees an absolute source path, which is observable through __FILE__. + (tmp_path / "grid.cuh").write_text("header") + sources = [] + for folder in ("first", "second"): + source = tmp_path / folder / "same.cpp" + source.parent.mkdir() + source.write_text('const char *file = __FILE__;') + sources.append(source) + monkeypatch.setattr(artifacts, "_toolchain_tag", lambda: "fake") + inputs = [artifacts._input_key(b"urdf", {}, [p], [], [], [], [], [], "artifact.so", {}) for p in sources] + contents = [artifacts._content_key(tmp_path, [p], [], ["nvcc"], [], "artifact.so", python_abi=None, + toolchain=None, dependencies=None) for p in sources] + units = [artifacts._unit_object_key(p, tmp_path, [], ["nvcc"], python_abi=None, toolchain=None, + dependencies=None) for p in sources] + for keys in (inputs, contents, units): + assert len(set(keys)) == 2 + + +def test_collision_mesh_change_invalidates_fast_pointer(consumer): + mesh = consumer.urdf.parent / "body.stl" + mesh.write_bytes(b"mesh-one") + consumer.urdf.write_text('' + '') + first = consumer.build(collision=True, plan_only=True) + consumer.build(collision=True) + assert consumer.build(collision=True, plan_only=True)["cached"] + mesh.write_bytes(b"mesh-two") + second = consumer.build(collision=True, plan_only=True) + assert first["input_key"] != second["input_key"] and not second["cached"] + mesh.unlink() + with pytest.raises(ValueError, match="could not be resolved"): + consumer.build(collision=True) + + +def test_package_resolution_participates_in_identity(tmp_path, monkeypatch): + urdf_dir = tmp_path / "robot" + urdf_dir.mkdir() + urdf = urdf_dir / "robot.urdf" + urdf.write_text('' + '') + records = [] + for name in ("first", "second"): + root = tmp_path / name / "pkg" + root.mkdir(parents=True) + (root / "body.stl").write_bytes(b"same geometry") + monkeypatch.setenv("ROS_PACKAGE_PATH", str(root.parent)) + records.append(artifacts._collision_mesh_identity(urdf)) + assert records[0][0]["sha256"] == records[1][0]["sha256"] + assert records[0][0]["path"] != records[1][0]["path"] + + +@pytest.mark.parametrize("phase", ["generate", "compile"]) +@pytest.mark.parametrize("target", ["urdf", "source"]) +def test_mutation_does_not_publish_under_earlier_key(consumer, monkeypatch, phase, target): + path = getattr(consumer, target) + original_bytes = path.read_bytes() + name = "generate_grid_cuh" if phase == "generate" else "_build" + original = getattr(artifacts, name) + + def mutate(*a, **kw): + value = original(*a, **kw) + path.write_bytes(original_bytes + b"\n") + return value + + monkeypatch.setattr(artifacts, name, mutate) + with pytest.raises(RuntimeError, match="inputs changed"): + consumer.build() + path.write_bytes(original_bytes) + monkeypatch.setattr(artifacts, name, original) + assert not consumer.build(plan_only=True)["cached"] + assert consumer.build().compiled + + +def test_changed_source_is_rejected_before_object_publication(tmp_path, monkeypatch): + gen = tmp_path / "gen" + gen.mkdir() + (gen / "grid.cuh").write_text("header") + source = tmp_path / "kernel.cu" + source.write_text("original") + objects = tmp_path / "objects" + monkeypatch.setattr(artifacts, "_toolchain_tag", lambda: "fake") + + def validate(): + if source.read_text() != "original": + raise RuntimeError("changed source") + + def nvcc(cmd, **kw): + source.write_text("changed") + Path(cmd[cmd.index("-o") + 1]).write_bytes(b"object from changed source") + return SimpleNamespace(returncode=0) + + monkeypatch.setattr(artifacts.subprocess, "run", nvcc) + with pytest.raises(RuntimeError, match="changed source"): + artifacts._build(gen, [source], [], ["fake"], [], gen / "artifact.so", objects, True, + validate_unit=lambda unit: validate(), python_abi=None, toolchain=FakeToolchain(), + dependencies={"units": {str(source): []}}) + assert not list(objects.glob("*.o")) + + +def test_strict_collision_opt_out_is_keyed_and_bounded(consumer, tmp_path): + """The default records nothing; the opt-out is part of the artifact's options, and it + has no meaning on a supplied sphere model, which is validated rather than spherized.""" + strict = consumer.build(collision=True) + assert "strict_collision" not in strict.meta["code_options"] + lenient = consumer.build(collision=True, strict_collision=False) + # The opt-out is an input of the build, so it is in the input key; the artifact is + # content keyed, and a spherizer that raised no warning generates the same header. + assert lenient.input_key != strict.input_key + assert lenient.content_key == strict.content_key + spheres = tmp_path / "spheres.urdf" + spheres.write_text('') + with pytest.raises(ValueError, match="strict_collision=False applies to the spherizer"): + consumer.build(spherized_urdf=spheres, strict_collision=False) diff --git a/test/test_consumer_dependencies.py b/test/test_consumer_dependencies.py new file mode 100644 index 00000000..83556511 --- /dev/null +++ b/test/test_consumer_dependencies.py @@ -0,0 +1,207 @@ +"""Compiler dependency capture and lookup selection without a CUDA runtime.""" +import json +import os +from pathlib import Path +import re +from types import SimpleNamespace + +import pytest + +from grid_rbd import _artifacts as artifacts +from test._consumer_model_fixture import FakeToolchain, generate_metadata_header +from grid_rbd._consumer_dependencies import parse_dependencies, discover_dependencies + + +def test_make_dependency_escaping(tmp_path): + paths = [tmp_path / name for name in ["space name.h", "hash#name.h", "dollar$name.h"]] + encoded = [str(p).replace(" ", r"\ ") for p in paths] + output = "grid_dependency : " + " \\\n ".join(encoded) + "\n" + assert parse_dependencies(output) == sorted(paths) + + +@pytest.mark.parametrize("output", ["other: a", "grid_dependency:", + "grid_dependency: a\nother: b", "grid_dependency: a\\"]) +def test_invalid_make_dependencies_fail_closed(output): + with pytest.raises(RuntimeError): + parse_dependencies(output) + + +def test_dependency_parser_keeps_selected_symlink_spelling(tmp_path): + target = tmp_path / "target.h" + alias = tmp_path / "alias.h" + target.write_text("// header\n") + alias.symlink_to(target) + assert parse_dependencies(f"grid_dependency: {alias}\n") == [alias] + + +@pytest.mark.parametrize("flag", ["-E", "-c", "--ptx", "-M", "-MF=dep", "-MTother", "--output-file=x"]) +def test_phase_overrides_rejected_before_preprocessing(tmp_path, monkeypatch, flag): + monkeypatch.setattr(artifacts.subprocess, "run", lambda *a, **k: pytest.fail("must not invoke nvcc")) + with pytest.raises(ValueError, match="phase/output"): + discover_dependencies(tmp_path, [tmp_path / "unit.cu"], ["nvcc", flag]) + + +@pytest.fixture +def case(tmp_path, monkeypatch): + urdf = tmp_path / "robot.urdf" + urdf.write_text('') + early, late = tmp_path / "early", tmp_path / "late" + early.mkdir(); late.mkdir() + header = late / "config.h" + header.write_text("#define VALUE 1\n") + source = tmp_path / "kernel.cu" + source.write_text('#include "grid.cuh"\n#include "config.h"\n') + state = SimpleNamespace(compiles=0, scans=0, early=early, late=late, + header=header, source=source, mutate=None, fail_scan=False) + monkeypatch.setattr(artifacts, "artifact_identity", lambda arch: { + "key_schema": artifacts._KEY_SCHEMA, "artifact_schema": artifacts.ARTIFACT_SCHEMA, + "cuda_arch": arch}) + monkeypatch.setattr(artifacts, "resolve_consumer_toolchain", lambda *a: FakeToolchain()) + monkeypatch.setattr(artifacts, "_toolchain_tag", lambda: "fake") + monkeypatch.setattr(artifacts, "_build_jobs", lambda: 1) + monkeypatch.setattr(artifacts, "_validate_python_exports", lambda *a: None) + monkeypatch.setattr(artifacts, "generate_grid_cuh", generate_metadata_header) + monkeypatch.setattr(artifacts, "_compile_command", lambda gen, arch, incs, *a: + ["fake-nvcc", f"-I{gen}", *[f"-I{p}" for p in incs]]) + + def dependencies(unit, includes, found): + unit = unit.resolve() + if unit in found: + return + found.append(unit) + for name in re.findall(r'#include "([^"]+)"', unit.read_text()): + chosen = next((p / name for p in [unit.parent, *includes] if (p / name).is_file()), None) + if chosen is None: + raise FileNotFoundError(name) + dependencies(chosen, includes, found) + + def nvcc(cmd, **kwargs): + if "-MM" in cmd: + state.scans += 1 + if state.fail_scan: + return SimpleNamespace(returncode=1, stdout="", stderr="preprocessor failed") + includes = [Path(a[2:]) for a in cmd if a.startswith("-I")] + found = [] + try: + dependencies(Path(cmd[-1]), includes, found) + except FileNotFoundError as exc: + return SimpleNamespace(returncode=1, stdout="", stderr=str(exc)) + escaped = [str(p).replace(" ", "\\ ") for p in found] + return SimpleNamespace(returncode=0, stdout="grid_dependency: " + " ".join(escaped) + "\n", stderr="") + out = Path(cmd[cmd.index("-o") + 1]) + if "-c" in cmd: + state.compiles += 1 + if state.mutate: + state.mutate() + out.write_bytes(b"compiled") + return SimpleNamespace(returncode=0) + + monkeypatch.setattr(artifacts.subprocess, "run", nvcc) + state.build = lambda **kw: artifacts.build_artifact( + "deps", "robot", urdf, sources=kw.pop("sources", [source]), + include_dirs=[early, late], cache_dir=tmp_path / "cache", cuda_arch=120, **kw) + state.load = lambda: artifacts.load_artifact("deps", "robot", tmp_path / "cache", cuda_arch=120) + return state + + +@pytest.mark.parametrize("change", ["direct", "transitive", "shadow", "delete_selected"]) +def test_dependency_selection_and_bytes_rekey(case, change): + if change == "transitive": + case.header.write_text('#include "detail.h"\n') + (case.late / "detail.h").write_text("one") + if change == "delete_selected": + (case.early / "config.h").write_text("early") + first = case.build() + if change == "direct": + case.header.write_text("two") + elif change == "transitive": + (case.late / "detail.h").write_text("two") + elif change == "shadow": + (case.early / "config.h").write_text("new earlier include") + else: + (case.early / "config.h").unlink() + assert not case.build(plan_only=True)["cached"] + second = case.build() + assert second.content_key != first.content_key + assert second.compiled and case.compiles == 2 + + +def test_touch_hits_and_generated_staging_paths_do_not_rekey(case): + first = case.build() + os.utime(case.header, None) + assert case.build(plan_only=True)["cached"] + assert not case.build().compiled + # A fresh generation directory must not change content identity. + second = case.build(force_rebuild=True) + assert first.content_key == second.content_key + + +def test_unrelated_unit_keeps_object(case): + other = case.source.parent / "other.cu" + other.write_text('// no config dependency\n') + first = case.build(sources=[case.source, other]) + case.header.write_text("two") + second = case.build(sources=[case.source, other]) + assert first.content_key != second.content_key + assert second.meta["build"]["units"][case.source.name]["compiled"] + assert not second.meta["build"]["units"][other.name]["compiled"] + + +def test_dependency_preprocessing_scales_linearly_with_units(case): + sources = [case.source] + for i in range(3): + source = case.source.parent / f"other{i}.cu" + source.write_text("// independent unit\n") + sources.append(source) + case.build(sources=sources) + # Initial, pre-key, pre/post unit, post-build and pre-publication scans. + assert case.scans == 6 * len(sources) + + +def test_discovery_failure_does_not_return_old_hit(case): + case.build() + case.fail_scan = True + with pytest.raises(RuntimeError, match="dependenc"): + case.build() + + +def test_mutated_discovered_header_is_rejected_before_object_publication(case): + case.mutate = lambda: case.header.write_text("changed during compile") + with pytest.raises(RuntimeError, match="changed"): + case.build() + assert not list((case.source.parent / "cache").rglob("*.o")) + + +def test_prepared_load_does_not_discover_or_read_source(case): + first = case.build() + case.header.unlink(); case.source.unlink() + case.fail_scan = True + scans = case.scans + assert case.load().content_key == first.content_key + assert case.scans == scans + + +def test_dependency_manifest_records_paths_and_hashes(case): + first = case.build() + record = json.loads((first.store_dir / "meta.json").read_text())["dependencies"] + assert record["schema"] == 1 + deps = record["units"][str(case.source)] + assert any(row["path"] == str(case.header) and len(row["sha256"]) == 64 for row in deps) + assert any(row["path"] == "@generated/grid.cuh" for row in deps) + + +@pytest.mark.parametrize("damage", [None, {"schema": 999}]) +def test_dependency_metadata_damage_is_repaired(case, damage): + first = case.build() + path = first.store_dir / "meta.json" + meta = json.loads(path.read_text()) + if damage is None: + del meta["dependencies"] + else: + meta["dependencies"] = damage + path.write_text(json.dumps(meta)) + assert not case.build(plan_only=True)["cached"] + repaired = case.build() + assert repaired.content_key == first.content_key + assert repaired.meta["dependencies"]["schema"] == 1 + assert case.build(plan_only=True)["cached"] diff --git a/test/test_consumer_python_abi.py b/test/test_consumer_python_abi.py new file mode 100644 index 00000000..15d1090b --- /dev/null +++ b/test/test_consumer_python_abi.py @@ -0,0 +1,134 @@ +"""Python ABI must guard every consumer reuse layer, not only prepared loads.""" +import json +from pathlib import Path +from types import SimpleNamespace + +import pytest + +from grid_rbd import _artifacts as artifacts +from test._consumer_model_fixture import FakeToolchain, generate_metadata_header + + +@pytest.fixture +def build_case(tmp_path, monkeypatch): + urdf = tmp_path / "robot.urdf" + urdf.write_text('') + source = tmp_path / "extension.cpp" + source.write_text("// unchanged consumer source") + state = SimpleNamespace(builds=0, abi={"soabi": "cpython-test-a"}) + monkeypatch.setattr(artifacts, "_python_abi_identity", lambda: dict(state.abi), raising=False) + monkeypatch.setattr(artifacts, "artifact_identity", lambda arch: { + "key_schema": artifacts._KEY_SCHEMA, "artifact_schema": artifacts.ARTIFACT_SCHEMA, + "cuda_arch": arch}) + monkeypatch.setattr(artifacts, "_compile_command", lambda gen, *a: ["fake", f"-I{gen}"]) + monkeypatch.setattr(artifacts, "_toolchain_tag", lambda: "fake") + monkeypatch.setattr(artifacts, "generate_grid_cuh", generate_metadata_header) + monkeypatch.setattr(artifacts, "_validate_python_exports", lambda *a: None, raising=False) + monkeypatch.setattr(artifacts, "resolve_consumer_toolchain", lambda *a: FakeToolchain()) + monkeypatch.setattr(artifacts, "discover_dependencies", lambda *a: { + "schema": 1, "mode": "nvcc-MM", "units": {str(source): []}}) + + def compile(gen, srcs, hdrs, cmd, flags, out, objects, **kw): + state.builds += 1 + out.write_bytes(b"artifact") + return {"compiled_units": 1, "units": {}} + + monkeypatch.setattr(artifacts, "_build", compile) + state.build = lambda **kw: artifacts.build_artifact( + "abi", "robot", urdf, sources=[source], cuda_arch=120, cache_dir=tmp_path, **kw) + state.load = lambda: artifacts.load_artifact("abi", "robot", cache_dir=tmp_path, cuda_arch=120) + return state + + +def test_python_abi_guards_pointer_content_and_load(build_case): + first = build_case.build(python_extension=True) + assert not build_case.build(python_extension=True).compiled + assert build_case.load().path == first.path + build_case.abi = {"soabi": "cpython-test-b"} + assert not build_case.build(python_extension=True, plan_only=True)["cached"] + with pytest.raises(artifacts.ArtifactIncompatibleError, match="python_abi"): + build_case.load() + second = build_case.build(python_extension=True) + assert second.content_key != first.content_key + assert build_case.builds == 2 + assert build_case.load().path == second.path + + +def test_native_artifact_does_not_depend_on_python_abi(build_case, monkeypatch): + first = build_case.build() + monkeypatch.setattr(artifacts, "_python_abi_identity", lambda: pytest.fail("native used Python ABI")) + assert not build_case.build().compiled + assert build_case.load().path == first.path + + +def test_extension_declaration_cannot_reuse_native_content(build_case): + native = build_case.build() + extension = build_case.build(python_extension=True) + assert extension.content_key != native.content_key + assert build_case.builds == 2 + + +def test_python_abi_participates_in_unit_key(tmp_path, monkeypatch): + (tmp_path / "grid.cuh").write_text("header") + unit = tmp_path / "module.cpp" + unit.write_text("// identical source") + monkeypatch.setattr(artifacts, "_toolchain_tag", lambda: "fake") + keys = [artifacts._unit_object_key(unit, tmp_path, [], ["nvcc"], python_abi=abi, toolchain=None, + dependencies=None) + for abi in (None, {"soabi": "a"}, {"soabi": "b"})] + assert len(set(keys)) == 3 + + +def test_ambiguous_legacy_prepared_artifact_is_refused(build_case): + first = build_case.build() + record = first.store_dir / "build_inputs.json" + data = json.loads(record.read_text()) + data.pop("python_abi", None) + record.write_text(json.dumps(data)) + with pytest.raises(artifacts.ArtifactIncompatibleError, match="python_abi"): + build_case.load() + assert not build_case.build(plan_only=True)["cached"] + assert build_case.build().compiled + assert build_case.load().path == first.path + + +def test_incompatible_abi_sidecar_cannot_be_used_on_a_content_hit(build_case): + first = build_case.build(python_extension=True) + record = first.store_dir / "build_inputs.json" + data = json.loads(record.read_text()) + data["python_abi"] = {"soabi": "incompatible"} + record.write_text(json.dumps(data)) + assert build_case.build(python_extension=True).compiled + assert build_case.load().path == first.path + + +def test_extension_cannot_be_relabeled_native_by_a_damaged_sidecar(build_case): + first = build_case.build(python_extension=True) + record = first.store_dir / "build_inputs.json" + data = json.loads(record.read_text()) + data["python_abi"] = None + record.write_text(json.dumps(data)) + with pytest.raises(artifacts.ArtifactIncompatibleError, match="python_abi"): + build_case.load() + + +@pytest.mark.parametrize("field,value", [("python_abi", None), ("python_extension", False)]) +def test_pointer_hit_must_not_trust_damaged_metadata(build_case, field, value): + first = build_case.build(python_extension=True) + path = first.store_dir / "meta.json" + meta = json.loads(path.read_text()) + meta[field] = value + path.write_text(json.dumps(meta)) + with pytest.raises(artifacts.ArtifactIncompatibleError, match="python_abi"): + build_case.load() + assert not build_case.build(python_extension=True, plan_only=True)["cached"] + assert build_case.build(python_extension=True).compiled + assert build_case.load().path == first.path + + +@pytest.mark.parametrize("exported,declared", [(True, False), (False, True)]) +def test_python_export_declaration_mismatch_is_rejected(monkeypatch, exported, declared): + monkeypatch.setattr(artifacts.subprocess, "run", lambda *a, **kw: SimpleNamespace( + returncode=0, stdout="000001 T PyInit_probe\n" if exported else "000001 T c_entry\n", stderr="")) + with pytest.raises(ValueError, match="python_extension"): + artifacts._validate_python_exports(Path("probe.so"), declared) diff --git a/test/test_consumer_toolchain.py b/test/test_consumer_toolchain.py new file mode 100644 index 00000000..7ac21e0a --- /dev/null +++ b/test/test_consumer_toolchain.py @@ -0,0 +1,64 @@ +"""Consumer-only compiler selection and visibility policy, without compilation.""" +from types import SimpleNamespace + +import pytest + +from grid_rbd import _consumer_toolchain as tc + + +@pytest.fixture +def compiler(tmp_path, monkeypatch): + host = tmp_path / "g++" + host.write_bytes(b"fake host") + for key in tc._ENV_KEYS: + monkeypatch.delenv(key, raising=False) + monkeypatch.setattr(tc, "_resolve_host", lambda selected: str(host) if not selected else selected) + monkeypatch.setattr(tc.subprocess, "run", lambda cmd, **kw: SimpleNamespace( + stdout="#define __GNUC__ 13\n" if "-dM" in cmd else "test compiler")) + return host + + +def test_default_gcc_isolation_and_pinned_link(compiler): + result = tc.resolve_consumer_toolchain([], []) + assert "-fno-gnu-unique" in result.isolation[0] + assert "-fvisibility=hidden" in result.isolation[0] + assert f"-ccbin={compiler}" in result.compile_flags() + assert f"-ccbin={compiler}" in result.link_command("nvcc", "out.so", ["unit.o"]) + + +def test_clang_does_not_receive_gcc_only_flag(compiler, monkeypatch): + monkeypatch.setattr(tc.subprocess, "run", lambda *a, **kw: SimpleNamespace( + stdout="#define __clang__ 1\n#define __GNUC__ 4\n")) + assert "-fno-gnu-unique" not in tc.resolve_consumer_toolchain([], []).isolation[0] + + +def test_flags_and_environment_follow_one_host(compiler, monkeypatch): + monkeypatch.setenv("NVCC_PREPEND_FLAGS", "-DENV_PRE=1 -ccbin=/ignored") + monkeypatch.setenv("NVCC_APPEND_FLAGS", f"--compiler-bindir={compiler} -DENV_POST=2") + result = tc.resolve_consumer_toolchain(["-ccbin", "/also-ignored", "-DUSER=3"], ["-lm"]) + assert result.host == str(compiler) + assert result.prepend == ["-DENV_PRE=1"] + assert result.append == ["-DENV_POST=2"] + assert "-lm" in result.link_command("nvcc", "out", []) + assert not any(key in result.environment() for key in tc._ENV_KEYS) + monkeypatch.setenv("NVCC_APPEND_FLAGS", "-DCHANGED=1") + with pytest.raises(RuntimeError, match="environment changed"): + result.environment() + + +def test_mismatched_link_host_is_rejected(compiler): + with pytest.raises(ValueError, match="same host"): + tc.resolve_consumer_toolchain([f"-ccbin={compiler}"], ["-ccbin=/different"]) + + +@pytest.mark.parametrize("flags", [["-optf", "file"], ["--options-file=file"], ["@file"], ["-ccbin"]]) +def test_untracked_or_missing_flag_inputs_fail(compiler, flags): + with pytest.raises(ValueError): + tc.resolve_consumer_toolchain(flags, []) + + +def test_host_bytes_participate_in_identity(compiler): + before = tc.resolve_consumer_toolchain([], []) + compiler.write_bytes(b"replacement host") + after = tc.resolve_consumer_toolchain([], []) + assert before.identity["host_sha256"] != after.identity["host_sha256"] diff --git a/test/test_gpu_proof_scope.py b/test/test_gpu_proof_scope.py index df961a3d..f6b9f42f 100644 --- a/test/test_gpu_proof_scope.py +++ b/test/test_gpu_proof_scope.py @@ -5,11 +5,12 @@ import pytest import yaml -# pytest-gpu-proof requires CPython 3.11 or newer (it reads pyproject with -# tomllib); the CPU lane on 3.10 cannot install it, so this module skips there -# and runs in full on 3.12 and 3.13. +# Optional in a runtime-only environment, mandatory in the CPU CI matrix. pytest.importorskip("pytest_gpu_proof") -import tomllib # noqa: E402 +try: + import tomllib +except ModuleNotFoundError: # Python 3.10; installed by pytest-gpu-proof. + import tomli as tomllib from pytest_gpu_proof.fingerprint import compute_fingerprint # noqa: E402 ROOT = Path(__file__).resolve().parents[1] @@ -63,6 +64,7 @@ def test_release_policy_requires_exact_recorded_scope(): 'grid_codegen/input.py', 'bindings/grid_rbd/input.py', 'bindings/src/input.py', 'test/conftest.py', 'test/cuda_equivalents/input.py', 'grid_assets/input.py', 'tools/check_installed_numerics.py', + 'bindings/examples/consumer_artifact_migration.py', ]) def test_correctness_edit_changes_fingerprint(repo, path): before = fingerprint(repo) diff --git a/test/test_gpu_proof_upgrade.py b/test/test_gpu_proof_upgrade.py new file mode 100644 index 00000000..e31492dc --- /dev/null +++ b/test/test_gpu_proof_upgrade.py @@ -0,0 +1,71 @@ +"""CPU integration contracts for the pinned receipt tool; never sign evidence.""" +from copy import deepcopy +from pathlib import Path + +import pytest +import yaml + +from pytest_gpu_proof.merge import MergeError, merge_payloads + + +def payload(node, compiler=None): + return { + "schema_version": "3", + "repo": {"commit_sha": "synthetic", "dirty": False}, + "fingerprint": {"digest": "synthetic"}, + "mode": "local", + "environment": {"nvcc_version": compiler}, + "session": {"started_at": "2026-01-01T00:00:00Z", + "ended_at": "2026-01-01T00:00:01Z"}, + "tests": [{"node_id": node, "outcome": "passed", "duration_s": 0}], + } + + +@pytest.mark.parametrize("other", ["CUDA V12.8", None]) +def test_fresh_merge_rejects_different_or_unknown_compiler(other): + with pytest.raises(MergeError, match="environment.nvcc_version"): + merge_payloads([payload("a", "CUDA V13.2"), payload("b", other)], ["a", "b"]) + + +def test_matching_compiler_survives_unsigned_merge(): + merged = merge_payloads([payload("a", "CUDA V13.2"), payload("b", "CUDA V13.2")], ["a", "b"]) + assert merged["environment"]["nvcc_version"] == "CUDA V13.2" + assert [t["node_id"] for t in merged["tests"]] == ["a", "b"] + assert not merged.get("signature") + + +def test_legacy_missing_compiler_is_not_invented_or_input_mutated(): + old = payload("a") + del old["environment"]["nvcc_version"] + before = deepcopy(old) + merged = merge_payloads([old, payload("b")], ["old", "unknown"]) + assert old == before + assert merged["environment"].get("nvcc_version") is None + assert len(merged["tests"]) == 2 + + +def test_cpu_ci_installs_receipt_tool_on_every_supported_interpreter(): + root = Path(__file__).resolve().parents[1] + workflow = yaml.safe_load((root / ".github/workflows/verify-gpu-proof.yml").read_text()) + lane = workflow["jobs"]["cpu-lane"] + assert "3.10" in lane["strategy"]["matrix"]["python"] + step = next(s for s in lane["steps"] if s.get("name") == "Install GRiD (CPU deps only)") + assert "if" not in step + assert "sys.version_info" not in step["run"] + assert "pytest-gpu-proof==" in step["run"] + + +def test_plugin_compiler_metadata_matches_wrapper_selection(monkeypatch): + # No compiler/GPU required: both paths must honor PATH, not a separately + # selected compiler override that the signed plugin field cannot describe. + import subprocess + from grid_rbd._compile import find_nvcc + from pytest_gpu_proof.receipt import _nvcc_version + + monkeypatch.setattr("grid_rbd._compile.shutil.which", lambda name: "/private/cuda/bin/nvcc" if name == "nvcc" else None) + def run(args, **kwargs): + assert args == ["nvcc", "--version"] + return subprocess.CompletedProcess(args, 0, " compiler identity\n", "") + monkeypatch.setattr("pytest_gpu_proof.receipt.subprocess.run", run) + assert find_nvcc() == "/private/cuda/bin/nvcc" + assert _nvcc_version() == "compiler identity" diff --git a/test/test_header_fragments.py b/test/test_header_fragments.py index 2a7122e4..5d44de7d 100644 --- a/test/test_header_fragments.py +++ b/test/test_header_fragments.py @@ -34,7 +34,7 @@ "forward_dynamics_gradient", "f_ext_gradient", "aba", "crba", "integrator", "second_order", "centroidal", "frame_jacobian_family", "ee_runtime", "combinations", "init_close", - "grid_plant", "collision", "suggested_threads", + "grid_plant", "collision", "model_metadata", "suggested_threads", ] diff --git a/test/test_manifold_conventions.py b/test/test_manifold_conventions.py new file mode 100644 index 00000000..d0a72c36 --- /dev/null +++ b/test/test_manifold_conventions.py @@ -0,0 +1,194 @@ +"""Independent value/derivative checks for the native manifold convention. + +Tolerances are fixed before implementation: double precision value FD uses a +fourth-order stencil at 2e-4; analytic retract derivatives use a 2e-8 bound. +These are CPU correctness tests, not a provider or solver implementation. +""" +from pathlib import Path + +import numpy as np +import pytest +from RBDReference import RBDReference +from URDFParser import URDFParser + +from test.cuda_equivalents.manifold_oracle import FIX, derivative, reference, state + + +@pytest.mark.parametrize('angle', [0., 1e-9, 1e-8, 3e-8, 1e-7, 1e-6, 1e-4, 1e-3]) +def test_small_angle_so3_against_matrix_exponential(angle): + # Independent convergent power series of the Lie algebra, not the + # cancellation-prone trigonometric coefficients used by the implementation. + phi = np.array([1., -2., 3.]) * (angle / np.sqrt(14.)) + S = RBDReference._so3_skew(phi) + power = np.eye(3) + exp = power.copy(); left = power.copy(); right = power.copy() + factorial = 1. + for k in range(1, 14): + power = power @ S; factorial *= k + exp += power / factorial + left += power / (factorial*(k+1)) + right += (-1)**k * power / (factorial*(k+1)) + np.testing.assert_allclose(RBDReference._so3_exp(phi), exp, atol=2e-15, rtol=2e-15) + np.testing.assert_allclose(RBDReference._so3_V_matrix(phi), left, atol=2e-15, rtol=2e-15) + np.testing.assert_allclose(RBDReference._so3_right_jacobian(phi), right, atol=2e-15, rtol=2e-15) + + +@pytest.mark.parametrize('name,floating', [('spherical_arm', False), + ('mixed_spherical_arm', False), ('mixed_spherical_arm', True), + ('rotated_inertial_arm', False), ('rotated_inertial_arm', True)]) +@pytest.mark.parametrize('arg', ['q', 'v']) +def test_d2integrate_is_derivative_of_admitted_retract(name, floating, arg): + ref = reference(name, floating) + q, w = state(ref) + expected = derivative(lambda e: ref.dIntegrate(q, w+e, arg), len(w)) + actual = ref.d2Integrate(q, w, arg, 'v') + np.testing.assert_allclose(actual, expected, atol=2e-8, rtol=2e-8) + np.testing.assert_array_equal(ref.d2Integrate(q, w, arg, 'q'), np.zeros_like(actual)) + + +@pytest.mark.parametrize('name,floating', [('mixed_spherical_arm', False), + ('mixed_spherical_arm', True), ('rotated_inertial_arm', False), + ('rotated_inertial_arm', True)]) +def test_difference_both_arguments_and_sign_controls(name, floating): + ref = reference(name, floating) + a, w = state(ref) + b = ref.integrate(a, w) + nv = len(w) + for arg in ('from', 'to'): + expected = derivative(lambda e: ref.difference(ref.integrate(a, e), b) + if arg == 'from' else ref.difference(a, ref.integrate(b, e)), nv) + actual = ref.dDifference(a, b, arg) + np.testing.assert_allclose(actual, expected, atol=2e-8, rtol=2e-8) + assert np.max(np.abs(actual + expected)) > 1 + if ref._joint_retract_specs(): + assert np.max(np.abs(actual - actual.T)) > .01 + assert np.max(np.abs(actual - (1 if arg == 'to' else -1)*np.eye(nv))) > .01 + flipped = b.copy() + for _, iq, _ in ref._joint_retract_specs(): + flipped[iq[-4:]] *= -1 + np.testing.assert_allclose(ref.difference(a, flipped), w, atol=2e-14) + + +@pytest.mark.parametrize('name', ['spherical_arm', 'mixed_spherical_arm']) +@pytest.mark.parametrize('scheme', ['euler', 'semi_implicit_euler']) +def test_spherical_plant_hessian_matches_moving_gradient(name, scheme): + ref = reference(name) + q, v = state(ref) + u = np.linspace(-.4, .7, len(v)) + nv = len(v) + def gradient(z): + return ref.plant_step_gradient(ref.integrate(q, z[:nv]), v+z[nv:2*nv], + u+z[2*nv:], .02, integrator_type=scheme) + expected = derivative(gradient, 3*nv).transpose(0, 2, 1) + actual = ref.plant_step_hessian(q, v, u, .02, integrator_type=scheme) + np.testing.assert_allclose(actual, expected, atol=2e-7, rtol=2e-7) + + +def test_moving_hessian_is_not_fixed_chart_curvature(): + ref=reference('spherical_arm');q,v=state(ref);nv=len(v) + u=np.linspace(-.2,.3,nv);dt=.02 + y=ref.plant_step(q,v,u,dt,'euler');nq=len(q) + weight=np.linspace(.2,1.,2*nv) + def scalar(z): + other=ref.plant_step(ref.integrate(q,z[:nv]),v+z[nv:2*nv],u+z[2*nv:],dt,'euler') + return weight@np.r_[ref.difference(y[:nq],other[:nq]),other[nq:]-y[nq:]] + H=np.einsum('o,oab->ab',weight,ref.plant_step_hessian(q,v,u,dt,'euler')) + # Two different spherical axes: moving q/v blocks are not symmetric. + a,b=0,nv+1;h=3e-4;da=np.eye(3*nv)[a]*h;db=np.eye(3*nv)[b]*h + fixed=(scalar(da+db)-scalar(da-db)-scalar(-da+db)+scalar(-da-db))/(4*h*h) + assert abs(H[a,b]-H[b,a])>1e-3 + assert max(abs(fixed-H[a,b]),abs(fixed-H[b,a]))>5e-4 + # Swapping the perturbations in a fixed-chart scalar second difference is + # symmetric; this does not license symmetrizing the moving-G API tensor. + reversed=(scalar(db+da)-scalar(db-da)-scalar(-db+da)+scalar(-db-da))/(4*h*h) + np.testing.assert_allclose(fixed,reversed,atol=2e-12,rtol=2e-12) + + +def test_unequal_dimension_analytic_fixture_axes_and_contractions(): + z = np.array([.3, -.4, .7, .2, -.6]) # x in R3, u in R2 + def value(d): + a,b,c,p,q = z+d + return np.array([a+b*p+q*q, b+c*q+p*p, c+np.sin(a)+p*q]) + a,b,c,p,q = z + G = np.array([[1,p,0,b,2*q], [0,1,q,2*p,c], [np.cos(a),0,1,q,p]]) + H = np.zeros((3,5,5)) + H[0,1,3] = H[0,3,1] = 1 + H[0,4,4] = H[1,3,3] = 2 + H[1,2,4] = H[1,4,2] = H[2,3,4] = H[2,4,3] = 1 + H[2,0,0] = -np.sin(a) + np.testing.assert_allclose(G, derivative(value, 5), atol=1e-11, rtol=1e-11) + h = 1e-3 + Hfd = derivative(lambda d: derivative(lambda e: value(d+e), 5, h), 5, h) + np.testing.assert_allclose(H, Hfd, atol=2e-9, rtol=2e-9) + weights = np.array([.2,-.7,1.3]) + np.testing.assert_allclose(np.einsum('o,oab->ab', weights, H), + derivative(lambda d: derivative(lambda e: weights@value(d+e), 5, h), 5, h), + atol=2e-9, rtol=2e-9) + + +@pytest.mark.parametrize('name', ['spherical_arm', 'mixed_spherical_arm']) +def test_spherical_pinocchio_anchor(name): + import pinocchio as pin + from RBDReference.tests.test_spherical_joint_equivalence import _build_pin_spherical, _build_pin_mixed + model = (_build_pin_spherical if name == 'spherical_arm' else _build_pin_mixed)() + data = model.createData() + ref = reference(name) + q, v = state(ref) + nv = len(v) + u = np.linspace(-.4, .7, nv) + b = pin.integrate(model, q, v) + for arg, flag in [('from', pin.ARG0), ('to', pin.ARG1)]: + np.testing.assert_allclose(ref.dDifference(q, b, arg), pin.dDifference(model, q, b, flag), + atol=2e-11, rtol=2e-11) + for arg, flag in [('q', pin.ARG0), ('v', pin.ARG1)]: + fd = derivative(lambda e: np.array(pin.dIntegrate(model, q, v+e, flag)), nv) + np.testing.assert_allclose(ref.d2Integrate(q, v, arg, 'v'), fd, atol=2e-8, rtol=2e-8) + dt = .02 + for scheme in ('euler', 'semi_implicit_euler'): + def pin_gradient(z): + qp = pin.integrate(model, q, z[:nv]); vp=v+z[nv:2*nv]; up=u+z[2*nv:] + acc = np.array(pin.aba(model, data, qp, vp, up), copy=True) + dq,dv,du = [np.array(x, copy=True) for x in pin.computeABADerivatives(model, data, qp, vp, up)] + V = np.hstack([dt*dq, np.eye(nv)+dt*dv, dt*du]) + w = dt*(vp+dt*acc if scheme=='semi_implicit_euler' else vp) + A,B = pin.dIntegrate(model, qp, w) + Q = np.hstack([A,np.zeros((nv,2*nv))]) + Q += B@(dt*V if scheme=='semi_implicit_euler' else np.hstack([np.zeros((nv,nv)),dt*np.eye(nv),np.zeros((nv,nv))])) + return np.vstack([Q,V]) + np.testing.assert_allclose(ref.plant_step_gradient(q,v,u,dt,scheme), pin_gradient(np.zeros(3*nv)),atol=2e-10,rtol=2e-10) + expected=derivative(pin_gradient,3*nv).transpose(0,2,1) + np.testing.assert_allclose(ref.plant_step_hessian(q,v,u,dt,scheme),expected,atol=2e-7,rtol=2e-7) + + +@pytest.mark.parametrize('reverse', [False, True]) +def test_floating_nonzero_defect_value_fd_and_row_normalization(reverse): + # Fixture has positive inertials, unlike the kinematics-only rotated arm. + ref=reference('mixed_spherical_arm',True) + q,v=state(ref);nv=len(v);u=np.linspace(-.4,.7,nv);dt=.02 + def step(z): + return ref.plant_step(ref.integrate(q,z[:nv]),v+z[nv:2*nv],u+z[2*nv:],dt,'euler') + nq=len(q);y=step(np.zeros(3*nv));delta=np.linspace(-.35,.4,nv) + s=np.r_[ref.integrate(y[:nq],delta),y[nq:]+.2] + def difference(a,b): return np.r_[ref.difference(a[:nq],b[:nq]),b[nq:]-a[nq:]] + def jacobian(a,b,arg): + J=np.zeros((2*nv,2*nv));J[:nv,:nv]=ref.dDifference(a[:nq],b[:nq],arg) + J[nv:,nv:]=(1 if arg=='to' else -1)*np.eye(nv) + return J + a,b=(s,y) if reverse else (y,s) + Jpred=jacobian(a,b,'to' if reverse else 'from') + Jsucc=jacobian(a,b,'from' if reverse else 'to') + G=ref.plant_step_gradient(q,v,u,dt,'euler') + pred_fd=derivative(lambda z: difference(s,step(z)) if reverse else difference(step(z),s),3*nv) + succ_fd=derivative(lambda z: difference(np.r_[ref.integrate(s[:nq],z[:nv]),s[nq:]+z[nv:]],y) + if reverse else difference(y,np.r_[ref.integrate(s[:nq],z[:nv]),s[nq:]+z[nv:]]),2*nv) + np.testing.assert_allclose(Jpred@G,pred_fd,atol=3e-7,rtol=3e-7) + np.testing.assert_allclose(Jsucc,succ_fd,atol=3e-8,rtol=3e-8) + assert np.max(abs(Jpred@G-(1 if reverse else -1)*G))>.01 + rng=np.random.default_rng(722) + dz=rng.normal(size=3*nv);ds=rng.normal(size=2*nv);mu=rng.normal(size=2*nv) + c=difference(a,b);raw=c+Jpred@G@dz+Jsucc@ds + normalized=np.linalg.solve(Jsucc,c)+np.linalg.solve(Jsucc,Jpred@G)@dz+ds + np.testing.assert_allclose(Jsucc@normalized,raw,atol=2e-13,rtol=2e-13) + original_covector=np.linalg.solve(Jsucc.T,mu) + np.testing.assert_allclose(mu@normalized,original_covector@raw,atol=2e-13,rtol=2e-13) + assert abs(mu@raw-mu@normalized)>.001 # omit covector map: negative control diff --git a/test/test_model_descriptor.py b/test/test_model_descriptor.py new file mode 100644 index 00000000..2be84729 --- /dev/null +++ b/test/test_model_descriptor.py @@ -0,0 +1,244 @@ +"""Schema invariants and independent descriptor-only forward kinematics.""" +import copy +import hashlib +import json +import subprocess +from pathlib import Path + +import numpy as np +import pytest +from URDFParser import URDFParser +from RBDReference import RBDReference +from grid_codegen.model_descriptor import (canonical_bytes, descriptor_from_robot, + descriptor_id, validate_descriptor) +from grid_rbd._compile import configuration_layout_from_robot +from grid_codegen.model_metadata_cpp import emit_model_metadata +from grid_rbd import describe_model +from test.cuda_equivalents.constant_model import constant_model +from test.cuda_equivalents.descriptor_oracle import assert_reference, descriptor_fk + +ROOT = Path(__file__).resolve().parents[1] + + +def test_public_cpu_description_and_option_rejection(tmp_path, monkeypatch, capsys): + robot = constant_model(tmp_path) + capsys.readouterr() + source = tmp_path/'model-False-False-False.urdf' + monkeypatch.setattr(subprocess, 'run', lambda *a, **k: pytest.fail('CPU inspection started a process')) + assert describe_model(source) == descriptor_from_robot(robot) + assert not capsys.readouterr().out + with pytest.raises(ValueError, match='schema'): + describe_model(source, schema_version=2) + with pytest.raises(TypeError): + describe_model(source, runtime_transform='false') + with pytest.raises(TypeError): + describe_model(source, arbitrary_options=True) + + +@pytest.mark.parametrize('fixture,floating',[ + ('mixed_spherical_arm',False),('branching_skew_arm',False), + ('constant',False),('constant',True)]) +def test_generated_wrapper_metadata_has_one_coordinate_source(tmp_path,fixture,floating): + from types import SimpleNamespace + from grid_rbd._compile import generate_grid_cuh + from grid_rbd._handle import RobotHandle + if fixture=='constant': + constant_model(tmp_path) + source=tmp_path/'model-False-False-False.urdf' + else: + source=ROOT/'external/URDFParser/tests/fixtures'/f'{fixture}.urdf' + out=tmp_path/'grid.cuh' + meta=generate_grid_cuh(source,dict(floating_base=floating, + algorithm_list=['end_effector_pose'],emit_plant=False,enable_mujoco_kernels=False),out) + d=describe_model(source,floating_base=floating) + assert meta['model_descriptor']==d==json.loads((tmp_path/'model_descriptor.json').read_text()) + assert [meta[k] for k in ('num_joints','num_vel','num_bodies')]==[d[k] for k in ('nq','nv','nb')] + assert meta['joint_names']==[j['name'] for j in d['joints']] + handle=object.__new__(RobotHandle) + handle._meta=meta + handle._runner=SimpleNamespace(num_joints=d['nq'],num_vel=d['nv'],num_bodies=d['nb']) + assert handle.model_descriptor==d + handle.model_descriptor['model_name']='changed copy' + assert handle.model_descriptor==d + from grid_rbd._surface_common import BaseDelegateMixin + # JAX and torch share this metadata delegate; no framework/GPU import needed. + delegated=BaseDelegateMixin() + delegated._base=handle + assert delegated.model_descriptor==d + delegated.model_descriptor['model_name']='changed delegated copy' + assert handle.model_descriptor==d + assert tuple(handle.configuration_layout)==tuple(tuple(b[k] for k in + ('kind','q_start','v_start','nq','nv')) for b in d['configuration_layout']) + + +def test_canonical_encoding_golden(): + value = {'z': [None, True, 1, -0.0, 1.5], 'a': 'é'} + expected = ('["map",[["a",["str","é"]],["z",["list",[["null"],["bool",true],' + '["int","1"],["f64","0000000000000000"],["f64","3ff8000000000000"]]]]]]').encode() + assert canonical_bytes(value) == expected + assert descriptor_id(value) == hashlib.sha256(expected).hexdigest() + assert canonical_bytes(-0.) == canonical_bytes(0.) + assert canonical_bytes(1) != canonical_bytes(1.) + assert canonical_bytes('é') != canonical_bytes('e\u0301') + + +@pytest.mark.parametrize('bad', [float('nan'), float('inf'), -float('inf'), '\ud800', {1: 'key'}, (1, 2)]) +def test_canonical_encoding_rejects_bad_values(bad): + with pytest.raises((ValueError, UnicodeError)): + canonical_bytes(bad) + + +@pytest.mark.parametrize('name,floating', [ + ('branching_skew_arm', False), ('mixed_spherical_arm', False), + ('helical_arm', False), ('rotated_inertial_arm', False), + ('rotated_inertial_arm', True), ('constant', False), ('constant', True), ('empty', False), +]) +def test_descriptor_fk_and_ownership(tmp_path, name, floating): + if name in ('constant', 'empty'): + constant_model(tmp_path, zero=name == 'empty') + source = tmp_path / f'model-{name == "empty"}-False-False.urdf' + else: + source = ROOT/'external/URDFParser/tests/fixtures'/f'{name}.urdf' + robot = URDFParser().parse(str(source), floating_base=floating) + d = descriptor_from_robot(robot) + assert validate_descriptor(json.loads(json.dumps(d))) == d + expected = [tuple(b[k] for k in ('kind', 'q_start', 'v_start', 'nq', 'nv')) + for b in d['configuration_layout']] + assert tuple(expected) == configuration_layout_from_robot(robot) + rng = np.random.default_rng(3010) + for _ in range(12): + q = rng.uniform(-.6, .6, d['nq']) + for block in d['configuration_layout']: + if block['kind'] in ('floating', 'spherical'): + end = block['q_start']+block['nq'] + quat = rng.normal(size=4) + q[end-4:end] = quat/np.linalg.norm(quat) + assert_reference(d, robot, q) + assert [j['name'] for j in d['joints']] == [j.name for j in robot.get_joints_ordered_by_id()] + + +def test_descriptor_hand_anchors_and_negative_controls(tmp_path): + robot = constant_model(tmp_path) + d = descriptor_from_robot(robot) + q = np.array([0.]) + poses = descriptor_fk(d, q) + constant = next(f for f in d['frames'] if f['name'] == 'constant') + c, s = np.cos(.17), np.sin(.17) + expected = np.array([[c, -s, 0, .3], [s, c, 0, 0], [0, 0, 1, .5], [0, 0, 0, 1.]]) + np.testing.assert_allclose(poses[constant['id']], expected, atol=1e-12, rtol=0) + reversed_transform = copy.deepcopy(d) + reversed_transform['joints'][0]['T_parent_joint_zero'] = np.linalg.inv( + reversed_transform['joints'][0]['T_parent_joint_zero']).tolist() + reversed_transform['descriptor_id'] = descriptor_id(reversed_transform) + validate_descriptor(reversed_transform) # Structurally valid, semantically wrong. + with pytest.raises(AssertionError): + assert_reference(reversed_transform, robot, q) + bad = copy.deepcopy(d) + bad['joints'][1]['q_start'] = 0 + bad['descriptor_id'] = descriptor_id(bad) + with pytest.raises(ValueError, match='dependent slices'): + validate_descriptor(bad) + source = tmp_path/'model-False-False-False.urdf' + floating = URDFParser().parse(str(source), floating_base=True) + fd = descriptor_from_robot(floating) + fq = np.r_[1., 2., 3., 0., 0., np.sqrt(.5), np.sqrt(.5), 0.] + base = descriptor_fk(fd, fq)[1] + np.testing.assert_allclose(base, [[0, -1, 0, 1], [1, 0, 0, 2], [0, 0, 1, 3], [0, 0, 0, 1]], atol=1e-12) + swapped = fq.copy() + swapped[3:7] = np.roll(swapped[3:7], 1) + with pytest.raises(AssertionError): + np.testing.assert_allclose(descriptor_fk(fd, swapped)[1], base, atol=1e-11) + + +@pytest.mark.parametrize('mutation', [ + lambda d: d.update(schema_version=2), + lambda d: d.update(nq=d['nq']+1), + lambda d: d['configuration_layout'][0].update(nv=2), + lambda d: d['bodies'][0].update(parent_body=0), + lambda d: d['frames'][1].update(attached_body=-1), + lambda d: d['joints'][0].update(axis=[2., 0., 0.]), + lambda d: d['joints'][1]['mimic'].update(multiplier=1.), + lambda d: d['joints'][0]['T_parent_joint_zero'][0].__setitem__(0, -1.), +]) +def test_rehash_does_not_bless_invalid_metadata(tmp_path, mutation): + d = descriptor_from_robot(constant_model(tmp_path)) + mutation(d) + d['descriptor_id'] = descriptor_id(d) + with pytest.raises(ValueError): + validate_descriptor(d) + + +def test_identity_boundaries(tmp_path): + robot = constant_model(tmp_path) + d = descriptor_from_robot(robot) + path = tmp_path/'model-False-False-False.urdf' + raw = path.read_text() + path.write_text(raw.replace('mass value="1"', 'mass value="1.7"')) + changed = descriptor_from_robot(URDFParser().parse(str(path))) + assert changed == d # Descriptor is NOT a complete physical-model identity. + path.write_text(raw.replace('xyz=".2 0 .3"', 'xyz=".2 .1 .3"')) + assert descriptor_from_robot(URDFParser().parse(str(path)))['descriptor_id'] != d['descriptor_id'] + assert descriptor_from_robot(robot, runtime_transform=True)['descriptor_id'] != d['descriptor_id'] + d['model_name'] += '-changed' + with pytest.raises(ValueError, match='digest'): + validate_descriptor(d) + + +def test_cpp_host_metadata_roundtrip_and_constexpr(tmp_path): + d = descriptor_from_robot(constant_model(tmp_path)) + d['joints'][0]['name'] = 'quoted"\\é' + d['frames'][1]['name'] = d['joints'][0]['name'] + d['descriptor_id'] = descriptor_id(d) + (tmp_path/'metadata.hpp').write_text(emit_model_metadata(d)) + source = tmp_path/'probe.cpp' + source.write_text(''' +#define __host__ +#define __device__ +#include "metadata.hpp" +#include +constexpr bool check() { + grid::model::joint_record j{}; + return grid::model::joint(0,j) && j.q_start==0 && j.axis[1]==1.0; +} +static_assert(check()); +int main(){ + puts(grid::model::descriptor_json); + puts(grid::model::joint_name(0)); + for(int i=0;i=SPHERICAL_HESSIAN_SCRATCH diff --git a/test/test_package_resources.py b/test/test_package_resources.py index ec1c5464..a6d93ed7 100644 --- a/test/test_package_resources.py +++ b/test/test_package_resources.py @@ -63,6 +63,18 @@ def test_installed_source_identity_is_location_independent_and_detects_edits(tmp assert _cache._codegen_source_hash() != original +def test_shared_model_parser_changes_both_generation_and_compile_identity(monkeypatch): + from grid_rbd import _cache + before=(_cache._codegen_source_hash(),_cache._compile_source_hash()) + read=Path.read_bytes + def changed(path): + result=read(path) + return result+b'\n# changed model parsing\n' if path.name=='_model.py' else result + monkeypatch.setattr(Path,'read_bytes',changed) + after=(_cache._codegen_source_hash(),_cache._compile_source_hash()) + assert all(a!=b for a,b in zip(before,after)) + + def test_missing_toolkit_error_explains_registration_requirement(monkeypatch): from grid_rbd import _compile monkeypatch.setattr(_compile.shutil, 'which', lambda _: None) diff --git a/test/test_plant_gradient_arena.py b/test/test_plant_gradient_arena.py new file mode 100644 index 00000000..bb234473 --- /dev/null +++ b/test/test_plant_gradient_arena.py @@ -0,0 +1,46 @@ +"""CPU guards for the plant gradient's distinct full-shared contract.""" +from pathlib import Path +import re +import pytest +from URDFParser import URDFParser +from grid_codegen import GRiDCodeGenerator +from grid_codegen.algorithms._plant import _plant_step_gradient_extra_t_buffers + + +def test_spherical_plant_gradient_scratch(tmp_path): + inertial = ('') + links = ''.join(f'{inertial}' for i in range(6)) + joints = ''.join(f'' + f'' for i in range(5)) + path = tmp_path / 'spherical.urdf' + path.write_text(f'{links}{joints}') + robot = URDFParser().parse(str(path)) + assert robot.get_num_pos() == 20 and robot.get_num_vel() == 15 + slots = dict(_plant_step_gradient_extra_t_buffers(GRiDCodeGenerator(robot))) + assert slots['s_x'] == 35 + assert slots['s_q_orig'] == 20 + assert slots['s_dInt_q_6x6'] == slots['s_dInt_v_6x6'] == 45 + + +def test_every_plant_gradient_wrapper_uses_full_shared_reservation(): + source = (Path(__file__).parents[1] / 'bindings/grid_rbd/wrapper_template.cu').read_text() + for function in ['launch_plant_step_gradient', 'launch_plant_step_gradient_mujoco', + 'launch_plant_step_gradient_jax', 'torch_launch_plant_step_gradient']: + body = source.split(f'void {function}(', 1)[1].split('\n}', 1)[0] + assert 'grid_plant::PLANT_STEP_GRADIENT_DYNAMIC_SHARED_MEM_BYTES()' in body + assert 'grid::INTEGRATOR_DU_DYNAMIC_SHARED_MEM_BYTES' not in body + assert source.count('plant_step_gradient_check_shared_memory()') == 4 + + +@pytest.mark.parametrize('suffix', ['', '_gradient', '_hessian']) +@pytest.mark.parametrize('convention', ['', '_mujoco']) +def test_plant_step_cabi_scalars_match_precision(suffix, convention): + root = Path(__file__).parents[1] + wrapper = (root / 'bindings/grid_rbd/wrapper_template.cu').read_text() + name = f'grid_plant_step{suffix}{convention}' + signature = re.search(r'extern "C" int ' + name + r'\(([^)]*)\)', wrapper).group(1) + assert 'T gravity, T dt' in signature + core = (root / 'bindings/src/_core.cpp').read_text() + pointer = core.split('using fn_plant_step_t', 1)[1].split(';', 1)[0] + assert 'int, CT, CT, int)' in pointer diff --git a/test/test_plant_workspace_codegen.py b/test/test_plant_workspace_codegen.py new file mode 100644 index 00000000..095bcaca --- /dev/null +++ b/test/test_plant_workspace_codegen.py @@ -0,0 +1,93 @@ +"""Public composition views must share the production arena's authorities.""" +from test.test_plant_menu_codegen import _gen, _robot +from grid_codegen.algorithms._plant import _plant_hessian_shared_slots, _plant_hessian_pool_size +import pytest +import re + + +def test_workspace_surfaces_and_scope(tmp_path): + gen, text = _gen(tmp_path, _robot('iiwa14'), + algorithm_list=['integrator_hessian', 'frame_jacobian']) + assert '#define GRID_PLANT_HAS_HESSIAN_ARENA 1' in text + assert 'struct tracking_cost_menu_arena' in text + assert 'plant_hessian_layout::full_shared' in text + assert 'plant_hessian_layout::deep_spill' in text + assert 'workspace_capacity < workspace_bytes()' in text + assert 'reinterpret_cast(workspace) % 16' in text + assert 'FRAME_EVAL_DYNAMIC_SHARED_MEM_BYTES()' in text + assert 'rows > 93 ? rows : 93' in text + slots = _plant_hessian_shared_slots(gen, False, False) + assert dict(slots)['s_d2AB'] == 18 * 7 ** 3 + assert 's_d2AB' not in dict(_plant_hessian_shared_slots(gen, True, True)) + assert _plant_hessian_pool_size(gen) > 0 + + +def test_floating_hessian_composition_is_not_silently_advertised(tmp_path): + _, text = _gen(tmp_path, _robot('go2', True), algorithm_list=['integrator_hessian']) + assert '#define GRID_PLANT_HAS_HESSIAN_ARENA 0' in text + assert 'void plant_step_hessian_kernel(' in text + assert 'enum class plant_hessian_layout' not in text + + +@pytest.mark.parametrize('name,target,leaves', [('iiwa14','iiwa_joint_ee',1),('go2','FR_foot_joint',4),('go2','',4)]) +def test_exact_inner_pose_queries_follow_selected_target(tmp_path,name,target,leaves): + gen,text=_gen(tmp_path,_robot(name,name=='go2'),fixed_target_name=target, + algorithm_list=['end_effector_pose','end_effector_pose_gradient']) + pose_query=re.search(r'EE_TARGET_POSE_INNER_SCRATCH_BYTES\(\).*?size_t\((\d+)\)',text) + assert pose_query and int(pose_query[1])==32*(1 if target else leaves) + gradient_query=re.search(r'EE_TARGET_GRAD_INNER_SCRATCH_BYTES\(\).*?size_t\((\d+)\)',text) + assert gradient_query and int(gradient_query[1])==gen.gen_end_effector_pose_gradient_inner_temp_mem_size(target) + assert f'static_cast({32*leaves})' in text.split('EE_POSE_INNER_SCRATCH_BYTES()',1)[1].split('\n',1)[0] + + +def test_runtime_hessian_query_matches_resolved_kernel_layout(tmp_path): + from grid_codegen.algorithms._plant import _plant_hessian_layout + gen,text=_gen(tmp_path,_robot('iiwa14'),runtime_transform=True,algorithm_list=['integrator_hessian']) + query=text.split('INTEGRATOR_HESSIAN_DYNAMIC_SHARED_MEM_BYTES()',1)[1].split('\n',1)[0] + counts=[int(n) for n in re.findall(r'grid_shared_arena_bytes\((\d+),',query)] + expected=[] + for pick in gen.plant_step_hessian_spill_tier_3way: + layout=_plant_hessian_layout(gen,bool(pick),bool(pick),bool(pick)) + expected.append(sum(c for _,c in layout.t_buffers)+layout.ximat_size+layout.temp_mem_size) + assert counts==expected + + +def test_empty_coordinates_do_not_advertise_hessian_composition(tmp_path): + from test.cuda_equivalents.constant_model import constant_model + _,text=_gen(tmp_path,constant_model(tmp_path,zero=True),algorithm_list=['integrator_hessian']) + assert '#define GRID_PLANT_HAS_HESSIAN_ARENA 0' in text + + +def _constant_slot_report(case): + from test.ee_constant_slot_warnings import CASES + target, accesses, groups = CASES[case] + detail = ("========= Warning: (Warp Level Programming) Potential WAR hazard detected at __shared__ 0x10\n" + "========= Read Thread at grid::glass::dot_strided\n" + f"========= Write Thread at grid::{target}\n" + "========= Current Value : 63, Incoming Value : 63\n") + group = ("========= Warning: Race reported between Read access at grid::glass::dot_strided\n" + f"========= and Write access at grid::{target} [8 hazards]\n") + total = accesses + groups + return detail * accesses + group * groups + f'========= RACECHECK SUMMARY: {total} hazards displayed (0 errors, {total} warnings)\n' + + +@pytest.mark.parametrize('case', ['test_exact_target_scratch[float-iiwa14-iiwa_joint_ee]', + 'test_exact_target_scratch[float-go2-FR_foot_joint]', 'test_exact_target_scratch[float-go2-]']) +def test_known_ee_warning_classification_is_narrow(case): + from test.ee_constant_slot_warnings import known_constant_slot_warning + report = _constant_slot_report(case) + assert known_constant_slot_warning(report, case) + for damaged in ( + report.replace('Incoming Value : 63', 'Incoming Value : 62', 1), + report.replace('Potential WAR', 'Potential RAW', 1), + report.replace('grid::glass::dot_strided', 'other_read', 1), + report.replace('0 errors', '1 errors'), + report.replace('Incoming Value : 63', 'Incoming Value : unknown', 1), + report.replace('Current Value : 63, Incoming Value : 63', 'Current Value : 42, Incoming Value : 42', 1), + report + '========= Error: invalid memory access\n', + report + '========= Warning: unexpected finding\n', + report.replace('[8 hazards]', '[9 hazards]', 1), + report.replace('Read Thread', 'Missing detail', 1), + ): + assert not known_constant_slot_warning(damaged, case) + assert not known_constant_slot_warning(report, 'unreviewed_fixture') diff --git a/test/test_prismatic_fixed_target.py b/test/test_prismatic_fixed_target.py new file mode 100644 index 00000000..8450789c --- /dev/null +++ b/test/test_prismatic_fixed_target.py @@ -0,0 +1,59 @@ +"""G13: all-prismatic kinematics with a nonidentity fixed tool.""" +import numpy as np +import pytest +from scipy.spatial.transform import Rotation + +from grid_assets import robot_urdf +from grid_codegen import GRiDCodeGenerator +from URDFParser import URDFParser + + +def test_single_dof_contraction_uses_one_tile(): + from grid_codegen.algorithms._fdsva_so import _fdsva_so_minv_tile + assert _fdsva_so_minv_tile(1) == 1 + for n in range(2, 65): + width = _fdsva_so_minv_tile(n) + assert 1 <= width <= 8 and n % width == 0 and n // width >= 2 + + +@pytest.mark.parametrize("profile,axis", [("kinematics", "0.6 0 0.8"), ("all", "0 0 1")]) +@pytest.mark.parametrize("model", ["slider_fixed_tool", "slider_mimic_fixed_tool"]) +def test_prismatic_fixed_tool_generation(tmp_path, profile, axis, model): + # General-axis second-order dynamics are not supported by the all profile. + # Exercise the reported skew kinematics case and cardinal full generation. + urdf = tmp_path / "slider.urdf" + urdf.write_text(robot_urdf(model).read_text().replace("0.6 0 0.8", axis)) + robot = URDFParser().parse(str(urdf)) + output = tmp_path / "grid.cuh" + gen = GRiDCodeGenerator(robot, False, False, "grid") + gen.gen_all_code(output_path=str(output), codegen_profile=profile, + include_homogenous_transforms=True, fixed_target_name="tool", + enable_mujoco_kernels=False) + text = output.read_text() + assert "load_update_XmatsHom_helpers" in text + # q-dependent translation must still be updated when there is no sin/cos. + helper = text.split("void load_update_XmatsHom_helpers(", 1)[1].split("\n }", 1)[0] + assert "s_q[0]" in helper + assert "cgrps::memcpy_async(tgrp" not in text + if "mimic" in model: + for loader in ("load_update_XmatsHom_helpers", "load_update_XImats_helpers"): + body = text.split(f"void {loader}(", 1)[1].split("\n }", 1)[0] + assert "static_cast(-0.5) * s_q[0] + static_cast(0.17)" in body + + +@pytest.mark.parametrize("q", [0., 0.13, 0.5]) +@pytest.mark.parametrize("mimic", [False, True]) +def test_slider_oracle_matches_analytic_transform_and_jacobian(q, mimic): + from RBDReference import RBDReference + robot = URDFParser().parse(str(robot_urdf("slider_mimic_fixed_tool" if mimic else "slider_fixed_tool"))) + ref = RBDReference(robot) + r = Rotation.from_euler("xyz", [0.3, 0.2, 0.1]).as_matrix() + p = np.array([0.04, 0.01, 0.02]) + axis = np.array([0.6, 0., 0.8]) + displacement = 0.5 * q + 0.17 if mimic else q + expected = np.r_[p + r @ (axis * displacement + p), Rotation.from_matrix(r @ r).as_euler("xyz")] + actual = np.asarray(ref.end_effector_pose(np.array([q]), ee_joint_names=["tool"])).reshape(6) + jac = np.asarray(ref.end_effector_pose_gradient(np.array([q]), ee_joint_names=["tool"])).reshape(6) + np.testing.assert_allclose(actual, expected, atol=1e-12, rtol=1e-12) + np.testing.assert_allclose(jac, np.r_[(0.5 if mimic else 1.) * (r @ axis), np.zeros(3)], + atol=1e-12, rtol=1e-12) diff --git a/test/test_rbd_cache_identity.py b/test/test_rbd_cache_identity.py index 4dff7f4f..8275187d 100644 --- a/test/test_rbd_cache_identity.py +++ b/test/test_rbd_cache_identity.py @@ -237,11 +237,42 @@ def test_pointer_without_sidecar_is_a_miss(stubs): assert (so.parent / _cache.BUILD_INPUTS_FILE).exists(), "sidecar back-filled" -def test_incomplete_entry_rebuilds(stubs): +@pytest.mark.parametrize("missing", ["robot.so", "meta.json", "grid.cuh"]) +def test_incomplete_entry_rebuilds(stubs, missing): key, so, _ = _warm(stubs) - so.unlink() + (so.parent / missing).unlink() _warm(stubs) assert (stubs.generate_calls, stubs.compile_calls) == (2, 2) + assert so.is_file() and (so.parent / missing).is_file() + + +def test_corrupt_metadata_rebuilds(stubs): + _, so, _ = _warm(stubs) + (so.parent / "meta.json").write_text("{broken") + _warm(stubs) + assert (stubs.generate_calls, stubs.compile_calls) == (2, 2) + assert isinstance(json.loads((so.parent / "meta.json").read_text()), dict) + + +def test_concurrent_wrapper_builds_keep_private_staging(stubs, monkeypatch): + from concurrent.futures import ThreadPoolExecutor + barrier = threading.Barrier(2) + paths = [] + original = stubs.generate_sources + + def generate(urdf, options, out): + meta = original(urdf, options, out) + paths.append(out) + barrier.wait(timeout=10) + assert (out / "grid.cuh").is_file() + return meta + + monkeypatch.setattr(grid_rbd, "generate_sources", generate) + with ThreadPoolExecutor(2) as pool: + results = list(pool.map(lambda _: _warm(stubs), range(2))) + assert len(set(paths)) == 2 + assert results[0][:2] == results[1][:2] + assert results[0][1].is_file() def test_stale_reasons_are_readable(tmp_path): diff --git a/test/test_revolute_mimic_mechanics.py b/test/test_revolute_mimic_mechanics.py new file mode 100644 index 00000000..6e5830bf --- /dev/null +++ b/test/test_revolute_mimic_mechanics.py @@ -0,0 +1,62 @@ +"""Skew revolute mimic dynamics in the numpy oracle against closed-form mechanics. + +Pinocchio has no revolute mimic with a nonunit multiplier, so this fixture's +inverse dynamics and mass matrix are pinned to the independent closed form in +``test/cuda_equivalents/revolute_mimic_mechanics.py`` instead (placements and inertias are still +checked against Pinocchio by ``test_urdf_oracle``). The CUDA twin is +``test/cuda_equivalents/test_cuda_revolute_mimic_mechanics.py``. +""" +import numpy as np +import pytest +from URDFParser import URDFParser +from RBDReference import RBDReference +from grid_assets import robot_urdf +from test.cuda_equivalents import revolute_mimic_mechanics as mech + +CASES = ((0., 0., 0.), (.13, -.4, 2.), (.5, .7, -3.), (-1.2, .25, .4)) + + +@pytest.fixture(scope="module") +def reference(): + robot = URDFParser().parse(str(robot_urdf("revolute_mimic_fixed_tool"))) + assert any(getattr(j, "is_mimic", False) for j in robot.get_joints_ordered_by_id()) + return RBDReference(robot) + + +def test_generalized_mass_is_constant_and_matches_closed_form(reference): + expected = mech.generalized_mass() + for q, _, _ in CASES: + np.testing.assert_allclose(reference.crba(np.array([q])), [[expected]], atol=1e-12, rtol=1e-12) + np.testing.assert_allclose(reference.minv(np.array([q])), [[1. / expected]], atol=1e-10, rtol=1e-10) + + +def test_inverse_dynamics_matches_closed_form(reference): + for q, qd, qdd in CASES: + tau = reference.inverse_dynamics(np.array([q]), np.array([qd]), np.array([qdd]))[0] + np.testing.assert_allclose(np.asarray(tau).reshape(-1), [mech.inverse_dynamics(q, qdd)], + atol=1e-12, rtol=1e-12) + + +def test_forward_dynamics_and_aba_match_closed_form(reference): + for q, qd, tau in CASES: + expected = [mech.forward_dynamics(q, tau)] + np.testing.assert_allclose(np.asarray(reference.forward_dynamics(np.array([q]), np.array([qd]), + np.array([tau]))).reshape(-1), + expected, atol=1e-10, rtol=1e-10) + np.testing.assert_allclose(np.asarray(reference.aba(np.array([q]), np.array([qd]), + np.array([tau]))).reshape(-1), + expected, atol=1e-10, rtol=1e-10) + + +def test_gravity_torque_is_the_potential_derivative(): + for q, _, _ in CASES: + h = 1e-6 + def potential(x): + from scipy.spatial.transform import Rotation + total = 0. + for mass, com, slope, offset in mech.BODIES: + p = mech.ORIGIN_R @ Rotation.from_rotvec(mech.AXIS * (slope * x + offset)).apply(com) + total += mass * mech.GRAVITY * p[2] + return total + np.testing.assert_allclose(mech.gravity_torque(q), (potential(q + h) - potential(q - h)) / (2 * h), + atol=1e-8, rtol=1e-8) diff --git a/test/test_split_partition.py b/test/test_split_partition.py index a6b188cd..b62a01c2 100644 --- a/test/test_split_partition.py +++ b/test/test_split_partition.py @@ -26,6 +26,13 @@ def test_installed_numerics_helper_is_fingerprinted_and_warmed(): assert '"${REPO_ROOT}/tools/check_installed_numerics.py"' in source +def test_consumer_migration_example_is_fingerprinted(): + path = "bindings/examples/consumer_artifact_migration.py" + assert path in rss.wrapper_fingerprint_paths("test_consumer_artifact") + assert path in (rss.REPO_ROOT / "pyproject.toml").read_text() + assert path in (rss.REPO_ROOT / "test/gpu-proof-policy-release.yaml").read_text() + + @pytest.fixture(autouse=True) def unchanged_oracle_for_synthetic_receipts(monkeypatch): # Synthetic receipts below use fictitious commit SHAs; isolate their @@ -863,6 +870,20 @@ def test_fingerprint_follows_named_runners_and_local_helpers(): assert "test/cuda_equivalents/executable_cache.py" not in narrow +def test_manifold_and_descriptor_oracles_are_in_narrow_gpu_scope(): + """The descriptor, manifold and constant-model oracles are helper modules under + test/cuda_equivalents, so the helper-import rule fingerprints them into every + shard that imports them; no test module is imported for its helpers.""" + from test import run_split_suite as R + paths=R._shard_fingerprint_paths({"test_cuda_model_primitives"}) + for helper in ("descriptor_oracle", "manifold_oracle", "constant_model"): + assert f"test/cuda_equivalents/{helper}.py" in paths + assert not [p for p in paths if p.startswith("test/test_")] + for module, helper in (("test_cuda_joint_transform_suffix", "descriptor_oracle"), + ("test_cuda_spherical_plant_hessian", "manifold_oracle")): + assert f"test/cuda_equivalents/{helper}.py" in R._shard_fingerprint_paths({module}) + + def test_resume_prunes_dirty_recorded_clean_rows(tmp_path): """2026-10-10: a clean resume row whose per-shard receipt recorded repo.dirty re-runs (the merge would fold the dirty flag into the merged @@ -924,12 +945,11 @@ def test_driver_import_puts_repo_root_ahead_of_stdlib_test_package(tmp_path): def test_plan_refresh_uses_the_real_plan_carry(tmp_path, monkeypatch): - """Integration with pytest-gpu-proof's OWN `plan_carry` (0.4.1): the base + """Integration with pytest-gpu-proof's OWN `plan_carry`: the base verdicts plan_refresh consumes come from the tool against a real git repo, so a clean shard carries, a touched test file stales exactly that shard, and a dirty recording tree stales everything.""" import subprocess - pytest.importorskip("pytest_gpu_proof") # needs CPython 3.11 or newer from pytest_gpu_proof.fingerprint import compute_fingerprint from pytest_gpu_proof.merge import plan_carry diff --git a/test/test_store_publication.py b/test/test_store_publication.py new file mode 100644 index 00000000..80a33aed --- /dev/null +++ b/test/test_store_publication.py @@ -0,0 +1,190 @@ +"""Publication failures and synchronized same-process cache writers, CPU only.""" +from concurrent.futures import ThreadPoolExecutor +import errno +import json +import multiprocessing +import os +import threading + +import pytest + +from grid_rbd import _store + + +def _stage(root, value): + stage = _store.staging_dir(root, "same-key") + (stage / "grid.cuh").write_text("header") + (stage / "robot.so").write_text(value) + (stage / "meta.json").write_text(json.dumps({"value": value})) + return stage + + +def test_same_process_staging_is_private(tmp_path): + a = _stage(tmp_path, "a") + b = _stage(tmp_path, "b") + assert a != b and (a / "robot.so").read_text() == "a" + + +@pytest.mark.parametrize("existing", ["missing", "partial", "bad-json"]) +def test_concurrent_repair_adopts_only_one_complete_winner(tmp_path, existing): + entry = tmp_path / "key" + if existing != "missing": + entry.mkdir() + (entry / "meta.json").write_text("{" if existing == "bad-json" else "{}") + a, b = _stage(tmp_path, "a"), _stage(tmp_path, "b") + barrier = threading.Barrier(2) + + def publish(stage): + barrier.wait(timeout=10) + return _store.publish_entry(stage, entry, "robot.so") + + with ThreadPoolExecutor(2) as pool: + results = list(pool.map(publish, [a, b])) + assert results[0] == results[1] == _store.entry_metadata(entry, "robot.so") + assert (entry / "robot.so").read_text() == results[0]["value"] + assert not list(tmp_path.glob(".replaced_*")) + + +def test_publish_io_failure_restores_previous_entry(tmp_path, monkeypatch): + entry = _stage(tmp_path, "old") + stage = _stage(tmp_path, "new") + original = _store.os.replace + + def fail(src, dst): + if src == stage: + raise PermissionError(errno.EACCES, "injected failure") + return original(src, dst) + + monkeypatch.setattr(_store.os, "replace", fail) + with pytest.raises(PermissionError, match="injected"): + _store.publish_entry(stage, entry, "robot.so", force=True) + assert _store.entry_metadata(entry, "robot.so") == {"value": "old"} + assert (stage / "robot.so").read_text() == "new" + + +def test_object_publish_uses_unique_thread_temporaries(tmp_path, monkeypatch): + a, b = tmp_path / "a.o", tmp_path / "b.o" + a.write_bytes(b"object") + b.write_bytes(b"object") + cached = tmp_path / "cached.o" + original = _store.os.replace + barrier = threading.Barrier(2) + names = [] + + def synchronized(src, dst): + names.append(src) + barrier.wait(timeout=10) + return original(src, dst) + + monkeypatch.setattr(_store.os, "replace", synchronized) + with ThreadPoolExecutor(2) as pool: + list(pool.map(lambda obj: _store.publish_object(obj, cached), [a, b])) + assert len(set(names)) == 2 + assert cached.read_bytes() == b"object" and not list(tmp_path.glob("*.tmp")) + + +def _process_publish(root, value, barrier, results): + """Top-level target also exercises separately imported stores under spawn.""" + stage = _stage(root, value) + barrier.wait(timeout=20) + meta = _store.publish_entry(stage, root / "key", "robot.so") + results.put(meta) + + +@pytest.mark.parametrize("existing", ["missing", "partial", "bad-json"]) +def test_process_repair_shares_the_external_lock_inode(tmp_path, existing): + entry = tmp_path / "key" + if existing != "missing": + entry.mkdir() + (entry / "meta.json").write_text("{" if existing == "bad-json" else "{}") + ctx = multiprocessing.get_context("spawn") + barrier, results = ctx.Barrier(2), ctx.Queue() + workers = [ctx.Process(target=_process_publish, args=(tmp_path, value, barrier, results)) + for value in ("a", "b")] + try: + for worker in workers: + worker.start() + values = [results.get(timeout=30) for _ in workers] + for worker in workers: + worker.join(timeout=20) + assert worker.exitcode == 0 + assert values[0] == values[1] == _store.entry_metadata(entry, "robot.so") + assert (entry / "robot.so").read_text() == values[0]["value"] + assert not list(tmp_path.glob(".replaced_*")) + finally: + for worker in workers: + if worker.pid is not None and worker.is_alive(): + worker.terminate() # only this test's own child + worker.join(timeout=10) + results.close() + + +@pytest.mark.parametrize("empty", ["robot.so", "grid.cuh"]) +def test_empty_runtime_file_is_not_a_complete_entry(tmp_path, empty): + stage = _stage(tmp_path, "valid") + (stage / empty).write_bytes(b"") + assert _store.entry_metadata(stage, "robot.so") is None + with pytest.raises(RuntimeError, match="incomplete artifact"): + _store.publish_entry(stage, tmp_path / "key", "robot.so") + assert not (tmp_path / "key").exists() + + +def _interrupted_publish(root, checkpoint): + """Abrupt death while holding the entry lock: no Python finally/rollback.""" + entry = root / "key" + staged = _stage(root, "interrupted") + replace = _store.os.replace + + def interrupt(src, dst): + if src == staged and checkpoint == "before-install": + os._exit(73) + replace(src, dst) + if src == staged and checkpoint == "after-install": + os._exit(73) + + _store.os.replace = interrupt + _store.publish_entry(staged, entry, "robot.so", force=True) + + +@pytest.mark.parametrize("checkpoint", ["before-install", "after-install"]) +@pytest.mark.parametrize("existing", ["complete", "damaged"]) +def test_retry_after_process_death_releases_lock_and_preserves_objects(tmp_path, checkpoint, existing): + entry = tmp_path / "key" + _stage(tmp_path, "old").rename(entry) + if existing == "damaged": + (entry / "robot.so").unlink() + objects = tmp_path / "objects" + objects.mkdir() + cached = objects / "retained.o" + cached.write_bytes(b"reusable object") + ctx = multiprocessing.get_context("spawn") + worker = ctx.Process(target=_interrupted_publish, args=(tmp_path, checkpoint)) + try: + worker.start() + worker.join(timeout=20) + assert worker.exitcode == 73 + finally: + if worker.pid is not None and worker.is_alive(): + worker.terminate() + worker.join(timeout=10) + # A crash can leave a backup and a temporarily absent entry. It must not + # advertise a partial result or strand the OS lock. Retrying repairs it; + # no scavenging/deletion of another process's backup is required. + observed = _store.entry_metadata(entry, "robot.so") + assert observed == ({"value": "interrupted"} if checkpoint == "after-install" else None) + results, barrier = ctx.Queue(), ctx.Barrier(1) + retry = ctx.Process(target=_process_publish, args=(tmp_path, "retry", barrier, results)) + try: + retry.start() + winner = results.get(timeout=30) + retry.join(timeout=20) + assert retry.exitcode == 0 + assert winner == {"value": "interrupted" if checkpoint == "after-install" else "retry"} + assert (entry / "robot.so").read_text() == winner["value"] + assert cached.read_bytes() == b"reusable object" + assert len(list(tmp_path.glob(".replaced_*"))) == 1 + finally: + if retry.pid is not None and retry.is_alive(): + retry.terminate() + retry.join(timeout=10) + results.close() diff --git a/test/test_topology_helpers_count.py b/test/test_topology_helpers_count.py index e935f094..06cf06a9 100644 --- a/test/test_topology_helpers_count.py +++ b/test/test_topology_helpers_count.py @@ -39,7 +39,7 @@ def _gen(tmp_path, name, floating, debug): def _expected_count(robot): n = max(robot.get_num_pos(), robot.get_num_joints()) count = 0 if robot.is_serial_chain() else 5 * n + 1 - if not robot.are_Ss_identical(list(range(robot.get_num_pos()))): + if not robot.are_Ss_identical(list(range(robot.get_num_joints()))): count += n return count diff --git a/test/test_urdf_oracle.py b/test/test_urdf_oracle.py index 698fe4b4..72dda0a4 100644 --- a/test/test_urdf_oracle.py +++ b/test/test_urdf_oracle.py @@ -37,7 +37,8 @@ def _parse(path): def _pin_joint_of_body(model, link_name): - return model.frames[model.getFrameId(link_name)].parentJoint + # URDF joint and link names occupy distinct namespaces and may coincide. + return model.frames[model.getFrameId(link_name, pin.FrameType.BODY)].parentJoint def _snapped_pin_model(path): diff --git a/tools/check_installed_package.py b/tools/check_installed_package.py index 66235b7c..feaf8cf2 100644 --- a/tools/check_installed_package.py +++ b/tools/check_installed_package.py @@ -57,6 +57,8 @@ def main(): urdf = args.work / 'probe.urdf' urdf.write_text(URDF) robot = URDFParser.URDFParser().parse(str(urdf)) + descriptor = grid_rbd.describe_model(urdf) + assert descriptor['nq'] == robot.get_num_pos() and descriptor['nv'] == robot.get_num_vel() if not args.reload: grid_codegen.GRiDCodeGenerator(robot).gen_all_code( algorithm_list=['inverse_dynamics', 'inverse_dynamics_gradient'], @@ -83,6 +85,10 @@ def forbidden(*a, **k): expected = np.stack([ref.inverse_dynamics(a, b, c)[0] for a,b,c in zip(q,qd,qdd)]).reshape(2,1) handles = {b: grid_rbd.register_robot('package_probe', backend=b, **options) for b in ('numpy', 'jax', 'torch')} + for handle in handles.values(): + assert handle.model_descriptor == descriptor + handle.model_descriptor['model_name'] = 'mutated caller copy' + assert handle.model_descriptor == descriptor value = handles['numpy'].inverse_dynamics(q, qd, qdd) np.testing.assert_allclose(value, expected, rtol=2e-5, atol=2e-6) jvalue = handles['jax'].inverse_dynamics(jnp.asarray(q), jnp.asarray(qd), jnp.asarray(qdd))