Skip to content

Commit 9668591

Browse files
authored
Merge pull request #10 from marysia/3d-conv
3D GrouPy
2 parents c6f40f2 + 35206e8 commit 9668591

25 files changed

+2064
-40
lines changed

groupy/garray/C4h_array.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import numpy as np
2+
from groupy.garray.finitegroup import FiniteGroup
3+
from groupy.garray.matrix_garray import MatrixGArray
4+
from groupy.garray.C4ht_array import C4htArray
5+
from groupy.garray.Z3_array import Z3Array
6+
7+
"""
8+
Implementation of the finite group C4h, the cyclic group of rectangular cuboid symmetry, consisting of 8 elements in total:
9+
- Identity element
10+
- one 4-fold axis over opposing square faces (90 degree rotations)
11+
- two 2-fold axes over opposing rectangular faces (180 degree rotations)
12+
- two 2-fold axes over opposing long edges (180 degree rotations)
13+
As C4h is abelian, each transformation can be described in terms of number of rotations around the various fold axes.
14+
However, all elements from C4h can also be generated by two elements: 180 degree rotation over the y-axis and 90
15+
degree rotation over the z-axis.
16+
17+
The int parameterization is in the form of (y, z) where y represents the number of 180 degree rotations
18+
over y (in {0, 1}) and z the number of 90 degree rotations over z (in {0, 1, 2, 3})
19+
"""
20+
21+
22+
class C4hArray(MatrixGArray):
23+
parameterizations = ['int', 'mat', 'hmat']
24+
_g_shapes = {'int': (2,), 'mat': (3, 3), 'hmat': (4, 4)}
25+
_left_actions = {}
26+
_reparameterizations = {}
27+
_group_name = 'C4h'
28+
29+
def __init__(self, data, p='int'):
30+
data = np.asarray(data)
31+
assert data.dtype == np.int
32+
33+
# classes C4hArray can be multiplied with
34+
self._left_actions[C4hArray] = self.__class__.left_action_hmat
35+
self._left_actions[C4htArray] = self.__class__.left_action_hmat
36+
self._left_actions[Z3Array] = self.__class__.left_action_vec
37+
38+
super(C4hArray, self).__init__(data, p)
39+
self.elements = self.get_elements()
40+
41+
def mat2int(self, mat_data):
42+
'''
43+
Transforms 3x3 matrix representation to int representation.
44+
To handle any size and shape of mat_data, the original mat_data
45+
is reshaped to a long list of 3x3 matrices, converted to a list of
46+
int representations, and reshaped back to the original mat_data shape.
47+
48+
mat-2-int is achieved by taking the matrix, looking up the index in the
49+
element list, and converting that index to two numbers: y and z. The index
50+
is the result of (y * 4) + z.
51+
'''
52+
53+
input = mat_data.reshape((-1, 3, 3))
54+
data = np.zeros((input.shape[0], 2), dtype=np.int)
55+
for i in xrange(input.shape[0]):
56+
index = self.elements.index(input[i].tolist())
57+
z = int(index % 4)
58+
y = int((index - z) / 4)
59+
data[i, 0] = y
60+
data[i, 1] = z
61+
data = data.reshape(mat_data.shape[:-2] + (2,))
62+
return data
63+
64+
def int2mat(self, int_data):
65+
'''
66+
Transforms integer representation to 3x3 matrix representation.
67+
Original int_data is flattened and later reshaped back to its original
68+
shape to handle any size and shape of input.
69+
70+
The element is located in the list at index (y * 4) + z.
71+
'''
72+
y = int_data[..., 0].flatten()
73+
z = int_data[..., 1].flatten()
74+
data = np.zeros((len(y),) + (3, 3), dtype=np.int)
75+
76+
for j in xrange(len(y)):
77+
index = (y[j] * 4) + z[j]
78+
mat = self.elements[index]
79+
data[j, 0:3, 0:3] = mat
80+
81+
data = data.reshape(int_data.shape[:-1] + (3, 3))
82+
return data
83+
84+
def _multiply(self, element, generator, times):
85+
element = np.array(element)
86+
for i in range(times):
87+
element = np.dot(element, np.array(generator))
88+
return element
89+
90+
def get_elements(self):
91+
'''
92+
Elements are stored as lists rather than numpy arrays to enable
93+
lookup through self.elements.index(x) and sorting.
94+
95+
All elements are found by multiplying the identity matrix with all
96+
possible combinations of the generators, i.e. 0 or 1 rotations over y
97+
and 0, 1, 2, or 3 rotations over z.
98+
'''
99+
# specify generators
100+
mode = 'zyx'
101+
if mode == 'xyz':
102+
g1 = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, -1]]) # 180 degrees over y
103+
g2 = np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) # 90 degrees over z
104+
elif mode == 'zyx':
105+
g1 = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, -1]]) # 180 degrees over y
106+
g2 = np.array([[1, 0, 0], [0, 0, -1], [0, 1, 0]]) # 90 degrees over z
107+
108+
109+
element_list = []
110+
element = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
111+
for i in range(0, 2):
112+
element = self._multiply(element, g1, i)
113+
for j in range(0, 4):
114+
element = self._multiply(element, g2, j)
115+
element_list.append(element.tolist())
116+
return element_list
117+
118+
119+
class C4hGroup(FiniteGroup, C4hArray):
120+
def __init__(self):
121+
C4hArray.__init__(
122+
self,
123+
data=np.array([[i, j] for i in xrange(2) for j in xrange(4)]),
124+
p='int'
125+
)
126+
FiniteGroup.__init__(self, C4hArray)
127+
128+
def factory(self, *args, **kwargs):
129+
return C4hArray(*args, **kwargs)
130+
131+
C4h = C4hGroup()
132+
133+
def rand(size=()):
134+
'''
135+
Returns an C4hArray of shape size, with randomly chosen elements in int parameterization.
136+
'''
137+
data = np.zeros(size + (2,), dtype=np.int)
138+
data[..., 0] = np.random.randint(0, 2, size) # rotations over y
139+
data[..., 1] = np.random.randint(0, 4, size) # rotations over z
140+
return C4hArray(data=data, p='int')
141+
142+
def identity(p='int'):
143+
'''
144+
Returns the identity element: a matrix with 1's on the diagonal.
145+
'''
146+
li = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
147+
e = C4hArray(data=np.array(li, dtype=np.int), p='mat')
148+
return e.reparameterize(p)

