Metadata-Version: 2.4
Name: articulatearena
Version: 0.1.0
Summary: A quotient metric on articulated kinematics via Lie-algebra endpoint twists
Author-email: Yumeng He <heyumeng0928@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://heyumeng.com/ArticulateArena-web/
Project-URL: Documentation, https://heyumeng.com/ArticulateArena-web/docs.html
Project-URL: Repository, https://github.com/YumengHe/ArticulateMetrics
Project-URL: Issues, https://github.com/YumengHe/ArticulateMetrics/issues
Project-URL: Paper, https://arxiv.org/abs/2609.33931
Keywords: articulated objects,kinematics,metric,evaluation,URDF,se(3),screw theory,robotics
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.20.0
Requires-Dist: scipy>=1.7.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: mesh
Requires-Dist: trimesh>=3.9.0; extra == "mesh"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Dynamic: license-file

# ArticulateArena

[![PyPI](https://img.shields.io/pypi/v/articulatearena.svg)](https://pypi.org/project/articulatearena/)
[![Python](https://img.shields.io/pypi/pyversions/articulatearena.svg)](https://pypi.org/project/articulatearena/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/YumengHe/ArticulateMetrics/blob/main/LICENSE)
[![arXiv](https://img.shields.io/badge/arXiv-2609.33931-b31b1b.svg)](https://arxiv.org/abs/2609.33931)

A quotient metric on articulated kinematics via Lie-algebra endpoint twists.

`articulatearena` is the reference implementation of the metric **E** from the
paper *ArticulateArena: A Metric for Articulated Kinematics*. It gives a single
principled distance between a predicted articulated object and its ground
truth, in place of the hand-combined type / axis / origin / limit scores used by
earlier evaluation protocols.

A one-degree-of-freedom joint J = (ξ, [l⁻, l⁺]) with unit screw twist
ξ ∈ se(3) and limits l⁻, l⁺ is represented by its **unordered pair of endpoint
twists** {l⁻ξ, l⁺ξ}. The joint metric is the quotient distance of
se(3) × se(3) under the swap of the two endpoints:

```
E(J1, J2) = min{ ‖z1⁻ − z2⁻‖² + ‖z1⁺ − z2⁺‖²,  ‖z1⁻ − z2⁺‖² + ‖z1⁺ − z2⁻‖² }^(1/2)
```

where ‖·‖ is an se(3) norm that the caller injects. Because it is a quotient of
a flat space by a finite group of isometries, E is a genuine metric (symmetric,
with the triangle inequality), it is invariant to the sign of the axis and to
which limit is called "lower", and it is zero exactly when the two joints
produce the same motion.

The package provides:

- the joint metric `E` and a smooth, swap-invariant training surrogate;
- three injected se(3) norms: the dimensionless **split norm** (E_α), the
  **kinetic-energy norm** of the moving link (E_B, in meters), and a general
  Riemannian norm;
- the **radial compactification** ϕ that extends E continuously to unbounded
  (continuous) joints (E_ϕ);
- two object-level lifts: an optimal-assignment **skeleton distance** with a
  topology penalty, and a motion-aware **tree edit distance** (E_tree) with a
  certified assignment relaxation;
- loaders for YAML configs and URDF files (axes resolved to the world frame by
  forward kinematics), a JSON / Markdown report writer, and a command-line tool;
- the component scores of the earlier evaluation template, kept for baseline
  comparisons.

Links: [project page](https://heyumeng.com/ArticulateArena-web/) ·
[documentation](https://heyumeng.com/ArticulateArena-web/docs.html) ·
[paper](https://arxiv.org/abs/2609.33931) ·
[source](https://github.com/YumengHe/ArticulateMetrics)

## Installation

```bash
pip install articulatearena
```

Python 3.10 or newer. The core depends only on `numpy`, `scipy` and `pyyaml`.
Two optional extras exist:

```bash
pip install "articulatearena[mesh]"   # trimesh, for mesh-derived link inertia (kinetic-energy norm)
pip install "articulatearena[dev]"    # pytest, build, twine
```

From a source checkout:

```bash
git clone https://github.com/YumengHe/ArticulateMetrics.git
cd ArticulateMetrics
pip install -e ".[dev]"
pytest
```

## Quickstart

Build the endpoint pairs of two joints, pick an se(3) norm, and evaluate
`joint_metric`. The distance is `0` exactly when the two joints induce the same
motion.

```python
import numpy as np
from articulatearena import joint_metric, make_endpoint_pair, make_split_norm

xi = np.array([0.0, 0.0, 1.0, 0.0, 0.0, 0.0])   # unit screw twist (omega; nu)
gt = make_endpoint_pair(xi, 0.0, 1.0)             # joint limits [a, b]
pred = make_endpoint_pair(xi, 0.0, 1.2)

norm = make_split_norm(alpha=1.0)                 # the norm is injected, never hardcoded
E = joint_metric(gt.u.data, gt.v.data, pred.u.data, pred.v.data, norm)
print(E)   # 0.2 (only the upper endpoint moved by 0.2 along the axis)
```

Reversing the axis and negating the limits describes the same motion, and E
sees that without any special casing:

```python
flipped = make_endpoint_pair(-xi, -1.0, 0.0)
print(joint_metric(gt.u.data, gt.v.data, flipped.u.data, flipped.v.data, norm))   # 0.0
```

To score real URDF joints, load them through the reader layer instead of
constructing twists by hand. The loader resolves each joint's axis into the
world frame with forward kinematics at the zero configuration:

```python
from articulatearena import load_joint, joint_to_endpoint_pair

joint = load_joint("sample_data/45168/mobility.urdf", "joint_0")
pair = joint_to_endpoint_pair(joint)              # unordered {a*xi, b*xi}
```

Continuous (unbounded) joints have no finite endpoint pair. Compare them, or
mix them with bounded joints, through the compactified metric E_ϕ:

```python
import math
from articulatearena import compactified_joint_metric, continuous_joint_repr, radial_compactify

kappa = math.pi                                   # benchmark setting

# two bounded joints: E after mapping all four endpoints into the unit ball
E_phi = compactified_joint_metric(gt.u.data, gt.v.data, pred.u.data, pred.v.data, norm, kappa)

# a bounded joint against a continuous joint about the same axis
gt_ball = [radial_compactify(z, kappa, norm) for z in (gt.u.data, gt.v.data)]
u2, v2 = continuous_joint_repr(xi, norm)          # antipodal boundary pair, already in the closed ball
E_phi = joint_metric(*gt_ball, u2, v2, norm)
```

Whole kinematic trees are compared with the assignment relaxation of the tree
edit distance, `assignment_tree_distance`, or the exact `exact_tree_distance`
when a certificate is wanted. See the
[documentation](https://heyumeng.com/ArticulateArena-web/docs.html) for the
full API and a tutorial on the underlying concepts.

## Command line

The `articulatearena` command loads a config, runs the pure metric functions
and writes a JSON report:

```bash
articulatearena                                  # built-in demo joints, report under ./out
articulatearena configs/default.yaml             # the URDF case named in the config (run from the repo root)
articulatearena my_config.yaml --data joints.json --output out
```

`--data` points at a JSON file with top-level `prediction` and `ground_truth`
lists, each joint being `{"xi_hat": [6 numbers], "a": lower, "b": upper}`.

The config groups the knobs that the paper treats as caller-supplied
(`inner_product.form` and `split_norm_alpha`, `compactification.kappa`,
`skeleton.lambda_topology`, the aggregation form and its smoothing ε) plus an
optional evaluation case (`prediction` / `ground_truth`, each a URDF path and a
joint name). The packaged default lives at
[`src/articulatearena/configs/default.yaml`](https://github.com/YumengHe/ArticulateMetrics/blob/main/src/articulatearena/configs/default.yaml).
Anything left `null` has to be provided before use. `split_norm_alpha` is a
characteristic inverse length. Set it to match your length units before
comparing across methods.

In a source checkout the same entry point runs without installing:

```bash
python scripts/run.py configs/default.yaml
```

## Package layout

```
articulatearena/
  types.py, config.py, validation.py   # typed data models, flat Config, input checks
  new_equation/                        # the paper's formulas
    representation.py                  #   screw twist, endpoint pair {a*xi, b*xi}, revolute wrap
    E.py                               #   joint_metric E and the smooth training_loss
    inner_product.py                   #   split / kinetic-energy / Riemannian norms
    continuous.py                      #   radial compactification, continuous joints, E_phi
    tree.py                            #   motion-aware tree edit distance and its relaxation
    skeleton.py                        #   assignment skeleton distance with topology penalty
    material_motion.py                 #   finite-displacement material discrepancy (calibration)
  general_equation/                    # background math: se(3) exp map, Hungarian assignment,
                                       #   Zhang-Shasha tree edit, L2 configuration difference
  previous_metrics/                    # baseline component scores of the earlier template
  read/                                # config, URDF, evaluation-case and mesh-inertia loaders
  write/                               # JSON / Markdown report generation
  cli.py                               # the `articulatearena` command
  configs/default.yaml                 # packaged default configuration
```

All metric functions are pure: norms, weights and tolerances are passed in by
the caller and never hardcoded, and nothing in the metric layer reads or writes
files.

## Scope and conventions

- One-DOF joints only: revolute, prismatic, continuous and helical. Multi-DOF
  joints are out of scope.
- Twists are `(6,)` float64 arrays with the angular part first. Revolute and
  helical joints use a unit angular part, prismatic joints a unit linear part.
- Coordinate frames and units are the caller's responsibility. All inputs
  must share one reference frame, and `alpha` must match the length unit.
- `E` is not normalized. Only compare values produced under the same norm and
  the same `alpha` or inertia.

## Repository contents beyond the package

The [GitHub repository](https://github.com/YumengHe/ArticulateMetrics) also
holds material that is not part of the wheel:

- [`examples/`](https://github.com/YumengHe/ArticulateMetrics/tree/main/examples):
  end-to-end runs on real PartNet-Mobility objects.
- [`scripts/`](https://github.com/YumengHe/ArticulateMetrics/tree/main/scripts):
  the benchmark driver for the baseline component scores and a generator of
  controlled error cases.
- [`render/`](https://github.com/YumengHe/ArticulateMetrics/tree/main/render):
  URDF and joint-motion renderers used for the figures (see its README).
- [`ablation/`](https://github.com/YumengHe/ArticulateMetrics/tree/main/ablation):
  the parameter ablations of the paper.
- [`docs/IMPLEMENTATION.md`](https://github.com/YumengHe/ArticulateMetrics/blob/main/docs/IMPLEMENTATION.md):
  formula coverage and test strategy, and
  [`docs/RELEASING.md`](https://github.com/YumengHe/ArticulateMetrics/blob/main/docs/RELEASING.md)
  for the release procedure.

## Tests

```bash
pytest
```

Every formula has a dedicated test class with hand-computed values, metric
properties (identity, symmetry, triangle inequality, swap invariance) and edge
cases. `examples/run_real_cases.py` adds real-data invariants: zero
self-distance, zero assignment cost against an identical copy, and monotone
growth of E under axis perturbation.

## Citation

```bibtex
@article{he2026articulatearena,
  title   = {ArticulateArena: A Metric for Articulated Kinematics},
  author  = {He, Yumeng and She, Yongfei and Chen, Huanyu and Yuan, Chun and Li, Peihao
             and Masterjohn, Joseph and Yang, Yin and Jiang, Ying and Jiang, Chenfanfu},
  journal = {arXiv preprint arXiv:2609.33931},
  year    = {2026}
}
```

## License

MIT. See [LICENSE](https://github.com/YumengHe/ArticulateMetrics/blob/main/LICENSE).
