Commit 18f2ec3d authored by Adam P. Goucher's avatar Adam P. Goucher

Determine linear automorphisms of finite sets of lattice points

parent b57e4568
Pipeline #49898801 passed with stages
in 7 minutes and 16 seconds
# Determine linear automorphisms of a finite set of integer points
from itertools import combinations, permutations
def subdets(pts):
'''
Compute determinants of all (n choose d) size-d subsets of n
points in Z^d.
'''
n = len(pts)
d = len(pts[0])
s = {tuple([]): 1}
for e in range(1, d+1):
for t in combinations(range(n), e):
t = tuple(t)
sgn = 1
val = 0
for i in range(e):
subt = t[:i] + t[i+1:]
val += s[subt] * pts[t[i]][-e] * sgn
sgn = 0 - sgn
s[t] = val
return s
def adjugate_row(pts, j):
n = len(pts)
s = subdets([p[:j] + p[j+1:] for p in pts])
t = tuple(range(n))
for i in range(n):
subt = t[:i] + t[i+1:]
sgn = 1 - (((i + j) % 2) * 2)
yield s[subt] * sgn
def adjugate(mat):
'''
The inverse of a unimodular matrix
'''
return [list(adjugate_row(mat, j)) for j in range(len(mat))]
def matmulvec(m, v):
pass
def matperms(pts):
n = len(pts)
d = len(pts[0])
s = subdets(pts)
adj = None
adjt = None
basis = None
ptsset = None
for t in combinations(range(n), d):
t = tuple(t)
det = s[t]
if abs(det) == 1:
coords = [pts[i] for i in t]
if adj is None:
basis = coords
adj = adjugate(basis)
adjt = [[a[i] * det for a in adj] for i in range(d)]
# Transform by adjugate matrix:
pts = [[sum([x*y for (x, y) in zip(a, m)]) for a in adjt] for m in pts]
ptsset = {tuple(p): i for (i, p) in enumerate(pts)}
for t in permutations(t):
t = tuple(t)
coordst = [[pts[i][j] for i in t] for j in range(d)]
perm = []
for p in pts:
q = tuple([sum([x*y for (x, y) in zip(a, p)]) for a in coordst])
if q in ptsset:
perm.append(ptsset[q])
else:
break
if len(perm) == len(pts):
yield perm
......@@ -78,6 +78,12 @@ def make_tree(n_states, nhood, list_of_nodes):
return compress_tree(list_of_nodes, n_states, numInputs, seqonly=False)
def normalise_neighbourhood(nhood):
x0, y0 = nhood[-1]
return [(x - x0, y - y0) for (x, y) in nhood]
def optimise_tree(n_states, nhood, list_of_nodes):
numInputs = GetNumberOfInputs(nhood)
......@@ -118,6 +124,9 @@ def optimise_tree(n_states, nhood, list_of_nodes):
# Reoptimise:
list_of_nodes = compress_tree(list_of_nodes, n_states, numInputs)
if len(nhood) == numInputs + 1:
nhood = normalise_neighbourhood(nhood)
return n_states, nhood, list_of_nodes
......
from .parsetable import GetNumberOfInputs
def normalise_neighbourhood(nhood):
x0, y0 = nhood[-1]
return [(x - x0, y - y0) for (x, y) in nhood]
from .parsetree import normalise_neighbourhood
def NodesToCode(nhood, nodes, is16bit):
......
......@@ -3,9 +3,31 @@ import os
import lifelib
import unittest
import lifelib.genera.rulefiles.parsetree as parsetree
import lifelib.genera.rulefiles.automorph as automorph
class TestTreeduce(unittest.TestCase):
def test_adjugate(self):
'''
Test Wikipedia's example of an adjugate matrix
'''
mat = [[-3, 2, -5], [-1, 0, -2], [3, -4, 1]]
adj = [[-8, 18, -4], [-5, 12, -1], [4, -6, 2]]
adj2 = automorph.adjugate(mat)
self.assertEqual(adj2, adj)
def test_matperms(self):
'''
Test ability to determine symmetry group of a cross.
'''
mp = automorph.matperms([[0, 0], [1, 0], [0, 1], [-1, 0], [0, -1]])
mp2 = [[0, 1, 2, 3, 4], [0, 2, 1, 4, 3], [0, 1, 4, 3, 2], [0, 4, 1, 2, 3],
[0, 2, 3, 4, 1], [0, 3, 2, 1, 4], [0, 3, 4, 1, 2], [0, 4, 3, 2, 1]]
self.assertEqual(set(map(tuple, mp)), set(map(tuple, mp2)))
def test_reduce(self):
'''
Tests that we can recognise an hexagonal rule embedded as a Moore CA.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment