fuse-element 0.1.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fuse/groups.py ADDED
@@ -0,0 +1,388 @@
1
+ import fuse.cells as cells
2
+ from sympy.combinatorics import PermutationGroup, Permutation
3
+ from sympy.combinatorics.named_groups import SymmetricGroup, DihedralGroup, CyclicGroup, AlternatingGroup
4
+ from sympy.matrices.expressions import PermutationMatrix
5
+ import numpy as np
6
+ import sympy as sp
7
+ import math
8
+
9
+
10
+ def perm_matrix_to_perm_array(p_mat):
11
+ summed = np.sum(p_mat, axis=0)
12
+ assert np.all(summed == np.ones_like(summed))
13
+ res = []
14
+ for row in p_mat:
15
+ indices = list(row).index(1)
16
+ res += [indices]
17
+ return res
18
+
19
+
20
+ class GroupMemberRep(object):
21
+
22
+ def __init__(self, perm, M, group):
23
+ self.perm = perm
24
+ self.transform_matrix = M
25
+ self.group = group
26
+
27
+ def __call__(self, x):
28
+ if isinstance(x, cells.Point):
29
+ return x.orient(self)
30
+ if isinstance(x, sp.Expr):
31
+ x_ones = sp.r_[sp.array(x), sp.ones(1)]
32
+ else:
33
+ x_ones = np.r_[np.array(x), np.ones(1)]
34
+
35
+ sum = np.matmul(x_ones, self.transform_matrix)
36
+ if len(sum.shape) > 1:
37
+ return tuple(map(tuple, sum))
38
+ return tuple(sum)
39
+
40
+ def permute(self, lst):
41
+ n = len(lst)
42
+ if n > self.perm.size:
43
+ temp_perm = Permutation(self.perm, size=n)
44
+ return temp_perm(lst)
45
+ return self.perm(lst)
46
+
47
+ def compute_perm(self, base_val=None):
48
+ if base_val:
49
+ val_list = [x + base_val for x in self.perm.array_form]
50
+ else:
51
+ val_list = self.perm.array_form
52
+ val = self.numeric_rep()
53
+ return val, val_list
54
+
55
+ def numeric_rep(self):
56
+ identity = self.group.identity.perm.array_form
57
+ m_array = self.perm.array_form
58
+ val = 0
59
+ for i in range(len(identity)):
60
+ loc = m_array.index(identity[i])
61
+ m_array.remove(identity[i])
62
+ val += loc * math.factorial(len(identity) - i - 1)
63
+ return val
64
+
65
+ def __eq__(self, x):
66
+ assert isinstance(x, GroupMemberRep)
67
+ return self.perm == x.perm and self.group.cell.dim() == x.group.cell.dim()
68
+
69
+ def __hash__(self):
70
+ return hash((self.perm, self.group))
71
+
72
+ def __mul__(self, x):
73
+ assert isinstance(x, GroupMemberRep)
74
+ return self.group.get_member(self.perm * x.perm)
75
+
76
+ def __invert__(self):
77
+ return self.group.get_member(~self.perm)
78
+
79
+ def __repr__(self):
80
+ string = "g"
81
+ string += str(self.perm.array_form)
82
+ return string
83
+
84
+ def matrix_form(self):
85
+ return np.array(PermutationMatrix(self.perm).as_explicit()).astype(np.float64)
86
+
87
+ def lin_combination_form(self):
88
+ if self.group.cell.dimension == 0:
89
+ return [1]
90
+ bvs = self.group.cell.basis_vectors()
91
+ M = np.array(bvs).T
92
+ trans_bvs = np.array([self(bvs[i]) for i in range(len(bvs))]).T
93
+ return np.linalg.solve(M, trans_bvs)
94
+
95
+
96
+ class PermutationSetRepresentation():
97
+ """
98
+ A representation of a set of permutations (can be a full group) on a cell.
99
+
100
+ Args:
101
+ [permutations]: the list of permutations in the set
102
+ cell (optional): the cell the group is representing the operations on
103
+
104
+ """
105
+ def __init__(self, perm_list, cell=None):
106
+ assert len(perm_list) > 0
107
+ self.perm_list = perm_list
108
+
109
+ if not any([p.is_Identity for p in self.perm_list]):
110
+ p = self.perm_list[0]
111
+ self.perm_list.append(Permutation([i for i in range(0, p.size)]))
112
+
113
+ if cell is not None:
114
+ self.cell = cell
115
+ vertices = cell.vertices(return_coords=True)
116
+ self._members = []
117
+ counter = 0
118
+
119
+ for p in self.perm_list:
120
+ if len(vertices) > p.size:
121
+ temp_perm = Permutation(p, size=len(vertices))
122
+ reordered = temp_perm(vertices)
123
+ else:
124
+ reordered = p(vertices)
125
+ A = np.c_[np.array(vertices, dtype=float), np.ones(len(vertices))]
126
+ b = np.array(reordered, dtype=float)
127
+
128
+ M, _, _, _ = np.linalg.lstsq(A, b, rcond=None)
129
+ p_rep = GroupMemberRep(p, M, self)
130
+ if p.is_Identity:
131
+ self.identity = p_rep
132
+ self._members.append(p_rep)
133
+ counter += 1
134
+
135
+ def add_cell(self, cell):
136
+ return PermutationSetRepresentation(self.perm_list, cell=cell)
137
+
138
+ def members(self, perm=False):
139
+ if self.cell is None:
140
+ raise ValueError("Group does not have a domain - members have not been calculated")
141
+ if perm:
142
+ return [m.perm for m in self._members]
143
+ return self._members
144
+
145
+ def transform_between_perms(self, perm1, perm2):
146
+ member_perms = self.members(perm=True)
147
+ perm1 = Permutation.from_sequence(perm1)
148
+ perm2 = Permutation.from_sequence(perm2)
149
+ assert perm1 in member_perms
150
+ assert perm2 in member_perms
151
+ return self.get_member(~Permutation(perm1)) * self.get_member(Permutation(perm2))
152
+
153
+ def get_member(self, perm):
154
+ if not isinstance(perm, Permutation):
155
+ perm = Permutation.from_sequence(perm)
156
+ for m in self.members():
157
+ if m.perm == perm:
158
+ return m
159
+ raise ValueError("Permutation not a member of group")
160
+
161
+ def compute_num_reps(self, base_val=0):
162
+ """ Compute the numerical represention of each member as compared to the identity.
163
+ Where the numerical rep is:
164
+
165
+ M.index(id[0]) = a; M.remove(id[0])
166
+ M.index(id[1]) = b; M.remove(id[1])
167
+ M.index(id[2]) = c; M.remove(id[2])
168
+
169
+ o = (a * 2!) + (b * 1!) + (c * 0!)
170
+ """
171
+ members = self.members()
172
+ res = {}
173
+ for m in members:
174
+ val, perm = m.compute_perm(base_val)
175
+ res[val] = perm
176
+ return res
177
+
178
+ def size(self):
179
+ return len(self.perm_list)
180
+
181
+ def __mul__(self, other_group):
182
+ # convert to set to remove duplicates
183
+ return PermutationSetRepresentation(list(set(self.perm_list + other_group.perm_list)))
184
+
185
+ def __repr__(self):
186
+ return "GS" + str(self.size())
187
+
188
+
189
+ class GroupRepresentation(PermutationSetRepresentation):
190
+ """
191
+ A representation of a group by its matrix operations.
192
+
193
+ Args:
194
+ base_group: the sympy group that is being represented
195
+ cell (optional): the cell the group is representing the operations on
196
+
197
+ """
198
+
199
+ def __init__(self, base_group, cell=None):
200
+ assert isinstance(base_group, PermutationGroup)
201
+ self.base_group = base_group
202
+ self.generators = []
203
+ if cell is not None:
204
+ self.cell = cell
205
+ vertices = cell.vertices(return_coords=True)
206
+
207
+ self._members = []
208
+ counter = 0
209
+ for g in self.base_group.elements:
210
+ if len(vertices) > g.size:
211
+ temp_perm = Permutation(g, size=len(vertices))
212
+ reordered = temp_perm(vertices)
213
+ else:
214
+ reordered = g(vertices)
215
+ A = np.c_[np.array(vertices, dtype=float), np.ones(len(vertices))]
216
+ b = np.array(reordered, dtype=float)
217
+
218
+ M, _, _, _ = np.linalg.lstsq(A, b, rcond=None)
219
+ p_rep = GroupMemberRep(g, M, self)
220
+ self._members.append(p_rep)
221
+ if g.is_Identity:
222
+ self.identity = p_rep
223
+ counter += 1
224
+
225
+ # this order produces simpler generator lists
226
+ # self.generators.reverse()
227
+
228
+ # temp_group_elems = self.base_group.elements
229
+
230
+ # temp_group_elems.remove(self.base_group.identity)
231
+
232
+ # remaining_members = self.compute_reps(self.base_group.identity,
233
+ # None, temp_group_elems)
234
+ # assert (len(remaining_members) == 0)
235
+ else:
236
+ self.cell = None
237
+
238
+ def conjugacy_class(self, g):
239
+ conj_class = set()
240
+ for x in self.members():
241
+ res = ~x * g * x
242
+ conj_class.add(res)
243
+ return conj_class
244
+
245
+ def add_cell(self, cell):
246
+ return GroupRepresentation(self.base_group, cell=cell)
247
+
248
+ def size(self):
249
+ if hasattr(self, "_members"):
250
+ assert len(self._members) == self.base_group.order()
251
+ return self.base_group.order()
252
+
253
+ def members(self, perm=False):
254
+ if self.cell is None:
255
+ raise ValueError("Group does not have a domain - members have not been calculated")
256
+ if perm:
257
+ return [m.perm for m in self._members]
258
+ return self._members
259
+
260
+ def transform_between_perms(self, perm1, perm2):
261
+ member_perms = self.members(perm=True)
262
+ perm1 = Permutation.from_sequence(perm1)
263
+ perm2 = Permutation.from_sequence(perm2)
264
+ assert perm1 in member_perms
265
+ assert perm2 in member_perms
266
+ return ~self.get_member(Permutation(perm1)) * self.get_member(Permutation(perm2))
267
+
268
+ def get_member(self, perm):
269
+ for m in self.members():
270
+ if m.perm == perm:
271
+ return m
272
+ raise ValueError("Permutation not a member of group")
273
+
274
+ # def compute_reps(self, g, path, remaining_members):
275
+ # # breadth first search to find generator representations of all members
276
+ # if len(remaining_members) == 0:
277
+ # return remaining_members
278
+
279
+ # next_candidates = []
280
+ # for generator in self.generators:
281
+ # new_perm = g*generator.perm
282
+ # if new_perm in remaining_members:
283
+ # if not path:
284
+ # new_path = generator.rep
285
+ # new_M = generator.transform_matrix
286
+ # print(new_M)
287
+ # assert (new_perm == generator.perm)
288
+ # self._members.append(generator)
289
+ # else:
290
+ # new_path = path.copy()
291
+ # new_path.extend(generator.rep)
292
+ # print(path)
293
+ # new_M = np.matmul(generator.transform_matrix, new_M)
294
+ # self._members.append(GroupMemberRep(new_perm,
295
+ # new_path,
296
+ # self))
297
+ # remaining_members.remove(new_perm)
298
+ # next_candidates.append((new_perm, new_path))
299
+
300
+ # for (new_perm, new_path) in next_candidates:
301
+ # remaining_members = self.compute_reps(new_perm,
302
+ # new_path,
303
+ # remaining_members)
304
+ # return remaining_members
305
+
306
+ def __mul__(self, other_group):
307
+ return GroupRepresentation(PermutationGroup(self.base_group.generators + other_group.base_group.generators))
308
+
309
+ def __truediv__(self, other_frac):
310
+ """ This isn't a mathematically accurate representation of
311
+ what it means to be a quotient group but it works on S3/S2
312
+ Have to compare cyclic forms as groups may not be defined on
313
+ the same number of elements
314
+ Doesn't work on D4/S2 but does on D4/C4 """
315
+ assert isinstance(other_frac, GroupRepresentation)
316
+ self_cyclic_gens = [gen.cyclic_form
317
+ for gen in self.base_group.generators]
318
+ other_cyclic_gens = [gen.cyclic_form
319
+ for gen in other_frac.base_group.generators]
320
+ if not all([c2 in self_cyclic_gens for c2 in other_cyclic_gens]):
321
+ raise ValueError("Invalid Quotient - mismatched cycles")
322
+ remaining_perms = [gen for gen in self.base_group.generators
323
+ if gen.cyclic_form not in other_cyclic_gens]
324
+
325
+ if len(remaining_perms) == 0:
326
+ raise ValueError("Invalid Quotient - no group formed")
327
+
328
+ return GroupRepresentation(PermutationGroup(remaining_perms))
329
+
330
+ def __repr__(self):
331
+ return "GR" + str(self.size())
332
+
333
+ # def __eq__(self, other):
334
+ # # TODO work on idea of group equality
335
+ # assert isinstance(other, GroupRepresentation)
336
+ # res = True
337
+ # for m in self.members():
338
+ # res = res and m in other.members()
339
+ # return res
340
+
341
+ def _to_dict(self):
342
+ return {"members": [m.perm.array_form for m in self._members]}
343
+
344
+ def dict_id(self):
345
+ return "Group"
346
+
347
+ def _from_dict(o_dict):
348
+ perm_group = PermutationGroup([Permutation(m) for m in o_dict["members"]])
349
+ # , o_dict["cell"]
350
+ return GroupRepresentation(perm_group)
351
+
352
+
353
+ def get_sym_group(n):
354
+ return GroupRepresentation(SymmetricGroup(n))
355
+
356
+
357
+ def get_cyc_group(n):
358
+ return GroupRepresentation(CyclicGroup(n))
359
+
360
+
361
+ S1 = GroupRepresentation(SymmetricGroup(1))
362
+ S2 = GroupRepresentation(SymmetricGroup(2))
363
+ S3 = GroupRepresentation(SymmetricGroup(3))
364
+ S4 = GroupRepresentation(SymmetricGroup(4))
365
+
366
+ D4 = GroupRepresentation(DihedralGroup(4))
367
+
368
+ C3 = GroupRepresentation(CyclicGroup(3))
369
+ C4 = GroupRepresentation(CyclicGroup(4))
370
+
371
+ Z2 = GroupRepresentation(CyclicGroup(2))
372
+ Z3 = GroupRepresentation(CyclicGroup(3))
373
+ Z4 = GroupRepresentation(CyclicGroup(4))
374
+
375
+
376
+ D2 = GroupRepresentation(DihedralGroup(2))
377
+ A4 = GroupRepresentation(AlternatingGroup(4))
378
+ A3 = GroupRepresentation(AlternatingGroup(3))
379
+
380
+ tri_C3 = PermutationSetRepresentation([Permutation([0, 1, 2]), Permutation([2, 0, 1]), Permutation([1, 0, 2])])
381
+ # tet_edges = PermutationSetRepresentation([Permutation([0, 1, 2, 3]), Permutation([0, 2, 3, 1]), Permutation([1, 2, 0, 3]),
382
+ # Permutation([0, 3, 1, 2]), Permutation([1, 3, 2, 0]), Permutation([2, 3, 0, 1])])
383
+ tet_edges = PermutationSetRepresentation([Permutation([0, 1, 2, 3]), Permutation([1, 2, 3, 0]), Permutation([2, 3, 0, 1]),
384
+ Permutation([1, 3, 0, 2]), Permutation([2, 0, 1, 3]), Permutation([3, 0, 1, 2])])
385
+ tet_faces = PermutationSetRepresentation([Permutation([0, 1, 2, 3]), Permutation([1, 2, 3, 0]), Permutation([1, 3, 2, 0]),
386
+ Permutation([3, 0, 2, 1])])
387
+
388
+ sq_edges = PermutationSetRepresentation([Permutation([0, 1, 2, 3]), Permutation([1, 2, 3, 0]), Permutation([3, 0, 1, 2]), Permutation([2, 3, 0, 1])])
fuse/serialisation.py ADDED
@@ -0,0 +1,121 @@
1
+ import json
2
+ from fuse import *
3
+ from fuse.spaces.polynomial_spaces import ConstructedPolynomialSpace
4
+ from fuse.spaces.element_sobolev_spaces import ElementSobolevSpace
5
+ from fuse.spaces.interpolation_spaces import InterpolationSpace
6
+ from fuse.traces import Trace
7
+ from fuse.triples import ImmersedDOFs
8
+ import sympy as sp
9
+
10
+
11
+ class ElementSerialiser():
12
+ """
13
+ This class provides encoding and decoding to json for objects in the
14
+ fuse language.
15
+
16
+ Methods
17
+ --------
18
+
19
+ encode: obj: fuse class
20
+ - converts object to a json representation
21
+ decode: obj_str: String
22
+ - converts json string to obj
23
+ """
24
+
25
+ def __init__(self):
26
+ self.obj_id_counter = {}
27
+ self.seen_objs = {}
28
+ self.obj_storage = {}
29
+
30
+ self.obj_types = {"Cell": Point,
31
+ "Edge": Edge,
32
+ "Triple": ElementTriple,
33
+ "Group": GroupRepresentation,
34
+ "SobolevSpace": ElementSobolevSpace,
35
+ "InterpolationSpace": InterpolationSpace,
36
+ "PolynomialSpace": PolynomialSpace,
37
+ "ConstructedPolynomialSpace": ConstructedPolynomialSpace,
38
+ "DOF": DOF,
39
+ "ImmersedDOF": ImmersedDOFs,
40
+ "DOFGen": DOFGenerator,
41
+ "Delta": DeltaPairing,
42
+ "L2Inner": L2Pairing,
43
+ "PolynomialKernel": PolynomialKernel,
44
+ "PointKernel": PointKernel,
45
+ "Trace": Trace
46
+ }
47
+
48
+ def encode(self, obj):
49
+ base_obj = self.encode_traverse(obj)
50
+ self.obj_storage["encoded_obj"] = base_obj
51
+ return json.dumps(self.obj_storage, indent=2)
52
+
53
+ def decode(self, obj_str):
54
+ obj_dict = json.loads(obj_str)
55
+ obj = self.decode_traverse(obj_dict["encoded_obj"], obj_dict)
56
+ return obj
57
+
58
+ def encode_traverse(self, obj, path=[]):
59
+ obj_dict = {}
60
+
61
+ if isinstance(obj, list) or isinstance(obj, tuple):
62
+ res_array = [{} for i in range(len(obj))]
63
+ for i in range(len(obj)):
64
+ dfs_res = self.encode_traverse(obj[i], path + [i])
65
+ res_array[i] = dfs_res
66
+ return res_array
67
+
68
+ if obj in self.seen_objs.keys():
69
+ return self.seen_objs[obj]["id"]
70
+
71
+ if hasattr(obj, "_to_dict"):
72
+ for (key, val) in obj._to_dict().items():
73
+ obj_dict[key] = self.encode_traverse(val, path + [key])
74
+ obj_id = self.get_id(obj)
75
+ self.store_obj(obj, obj.dict_id(), obj_id, obj_dict, path)
76
+ return obj.dict_id() + "/" + str(obj_id)
77
+
78
+ if isinstance(obj, sp.core.containers.Tuple) or isinstance(obj, sp.Expr):
79
+ return "Sympy/" + sp.srepr(obj)
80
+
81
+ return obj
82
+
83
+ def get_id(self, obj):
84
+ obj_name = obj.dict_id()
85
+ if obj_name in self.obj_id_counter.keys():
86
+ obj_id = self.obj_id_counter[obj_name]
87
+ self.obj_id_counter[obj_name] += 1
88
+ else:
89
+ obj_id = 0
90
+ self.obj_id_counter[obj_name] = 1
91
+ return obj_id
92
+
93
+ def store_obj(self, obj, name, obj_id, obj_dict, path):
94
+ self.seen_objs[obj] = {"id": name + "/" + str(obj_id), "path": path, "dict": obj_dict}
95
+ if name in self.obj_storage.keys():
96
+ self.obj_storage[name][obj_id] = obj_dict
97
+ else:
98
+ self.obj_storage[name] = {obj_id: obj_dict}
99
+
100
+ def decode_traverse(self, obj, obj_dict):
101
+
102
+ if isinstance(obj, str):
103
+ split_str = obj.split("/")
104
+ if split_str[0] in self.obj_types.keys():
105
+ name, obj_id = split_str[0], split_str[1]
106
+ sub_dict = obj_dict[name][obj_id]
107
+ for (key, value) in sub_dict.items():
108
+ sub_dict[key] = self.decode_traverse(value, obj_dict)
109
+ return self.obj_types[name]._from_dict(sub_dict)
110
+ elif split_str[0] == "Sympy":
111
+ return sp.parse_expr(" ".join(split_str[1:]))
112
+ else:
113
+ return obj
114
+ elif isinstance(obj, list) or isinstance(obj, tuple):
115
+ res_array = [0 for i in range(len(obj))]
116
+ for i in range(len(obj)):
117
+ dfs_res = self.decode_traverse(obj[i], obj_dict)
118
+ res_array[i] = dfs_res
119
+ return res_array
120
+
121
+ return obj
File without changes
@@ -0,0 +1,80 @@
1
+ class ElementSobolevSpace(object):
2
+ """
3
+ Representation of a Sobolev space on a single cell
4
+
5
+ :param: *underlying_space*: The UFL representation of the Sobolev Space
6
+ :param: *domain*: (Optional) the cell defined over- if not originally provided it should be provided during use.
7
+
8
+ """
9
+
10
+ def __init__(self, parents, domain=None):
11
+ self.domain = domain
12
+ self.parents = parents
13
+
14
+ def __lt__(self, other):
15
+ """In common with intrinsic Python sets, < indicates "is a proper subset of"."""
16
+ return any([isinstance(other, p) for p in self.parents])
17
+
18
+ def _to_dict(self):
19
+ return {"space": str(self)}
20
+
21
+ def dict_id(self):
22
+ return "SobolevSpace"
23
+
24
+ def _from_dict(obj_dict):
25
+ space_name = obj_dict["space"]
26
+ if space_name == "L2":
27
+ return CellL2
28
+ elif space_name == "H1":
29
+ return CellH1
30
+ elif space_name == "HDiv":
31
+ return CellHDiv
32
+ elif space_name == "HCurl":
33
+ return CellHCurl
34
+ elif space_name == "H2":
35
+ return CellH2
36
+
37
+
38
+ class CellH1(ElementSobolevSpace):
39
+
40
+ def __init__(self, cell):
41
+ super(CellH1, self).__init__([CellL2, CellHDiv, CellHCurl], cell)
42
+
43
+ def __repr__(self):
44
+ return "H1"
45
+
46
+
47
+ class CellHDiv(ElementSobolevSpace):
48
+
49
+ def __init__(self, cell):
50
+ super(CellHDiv, self).__init__([CellL2], cell)
51
+
52
+ def __repr__(self):
53
+ return "HDiv"
54
+
55
+
56
+ class CellHCurl(ElementSobolevSpace):
57
+
58
+ def __init__(self, cell):
59
+ super(CellHCurl, self).__init__([CellL2], cell)
60
+
61
+ def __repr__(self):
62
+ return "HCurl"
63
+
64
+
65
+ class CellH2(ElementSobolevSpace):
66
+
67
+ def __init__(self, cell):
68
+ super(CellH2, self).__init__([CellL2, CellHDiv, CellHCurl, CellH1], cell)
69
+
70
+ def __repr__(self):
71
+ return "H2"
72
+
73
+
74
+ class CellL2(ElementSobolevSpace):
75
+
76
+ def __init__(self, cell):
77
+ super(CellL2, self).__init__([], cell)
78
+
79
+ def __repr__(self):
80
+ return "L2"