Repository navigation
Commit e5abf12
committed
fix: broken docstring examples in Twist2/Twist3/DualQuaternion, and the root cause they shared
Found via the sphinx-pyrunblock rollout (PR #221): several `.. runblock::`
docstring examples were silently baking broken tracebacks into the
published docs instead of demonstrating real output.
## Twist2.unit() / Twist3.unit()
Branches were swapped (comments didn't match what the code under them
did), `self.w` had a `S.w` typo (undefined name), and the "prismatic"
branch passed a wrong-shape zero argument for Twist2 (3-element list
where a scalar is required). Fixed the branch logic, and use `abs()`
instead of `smb.norm()` for Twist2's scalar `w`.
## Twist2.pole
The docstring's own example called `S.pole()`, but `pole` is a
`@property`, not a method -- `TypeError: 'numpy.ndarray' object is not
callable`. (Twist3.pole's own example was already correct; only its
prose incorrectly said `X.pole()` too.)
## DualQuaternion: a shared root cause across three methods
Fixing `DualQuaternion.SE3()`'s docstring (called `d.T` instead of the
actual `d.SE3()` method) surfaced a real bug: `UnitQuaternion.conj()`
silently re-canonicalized its result's sign (forces scalar part >= 0),
which is correct for constructing a `UnitQuaternion` from arbitrary data
(q and -q are the same rotation) but wrong for conjugation, an algebraic
operation that must satisfy q*conj(q) == 1 for downstream algebra to be
correct.
Fixed `Quaternion.conj()` itself (spatialmath/quaternion.py) rather than
routing around it at each call site -- this is what "conjugate" should
mean regardless of caller. No existing test asserted the old
(re-canonicalizing) behaviour, only the return type.
This one fix resolved three separate, previously-untested DualQuaternion
bugs at once:
- `SE3()`: silently returned a transform with negated translation for
any rotation with negative quaternion scalar part.
- `norm()`: crashed outright (`math domain error`) for the same reason
-- also added a small floating-point clamp, since the norm-squared
terms are mathematically non-negative but rounding can leave e.g.
-1e-17 instead of exactly 0.
- Vector transformation (`dq * v`): found while re-verifying `norm()`'s
fix -- unrelated to the conj() sign bug, this affected *every* case
regardless of sign. The textbook q*P*conj(q) sandwich product's
translation terms cancel to exactly zero under this class's own
dual-part embedding convention (`__init__` builds `dual =
0.5*Pure(t)*real`, translation on the left) -- silently applying only
the rotation and dropping translation entirely. Since `SE3()` already
correctly extracts (R, t) from this same embedding, reused it instead
of hand-deriving a second, convention-specific sandwich formula.
## Test coverage
All of the above had zero prior test coverage (existing DualQuaternion
tests used SE3.Rx(pi/4) -- no translation, positive quaternion scalar
part -- which can't exercise any of these bugs). Added regression tests
for conj()'s sign behaviour (including a mixed-sign multi-valued case),
both branches of Twist2/Twist3.unit(), Twist2/Twist3.pole, and
DualQuaternion's SE3()/norm()/vector-transform with both a
negative-quaternion-scalar case and a general case.
Full suite: 334 passed (up from 326), 0 regressions. Real sphinx-build
verification (isolated venv, before/after): RUNBLOCK-ERROR count 6 -> 0,
warning count unchanged at 14 (confirmed stable across repeat builds of
both branches, ruling out build-to-build nondeterminism).1 parent f6a572c commit e5abf12
6 files changed
Lines changed: 202 additions & 21 deletions
File tree
- spatialmath
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
120 | | - | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
121 | 125 | | |
122 | 126 | | |
123 | 127 | | |
| |||
208 | 212 | | |
209 | 213 | | |
210 | 214 | | |
211 | | - | |
212 | | - | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
213 | 227 | | |
214 | 228 | | |
215 | 229 | | |
| |||
343 | 357 | | |
344 | 358 | | |
345 | 359 | | |
346 | | - | |
| 360 | + | |
347 | 361 | | |
348 | 362 | | |
349 | 363 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
306 | 306 | | |
307 | 307 | | |
308 | 308 | | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
309 | 325 | | |
310 | 326 | | |
311 | 327 | | |
312 | 328 | | |
313 | | - | |
| 329 | + | |
| 330 | + | |
314 | 331 | | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
315 | 335 | | |
316 | 336 | | |
317 | 337 | | |
318 | | - | |
319 | | - | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
320 | 356 | | |
321 | 357 | | |
322 | 358 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
813 | 813 | | |
814 | 814 | | |
815 | 815 | | |
816 | | - | |
| 816 | + | |
817 | 817 | | |
818 | 818 | | |
819 | 819 | | |
| |||
825 | 825 | | |
826 | 826 | | |
827 | 827 | | |
828 | | - | |
829 | | - | |
830 | | - | |
831 | | - | |
832 | | - | |
| 828 | + | |
| 829 | + | |
833 | 830 | | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
834 | 834 | | |
835 | 835 | | |
836 | 836 | | |
| |||
974 | 974 | | |
975 | 975 | | |
976 | 976 | | |
977 | | - | |
| 977 | + | |
978 | 978 | | |
979 | 979 | | |
980 | 980 | | |
| |||
1487 | 1487 | | |
1488 | 1488 | | |
1489 | 1489 | | |
1490 | | - | |
| 1490 | + | |
1491 | 1491 | | |
1492 | 1492 | | |
1493 | 1493 | | |
| |||
1497 | 1497 | | |
1498 | 1498 | | |
1499 | 1499 | | |
1500 | | - | |
| 1500 | + | |
1501 | 1501 | | |
1502 | 1502 | | |
1503 | 1503 | | |
| |||
1626 | 1626 | | |
1627 | 1627 | | |
1628 | 1628 | | |
1629 | | - | |
1630 | | - | |
1631 | | - | |
| 1629 | + | |
| 1630 | + | |
| 1631 | + | |
1632 | 1632 | | |
1633 | | - | |
1634 | | - | |
| 1633 | + | |
| 1634 | + | |
1635 | 1635 | | |
1636 | 1636 | | |
1637 | 1637 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
84 | 84 | | |
85 | 85 | | |
86 | 86 | | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
87 | 102 | | |
88 | 103 | | |
89 | 104 | | |
90 | 105 | | |
91 | 106 | | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
92 | 117 | | |
93 | 118 | | |
94 | 119 | | |
| |||
101 | 126 | | |
102 | 127 | | |
103 | 128 | | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
104 | 151 | | |
105 | 152 | | |
106 | 153 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
380 | 380 | | |
381 | 381 | | |
382 | 382 | | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
383 | 419 | | |
384 | 420 | | |
385 | 421 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
213 | 213 | | |
214 | 214 | | |
215 | 215 | | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
216 | 238 | | |
217 | 239 | | |
218 | 240 | | |
| |||
375 | 397 | | |
376 | 398 | | |
377 | 399 | | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
378 | 426 | | |
379 | 427 | | |
380 | 428 | | |
| |||
0 commit comments