GroupsMath 0.5.0__tar.gz → 0.6.0__tar.gz
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.
- {groupsmath-0.5.0 → groupsmath-0.6.0}/GroupsMath.egg-info/PKG-INFO +22 -14
- {groupsmath-0.5.0 → groupsmath-0.6.0}/PKG-INFO +22 -14
- {groupsmath-0.5.0 → groupsmath-0.6.0}/README.md +21 -13
- {groupsmath-0.5.0 → groupsmath-0.6.0}/groupsmath/core.py +382 -8
- {groupsmath-0.5.0 → groupsmath-0.6.0}/pyproject.toml +1 -1
- {groupsmath-0.5.0 → groupsmath-0.6.0}/GroupsMath.egg-info/SOURCES.txt +0 -0
- {groupsmath-0.5.0 → groupsmath-0.6.0}/GroupsMath.egg-info/dependency_links.txt +0 -0
- {groupsmath-0.5.0 → groupsmath-0.6.0}/GroupsMath.egg-info/requires.txt +0 -0
- {groupsmath-0.5.0 → groupsmath-0.6.0}/GroupsMath.egg-info/top_level.txt +0 -0
- {groupsmath-0.5.0 → groupsmath-0.6.0}/LICENSE +0 -0
- {groupsmath-0.5.0 → groupsmath-0.6.0}/groupsmath/__init__.py +0 -0
- {groupsmath-0.5.0 → groupsmath-0.6.0}/groupsmath/matrixgroups.py +0 -0
- {groupsmath-0.5.0 → groupsmath-0.6.0}/groupsmath/precooked.py +0 -0
- {groupsmath-0.5.0 → groupsmath-0.6.0}/groupsmath/shortcuts.py +0 -0
- {groupsmath-0.5.0 → groupsmath-0.6.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GroupsMath
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: The educational Python library for Group Theory
|
|
5
5
|
Author-email: Mario Sultan Romero <mariosultan.uem@gmail.com>
|
|
6
6
|
License: PolyForm Strict License 1.0.0.
|
|
@@ -20,16 +20,27 @@ Requires-Dist: matplotlib>=3.0.0
|
|
|
20
20
|
Requires-Dist: numpy>=1.20.0
|
|
21
21
|
Dynamic: license-file
|
|
22
22
|
|
|
23
|
-
# GroupsMath – v0.
|
|
23
|
+
# GroupsMath – v0.6.0
|
|
24
|
+
|
|
25
|
+
[](https://pypistats.org/packages/groupsmath)
|
|
26
|
+
[](https://pypi.org/project/groupsmath/)
|
|
27
|
+
[](https://pypi.org/project/groupsmath/)
|
|
28
|
+

|
|
29
|
+

|
|
30
|
+
<!--  -->
|
|
31
|
+
|
|
24
32
|
|
|
25
33
|

|
|
26
34
|
|
|
27
35
|
|
|
28
36
|
**GroupsMath** is a Python module for constructing, studying, and manipulating finite and infinite groups.
|
|
29
37
|
|
|
30
|
-
The project aims to provide a simple and intuitive interface for working with
|
|
38
|
+
The project aims to provide a **simple** and **intuitive** interface for working with groups and their algebraic properties.
|
|
39
|
+
|
|
40
|
+

|
|
41
|
+
|
|
42
|
+
> **⚠️ Notice:** Version v1.0.0 is nearing its official release.
|
|
31
43
|
|
|
32
|
-
> **Note:** GroupsMath is currently under active development. Its API is still evolving and may change before version `1.0.0`.
|
|
33
44
|
|
|
34
45
|
---
|
|
35
46
|
|
|
@@ -51,7 +62,7 @@ or visit https://github.com/MarioSultan/GroupsMath and download all the files.
|
|
|
51
62
|
|
|
52
63
|
---
|
|
53
64
|
|
|
54
|
-
##
|
|
65
|
+
## Quick start
|
|
55
66
|
|
|
56
67
|
### A program in GroupsMath
|
|
57
68
|
```python
|
|
@@ -102,14 +113,18 @@ GroupsMath is designed to provide tools for working with finite (and infinite) g
|
|
|
102
113
|
* Determination of the order of a group and its elements.
|
|
103
114
|
* Operation with group elements (multiplication, inverse, powers, ...)
|
|
104
115
|
* Computation of Cayley tables with `matplotlib.pyplot`.
|
|
105
|
-
* Detection of algebraic properties such as cyclicity and
|
|
116
|
+
* Detection of algebraic properties such as cyclicity, commutativity and solubility.
|
|
117
|
+
* Centralizers, conjugacy classes and conmutator.
|
|
106
118
|
* Computation and exploration of other structural properties of finite groups.
|
|
107
119
|
* Predefined group families in the library `groupsmath.precooked`, such as $C_n$, $S_n$, $A_n$, $D_n$, $V_4$, $Q_8$, ...
|
|
120
|
+
* Isomorphisms between groups.
|
|
108
121
|
|
|
109
122
|
**SUBGROUPS:**
|
|
110
123
|
* Study of subgroups (order, cosets, normal subgroups, quotients, ...).
|
|
111
124
|
* Normal subgroups.
|
|
112
125
|
* Quotient groups $Q=G/H$.
|
|
126
|
+
* Conmutator and derived subgroups $G'$.
|
|
127
|
+
* Abelianization $G/G'$
|
|
113
128
|
|
|
114
129
|
**AUTOMORPHISMS:**
|
|
115
130
|
* Class `Automorphism` and automorphism functions.
|
|
@@ -138,6 +153,7 @@ The GroupsMath team is currently working on theese ideas:
|
|
|
138
153
|
* Representation theory.
|
|
139
154
|
* and much more...
|
|
140
155
|
|
|
156
|
+
|
|
141
157
|
---
|
|
142
158
|
|
|
143
159
|
## Documentation
|
|
@@ -146,14 +162,6 @@ Detailed documentation is available in the [`docs`](https://github.com/MarioSult
|
|
|
146
162
|
|
|
147
163
|
---
|
|
148
164
|
|
|
149
|
-
## Project status
|
|
150
|
-
|
|
151
|
-
GroupsMath is currently under active development.
|
|
152
|
-
|
|
153
|
-
The project has successfully transitioned to an object-oriented interface centered around the `Group` class. The legacy function-based interface is being removed for future versions. The API should therefore be considered unstable until the `1.0.0` release, and breaking changes may occur between development versions.
|
|
154
|
-
|
|
155
|
-
---
|
|
156
|
-
|
|
157
165
|
## Contributing
|
|
158
166
|
|
|
159
167
|
Contributions, suggestions, and bug reports are welcome. If you find a bug or have an idea for improving GroupsMath, please open an issue in the GitHub repository.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GroupsMath
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: The educational Python library for Group Theory
|
|
5
5
|
Author-email: Mario Sultan Romero <mariosultan.uem@gmail.com>
|
|
6
6
|
License: PolyForm Strict License 1.0.0.
|
|
@@ -20,16 +20,27 @@ Requires-Dist: matplotlib>=3.0.0
|
|
|
20
20
|
Requires-Dist: numpy>=1.20.0
|
|
21
21
|
Dynamic: license-file
|
|
22
22
|
|
|
23
|
-
# GroupsMath – v0.
|
|
23
|
+
# GroupsMath – v0.6.0
|
|
24
|
+
|
|
25
|
+
[](https://pypistats.org/packages/groupsmath)
|
|
26
|
+
[](https://pypi.org/project/groupsmath/)
|
|
27
|
+
[](https://pypi.org/project/groupsmath/)
|
|
28
|
+

|
|
29
|
+

|
|
30
|
+
<!--  -->
|
|
31
|
+
|
|
24
32
|
|
|
25
33
|

|
|
26
34
|
|
|
27
35
|
|
|
28
36
|
**GroupsMath** is a Python module for constructing, studying, and manipulating finite and infinite groups.
|
|
29
37
|
|
|
30
|
-
The project aims to provide a simple and intuitive interface for working with
|
|
38
|
+
The project aims to provide a **simple** and **intuitive** interface for working with groups and their algebraic properties.
|
|
39
|
+
|
|
40
|
+

|
|
41
|
+
|
|
42
|
+
> **⚠️ Notice:** Version v1.0.0 is nearing its official release.
|
|
31
43
|
|
|
32
|
-
> **Note:** GroupsMath is currently under active development. Its API is still evolving and may change before version `1.0.0`.
|
|
33
44
|
|
|
34
45
|
---
|
|
35
46
|
|
|
@@ -51,7 +62,7 @@ or visit https://github.com/MarioSultan/GroupsMath and download all the files.
|
|
|
51
62
|
|
|
52
63
|
---
|
|
53
64
|
|
|
54
|
-
##
|
|
65
|
+
## Quick start
|
|
55
66
|
|
|
56
67
|
### A program in GroupsMath
|
|
57
68
|
```python
|
|
@@ -102,14 +113,18 @@ GroupsMath is designed to provide tools for working with finite (and infinite) g
|
|
|
102
113
|
* Determination of the order of a group and its elements.
|
|
103
114
|
* Operation with group elements (multiplication, inverse, powers, ...)
|
|
104
115
|
* Computation of Cayley tables with `matplotlib.pyplot`.
|
|
105
|
-
* Detection of algebraic properties such as cyclicity and
|
|
116
|
+
* Detection of algebraic properties such as cyclicity, commutativity and solubility.
|
|
117
|
+
* Centralizers, conjugacy classes and conmutator.
|
|
106
118
|
* Computation and exploration of other structural properties of finite groups.
|
|
107
119
|
* Predefined group families in the library `groupsmath.precooked`, such as $C_n$, $S_n$, $A_n$, $D_n$, $V_4$, $Q_8$, ...
|
|
120
|
+
* Isomorphisms between groups.
|
|
108
121
|
|
|
109
122
|
**SUBGROUPS:**
|
|
110
123
|
* Study of subgroups (order, cosets, normal subgroups, quotients, ...).
|
|
111
124
|
* Normal subgroups.
|
|
112
125
|
* Quotient groups $Q=G/H$.
|
|
126
|
+
* Conmutator and derived subgroups $G'$.
|
|
127
|
+
* Abelianization $G/G'$
|
|
113
128
|
|
|
114
129
|
**AUTOMORPHISMS:**
|
|
115
130
|
* Class `Automorphism` and automorphism functions.
|
|
@@ -138,6 +153,7 @@ The GroupsMath team is currently working on theese ideas:
|
|
|
138
153
|
* Representation theory.
|
|
139
154
|
* and much more...
|
|
140
155
|
|
|
156
|
+
|
|
141
157
|
---
|
|
142
158
|
|
|
143
159
|
## Documentation
|
|
@@ -146,14 +162,6 @@ Detailed documentation is available in the [`docs`](https://github.com/MarioSult
|
|
|
146
162
|
|
|
147
163
|
---
|
|
148
164
|
|
|
149
|
-
## Project status
|
|
150
|
-
|
|
151
|
-
GroupsMath is currently under active development.
|
|
152
|
-
|
|
153
|
-
The project has successfully transitioned to an object-oriented interface centered around the `Group` class. The legacy function-based interface is being removed for future versions. The API should therefore be considered unstable until the `1.0.0` release, and breaking changes may occur between development versions.
|
|
154
|
-
|
|
155
|
-
---
|
|
156
|
-
|
|
157
165
|
## Contributing
|
|
158
166
|
|
|
159
167
|
Contributions, suggestions, and bug reports are welcome. If you find a bug or have an idea for improving GroupsMath, please open an issue in the GitHub repository.
|
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
# GroupsMath – v0.
|
|
1
|
+
# GroupsMath – v0.6.0
|
|
2
|
+
|
|
3
|
+
[](https://pypistats.org/packages/groupsmath)
|
|
4
|
+
[](https://pypi.org/project/groupsmath/)
|
|
5
|
+
[](https://pypi.org/project/groupsmath/)
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
<!--  -->
|
|
9
|
+
|
|
2
10
|
|
|
3
11
|

|
|
4
12
|
|
|
5
13
|
|
|
6
14
|
**GroupsMath** is a Python module for constructing, studying, and manipulating finite and infinite groups.
|
|
7
15
|
|
|
8
|
-
The project aims to provide a simple and intuitive interface for working with
|
|
16
|
+
The project aims to provide a **simple** and **intuitive** interface for working with groups and their algebraic properties.
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
> **⚠️ Notice:** Version v1.0.0 is nearing its official release.
|
|
9
21
|
|
|
10
|
-
> **Note:** GroupsMath is currently under active development. Its API is still evolving and may change before version `1.0.0`.
|
|
11
22
|
|
|
12
23
|
---
|
|
13
24
|
|
|
@@ -29,7 +40,7 @@ or visit https://github.com/MarioSultan/GroupsMath and download all the files.
|
|
|
29
40
|
|
|
30
41
|
---
|
|
31
42
|
|
|
32
|
-
##
|
|
43
|
+
## Quick start
|
|
33
44
|
|
|
34
45
|
### A program in GroupsMath
|
|
35
46
|
```python
|
|
@@ -80,14 +91,18 @@ GroupsMath is designed to provide tools for working with finite (and infinite) g
|
|
|
80
91
|
* Determination of the order of a group and its elements.
|
|
81
92
|
* Operation with group elements (multiplication, inverse, powers, ...)
|
|
82
93
|
* Computation of Cayley tables with `matplotlib.pyplot`.
|
|
83
|
-
* Detection of algebraic properties such as cyclicity and
|
|
94
|
+
* Detection of algebraic properties such as cyclicity, commutativity and solubility.
|
|
95
|
+
* Centralizers, conjugacy classes and conmutator.
|
|
84
96
|
* Computation and exploration of other structural properties of finite groups.
|
|
85
97
|
* Predefined group families in the library `groupsmath.precooked`, such as $C_n$, $S_n$, $A_n$, $D_n$, $V_4$, $Q_8$, ...
|
|
98
|
+
* Isomorphisms between groups.
|
|
86
99
|
|
|
87
100
|
**SUBGROUPS:**
|
|
88
101
|
* Study of subgroups (order, cosets, normal subgroups, quotients, ...).
|
|
89
102
|
* Normal subgroups.
|
|
90
103
|
* Quotient groups $Q=G/H$.
|
|
104
|
+
* Conmutator and derived subgroups $G'$.
|
|
105
|
+
* Abelianization $G/G'$
|
|
91
106
|
|
|
92
107
|
**AUTOMORPHISMS:**
|
|
93
108
|
* Class `Automorphism` and automorphism functions.
|
|
@@ -116,6 +131,7 @@ The GroupsMath team is currently working on theese ideas:
|
|
|
116
131
|
* Representation theory.
|
|
117
132
|
* and much more...
|
|
118
133
|
|
|
134
|
+
|
|
119
135
|
---
|
|
120
136
|
|
|
121
137
|
## Documentation
|
|
@@ -124,14 +140,6 @@ Detailed documentation is available in the [`docs`](https://github.com/MarioSult
|
|
|
124
140
|
|
|
125
141
|
---
|
|
126
142
|
|
|
127
|
-
## Project status
|
|
128
|
-
|
|
129
|
-
GroupsMath is currently under active development.
|
|
130
|
-
|
|
131
|
-
The project has successfully transitioned to an object-oriented interface centered around the `Group` class. The legacy function-based interface is being removed for future versions. The API should therefore be considered unstable until the `1.0.0` release, and breaking changes may occur between development versions.
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
143
|
## Contributing
|
|
136
144
|
|
|
137
145
|
Contributions, suggestions, and bug reports are welcome. If you find a bug or have an idea for improving GroupsMath, please open an issue in the GitHub repository.
|
|
@@ -21,7 +21,7 @@ from math import gcd
|
|
|
21
21
|
|
|
22
22
|
#################### DEFINITIONS ####################
|
|
23
23
|
|
|
24
|
-
__version__ = "0.
|
|
24
|
+
__version__ = "0.6.0"
|
|
25
25
|
__errorcolor__ = "\033[31m"
|
|
26
26
|
|
|
27
27
|
tgl_color = "#2A646E"
|
|
@@ -50,7 +50,7 @@ def groupsmath_info():
|
|
|
50
50
|
print("———————————————————————————————————————————————————————————————————————————")
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
####################
|
|
53
|
+
#################### CLASSES ####################
|
|
54
54
|
|
|
55
55
|
class Group(ABC):
|
|
56
56
|
@classmethod
|
|
@@ -242,6 +242,95 @@ class ExplicitGroup(Group):
|
|
|
242
242
|
if subgroup.group is not self:
|
|
243
243
|
raise ValueError(__errorcolor__+"The subgroup does not belong to this group")
|
|
244
244
|
return subgroup.quotient()
|
|
245
|
+
|
|
246
|
+
def centralizer(self, element):
|
|
247
|
+
if element not in self.elements:
|
|
248
|
+
raise ValueError(__errorcolor__ + f"Element '{element}' is not in group")
|
|
249
|
+
|
|
250
|
+
cent = [g for g in self.elements if self.function(g, element) == self.function(element, g)]
|
|
251
|
+
sub_grp = ExplicitGroup(cent, self.function, _skip_validation=True)
|
|
252
|
+
return ExplicitSubgroup(sub_grp, self)
|
|
253
|
+
|
|
254
|
+
def conjugacy_class(self, element):
|
|
255
|
+
if element not in self.elements:
|
|
256
|
+
raise ValueError(__errorcolor__ + f"Element '{element}' is not in group")
|
|
257
|
+
|
|
258
|
+
c_class = set()
|
|
259
|
+
for g in self.elements:
|
|
260
|
+
g_inv = self.inverse(g)
|
|
261
|
+
# g * element * g^-1
|
|
262
|
+
conj = self.function(self.function(g, element), g_inv)
|
|
263
|
+
c_class.add(conj)
|
|
264
|
+
return list(c_class)
|
|
265
|
+
|
|
266
|
+
def conjugacy_classes(self):
|
|
267
|
+
classes = []
|
|
268
|
+
visited = set()
|
|
269
|
+
for e in self.elements:
|
|
270
|
+
if e not in visited:
|
|
271
|
+
c = self.conjugacy_class(e)
|
|
272
|
+
classes.append(c)
|
|
273
|
+
visited.update(c)
|
|
274
|
+
return classes
|
|
275
|
+
|
|
276
|
+
def commutator(self, a, b):
|
|
277
|
+
if a not in self.elements or b not in self.elements:
|
|
278
|
+
raise ValueError(__errorcolor__ + "Elements must belong to group")
|
|
279
|
+
a_inv = self.inverse(a)
|
|
280
|
+
b_inv = self.inverse(b)
|
|
281
|
+
return self.function(self.function(self.function(a, b), a_inv), b_inv)
|
|
282
|
+
|
|
283
|
+
def commutator_subgroup(self):
|
|
284
|
+
generators = set()
|
|
285
|
+
for a in self.elements:
|
|
286
|
+
for b in self.elements:
|
|
287
|
+
generators.add(self.commutator(a, b))
|
|
288
|
+
|
|
289
|
+
# Clausura del conjunto de conmutadores
|
|
290
|
+
elements = list(generators)
|
|
291
|
+
changed = True
|
|
292
|
+
while changed:
|
|
293
|
+
changed = False
|
|
294
|
+
for x in list(elements):
|
|
295
|
+
for y in list(elements):
|
|
296
|
+
prod = self.function(x, y)
|
|
297
|
+
if prod not in elements:
|
|
298
|
+
elements.append(prod)
|
|
299
|
+
changed = True
|
|
300
|
+
|
|
301
|
+
sub_grp = ExplicitGroup(elements, self.function, _skip_validation=True)
|
|
302
|
+
return ExplicitSubgroup(sub_grp, self)
|
|
303
|
+
|
|
304
|
+
def derived_subgroup(self, n=1):
|
|
305
|
+
if n < 0:
|
|
306
|
+
raise ValueError(__errorcolor__ + "n must be a non-negative integer")
|
|
307
|
+
if n == 0:
|
|
308
|
+
return ExplicitSubgroup(self, self)
|
|
309
|
+
|
|
310
|
+
current = self.commutator_subgroup()
|
|
311
|
+
for _ in range(n - 1):
|
|
312
|
+
current = current.commutator_subgroup()
|
|
313
|
+
return current
|
|
314
|
+
|
|
315
|
+
def derived_series(self):
|
|
316
|
+
series = [ExplicitSubgroup(self, self)]
|
|
317
|
+
current = self.commutator_subgroup()
|
|
318
|
+
series.append(current)
|
|
319
|
+
|
|
320
|
+
while True:
|
|
321
|
+
next_sub = current.commutator_subgroup()
|
|
322
|
+
if len(next_sub) == len(current):
|
|
323
|
+
break
|
|
324
|
+
series.append(next_sub)
|
|
325
|
+
current = next_sub
|
|
326
|
+
|
|
327
|
+
return series
|
|
328
|
+
|
|
329
|
+
def abelianization(self):
|
|
330
|
+
return self.quotient(self.commutator_subgroup())
|
|
331
|
+
|
|
332
|
+
def is_solvable(self):
|
|
333
|
+
return len(self.derived_series()[-1]) == 1
|
|
245
334
|
|
|
246
335
|
#–> AUTOMORPHISMS
|
|
247
336
|
|
|
@@ -254,6 +343,29 @@ class ExplicitGroup(Group):
|
|
|
254
343
|
def automorphism_group(self): # REVISAR (Aunque seguramente se quede así)
|
|
255
344
|
return self.toCayleyGroup().automorphism_group().toExplicitGroup()
|
|
256
345
|
|
|
346
|
+
#–> GENERATORS
|
|
347
|
+
|
|
348
|
+
def generators(self):
|
|
349
|
+
"""Devuelve un conjunto de generadores que genera todo el grupo."""
|
|
350
|
+
cayley_grp = self.toCayleyGroup()
|
|
351
|
+
gen_indices = cayley_grp.generators()
|
|
352
|
+
return [self.elements[i] for i in gen_indices]
|
|
353
|
+
|
|
354
|
+
def generates(self, subset):
|
|
355
|
+
"""Comprueba si un subconjunto de elementos genera el grupo."""
|
|
356
|
+
if not all(e in self.elements for e in subset):
|
|
357
|
+
raise ValueError(__errorcolor__ + "All elements in subset must belong to the group.")
|
|
358
|
+
|
|
359
|
+
cayley_grp = self.toCayleyGroup()
|
|
360
|
+
subset_indices = [self.elements.index(e) for e in subset]
|
|
361
|
+
sub_indices = _subgroup_generated_by(cayley_grp.cayley, subset_indices)
|
|
362
|
+
return len(sub_indices) == len(self)
|
|
363
|
+
|
|
364
|
+
#-> ISOMORPHISMS
|
|
365
|
+
|
|
366
|
+
def is_isomorphic_to(self, target):
|
|
367
|
+
return self.toCayleyGroup().is_isomorphic_to(target)
|
|
368
|
+
|
|
257
369
|
class CayleyGroup(Group):
|
|
258
370
|
|
|
259
371
|
def __init__(self, cayley, elements=None, _skip_validation=False):
|
|
@@ -497,6 +609,103 @@ class CayleyGroup(Group):
|
|
|
497
609
|
raise ValueError(__errorcolor__+"The subgroup does not belong to this group")
|
|
498
610
|
return subgroup.quotient()
|
|
499
611
|
|
|
612
|
+
def centralizer(self, element):
|
|
613
|
+
if element not in self.elements:
|
|
614
|
+
raise ValueError(__errorcolor__ + f"Element '{element}' is not in group")
|
|
615
|
+
|
|
616
|
+
a_idx = self._dict[element]
|
|
617
|
+
cent_indices = [g_idx for g_idx in range(self.order()) if self.cayley[g_idx][a_idx] == self.cayley[a_idx][g_idx]]
|
|
618
|
+
|
|
619
|
+
sub_names = [self.elements[i] for i in cent_indices]
|
|
620
|
+
sub_matrix = _reset_renaming(_subset(self.cayley, cent_indices))
|
|
621
|
+
sub_grp = CayleyGroup(sub_matrix, sub_names, _skip_validation=True)
|
|
622
|
+
return CayleySubgroup(sub_grp, self)
|
|
623
|
+
|
|
624
|
+
def conjugacy_class(self, element):
|
|
625
|
+
if element not in self.elements:
|
|
626
|
+
raise ValueError(__errorcolor__ + f"Element '{element}' is not in group")
|
|
627
|
+
|
|
628
|
+
a_idx = self._dict[element]
|
|
629
|
+
c_class_indices = set()
|
|
630
|
+
for g_idx in range(self.order()):
|
|
631
|
+
g_inv_idx = self.cayley[g_idx].index(self._dict[self.identity()])
|
|
632
|
+
conj_idx = self.cayley[self.cayley[g_idx][a_idx]][g_inv_idx]
|
|
633
|
+
c_class_indices.add(conj_idx)
|
|
634
|
+
return [self.elements[i] for i in c_class_indices]
|
|
635
|
+
|
|
636
|
+
def conjugacy_classes(self):
|
|
637
|
+
classes = []
|
|
638
|
+
visited = set()
|
|
639
|
+
for e in self.elements:
|
|
640
|
+
if e not in visited:
|
|
641
|
+
c = self.conjugacy_class(e)
|
|
642
|
+
classes.append(c)
|
|
643
|
+
visited.update(c)
|
|
644
|
+
return classes
|
|
645
|
+
|
|
646
|
+
def commutator(self, a, b):
|
|
647
|
+
return self.operation(self.operation(self.operation(a,b),self.inverse(a)),self.inverse(b))
|
|
648
|
+
|
|
649
|
+
def commutator_subgroup(self):
|
|
650
|
+
e_idx = self._dict[self.identity()]
|
|
651
|
+
gen_indices = set()
|
|
652
|
+
for a in range(self.order()):
|
|
653
|
+
for b in range(self.order()):
|
|
654
|
+
a_inv = self.cayley[a].index(e_idx)
|
|
655
|
+
b_inv = self.cayley[b].index(e_idx)
|
|
656
|
+
comm = self.cayley[self.cayley[self.cayley[a][b]][a_inv]][b_inv]
|
|
657
|
+
gen_indices.add(comm)
|
|
658
|
+
|
|
659
|
+
# Clausura
|
|
660
|
+
indices = list(gen_indices)
|
|
661
|
+
changed = True
|
|
662
|
+
while changed:
|
|
663
|
+
changed = False
|
|
664
|
+
for x in list(indices):
|
|
665
|
+
for y in list(indices):
|
|
666
|
+
prod = self.cayley[x][y]
|
|
667
|
+
if prod not in indices:
|
|
668
|
+
indices.append(prod)
|
|
669
|
+
changed = True
|
|
670
|
+
|
|
671
|
+
sub_names = [self.elements[i] for i in indices]
|
|
672
|
+
sub_matrix = _reset_renaming(_subset(self.cayley, indices))
|
|
673
|
+
sub_grp = CayleyGroup(sub_matrix, sub_names, _skip_validation=True)
|
|
674
|
+
return CayleySubgroup(sub_grp, self)
|
|
675
|
+
|
|
676
|
+
def derived_subgroup(self, n=1):
|
|
677
|
+
if n < 0:
|
|
678
|
+
raise ValueError(__errorcolor__ + "n must be a non-negative integer")
|
|
679
|
+
if n == 0:
|
|
680
|
+
total_grp = CayleyGroup(self.cayley, self.elements)
|
|
681
|
+
return CayleySubgroup(total_grp, self)
|
|
682
|
+
|
|
683
|
+
current = self.commutator_subgroup()
|
|
684
|
+
for _ in range(n - 1):
|
|
685
|
+
current = current.commutator_subgroup()
|
|
686
|
+
return current
|
|
687
|
+
|
|
688
|
+
def derived_series(self):
|
|
689
|
+
total_grp = CayleyGroup(self.cayley, self.elements)
|
|
690
|
+
series = [CayleySubgroup(total_grp, self)]
|
|
691
|
+
current = self.commutator_subgroup()
|
|
692
|
+
series.append(current)
|
|
693
|
+
|
|
694
|
+
while True:
|
|
695
|
+
next_sub = current.commutator_subgroup()
|
|
696
|
+
if len(next_sub) == len(current):
|
|
697
|
+
break
|
|
698
|
+
series.append(next_sub)
|
|
699
|
+
current = next_sub
|
|
700
|
+
|
|
701
|
+
return series
|
|
702
|
+
|
|
703
|
+
def abelianization(self):
|
|
704
|
+
return self.quotient(self.commutator_subgroup())
|
|
705
|
+
|
|
706
|
+
def is_solvable(self):
|
|
707
|
+
return len(self.derived_series()[-1]) == 1
|
|
708
|
+
|
|
500
709
|
#–> AUTOMORPHISMS
|
|
501
710
|
|
|
502
711
|
def is_automorphism(self, phi):
|
|
@@ -556,6 +765,133 @@ class CayleyGroup(Group):
|
|
|
556
765
|
names_aut = [str(auts[i]) for i in range(n)]
|
|
557
766
|
return CayleyGroup(cayley_aut, names_aut, _skip_validation=True)
|
|
558
767
|
|
|
768
|
+
#–> GENERATORS
|
|
769
|
+
|
|
770
|
+
def generators(self):
|
|
771
|
+
"""Devuelve una lista con los elementos de un conjunto generador minimal."""
|
|
772
|
+
e_idx = self.identity()
|
|
773
|
+
current_subgroup = {e_idx}
|
|
774
|
+
generators_indices = []
|
|
775
|
+
|
|
776
|
+
while len(current_subgroup) < self.order():
|
|
777
|
+
# Buscar el elemento fuera del subgrupo actual que maximice la clausura
|
|
778
|
+
best_candidate = None
|
|
779
|
+
best_closure = current_subgroup
|
|
780
|
+
|
|
781
|
+
for candidate in range(self.order()):
|
|
782
|
+
if candidate not in current_subgroup:
|
|
783
|
+
test_gens = generators_indices + [candidate]
|
|
784
|
+
closure = set(_subgroup_generated_by(self.cayley, test_gens))
|
|
785
|
+
|
|
786
|
+
if len(closure) > len(best_closure):
|
|
787
|
+
best_closure = closure
|
|
788
|
+
best_candidate = candidate
|
|
789
|
+
|
|
790
|
+
# Si ya alcanzamos todo el grupo, terminamos temprano
|
|
791
|
+
if len(closure) == self.order():
|
|
792
|
+
generators_indices.append(candidate)
|
|
793
|
+
return [self.elements[i] for i in generators_indices]
|
|
794
|
+
|
|
795
|
+
generators_indices.append(best_candidate)
|
|
796
|
+
current_subgroup = best_closure
|
|
797
|
+
|
|
798
|
+
return [self.elements[i] for i in generators_indices]
|
|
799
|
+
|
|
800
|
+
def generates(self, subset):
|
|
801
|
+
"""Comprueba si un subconjunto de elementos genera el grupo."""
|
|
802
|
+
if not all(e in self.elements for e in subset):
|
|
803
|
+
raise ValueError(__errorcolor__ + "All elements in subset must belong to the group.")
|
|
804
|
+
|
|
805
|
+
subset_indices = [self._dict[e] for e in subset]
|
|
806
|
+
sub_indices = _subgroup_generated_by(self.cayley, subset_indices)
|
|
807
|
+
return len(sub_indices) == self.order()
|
|
808
|
+
|
|
809
|
+
#-> ISOMORPHISMS
|
|
810
|
+
|
|
811
|
+
def is_isomorphic_to(self, target): #Revisar, pues es demasiado grande y algo puede salir mal
|
|
812
|
+
"""Comprueba si el grupo actual es isomorfo al grupo 'target'."""
|
|
813
|
+
# Convertir a CayleyGroup si se pasa un ExplicitGroup
|
|
814
|
+
if hasattr(target, "toCayleyGroup"):
|
|
815
|
+
H = target.toCayleyGroup()
|
|
816
|
+
else:
|
|
817
|
+
H = target
|
|
818
|
+
|
|
819
|
+
G = self
|
|
820
|
+
|
|
821
|
+
# --- FASE 1: Filtros de Invariantes Algebraicos ---
|
|
822
|
+
if G.order() != H.order():
|
|
823
|
+
return False
|
|
824
|
+
|
|
825
|
+
if G.is_abelian() != H.is_abelian():
|
|
826
|
+
return False
|
|
827
|
+
|
|
828
|
+
if G.order_distribution() != H.order_distribution():
|
|
829
|
+
return False
|
|
830
|
+
|
|
831
|
+
if len(G.center()) != len(H.center()):
|
|
832
|
+
return False
|
|
833
|
+
|
|
834
|
+
# --- FASE 2: Búsqueda de Isomorfismo vía Generadores ---
|
|
835
|
+
n = G.order()
|
|
836
|
+
|
|
837
|
+
# 1. Obtener generadores del grupo G (por índices)
|
|
838
|
+
e_idx_G = self._dict[G.identity()]
|
|
839
|
+
gens_G = G.generators()
|
|
840
|
+
gens_G_idx = [G._dict[g] for g in gens_G]
|
|
841
|
+
|
|
842
|
+
# 2. Candidatos en H para cada generador de G (deben tener el mismo orden)
|
|
843
|
+
orders_G = G.element_orders()
|
|
844
|
+
orders_H = H.element_orders()
|
|
845
|
+
|
|
846
|
+
candidates_per_gen = []
|
|
847
|
+
for g_idx in gens_G_idx:
|
|
848
|
+
g_order = orders_G[g_idx]
|
|
849
|
+
candidates = [h_idx for h_idx, o in enumerate(orders_H) if o == g_order]
|
|
850
|
+
candidates_per_gen.append(candidates)
|
|
851
|
+
|
|
852
|
+
# 3. Probar asignaciones de generadores
|
|
853
|
+
for tuple_h_indices in product(*candidates_per_gen):
|
|
854
|
+
# Mapear generadores de G -> H
|
|
855
|
+
mapping = {e_idx_G: H._dict[H.identity()]}
|
|
856
|
+
queue = [e_idx_G]
|
|
857
|
+
|
|
858
|
+
# Extender el mapeo a todo el grupo a través de la multiplicación por generadores
|
|
859
|
+
valid_extension = True
|
|
860
|
+
|
|
861
|
+
while queue and valid_extension:
|
|
862
|
+
curr = queue.pop(0)
|
|
863
|
+
for i, g_idx in enumerate(gens_G_idx):
|
|
864
|
+
h_gen = tuple_h_indices[i]
|
|
865
|
+
|
|
866
|
+
# Multiplicación curr * g en G
|
|
867
|
+
next_g = G.cayley[curr][g_idx]
|
|
868
|
+
expected_h = H.cayley[mapping[curr]][h_gen]
|
|
869
|
+
|
|
870
|
+
if next_g in mapping:
|
|
871
|
+
if mapping[next_g] != expected_h:
|
|
872
|
+
valid_extension = False
|
|
873
|
+
break
|
|
874
|
+
else:
|
|
875
|
+
mapping[next_g] = expected_h
|
|
876
|
+
queue.append(next_g)
|
|
877
|
+
|
|
878
|
+
# Comprobar si el mapeo generado es una biyección inyectiva sobre H y preserva toda la Cayley
|
|
879
|
+
if valid_extension and len(mapping) == n and len(set(mapping.values())) == n:
|
|
880
|
+
# Validación final de la preservación de la operación para todo (a, b)
|
|
881
|
+
is_iso = True
|
|
882
|
+
for a in range(n):
|
|
883
|
+
for b in range(n):
|
|
884
|
+
if mapping[G.cayley[a][b]] != H.cayley[mapping[a]][mapping[b]]:
|
|
885
|
+
is_iso = False
|
|
886
|
+
break
|
|
887
|
+
if not is_iso:
|
|
888
|
+
break
|
|
889
|
+
|
|
890
|
+
if is_iso:
|
|
891
|
+
return True
|
|
892
|
+
|
|
893
|
+
return False
|
|
894
|
+
|
|
559
895
|
class Subgroup(ABC):
|
|
560
896
|
pass
|
|
561
897
|
|
|
@@ -1126,6 +1462,30 @@ def _operate_cosets(G,C,A,B):
|
|
|
1126
1462
|
break
|
|
1127
1463
|
return r
|
|
1128
1464
|
|
|
1465
|
+
def _subgroup_generated_by(cayley, generators_indices):
|
|
1466
|
+
|
|
1467
|
+
generated = set(generators_indices)
|
|
1468
|
+
queue = list(generators_indices)
|
|
1469
|
+
|
|
1470
|
+
while queue:
|
|
1471
|
+
|
|
1472
|
+
current = queue.pop(0)
|
|
1473
|
+
|
|
1474
|
+
for g in generators_indices:
|
|
1475
|
+
|
|
1476
|
+
prod1 = cayley[current][g]
|
|
1477
|
+
prod2 = cayley[g][current]
|
|
1478
|
+
|
|
1479
|
+
if prod1 not in generated:
|
|
1480
|
+
generated.add(prod1)
|
|
1481
|
+
queue.append(prod1)
|
|
1482
|
+
|
|
1483
|
+
if prod2 not in generated:
|
|
1484
|
+
generated.add(prod2)
|
|
1485
|
+
queue.append(prod2)
|
|
1486
|
+
|
|
1487
|
+
return sorted(list(generated))
|
|
1488
|
+
|
|
1129
1489
|
def _order_preserving_permutations(orders):
|
|
1130
1490
|
|
|
1131
1491
|
classes = {}
|
|
@@ -1168,12 +1528,17 @@ def direct_product(A:CayleyGroup,B:CayleyGroup):
|
|
|
1168
1528
|
return CayleyGroup(G,names,_skip_validation=True)
|
|
1169
1529
|
|
|
1170
1530
|
def direct_power(G:CayleyGroup,n):
|
|
1171
|
-
if n
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1531
|
+
if n==2:
|
|
1532
|
+
return CayleyGroup([[0]],"e")
|
|
1533
|
+
elif n==1:
|
|
1534
|
+
return G
|
|
1535
|
+
elif n>2:
|
|
1536
|
+
H = G
|
|
1537
|
+
for i in range(n-1):
|
|
1538
|
+
H = direct_product(H,G)
|
|
1539
|
+
return H
|
|
1540
|
+
else:
|
|
1541
|
+
raise ValueError(__errorcolor__+"n must be a possitive integer.")
|
|
1177
1542
|
|
|
1178
1543
|
def semidirect_product(A: CayleyGroup, B: CayleyGroup, f: AutomorphismFunction):
|
|
1179
1544
|
G = []
|
|
@@ -1201,6 +1566,15 @@ def semidirect_product(A: CayleyGroup, B: CayleyGroup, f: AutomorphismFunction):
|
|
|
1201
1566
|
return CayleyGroup(G, names, _skip_validation=True)
|
|
1202
1567
|
|
|
1203
1568
|
|
|
1569
|
+
#################### ISOMORPHISMS ####################
|
|
1570
|
+
|
|
1571
|
+
def are_isomorphic(G: Group, H: Group) -> bool:
|
|
1572
|
+
"""Devuelve True si los grupos G y H son isomorfos, False en caso contrario."""
|
|
1573
|
+
if not isinstance(G, Group) or not isinstance(H, Group):
|
|
1574
|
+
raise TypeError(__errorcolor__ + "Both arguments must be instances of Group")
|
|
1575
|
+
return G.is_isomorphic_to(H)
|
|
1576
|
+
|
|
1577
|
+
|
|
1204
1578
|
#################### GENERATORS ####################
|
|
1205
1579
|
|
|
1206
1580
|
def cyclic_group(n):
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "GroupsMath" # Nombre en PyPI (ej: mi-paquete)
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.6.0"
|
|
8
8
|
description = "The educational Python library for Group Theory"
|
|
9
9
|
readme = "README.md" # Esta será la documentación visible en la portada de PyPI
|
|
10
10
|
requires-python = ">=3.8"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|