Fix Python Cl41 blade product table
This commit is contained in:
parent
5b34b72158
commit
4fe19c08ba
1 changed files with 41 additions and 59 deletions
100
algebra/cl41.py
100
algebra/cl41.py
|
|
@ -51,32 +51,42 @@ def _all_blades():
|
|||
_BLADES = _all_blades() # index -> tuple of basis vector indices
|
||||
_BLADE_TO_IDX = {b: i for i, b in enumerate(_BLADES)}
|
||||
|
||||
def _blade_product(blade_a, blade_b):
|
||||
"""Compute geometric product of two basis blades. Returns (sign, result_blade_tuple)."""
|
||||
# Concatenate and bubble-sort, tracking sign flips and metric contractions
|
||||
seq = list(blade_a) + list(blade_b)
|
||||
|
||||
def _compute_blade_product(blade_a, blade_b):
|
||||
"""
|
||||
Compute the geometric product of two canonical basis blades.
|
||||
|
||||
For blades A=e_{a1}...e_{am} and B=e_{b1}...e_{bn}, the sign is the
|
||||
parity of swaps required to move the concatenated basis list into
|
||||
canonical order, multiplied by the metric contractions for repeated
|
||||
basis vectors. The resulting blade is the symmetric difference of the
|
||||
two blade basis sets.
|
||||
|
||||
This implementation is deliberately bit/set based rather than mutating
|
||||
a bubble-sort list while contracting; the previous list mutation path
|
||||
corrupted multi-contractions and produced an invalid multiplication
|
||||
table.
|
||||
"""
|
||||
sign = 1
|
||||
# Bubble sort to canonical order, tracking swaps
|
||||
n = len(seq)
|
||||
for i in range(n):
|
||||
for j in range(n - i - 1):
|
||||
if seq[j] > seq[j + 1]:
|
||||
seq[j], seq[j + 1] = seq[j + 1], seq[j]
|
||||
sign *= -1
|
||||
elif seq[j] == seq[j + 1]:
|
||||
# Metric contraction: e_i^2 = signature[i]
|
||||
metric = int(SIGNATURE[seq[j]])
|
||||
sign *= metric
|
||||
seq.pop(j)
|
||||
seq.pop(j) # second element now at same index after first pop
|
||||
n -= 2
|
||||
break
|
||||
else:
|
||||
continue
|
||||
break
|
||||
# After contraction there may still be duplicates — recurse
|
||||
result = tuple(sorted(set(seq))) # this is wrong for multi-contraction; use proper loop
|
||||
return sign, tuple(seq)
|
||||
|
||||
# Anticommutation sign: each pair (a_i, b_j) with a_i > b_j requires
|
||||
# one swap to canonicalize A followed by B.
|
||||
swaps = 0
|
||||
for a in blade_a:
|
||||
for b in blade_b:
|
||||
if a > b:
|
||||
swaps += 1
|
||||
if swaps % 2:
|
||||
sign *= -1
|
||||
|
||||
# Metric contractions for duplicate basis vectors.
|
||||
common = set(blade_a).intersection(blade_b)
|
||||
for idx in common:
|
||||
sign *= int(SIGNATURE[idx])
|
||||
|
||||
result_blade = tuple(sorted(set(blade_a).symmetric_difference(blade_b)))
|
||||
return sign, result_blade
|
||||
|
||||
|
||||
def _build_multiplication_table():
|
||||
"""Precompute full 32x32 geometric product table."""
|
||||
|
|
@ -86,41 +96,11 @@ def _build_multiplication_table():
|
|||
for i, blade_a in enumerate(_BLADES):
|
||||
for j, blade_b in enumerate(_BLADES):
|
||||
sign, result_blade = _compute_blade_product(blade_a, blade_b)
|
||||
result_idx = _BLADE_TO_IDX.get(result_blade, 0)
|
||||
table_idx[i, j] = result_idx
|
||||
table_idx[i, j] = _BLADE_TO_IDX[result_blade]
|
||||
table_sign[i, j] = sign
|
||||
|
||||
return table_idx, table_sign
|
||||
|
||||
def _compute_blade_product(blade_a, blade_b):
|
||||
"""Compute geometric product of two basis blades via bubble sort + metric."""
|
||||
seq = list(blade_a) + list(blade_b)
|
||||
sign = 1
|
||||
i = 0
|
||||
while i < len(seq) - 1:
|
||||
j = i
|
||||
while j < len(seq) - 1:
|
||||
if seq[j] == seq[j + 1]:
|
||||
# Contract: e_k^2 = signature[k]
|
||||
sign *= int(SIGNATURE[seq[j]])
|
||||
seq.pop(j)
|
||||
seq.pop(j)
|
||||
if j > 0:
|
||||
i = max(0, j - 1)
|
||||
break
|
||||
elif seq[j] > seq[j + 1]:
|
||||
seq[j], seq[j + 1] = seq[j + 1], seq[j]
|
||||
sign *= -1
|
||||
j += 1
|
||||
else:
|
||||
j += 1
|
||||
else:
|
||||
i += 1
|
||||
result_blade = tuple(seq)
|
||||
if result_blade not in _BLADE_TO_IDX:
|
||||
return 0, ()
|
||||
return sign, result_blade
|
||||
|
||||
_TABLE_IDX, _TABLE_SIGN = _build_multiplication_table()
|
||||
|
||||
# --- Core operations ---
|
||||
|
|
@ -131,12 +111,14 @@ def geometric_product(A: np.ndarray, B: np.ndarray) -> np.ndarray:
|
|||
B = np.asarray(B, dtype=np.float32)
|
||||
result = np.zeros(N_COMPONENTS, dtype=np.float32)
|
||||
for i in range(N_COMPONENTS):
|
||||
if A[i] == 0.0:
|
||||
ai = A[i]
|
||||
if ai == 0.0:
|
||||
continue
|
||||
for j in range(N_COMPONENTS):
|
||||
if B[j] == 0.0:
|
||||
bj = B[j]
|
||||
if bj == 0.0:
|
||||
continue
|
||||
result[_TABLE_IDX[i, j]] += _TABLE_SIGN[i, j] * A[i] * B[j]
|
||||
result[_TABLE_IDX[i, j]] += _TABLE_SIGN[i, j] * ai * bj
|
||||
return result
|
||||
|
||||
def reverse(A: np.ndarray) -> np.ndarray:
|
||||
|
|
|
|||
Loading…
Reference in a new issue