groupy/garray/C4ht_array.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import numpy as np
2+
from groupy.garray.matrix_garray import MatrixGArray
3+
4+
"""
5+
Implementation of the non-orientation perserving variant of group C4h -- C4h with translations.
6+
The int parameterisation is similar to that of C4h, but with the added 3D translation (u, v, w) to indicate
7+
translation in Z3.
8+
9+
4x4 homogeneous matrices (hmat) are used to represent the transformation in matrix format.
10+
"""
11+
12+
class C4htArray(MatrixGArray):
13+
parameterizations = ['int', 'hmat']
14+
_g_shapes = {'int': (5,), 'hmat': (4, 4)}
15+
_left_actions = {}
16+
_reparameterizations = {}
17+
_group_name = 'C4ht'
18+
19+
def __init__(self, data, p='int'):
20+
data = np.asarray(data)
21+
assert data.dtype == np.int
22+
self._left_actions[C4htArray] = self.__class__.left_action_hmat
23+
super(C4htArray, self).__init__(data, p)
24+
self.elements = self.get_elements()
25+
26+
def hmat2int(self, hmat_data):
27+
'''
28+
Transforms 4x4 matrix representation to int representation.
29+
To handle any size and shape of hmat_data, the original hmat_data
30+
is reshaped to a long list of 4x4 matrices, converted to a list of
31+
int representations, and reshaped back to the original mat_data shape.
32+
33+
hmat-2-int is achieved by taking the matrix, looking up the index in the
34+
element list, and converting that index to two numbers: y and z. The index
35+
is the result of (y * 4) + z. u, v, w are retrieved by looking at the last
36+
column of the hmat.
37+
'''
38+
39+
input = hmat_data.reshape((-1, 4, 4))
40+
data = np.zeros((input.shape[0], 5), dtype=np.int)
41+
for i in xrange(input.shape[0]):
42+
hmat = input[i]
43+
mat = [elem[0:3] for elem in hmat.tolist()][0:3]
44+
index = self.elements.index(mat)
45+
z = int(index % 4)
46+
y = int((index - z) / 4)
47+
u, v, w, _ = hmat[:, 3]
48+
data[i, 0] = y
49+
data[i, 1] = z
50+
data[i, 2] = u
51+
data[i, 3] = v
52+
data[i, 4] = w
53+
data = data.reshape(hmat_data.shape[:-2] + (5,))
54+
return data
55+
56+
def int2hmat(self, int_data):
57+
'''
58+
Transforms integer representation to 4x4 matrix representation.
59+
Original int_data is flattened and later reshaped back to its original
60+
shape to handle any size and shape of input.
61+
'''
62+
# rotations over y and z
63+
y = int_data[..., 0].flatten()
64+
z = int_data[..., 1].flatten()
65+
66+
# translations
67+
u = int_data[..., 2].flatten()
68+
v = int_data[..., 3].flatten()
69+
w = int_data[..., 4].flatten()
70+
data = np.zeros((len(y),) + (4, 4), dtype=np.int)
71+
72+
for j in xrange(len(y)):
73+
index = (y[j] * 4) + z[j]
74+
mat = self.elements[index]
75+
76+
data[j, 0:3, 0:3] = mat
77+
data[j, 0, 3] = u[j]
78+
data[j, 1, 3] = v[j]
79+
data[j, 2, 3] = w[j]
80+
data[j, 3, 3] = 1
81+
82+
data = data.reshape(int_data.shape[:-1] + (4, 4))
83+
return data
84+
85+
def _multiply(self, element, generator, times):
86+
'''
87+
Helper function to multiply an _element_ with a _generator_
88+
_times_ number of times. Used in self.get_elements()
89+
'''
90+
element = np.array(element)
91+
for i in range(times):
92+
element = np.dot(element, np.array(generator))
93+
return element
94+
95+
def get_elements(self):
96+
'''
97+
Function to generate a list containing elements of group C4ht,
98+
similar to get_elements() of BArray.
99+
100+
These are the base elements in 3x3 matrix notation without translations.
101+
'''
102+
# specify generators
103+
mode = 'zyx'
104+
if mode == 'xyz':
105+
g1 = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, -1]]) # 180 degrees over y
106+
g2 = np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) # 90 degrees over z
107+
elif mode == 'zyx':
108+
g1 = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, -1]]) # 180 degrees over y
109+
g2 = np.array([[1, 0, 0], [0, 0, -1], [0, 1, 0]]) # 90 degrees over z
110+
111+
element_list = []
112+
element = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
113+
for i in range(0, 2):
114+
element = self._multiply(element, g1, i)
115+
for j in range(0, 4):
116+
element = self._multiply(element, g2, j)
117+
element_list.append(element.tolist())
118+
return element_list
119+
120+
121+
def rand(minu=0, maxu=5, minv=0, maxv=5, minw=0, maxw=5, size=()):
122+
'''
123+
Returns an C4htArray of shape size, with randomly chosen elements in int parameterization.
124+
'''
125+
data = np.zeros(size + (5,), dtype=np.int64)
126+
data[..., 0] = np.random.randint(0, 2, size) # rotations over y
127+
data[..., 1] = np.random.randint(0, 4, size) # rotations over x
128+
data[..., 2] = np.random.randint(minu, maxu, size) # translation on x
129+
data[..., 3] = np.random.randint(minv, maxv, size) # translation on y
130+
data[..., 4] = np.random.randint(minw, maxw, size) # translation on z
131+
return C4htArray(data=data, p='int')
132+
133+
134+
def identity(p='int'):
135+
'''
136+
Returns the identity element: a matrix with 1's on the diagonal.
137+
'''
138+
li = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]
139+
e = C4htArray(data=np.array(li, dtype=np.int), p='hmat')
140+
return e.reparameterize(p)
141+
142+
143+
def meshgrid(minu=-1, maxu=2, minv=-1, maxv=2, minw=-1, maxw=2):
144+
'''
145+
Creates a meshgrid of all elements of the group, within the given
146+
translation parameters.
147+
'''
148+
li = [[i, m, u, v, w] for i in xrange(2) for m in xrange(4) for u in xrange(minu, maxu) for v in xrange(minv, maxv)
149+
for
150+
w in xrange(minw, maxw)]
151+
return C4htArray(li, p='int')

groupy/garray/D4_array.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
from groupy.garray.matrix_garray import MatrixGArray
88

9-
109
class D4Array(MatrixGArray):
1110

1211
parameterizations = ['int', 'mat', 'hmat']

0 commit comments

Comments
 (0)