fix(mv): is_blade() now correctly handles null vectors and null blades#582
Conversation
Previously is_blade() delegated entirely to is_versor(), which returns False for null multivectors (they have no inverse). This caused grade-1 null vectors like e0+e1 in G(1,1), and grade-2 null blades like (e0+e1)^e2 in G(1,2), to be incorrectly reported as non-blades. Blade-ness is a metric-free concept; the fix: - Grade 0 and 1: always blades (no metric required) - Grade >= 2, non-null: is_versor() path (unchanged) - Grade >= 2, null: outer-product squaring test (B^B == 0), which is necessary for any blade and sufficient for grade 2 Closes pygae#537
There was a problem hiding this comment.
Thanks for the fix. Two issues to address before merging.
The not self.is_zero() guard lives in step 4 but the zero scalar never reaches it: i_grade = 0 is caught by i_grade <= 1 at step 2, returning True. Current behavior is False for mv(0).is_blade(). If zero should stay excluded (standard convention), the guard needs to be before step 2.
reflect_in_blade and project_in_blade gate on is_blade() then immediately compute / blade.qform(). For null blades, qform() is zero, so they'll divide by zero after this fix. Before, is_blade() = False for null blades effectively protected those methods. They need a null-blade guard now.
Also the test comment "null → no inverse → not a versor" is slightly off — galgebra's is_versor() checks V*V.rev() = 0, not the existence of an inverse. Minor but worth fixing.
- Move zero check before grade check to correctly exclude zero multivectors - Add null-blade guards to reflect_in_blade() and project_in_blade() - Update algorithm documentation to reflect new step ordering - All tests pass (35/35)
|
On the grade ≥ 3 limitation: the docstring says "sufficiently high dimension" but it's worth being concrete. The standard counterexample in R⁶ (Dorst, Fontijne & Mann, Geometric Algebra for Computer Science, 2007):
|
…rexample Adds a test pinning the known false positive for is_blade() in grade >= 3 with sufficiently high dimensions (R⁶). The Dorst/Fontijne/Mann counterexample satisfies (B^B).is_zero() but is not actually a 3-blade. This documents the known limitation as a test with a clear comment.
47e844a to
11d01b3
Compare
The test was incorrect on two counts: - is_blade() correctly returns False for the DFM counterexample in R^6 because B^B != 0 there (the outer-product test works fine) - The computation took ~32 minutes, making CI unusable The grade >= 3 limitation is documented in the is_blade() docstring only.
utensil
left a comment
There was a problem hiding this comment.
Thanks for addressing the feedback. Zero guard is correctly placed, reflect/project guards look good, and the test comment is accurate now.
Summary
is_blade()previously delegated entirely tois_versor(), which returnsFalsefor null multivectors (no inverse → not a versor)e0+e1inG(1,1)and grade-2 null blades like(e0+e1)^e2inG(1,2)to be incorrectly reported as non-bladesFix
FalseTrue(scalars and vectors are always blades)is_versor()path (unchanged)B ^ B == 0(necessary for any blade; sufficient for grade 2)The docstring notes the known limitation: for grades ≥ 3 in algebras of sufficiently high dimension,
B ^ B == 0may give false positives; full factorizability is not yet implemented.Test
Added
test_is_blade_nullcovering:G(1,1)G(1,2)with null constituent vectorCloses #537