Commit 45ed832
authored
feat(pose,quaternion): add __imatmul__ (@=) operator (#199)
## What
`X @= Y` now works as the augmented-assignment form of the existing `X @
Y`
(`__matmul__`, which composes with normalization) — previously only `X
*= Y`
existed, which composes *without* normalization. Useful when a pose or
unit
quaternion is updated incrementally over many cycles and you want the
normalized form without writing `X = X @ Y` by hand.
Added to `BasePoseMatrix` (covers `SO2`/`SE2`/`SO3`/`SE3`) and
`Quaternion`
(covers `Quaternion`/`UnitQuaternion`).
Note on semantics: like the existing `__imul__`, this doesn't mutate the
object in place — `__imatmul__` returns a new (normalized) object and
Python
rebinds the name, same as `X = X @ Y`. That matches the existing `*=`
pattern in this codebase exactly, just saves writing `X = X @ Y`.
## A bug found while adding test coverage
This started as a cherry-pick of old, never-merged WIP work. While
writing
tests I found `Quaternion.__imatmul__`'s docstring claimed `q1 @= q2`
sets
`q1 := qnorm(q1 * q2)`, but the implementation just delegated to
`__mul__`
— identical to plain `*=`, no normalization at all, contradicting both
the
docstring and the entire point of adding `@=`.
`UnitQuaternion.__matmul__` (pre-existing, unchanged) already normalizes
correctly via `smb.qunit(smb.qqmul(x, y))` — `qunit` being the actual
normalizer; `qnorm` just returns the scalar magnitude, so it was never
really the right function despite the docstring's wording.
Fixed by having `__imatmul__` delegate to `left @ right` instead of
`left.__mul__(right)`. Deliberately not `left.__matmul__(right)` either:
plain `Quaternion` has no `__matmul__` (only `UnitQuaternion` defines
one),
and calling the dunder directly as a plain attribute bypasses Python's
normal operator fallback, raising a confusing `AttributeError` instead
of
the `TypeError` that `q1 @ q2` already raises consistently for plain
`Quaternion`. `left @ right` matches `@`'s behaviour exactly in both
cases.
Also fixed the docstring's `-> bool` return type annotation (should be
`-> Quaternion`) and its example, which called `Quaternion.Eul()` — a
method that only exists on `UnitQuaternion`.
## Testing
- Added `@=` coverage for `SO3`/`SE3` (must match `@`) alongside the
existing `*=` tests in `test_pose3d.py`.
- Added `@=` coverage for `UnitQuaternion` (must match `@`, not `*`) and
for
plain `Quaternion` (must raise `TypeError`, matching `@`, not silently
degrade to `*=`) in `test_quaternion.py`.
- Full suite: 338 passed, 4 skipped.
- `black --check` clean at the pinned 23.10.0.1 parent b7e7bee commit 45ed832
4 files changed
Lines changed: 73 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1360 | 1360 | | |
1361 | 1361 | | |
1362 | 1362 | | |
| 1363 | + | |
| 1364 | + | |
| 1365 | + | |
| 1366 | + | |
| 1367 | + | |
| 1368 | + | |
| 1369 | + | |
| 1370 | + | |
| 1371 | + | |
| 1372 | + | |
| 1373 | + | |
| 1374 | + | |
| 1375 | + | |
| 1376 | + | |
1363 | 1377 | | |
1364 | 1378 | | |
1365 | 1379 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
669 | 669 | | |
670 | 670 | | |
671 | 671 | | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
672 | 706 | | |
673 | 707 | | |
674 | 708 | | |
| |||
1887 | 1921 | | |
1888 | 1922 | | |
1889 | 1923 | | |
1890 | | - | |
1891 | | - | |
1892 | 1924 | | |
1893 | 1925 | | |
1894 | 1926 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
419 | 419 | | |
420 | 420 | | |
421 | 421 | | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
422 | 427 | | |
423 | 428 | | |
424 | 429 | | |
| |||
1078 | 1083 | | |
1079 | 1084 | | |
1080 | 1085 | | |
| 1086 | + | |
| 1087 | + | |
| 1088 | + | |
| 1089 | + | |
| 1090 | + | |
| 1091 | + | |
| 1092 | + | |
1081 | 1093 | | |
1082 | 1094 | | |
1083 | 1095 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
508 | 508 | | |
509 | 509 | | |
510 | 510 | | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
511 | 516 | | |
512 | 517 | | |
513 | 518 | | |
| |||
990 | 995 | | |
991 | 996 | | |
992 | 997 | | |
| 998 | + | |
| 999 | + | |
| 1000 | + | |
| 1001 | + | |
| 1002 | + | |
| 1003 | + | |
| 1004 | + | |
| 1005 | + | |
993 | 1006 | | |
994 | 1007 | | |
995 | 1008 | | |
| |||
0 commit comments