-
Notifications
You must be signed in to change notification settings - Fork 86
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from marysia/3d-conv
3D GrouPy
- Loading branch information
Showing
25 changed files
with
2,064 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import numpy as np | ||
from groupy.garray.finitegroup import FiniteGroup | ||
from groupy.garray.matrix_garray import MatrixGArray | ||
from groupy.garray.C4ht_array import C4htArray | ||
from groupy.garray.Z3_array import Z3Array | ||
|
||
""" | ||
Implementation of the finite group C4h, the cyclic group of rectangular cuboid symmetry, consisting of 8 elements in total: | ||
- Identity element | ||
- one 4-fold axis over opposing square faces (90 degree rotations) | ||
- two 2-fold axes over opposing rectangular faces (180 degree rotations) | ||
- two 2-fold axes over opposing long edges (180 degree rotations) | ||
As C4h is abelian, each transformation can be described in terms of number of rotations around the various fold axes. | ||
However, all elements from C4h can also be generated by two elements: 180 degree rotation over the y-axis and 90 | ||
degree rotation over the z-axis. | ||
The int parameterization is in the form of (y, z) where y represents the number of 180 degree rotations | ||
over y (in {0, 1}) and z the number of 90 degree rotations over z (in {0, 1, 2, 3}) | ||
""" | ||
|
||
|
||
class C4hArray(MatrixGArray): | ||
parameterizations = ['int', 'mat', 'hmat'] | ||
_g_shapes = {'int': (2,), 'mat': (3, 3), 'hmat': (4, 4)} | ||
_left_actions = {} | ||
_reparameterizations = {} | ||
_group_name = 'C4h' | ||
|
||
def __init__(self, data, p='int'): | ||
data = np.asarray(data) | ||
assert data.dtype == np.int | ||
|
||
# classes C4hArray can be multiplied with | ||
self._left_actions[C4hArray] = self.__class__.left_action_hmat | ||
self._left_actions[C4htArray] = self.__class__.left_action_hmat | ||
self._left_actions[Z3Array] = self.__class__.left_action_vec | ||
|
||
super(C4hArray, self).__init__(data, p) | ||
self.elements = self.get_elements() | ||
|
||
def mat2int(self, mat_data): | ||
''' | ||
Transforms 3x3 matrix representation to int representation. | ||
To handle any size and shape of mat_data, the original mat_data | ||
is reshaped to a long list of 3x3 matrices, converted to a list of | ||
int representations, and reshaped back to the original mat_data shape. | ||
mat-2-int is achieved by taking the matrix, looking up the index in the | ||
element list, and converting that index to two numbers: y and z. The index | ||
is the result of (y * 4) + z. | ||
''' | ||
|
||
input = mat_data.reshape((-1, 3, 3)) | ||
data = np.zeros((input.shape[0], 2), dtype=np.int) | ||
for i in xrange(input.shape[0]): | ||
index = self.elements.index(input[i].tolist()) | ||
z = int(index % 4) | ||
y = int((index - z) / 4) | ||
data[i, 0] = y | ||
data[i, 1] = z | ||
data = data.reshape(mat_data.shape[:-2] + (2,)) | ||
return data | ||
|
||
def int2mat(self, int_data): | ||
''' | ||
Transforms integer representation to 3x3 matrix representation. | ||
Original int_data is flattened and later reshaped back to its original | ||
shape to handle any size and shape of input. | ||
The element is located in the list at index (y * 4) + z. | ||
''' | ||
y = int_data[..., 0].flatten() | ||
z = int_data[..., 1].flatten() | ||
data = np.zeros((len(y),) + (3, 3), dtype=np.int) | ||
|
||
for j in xrange(len(y)): | ||
index = (y[j] * 4) + z[j] | ||
mat = self.elements[index] | ||
data[j, 0:3, 0:3] = mat | ||
|
||
data = data.reshape(int_data.shape[:-1] + (3, 3)) | ||
return data | ||
|
||
def _multiply(self, element, generator, times): | ||
element = np.array(element) | ||
for i in range(times): | ||
element = np.dot(element, np.array(generator)) | ||
return element | ||
|
||
def get_elements(self): | ||
''' | ||
Elements are stored as lists rather than numpy arrays to enable | ||
lookup through self.elements.index(x) and sorting. | ||
All elements are found by multiplying the identity matrix with all | ||
possible combinations of the generators, i.e. 0 or 1 rotations over y | ||
and 0, 1, 2, or 3 rotations over z. | ||
''' | ||
# specify generators | ||
mode = 'zyx' | ||
if mode == 'xyz': | ||
g1 = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, -1]]) # 180 degrees over y | ||
g2 = np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) # 90 degrees over z | ||
elif mode == 'zyx': | ||
g1 = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, -1]]) # 180 degrees over y | ||
g2 = np.array([[1, 0, 0], [0, 0, -1], [0, 1, 0]]) # 90 degrees over z | ||
|
||
|
||
element_list = [] | ||
element = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) | ||
for i in range(0, 2): | ||
element = self._multiply(element, g1, i) | ||
for j in range(0, 4): | ||
element = self._multiply(element, g2, j) | ||
element_list.append(element.tolist()) | ||
return element_list | ||
|
||
|
||
class C4hGroup(FiniteGroup, C4hArray): | ||
def __init__(self): | ||
C4hArray.__init__( | ||
self, | ||
data=np.array([[i, j] for i in xrange(2) for j in xrange(4)]), | ||
p='int' | ||
) | ||
FiniteGroup.__init__(self, C4hArray) | ||
|
||
def factory(self, *args, **kwargs): | ||
return C4hArray(*args, **kwargs) | ||
|
||
C4h = C4hGroup() | ||
|
||
def rand(size=()): | ||
''' | ||
Returns an C4hArray of shape size, with randomly chosen elements in int parameterization. | ||
''' | ||
data = np.zeros(size + (2,), dtype=np.int) | ||
data[..., 0] = np.random.randint(0, 2, size) # rotations over y | ||
data[..., 1] = np.random.randint(0, 4, size) # rotations over z | ||
return C4hArray(data=data, p='int') | ||
|
||
def identity(p='int'): | ||
''' | ||
Returns the identity element: a matrix with 1's on the diagonal. | ||
''' | ||
li = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] | ||
e = C4hArray(data=np.array(li, dtype=np.int), p='mat') | ||
return e.reparameterize(p) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import numpy as np | ||
from groupy.garray.matrix_garray import MatrixGArray | ||
|
||
""" | ||
Implementation of the non-orientation perserving variant of group C4h -- C4h with translations. | ||
The int parameterisation is similar to that of C4h, but with the added 3D translation (u, v, w) to indicate | ||
translation in Z3. | ||
4x4 homogeneous matrices (hmat) are used to represent the transformation in matrix format. | ||
""" | ||
|
||
class C4htArray(MatrixGArray): | ||
parameterizations = ['int', 'hmat'] | ||
_g_shapes = {'int': (5,), 'hmat': (4, 4)} | ||
_left_actions = {} | ||
_reparameterizations = {} | ||
_group_name = 'C4ht' | ||
|
||
def __init__(self, data, p='int'): | ||
data = np.asarray(data) | ||
assert data.dtype == np.int | ||
self._left_actions[C4htArray] = self.__class__.left_action_hmat | ||
super(C4htArray, self).__init__(data, p) | ||
self.elements = self.get_elements() | ||
|
||
def hmat2int(self, hmat_data): | ||
''' | ||
Transforms 4x4 matrix representation to int representation. | ||
To handle any size and shape of hmat_data, the original hmat_data | ||
is reshaped to a long list of 4x4 matrices, converted to a list of | ||
int representations, and reshaped back to the original mat_data shape. | ||
hmat-2-int is achieved by taking the matrix, looking up the index in the | ||
element list, and converting that index to two numbers: y and z. The index | ||
is the result of (y * 4) + z. u, v, w are retrieved by looking at the last | ||
column of the hmat. | ||
''' | ||
|
||
input = hmat_data.reshape((-1, 4, 4)) | ||
data = np.zeros((input.shape[0], 5), dtype=np.int) | ||
for i in xrange(input.shape[0]): | ||
hmat = input[i] | ||
mat = [elem[0:3] for elem in hmat.tolist()][0:3] | ||
index = self.elements.index(mat) | ||
z = int(index % 4) | ||
y = int((index - z) / 4) | ||
u, v, w, _ = hmat[:, 3] | ||
data[i, 0] = y | ||
data[i, 1] = z | ||
data[i, 2] = u | ||
data[i, 3] = v | ||
data[i, 4] = w | ||
data = data.reshape(hmat_data.shape[:-2] + (5,)) | ||
return data | ||
|
||
def int2hmat(self, int_data): | ||
''' | ||
Transforms integer representation to 4x4 matrix representation. | ||
Original int_data is flattened and later reshaped back to its original | ||
shape to handle any size and shape of input. | ||
''' | ||
# rotations over y and z | ||
y = int_data[..., 0].flatten() | ||
z = int_data[..., 1].flatten() | ||
|
||
# translations | ||
u = int_data[..., 2].flatten() | ||
v = int_data[..., 3].flatten() | ||
w = int_data[..., 4].flatten() | ||
data = np.zeros((len(y),) + (4, 4), dtype=np.int) | ||
|
||
for j in xrange(len(y)): | ||
index = (y[j] * 4) + z[j] | ||
mat = self.elements[index] | ||
|
||
data[j, 0:3, 0:3] = mat | ||
data[j, 0, 3] = u[j] | ||
data[j, 1, 3] = v[j] | ||
data[j, 2, 3] = w[j] | ||
data[j, 3, 3] = 1 | ||
|
||
data = data.reshape(int_data.shape[:-1] + (4, 4)) | ||
return data | ||
|
||
def _multiply(self, element, generator, times): | ||
''' | ||
Helper function to multiply an _element_ with a _generator_ | ||
_times_ number of times. Used in self.get_elements() | ||
''' | ||
element = np.array(element) | ||
for i in range(times): | ||
element = np.dot(element, np.array(generator)) | ||
return element | ||
|
||
def get_elements(self): | ||
''' | ||
Function to generate a list containing elements of group C4ht, | ||
similar to get_elements() of BArray. | ||
These are the base elements in 3x3 matrix notation without translations. | ||
''' | ||
# specify generators | ||
mode = 'zyx' | ||
if mode == 'xyz': | ||
g1 = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, -1]]) # 180 degrees over y | ||
g2 = np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) # 90 degrees over z | ||
elif mode == 'zyx': | ||
g1 = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, -1]]) # 180 degrees over y | ||
g2 = np.array([[1, 0, 0], [0, 0, -1], [0, 1, 0]]) # 90 degrees over z | ||
|
||
element_list = [] | ||
element = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) | ||
for i in range(0, 2): | ||
element = self._multiply(element, g1, i) | ||
for j in range(0, 4): | ||
element = self._multiply(element, g2, j) | ||
element_list.append(element.tolist()) | ||
return element_list | ||
|
||
|
||
def rand(minu=0, maxu=5, minv=0, maxv=5, minw=0, maxw=5, size=()): | ||
''' | ||
Returns an C4htArray of shape size, with randomly chosen elements in int parameterization. | ||
''' | ||
data = np.zeros(size + (5,), dtype=np.int64) | ||
data[..., 0] = np.random.randint(0, 2, size) # rotations over y | ||
data[..., 1] = np.random.randint(0, 4, size) # rotations over x | ||
data[..., 2] = np.random.randint(minu, maxu, size) # translation on x | ||
data[..., 3] = np.random.randint(minv, maxv, size) # translation on y | ||
data[..., 4] = np.random.randint(minw, maxw, size) # translation on z | ||
return C4htArray(data=data, p='int') | ||
|
||
|
||
def identity(p='int'): | ||
''' | ||
Returns the identity element: a matrix with 1's on the diagonal. | ||
''' | ||
li = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] | ||
e = C4htArray(data=np.array(li, dtype=np.int), p='hmat') | ||
return e.reparameterize(p) | ||
|
||
|
||
def meshgrid(minu=-1, maxu=2, minv=-1, maxv=2, minw=-1, maxw=2): | ||
''' | ||
Creates a meshgrid of all elements of the group, within the given | ||
translation parameters. | ||
''' | ||
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) | ||
for | ||
w in xrange(minw, maxw)] | ||
return C4htArray(li, p='int') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.