Skip to content

Commit 8581e13

Browse files
committed
add isotypes
1 parent ca06ea6 commit 8581e13

File tree

2 files changed

+219
-0
lines changed

2 files changed

+219
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Some fairly standalone python scripts
99

1010
* oncampus: Should graphs be sent from a warwick computer to the screen or a file.
1111

12+
* isotypes: find the isotypical component of a tensor (as a numpy array) corresponding to a given partition
13+
1214
* shapes: function for printing the "shape" of something like a tuple of lists of numpy arrays
1315

1416
* tinisMultiRun: script for parallelizing another script across the 4 GPUs you get on tinis

isotypes.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import numpy as np
2+
from sympy.combinatorics.partitions import IntegerPartition
3+
from sympy.combinatorics.permutations import Permutation
4+
from sympy.combinatorics.generators import symmetric
5+
from sympy.utilities.iterables import partitions, permutations
6+
import sys, itertools, functools, operator, math
7+
np.set_printoptions(suppress=True)
8+
9+
#This file attempts to provide a function "project" to give an isotypical component of a tensor.
10+
#The rest of the file is just part of its implementation or testing
11+
12+
#(4,3,1) -> [[0,1,2,3],[4,5,6],[7]]
13+
def triangleOfNumbers(partition, forceList=False):
14+
a=0
15+
o=[]
16+
for i in partition:
17+
x=a+np.arange(i)
18+
if forceList:
19+
x=list(x)
20+
o.append(x)
21+
a += i
22+
return o
23+
24+
def isStandard(tableau):
25+
for row in tableau:
26+
if len(row)>1 and any(row[j-1]>row[j] for j in range(1,len(row))):
27+
return False
28+
for ncol in range(len(tableau[0])):
29+
for nrow in range(1,len(tableau)):
30+
if len(tableau[nrow])<ncol+1:
31+
break
32+
if tableau[nrow][ncol]<tableau[nrow-1][ncol]:
33+
return False
34+
return True
35+
36+
def conjugatePartition(partition):
37+
return [sum(j>i for j in partition) for i in range(partition[0])]
38+
39+
#output begins at 0
40+
#This is an inefficient but obvious way to enumerate standard Young tableaux.
41+
def enumerate_standard_Young_tableax(partition):
42+
n=sum(partition)
43+
orig = triangleOfNumbers(partition)
44+
o=[]
45+
for permutation in symmetric(n):
46+
new = [[permutation(i) for i in j] for j in orig]
47+
#new = [permutation(j) for j in orig]
48+
if isStandard(new):
49+
#yield new
50+
o.append(new)
51+
return o
52+
def enumerate_Young_tableax_and_count_standard(partition):
53+
n=sum(partition)
54+
orig = triangleOfNumbers(partition)
55+
o=[]
56+
count=0
57+
for permutation in symmetric(n):
58+
new = [[permutation(i) for i in j] for j in orig]
59+
#new = [permutation(j) for j in orig]
60+
if isStandard(new):
61+
count += 1
62+
o.append(new)
63+
return o,count
64+
65+
def getRowColumnPermutations(partition):
66+
each_row_permutation = [list(symmetric(i)) for i in partition]
67+
each_col_permutation = [list(symmetric(i)) for i in conjugatePartition(partition)]
68+
return each_row_permutation,each_col_permutation
69+
70+
#If tableau is a standard young tableau (which this function does
71+
#not check) then
72+
#return the corresponding young symmetrizer (a signed list of permutations)
73+
#as a list of tuples
74+
#This function spends a lot of its time in symmetric, which can be saved
75+
#by supplying the optional argument - this must be what this function would calculate for itself
76+
def unchecked_young_symmetrizer(tableau,
77+
each_row_col_permutation=None):
78+
if each_row_col_permutation is not None:
79+
each_row_permutation,each_column_permutation = each_row_col_permutation
80+
else:
81+
x=getRowColumnPermutations([len(i) for i in tableau])
82+
each_row_permutation,each_column_permutation = x
83+
row_permutations = []
84+
col_permutations = []
85+
for i in itertools.product(*each_row_permutation):
86+
row_transformed_tableau=[j(tableau[n]) for n,j in enumerate(i)]
87+
mapping = {i:j for I,J in zip(row_transformed_tableau,tableau) for i,j in zip(I,J)}
88+
row_permutation=Permutation([mapping[i] for i in range(len(mapping))])
89+
row_permutations.append(row_permutation)
90+
for i in itertools.product(*each_column_permutation):
91+
column_transformed_tableau=[[jj for jj in j] for j in tableau]
92+
for irow in range(len(tableau)):
93+
for icol in range(len(tableau[irow])):
94+
column_transformed_tableau[irow][icol]=tableau[i[icol](irow)][icol]
95+
mapping = {i:j for I,J in zip(column_transformed_tableau,tableau) for i,j in zip(I,J)}
96+
col_permutation=Permutation([mapping[i] for i in range(len(mapping))])
97+
col_permutations.append(col_permutation)
98+
return [(c.signature(),r*c) for r in row_permutations for c in col_permutations]
99+
100+
def unchecked_project_to_tableau(tensor,tableau,each_row_col_permutation=None):
101+
total=np.zeros_like(tensor, dtype="float64")
102+
n=tensor.ndim
103+
for sign,permutation in unchecked_young_symmetrizer(tableau,each_row_col_permutation):
104+
sequence = permutation(range(n))
105+
#Inverting the permutation here and reversing the r*c in unchecked_young_symmetrizer cancels out
106+
#sequence = (~permutation)(range(n))
107+
108+
#total += sign*np.transpose(tensor,sequence)
109+
if sign>0:
110+
total += np.transpose(tensor,sequence)
111+
else:
112+
total -= np.transpose(tensor,sequence)
113+
return total
114+
115+
116+
#tableaux could be optionally provided to this function to save time.
117+
#this is following Proposition 9.3.12 (p401) of Goodman & Wallach, "Representations and Invariants of the Classical Groups"
118+
def project(tensor, partition):
119+
"""the isotypical component of tensor corresponding to the partition"""
120+
d=tensor.shape[0]
121+
n=tensor.ndim
122+
ncols=partition[0]
123+
nrows=len(partition)
124+
assert all(i==d for i in tensor.shape)
125+
assert n==sum(partition)
126+
assert all(i>0 for i in partition)
127+
for i in range(1,len(partition)):
128+
assert(partition[i]<=partition[i-1])
129+
total=np.zeros_like(tensor, dtype="float64")
130+
each_row_col_permutation = getRowColumnPermutations(partition)
131+
tableaux,countStd=enumerate_Young_tableax_and_count_standard(partition)
132+
for tableau in tableaux:
133+
total += unchecked_project_to_tableau(tensor,tableau,each_row_col_permutation)
134+
factor=((countStd+0.0)/math.factorial(n))
135+
return total*factor*factor
136+
137+
def testIsotypePartition(tensor):
138+
"""Test that the components of tensor add up to tensor,
139+
that projecting them again the same way leaves them unchanged,
140+
and that projecting them differently leaves them zero"""
141+
#print("---")
142+
total = np.zeros_like(tensor, dtype="float64")
143+
for pp in partitions(tensor.ndim):
144+
p = IntegerPartition(pp).partition
145+
t=project(tensor, p)
146+
#print (p)
147+
#print(";")
148+
#print(t)
149+
#print(t-project(t,p))
150+
#print(project(t,p))
151+
assert np.allclose(t,project(t,p))
152+
#print(tensor,total,t)
153+
total += t
154+
for qq in partitions(tensor.ndim):
155+
q=IntegerPartition(qq).partition
156+
if q!=p:
157+
#print(p,q)
158+
#print(project(t,q))
159+
assert np.allclose(0,project(t,q))
160+
#print(".")
161+
#print(tensor)
162+
#print(total)
163+
assert np.allclose(tensor,total)
164+
print("test ok")
165+
166+
167+
def randomTensor(d,m):
168+
return np.random.rand(*([d]*m))
169+
def tensorWithSingleOne(index,min_d=None):
170+
"""the tensor full of zeros with a one at location index"""
171+
m=len(index)
172+
d=max(index)+1
173+
if min_d is not None and min_d>d:
174+
d=min_d
175+
o=np.zeros([d]*m)
176+
o.__setitem__(tuple(index),1)
177+
return o
178+
def testAllSingleOnes(d,m):
179+
for i in itertools.product(range(d),repeat=m):
180+
tensor = tensorWithSingleOne(i,min_d=d)
181+
try:
182+
testIsotypePartition(tensor)
183+
except AssertionError:
184+
print("{} failed".format(i))
185+
186+
if __name__=="__main__":
187+
188+
if 0:
189+
import young_symmetrization
190+
from young_symmetrization import young_symmetrizer, young
191+
for i in unchecked_young_symmetrizer([[0,1,2],[3,4]]):
192+
print (i)
193+
for i in young_symmetrizer( young.Young( [[1,2,3],[4,5]] ) ).data.items():
194+
print(i)
195+
sys.exit()
196+
197+
#testAllSingleOnes(2,5)
198+
199+
if 0:
200+
#a=np.random.rand(2,2)
201+
a=np.array([[2,3],[4,5]])
202+
a_anti=project(a,[1,1])
203+
#print("***")
204+
a_sym=project(a,[2])
205+
print(a)
206+
print(a_anti)
207+
print(a_sym)
208+
testIsotypePartition(a)
209+
210+
211+
212+
if 1:
213+
testIsotypePartition(np.random.rand(4,4))
214+
#print(project(np.random.rand(3,3,3),[1,1,1]))
215+
testIsotypePartition(np.random.rand(3,3,3))
216+
testIsotypePartition(np.random.rand(7,7,7,7))
217+
testIsotypePartition(np.random.rand(5,5,5,5,5))

0 commit comments

Comments
 (0)