Skip to content

Instantly share code, notes, and snippets.

@GregTJ
Last active June 16, 2022 00:52
Show Gist options
  • Save GregTJ/4abd00509a26b224551d3bacc6c38033 to your computer and use it in GitHub Desktop.
Save GregTJ/4abd00509a26b224551d3bacc6c38033 to your computer and use it in GitHub Desktop.
Levi-Civita symbol in n-dimensions.
import sparse
from typing import Tuple, Generator
def sjt_permutations(n: int) -> Generator[Tuple[int], None, None]:
"""An implementation of the Steinhaus–Johnson–Trotter
algorithm with Even's speedup for generating
permutations in order of alternating parity."""
# Each element of the permutation is
# initialized with a direction.
# All elements face left initially.
yield tuple(p := list(range(n))), (parity := 1)
d = [0] + [-1] * (n - 1)
# While any element is still directional,
while any(d):
# get the greatest element with nonzero direction,
# its index, and the index of the element it faces.
c, i, j = max((p[i], i, i + j) for i, j in enumerate(d) if j)
# All undirected elements greater than c have
# their directions set to face toward c.
d = [d[k] or (p[k] > c) * ((i > k) * 2 - 1) for k in range(n)]
# Swap the directions and elements at indices i and j.
# If the swap puts element c at the start or end
# of the permutation, or if the next element in the same direction
# is greater than c, zero out c's direction instead.
d[i], d[j] = d[j], 0 if j in {0, n - 1} or p[j + d[i]] > c else d[i]
p[i], p[j] = p[j], p[i]
# Permutation parity alternates.
yield tuple(p), (parity := -parity)
def levi_civita(n: int) -> sparse.COO:
"""Generates the sparse Levi-Civita symbol in n-dimensions."""
p, v = zip(*sjt_permutations(n))
return sparse.COO(tuple(zip(*p)), v, shape=(n,) * n)
@GregTJ
Copy link
Author

GregTJ commented Apr 13, 2021

levi_civita(3).todense() output:

[ 0  0  0]
[ 0  0  1]
[ 0 -1  0]

[ 0  0 -1]
[ 0  0  0]
[ 1  0  0]

[ 0  1  0]
[-1  0  0]
[ 0  0  0]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment