line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
=head1 Name |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
Math::Groups - Find automorphisms of groups and isomorphisms between groups. |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 Synopsis |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
use Math::Groups; |
8
|
|
|
|
|
|
|
use Data::Dump qw(dump); |
9
|
|
|
|
|
|
|
use Math::Cartesian::Product; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# Print a cyclic group of order 4 |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
print dump(Group{($_[0]*$_[1]) % 5} 1..4)."\n"; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# elements => { |
16
|
|
|
|
|
|
|
# 1 => { 1 => 1, 2 => 2, 3 => 3, 4 => 4 }, |
17
|
|
|
|
|
|
|
# 2 => { 1 => 2, 2 => 4, 3 => 1, 4 => 3 }, |
18
|
|
|
|
|
|
|
# 3 => { 1 => 3, 2 => 1, 3 => 4, 4 => 2 }, |
19
|
|
|
|
|
|
|
# 4 => { 1 => 4, 2 => 3, 3 => 2, 4 => 1 }, |
20
|
|
|
|
|
|
|
# }, |
21
|
|
|
|
|
|
|
# identity => 1, |
22
|
|
|
|
|
|
|
# inverses => { 1 => 1, 2 => 3, 3 => 2, 4 => 4 }, |
23
|
|
|
|
|
|
|
# orders => { 1 => 0, 2 => 4, 3 => 4, 4 => 2 }, |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# Find the automorphisms of the cyclic group of order 4 |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
autoMorphisms {print dump({@_})."\n"} |
29
|
|
|
|
|
|
|
Group{($_[0]+$_[1]) % 4} 0..3; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# { 1 => 1, 2 => 2, 3 => 3 } |
32
|
|
|
|
|
|
|
# { 1 => 3, 2 => 2, 3 => 1 } |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# Find the automorphisms of dihedral group of order 4 |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
my $corners = [cartesian {1} ([1,-1]) x 2]; |
37
|
|
|
|
|
|
|
my $cornerNumbers; |
38
|
|
|
|
|
|
|
map {my ($a, $b) = @{$$corners[$_]}; |
39
|
|
|
|
|
|
|
$cornerNumbers->{$a}{$b} = $_ |
40
|
|
|
|
|
|
|
} 0..$#$corners; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
autoMorphisms {print dump({@_})."\n"} |
43
|
|
|
|
|
|
|
Group |
44
|
|
|
|
|
|
|
{my ($a, $b, $c, $d) = map {@$_} @$corners[@_]; |
45
|
|
|
|
|
|
|
$cornerNumbers->{$a*$c}{$b*$d} |
46
|
|
|
|
|
|
|
} 0..$#$corners; |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# { 1 => 1, 2 => 2, 3 => 3 } |
49
|
|
|
|
|
|
|
# { 1 => 1, 2 => 3, 3 => 2 } |
50
|
|
|
|
|
|
|
# { 1 => 2, 2 => 1, 3 => 3 } |
51
|
|
|
|
|
|
|
# { 1 => 3, 2 => 1, 3 => 2 } |
52
|
|
|
|
|
|
|
# { 1 => 2, 2 => 3, 3 => 1 } |
53
|
|
|
|
|
|
|
# { 1 => 3, 2 => 2, 3 => 1 } |
54
|
|
|
|
|
|
|
=cut |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
package Math::Groups; |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
59
|
|
|
|
|
|
|
# Mathematical Groups |
60
|
|
|
|
|
|
|
# Philip R Brenan at gmail dot com, Appa Apps Ltd Inc, 2015 |
61
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
62
|
|
|
|
|
|
|
|
63
|
1
|
|
|
1
|
|
20121
|
use v5.18; |
|
1
|
|
|
|
|
3
|
|
64
|
1
|
|
|
1
|
|
4
|
use warnings FATAL => qw(all); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
44
|
|
65
|
1
|
|
|
1
|
|
11
|
use Carp; |
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
51
|
|
66
|
1
|
|
|
1
|
|
4
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
21
|
|
67
|
1
|
|
|
1
|
|
4
|
use utf8; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
68
|
1
|
|
|
1
|
|
720
|
use Math::Cartesian::Product; |
|
1
|
|
|
|
|
721
|
|
|
1
|
|
|
|
|
46
|
|
69
|
1
|
|
|
1
|
|
691
|
use Math::Permute::List; |
|
1
|
|
|
|
|
344
|
|
|
1
|
|
|
|
|
47
|
|
70
|
1
|
|
|
1
|
|
696
|
use Data::Dump qw(dump); |
|
1
|
|
|
|
|
7758
|
|
|
1
|
|
|
|
|
18481
|
|
71
|
|
|
|
|
|
|
|
72
|
360
|
|
|
360
|
0
|
949
|
sub Elements {qw(elements)} # Constants |
73
|
170
|
|
|
170
|
0
|
374
|
sub Identity {qw(identity)} |
74
|
39
|
|
|
39
|
0
|
73
|
sub Inverses {qw(inverses)} |
75
|
40
|
|
|
40
|
0
|
154
|
sub Orders {qw(orders)} |
76
|
|
|
|
|
|
|
|
77
|
204
|
|
|
204
|
0
|
342
|
sub 𝗲($) {$_[0]->{&Elements}} # Multiplication table |
78
|
159
|
|
|
159
|
0
|
290
|
sub e($) {$_[0]->{&Identity}} # Identity |
79
|
24
|
|
|
24
|
0
|
43
|
sub i($) {$_[0]->{&Inverses}} # Inverses |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub o($$$;$$) # Add one or two products to the group or retrieve a prior product |
82
|
0
|
|
|
0
|
0
|
0
|
{my ($g, $a, $b, $c, $𝗰) = @_; # Group, first element $a, second element $b, $a*$b, $b*$a |
83
|
0
|
|
|
|
|
0
|
my $𝗲 = 𝗲($g); # Elements |
84
|
0
|
0
|
|
|
|
0
|
if (@_ == 3) # Retrieve a prior product |
85
|
0
|
|
|
|
|
0
|
{return $𝗲->{$a}{$b}; |
86
|
|
|
|
|
|
|
} |
87
|
0
|
0
|
0
|
|
|
0
|
if (@_ == 4 or @_ == 5) # Add a product to the group for a*b |
88
|
0
|
|
|
|
|
0
|
{$g->{&Identity} = $g->{&Inverses} = undef; # Destroy cached identity and inverses as we have modified the group |
89
|
|
|
|
|
|
|
} |
90
|
0
|
0
|
|
|
|
0
|
if (@_ == 4) # Add a product to the group for a*b |
91
|
0
|
|
|
|
|
0
|
{$𝗲->{$a}{$b} = $c; |
92
|
0
|
|
|
|
|
0
|
return $g; # Return group to allow for chaining if desired |
93
|
|
|
|
|
|
|
} |
94
|
0
|
0
|
|
|
|
0
|
if (@_ == 5) # Add products to the group for a*b and b*a |
95
|
0
|
|
|
|
|
0
|
{$𝗲->{$a}{$b} = $c; |
96
|
0
|
|
|
|
|
0
|
$𝗲->{$b}{$a} = $𝗰; |
97
|
0
|
|
|
|
|
0
|
return $g; # Return group to allow for chaining if desired |
98
|
|
|
|
|
|
|
} |
99
|
0
|
|
|
|
|
0
|
confess "Wrong number of parameters, should be 3 to get a prior product or 4 for single put or 5 for double put!"; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub identity($) # Find the identity element - assumes that the group has been checked for uniqueness and closure |
103
|
15
|
|
|
15
|
1
|
17
|
{my ($g) = @_; # Group |
104
|
15
|
|
|
|
|
23
|
my $e = e($g); # Identity from cache |
105
|
15
|
100
|
|
|
|
51
|
return $e if $e; # Check cache |
106
|
11
|
|
|
|
|
16
|
my $𝗲 = 𝗲($g); # Elements |
107
|
11
|
|
|
|
|
23
|
for my $a(keys %$𝗲) # Find the identity and confirm that there is only one |
108
|
43
|
|
|
|
|
44
|
{my $n = 0; # Number of elements for which $a is an identity |
109
|
43
|
|
|
|
|
76
|
for my $b(keys %$𝗲) |
110
|
76
|
100
|
66
|
|
|
270
|
{last unless $𝗲->{$a}{$b} eq $b and $𝗲->{$b}{$a} eq $b; # Check whether it could be an indentity |
111
|
44
|
|
|
|
|
52
|
$n++ # Possible identity |
112
|
|
|
|
|
|
|
} |
113
|
43
|
100
|
|
|
|
114
|
return $g->{&Identity} = $a if $n == keys %$𝗲 # Save identity in cache |
114
|
|
|
|
|
|
|
} |
115
|
0
|
|
|
|
|
0
|
confess "No identity found!"; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub inverse($$) # Find the inverse of an element - assumes that identity has been dound |
119
|
24
|
|
|
24
|
1
|
41
|
{my ($g, $a) = @_; # Group, element for which an inverse is required |
120
|
24
|
|
|
|
|
41
|
my $𝗲 = 𝗲($g); # Elements |
121
|
24
|
|
|
|
|
50
|
my $i = i($g); # Inverses |
122
|
24
|
50
|
|
|
|
64
|
confess "Not a group element: $a" unless defined $𝗲->{$a}; # Validate element |
123
|
24
|
100
|
66
|
|
|
161
|
return $i->{$a} if defined($i) and defined($i->{$a}); # Return if inverse is in cache |
124
|
12
|
|
|
|
|
18
|
my $e = identity($g); # Find identity |
125
|
12
|
|
|
|
|
47
|
for my $b(keys %$𝗲) # Each element |
126
|
30
|
|
|
|
|
43
|
{my ($p, $q) = ($𝗲->{$a}{$b}, $𝗲->{$b}{$a}); # Product each way |
127
|
30
|
100
|
66
|
|
|
91
|
if ($p eq $e and $q eq $e) # Inverse if both products equal identity |
128
|
12
|
|
|
|
|
18
|
{$g->{&Inverses}->{$a} = $b; # Cache inverse |
129
|
12
|
|
|
|
|
39
|
return $b # Inverse |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
} |
132
|
0
|
|
|
|
|
0
|
confess "No inverse found for $a" |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub orders($) # Order of each element |
136
|
3
|
|
|
3
|
1
|
4
|
{my ($g) = @_; |
137
|
3
|
|
|
|
|
6
|
my $e = e($g); # Identity |
138
|
3
|
|
|
|
|
4
|
my $𝗲 = 𝗲($g); # Elements |
139
|
3
|
|
|
|
|
9
|
for my $A(keys %$𝗲) # Each element |
140
|
12
|
|
|
|
|
12
|
{my $a = $A; |
141
|
12
|
|
|
|
|
13
|
my $o = 1; |
142
|
12
|
|
|
|
|
21
|
for(1..keys %$𝗲) # Multiply until we reach the identity |
143
|
29
|
100
|
|
|
|
53
|
{last if $a eq $e; |
144
|
17
|
|
|
|
|
22
|
$a = $𝗲->{$a}{$A}; |
145
|
17
|
|
|
|
|
21
|
++$o; |
146
|
|
|
|
|
|
|
} |
147
|
12
|
|
|
|
|
20
|
$g->{&Orders}{$A} = $o; # Save order |
148
|
|
|
|
|
|
|
} |
149
|
3
|
|
|
|
|
6
|
$g->{&Orders}{$e} = 0; # Correct order of identity |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub order($;$) # Order of an element |
153
|
28
|
|
|
28
|
1
|
426
|
{my ($g, $a) = @_; |
154
|
28
|
|
|
|
|
50
|
my $𝗲 = 𝗲($g); |
155
|
28
|
100
|
|
|
|
104
|
return scalar keys %$𝗲 if @_ == 1; # Order of group |
156
|
12
|
|
|
|
|
29
|
$g->{&Orders}{$a}; # Order of element |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub elements($) # Elements in group |
160
|
0
|
|
|
0
|
1
|
0
|
{my ($g) = @_; |
161
|
0
|
|
|
|
|
0
|
my $𝗲 = 𝗲($g); |
162
|
0
|
|
|
|
|
0
|
sort keys %$𝗲 |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub check($) # Check that it really is a group |
166
|
3
|
|
|
3
|
0
|
4
|
{my ($g) = @_; |
167
|
3
|
|
|
|
|
8
|
my $𝗲 = 𝗲($g); # Elements |
168
|
3
|
|
|
|
|
9
|
for my $a(keys %$𝗲) # Check each operation |
169
|
12
|
|
|
|
|
12
|
{my %row; my %col; # Check each element is unique in each row and in each column |
170
|
12
|
|
|
|
|
21
|
for my $b(keys %$𝗲) |
171
|
48
|
|
|
|
|
67
|
{my ($c, $𝗰) = ($𝗲->{$a}{$b}, $𝗲->{$b}{$a}); # Result of operation each way |
172
|
48
|
50
|
|
|
|
85
|
confess "Missing product for $a * $b" unless defined $c; |
173
|
48
|
50
|
|
|
|
67
|
confess "Missing product for $b * $a" unless defined $𝗰; |
174
|
48
|
50
|
|
|
|
82
|
confess "Group not closed for $c == $a * $b" unless defined $𝗲->{$c}; |
175
|
48
|
50
|
|
|
|
77
|
confess "Group not closed for $𝗰 == $b * $a" unless defined $𝗲->{$𝗰}; |
176
|
48
|
50
|
|
|
|
88
|
if (defined(my $p = $row{$c})) # Check each product in a row is unique |
177
|
0
|
|
|
|
|
0
|
{confess "Duplicate product $c == $a * $b and $a * $p"; # Helpfully provided duplicated product |
178
|
|
|
|
|
|
|
} |
179
|
48
|
|
|
|
|
57
|
$row{$c} = $b; # Record product as already present in this row |
180
|
48
|
50
|
|
|
|
85
|
if (defined(my $p = $col{$𝗰})) # Check each product in a column is unique |
181
|
0
|
|
|
|
|
0
|
{confess "Duplicate product $𝗰 == $b * $a and $b * $p"; # Helpfully provided duplicated product |
182
|
|
|
|
|
|
|
} |
183
|
48
|
|
|
|
|
87
|
$row{$c} = $b; # Record product as already present in this column |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
} |
186
|
3
|
|
|
|
|
9
|
identity($g); # Check that the group has an identity |
187
|
3
|
|
|
|
|
7
|
for my $a(keys %$𝗲) # Find the identity and confirm that there is only one |
188
|
12
|
50
|
|
|
|
39
|
{confess "No inverse for: $a" unless defined inverse($g, $a); # Helpfully indicate element with no inverse |
189
|
|
|
|
|
|
|
} |
190
|
3
|
|
|
|
|
8
|
orders($g); # Order if each element |
191
|
3
|
|
|
|
|
3
|
1 # It is a group |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
sub Group(&@) # Create a group |
195
|
3
|
|
|
3
|
1
|
1482
|
{my $sub = shift; # Operator, elements |
196
|
3
|
|
|
|
|
6
|
my $g = bless {&Elements=>{}, &Inverses=>{}, &Orders=>{}}; # Empty group |
197
|
3
|
|
|
|
|
7
|
for my $a(@_) # Create multiplication table |
198
|
12
|
|
|
|
|
18
|
{for my $b(@_) |
199
|
48
|
|
|
|
|
77
|
{$g->{&Elements}{$a}{$b} = &$sub($a, $b); |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
} |
202
|
3
|
|
|
|
|
6
|
check($g); # Check we have a group |
203
|
3
|
|
|
|
|
6
|
$g # Return results |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
sub abelian($) # Abelian? |
207
|
3
|
|
|
3
|
1
|
21
|
{my ($g) = @_; # Group |
208
|
3
|
|
|
|
|
13
|
my $𝗲 = 𝗲($g); # Elements |
209
|
3
|
|
|
|
|
9
|
for my $a(keys %$𝗲) # Check each operation |
210
|
12
|
|
|
|
|
22
|
{for my $b(keys %$𝗲) |
211
|
48
|
50
|
|
|
|
70
|
{return 0 unless $g->{&Elements}{$a}{$b} == $g->{&Elements}{$b}{$a}; |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
1 # Abelian |
215
|
3
|
|
|
|
|
11
|
} |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub cyclic($) # Cyclic - return a generating element or undef if no such element |
218
|
3
|
|
|
3
|
1
|
6
|
{my ($g) = @_; # Group |
219
|
3
|
|
|
|
|
7
|
my $N = order($g); |
220
|
3
|
|
|
|
|
5
|
while(my ($e, $o) = each %{$g->{&Orders}}) # Order of each element |
|
10
|
|
|
|
|
17
|
|
221
|
9
|
100
|
100
|
|
|
46
|
{return $e if $o && $o == $N; # Return generating element |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
undef # Not cyclic |
224
|
1
|
|
|
|
|
42
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub subGroup($@) # Sub group |
227
|
9
|
|
|
9
|
1
|
15
|
{my $g = shift; # Group followed by sub group elements excluding identity |
228
|
9
|
|
|
|
|
26
|
my %g = map {$_=>1} @_, $g->e; # Add identity as that is always present in a sub group |
|
18
|
|
|
|
|
43
|
|
229
|
9
|
|
|
|
|
19
|
for my $a(@_) # Check each product |
230
|
9
|
|
|
|
|
14
|
{for my $b(@_) |
231
|
9
|
100
|
|
|
|
16
|
{return 0 unless $g{$g->{&Elements}{$a}{$b}}; # Not a sub group unless product is within sub group |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
1 # Sub group |
235
|
5
|
|
|
|
|
18
|
} |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub homoMorphic($$@) # Homomorphism between two groups |
238
|
61
|
|
|
61
|
1
|
83
|
{my $g = shift; # First group |
239
|
61
|
|
|
|
|
65
|
my $𝗴 = shift; # Second group |
240
|
61
|
50
|
|
|
|
187
|
ref($𝗴) eq __PACKAGE__ or confess "Second parameter must be a group too!"; # Check it is a group isomorphism |
241
|
61
|
|
|
|
|
144
|
my %m = @_; # Mapping between groups |
242
|
61
|
|
|
|
|
103
|
$m{e($g)} = e($𝗴); # Include identity to identity in mapping |
243
|
61
|
|
|
|
|
114
|
my $e = 𝗲($g); # Elements in first group |
244
|
61
|
|
|
|
|
107
|
my $𝗲 = 𝗲($𝗴); # Elements in second group |
245
|
61
|
|
|
|
|
185
|
while(my ($a, $b) = each %m) # Check elements come from the correct groups |
246
|
244
|
50
|
|
|
|
445
|
{confess "Not a group element of first group: $a" unless $e->{$a}; |
247
|
244
|
50
|
|
|
|
873
|
confess "Not a group element of second group: $b" unless $𝗲->{$b}; |
248
|
|
|
|
|
|
|
} |
249
|
61
|
|
|
|
|
137
|
for my $a(keys %m) # Check each product |
250
|
200
|
|
|
|
|
337
|
{for my $b(keys %m) |
251
|
756
|
100
|
|
|
|
2056
|
{return 0 unless $m{$e->{$a}{$b}} eq $𝗲->{$m{$a}}{$m{$b}}; # Apply |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
1 # Homomorphic |
255
|
43
|
|
|
|
|
252
|
} |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
sub isoMorphic($$@) # Isomorphic |
258
|
39
|
|
|
39
|
1
|
50
|
{my $g = shift; # First group |
259
|
39
|
|
|
|
|
43
|
my $𝗴 = shift; # Second group |
260
|
39
|
50
|
|
|
|
99
|
ref($𝗴) eq __PACKAGE__ or confess "Second parameter must be a group too!"; # Check it is a group isomorphism |
261
|
39
|
|
|
|
|
96
|
my %m = @_; # Mapping between groups |
262
|
39
|
|
|
|
|
109
|
my %𝗺 = reverse %m; # Mapping between groups |
263
|
39
|
50
|
|
|
|
102
|
keys(%m) == keys(%𝗺) or confess "Please supply a bijective mapping!"; # Check that the mapping is bijective |
264
|
39
|
100
|
|
|
|
106
|
$g->homoMorphic($𝗴, %m) && $𝗴->homoMorphic($g, %𝗺) # Bijective homomorphism is an isomorphism |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub isoMorphisms(&$$) # Find all the isomorphisms between two groups |
268
|
5
|
|
|
5
|
1
|
353
|
{my ($sub, $g, $𝗴) = @_; # Sub to call to process found isomorphisms, first group, second group |
269
|
5
|
50
|
|
|
|
19
|
ref($𝗴) eq __PACKAGE__ or confess "Second parameter must be a group too!"; # Check it is a group |
270
|
5
|
50
|
|
|
|
13
|
order($g) == order($𝗴) or confess "Groups have different orders!"; # Check groups have same order |
271
|
5
|
|
|
|
|
11
|
my $i = e($g); # Identity of first group |
272
|
5
|
|
|
|
|
13
|
my $𝗶 = e($𝗴); # Identity of second group |
273
|
5
|
|
|
|
|
8
|
my $e = [grep {$_ ne $i} sort keys %{𝗲($g)}]; # Elements of first group in fixed order without identity |
|
20
|
|
|
|
|
53
|
|
|
5
|
|
|
|
|
13
|
|
274
|
5
|
|
|
|
|
10
|
my $𝗲 = [grep {$_ ne $𝗶} sort keys %{𝗲($𝗴)}]; # Elements of second group in fixed order without identity |
|
20
|
|
|
|
|
40
|
|
|
5
|
|
|
|
|
11
|
|
275
|
|
|
|
|
|
|
permute # Permute the elements to obtain all possible mappings |
276
|
30
|
|
|
30
|
|
3263
|
{my %m = map {$$e[$_]=>$$𝗲[$_[$_]]} 0..$#_; # Mapping to test |
|
90
|
|
|
|
|
245
|
|
277
|
30
|
100
|
|
|
|
95
|
&$sub(%m) if isoMorphic($g, $𝗴, %m); # Process mapping if isomorphic |
278
|
5
|
|
|
|
|
43
|
} 0..$#$e; # Elements to permute |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub autoMorphic($@) # Automorphic |
282
|
5
|
|
|
5
|
1
|
7
|
{my $g = shift; # Group |
283
|
5
|
|
|
|
|
12
|
$g->isoMorphic($g, @_) # Check |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
sub autoMorphisms(&$) # Find all the automorphisms of a group |
287
|
3
|
|
|
3
|
1
|
6
|
{my ($sub, $g) = @_; # Sub to call to process found automorphisms, group |
288
|
3
|
|
|
|
|
7
|
&isoMorphisms($sub,$g,$g) |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# Export details |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
require 5; |
294
|
|
|
|
|
|
|
require Exporter; |
295
|
|
|
|
|
|
|
|
296
|
1
|
|
|
1
|
|
20
|
use vars qw(@ISA @EXPORT $VERSION); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
152
|
|
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
299
|
|
|
|
|
|
|
@EXPORT = qw(Group autoMorphisms isoMorphisms); |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
our $VERSION = '1.002'; # Sunday 23 Aug 2015 |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=head1 Description |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
Find automorphisms of groups and isomorphisms between groups. |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
A group automorphism is a bijection on the set of elements of a group which |
308
|
|
|
|
|
|
|
preserves the group product. |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
A group isomorphism is a bijection between the sets of elements of two groups |
311
|
|
|
|
|
|
|
which preserves the group product. |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=head2 identity(group) |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
Returns the identity element. |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=head2 inverse(group, element) |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
Returns the inverse of an element. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
=head2 orders(group) |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
Returns a hash which supplies the order of each element. The identity is |
324
|
|
|
|
|
|
|
assigned an order of zero. |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
=head2 order(group, element) |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
Returns the order of an element with the group. |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=head2 elements(group) |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
Returns a hash whose keys are the elements if the group. The value at each key |
333
|
|
|
|
|
|
|
of this hash is another hash which gives the product in this group. |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=head2 Group sub elements... |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
Creates a group with the specified elements as multiplied by C. The first |
338
|
|
|
|
|
|
|
parameter is a subroutine that forms the product of each pair of elements drawn |
339
|
|
|
|
|
|
|
from the following list of elements. |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=head2 abelian(group) |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
Returns 1 if the group is Abelian, else 0. |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=head2 cyclic(group) |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
If the group is cyclic, returns an element that generates the group, else |
348
|
|
|
|
|
|
|
undef. |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=head2 subGroup(groups, elements...) |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
Returns 1 if the elements specified plus the identity element form a sub group |
353
|
|
|
|
|
|
|
of the group else 0. |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=head2 homoMorphic(group1, group2, mapping...) |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Returns 1 if mapping forms a homomorphism from group 1 to group 2, else 0. |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
The mapping is a subset of the Cartesian product of the elements of |
360
|
|
|
|
|
|
|
group 1 and the elements of group 2 flattened into a list. The pair: |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
(identity of group 1, identity of group 2) |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
is added for you so there is no need to specify it unless you wish to. |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=head2 isoMorphic(group1, group2, mapping...) |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
Returns 1 if the mapping is an isomorphism from group 1 to group 2, else 0. |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
The mapping is a subset of the Cartesian product of the elements of |
371
|
|
|
|
|
|
|
group 1 and the elements of group 2 flattened into a list. The pair: |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
(identity of group 1, identity of group 2) |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
is added for you so there is no need to specify it unless you wish to. |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
=head2 isoMorphisms sub group1, group 2 |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
Finds all the isomorphisms between two groups and calls C to process each |
380
|
|
|
|
|
|
|
of them as they are discovered. |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
The parameter list to sub is a pair for each element of group 1 indicating the |
383
|
|
|
|
|
|
|
corresponding element of group 2 under the isomorphism. |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
=head2 autoMorphic(group, mapping) |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
Returns 1 if the mapping is an automorphism from the group to itself, else 0. |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
The mapping is a subset of the Cartesian product of the elements of |
390
|
|
|
|
|
|
|
the group squared flattened into a list. The pair: |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
(identity of group, identity of group) |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
is added for you so there is no need to specify it unless you wish to. |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=head2 autoMorphisms sub group |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
Finds all the automorphisms of the groups and calls C to process each |
399
|
|
|
|
|
|
|
of them as they are discovered. |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
The parameter list to sub is a pair for each element of the group indicating the |
402
|
|
|
|
|
|
|
corresponding element under the automorphism. |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
=head1 Export |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
The C, C, C functions are exported. |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=head1 Installation |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
Standard Module::Build process for building and installing modules: |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
perl Build.PL |
413
|
|
|
|
|
|
|
./Build |
414
|
|
|
|
|
|
|
./Build test |
415
|
|
|
|
|
|
|
./Build install |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
Or, if you're on a platform (like DOS or Windows) that doesn't require |
418
|
|
|
|
|
|
|
the "./" notation, you can do this: |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
perl Build.PL |
421
|
|
|
|
|
|
|
Build |
422
|
|
|
|
|
|
|
Build test |
423
|
|
|
|
|
|
|
Build install |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=head1 Author |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
Philip R Brenan at gmail dot com |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
http://www.appaapps.com |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
=head1 See Also |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=over |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=item L |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=item L |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=back |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=head1 Copyright |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
This module is free software. It may be used, redistributed and/or |
444
|
|
|
|
|
|
|
modified under the same terms as Perl itself. |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=cut |