File Coverage

blib/lib/Math/Group/Thompson.pm
Criterion Covered Total %
statement 9 144 6.2
branch 0 84 0.0
condition 0 60 0.0
subroutine 3 13 23.0
pod 9 9 100.0
total 21 310 6.7


line stmt bran cond sub pod time code
1             #
2             # OO methods that calculates #B(n) on Thompson group F
3             #
4             # Author: Roberto Alamos Moreno
5             #
6             # Copyright (c) 2004 Roberto Alamos Moreno. All rights reserved.
7             # This program is free software; you can redistribute it and/or
8             # modify it under the same terms as Perl itself.
9             #
10             # November 2004. Antofagasta, Chile.
11             #
12             package Math::Group::Thompson;
13              
14             $VERSION = '0.96';
15              
16 1     1   6830 use strict;
  1         2  
  1         48  
17 1     1   5 use warnings;
  1         2  
  1         37  
18              
19 1     1   975 use FileHandle;
  1         14801  
  1         7  
20              
21             =head1 NAME
22              
23             Math::Group::Thompson - OO methods that calculates the cardinality
24             of the ball of radius 'n' of Thompson group F
25              
26             =head1 SYNOPSIS
27              
28             use Math::Group::Thompson;
29              
30             my $F = Math::Group::Thompson->new( VERBOSE => 0 );
31             my $card = $F->cardBn(3,'');
32              
33             print "#B(3) = $card\n";
34              
35             =head1 DESCRIPTION
36              
37             The Math::Group::Thompson module provides objetct oriented methods
38             that calculates the cardinality of the ball of radius 'n'
39             of Thompson group F.
40              
41             This module uses the presentation of F
42              
43             F = < A,B | [AB^(-1),A^(-1)BA] = [AB^(-1),A^(-2)BA^2] = e >
44              
45             where A,B are formal symbols, [x,y] is the usual commutator and e
46             is the identity element of F.
47              
48             [x,y] = xyx^(-1)y^(-1)
49              
50             This means that for every g in F, g can be written as word
51              
52             g = a_{1}a_{2} ... a_{n}
53              
54             where all the a_{i} are A,B,A^(-1) or B^(-1) for all i <= n.
55             Internally, Math::Group::Thompson representates A,B,A^(-1),B^(-1) as
56             A,B,C,D respectively.
57              
58             Considering the set S = { A,B,A^(-1),B^(-1) } as a generator set for F.
59             One can define the length function L, as
60              
61             L(g) = min{ n | g can be written as a word with n elements of S }
62              
63             We have to define L(e) = 0
64              
65             With this definition, the ball of radius n of F, can be defined as
66              
67             B(n) = { g in F | L(g) <= n }
68              
69             So, what this module do is to calculate #B(n) or #(gB(n) - B(n)),
70             where g in F, depending on what you need. Note that by definition of S,
71              
72             B(n+1) = (AB(n)-B(n))U(BB(n)-B(n))U(CB(n)-B(n))U(DB(n)-B(n)) U B(n)
73              
74             so
75              
76             #B(n+1) = #(AB(n)-B(n))+#(BB(n)-B(n))+#(CB(n)-B(n))+#(DB(n)-B(n))+#B(n)
77              
78             Also, this module stores some special relations derived from
79             [AB^(-1),A^(-1)BA] = [AB^(-1),A^(-2)BA^2] = e that must me avoided when
80             counting the elements of B(n). For example, from [AB^(-1),A^(-1)BA] = e
81             it can be derived the relations
82              
83             A^(-1)BAA = AB^(-1)A^(-1)BAB
84             A^(-1)BAAB^(-1) = AB^(-1)A^(-1)BA
85              
86             among many other relations. The first relation show us that if
87             we have a word g that contains AB^(-1)A^(-1)BAB it MUST NOT be counted
88             as an element of B(n) for some n, because the word AB^(-1)A^(-1)BAB
89             can be reduced to A^(-1)BAA and this implies that g was already counted
90             as an element of B(n). Second relation tell us that if we have
91             a word w that contains A^(-1)BAAB^(-1) it MUST NOT be counted as an
92             element of B(n) because w was already counted (or will be counted) as
93             and element of B(n).
94              
95             Resuming, relation [AB^(-1),A^(-1)BA] = 1, allow us to derive relations
96             between words with length 4 and length 6, and between words of length 5.
97             And the second relation [AB^(-1),A^(-2)BA^2] = 1 can be used to derive
98             relations between words with length 6 and words with length 8, and
99             between words of length 7.
100              
101             =head1 METHODS
102              
103             =over 4
104              
105             =item new
106              
107             Creates the Thompson object.
108              
109             Usage: my $F = new->Math::Group::Thompson( VERBOSE => $v );
110              
111             Verbose argument tells Math::Group::Thompson whether print every
112             word generated ($v == 1) or not ($v == 0), or store them
113             in a file, where $v is the name of the file (obviously different
114             to 0 or 1). If the verbose file exists it is replaced, so you have to
115             check for its integrity.
116              
117             NOTE:
118             It's not recommend to store the words on a file because for
119             very small values of n, #B(n) or #gB(n)-B(n) are very very large.
120             For example for n = 19, #B(n) ~ 3^n = 1162261467 ~ 1.1 Giga, but
121             the space ocupped by the file will be (in bytes):
122              
123             #B(1) + sum(i=2 to 19){i*(#B(i) - #B(i-1))} =
124              
125             =cut
126             sub new {
127 0   0 0 1   my $class = shift || undef;
128 0 0         if(!defined $class) {
129 0           return undef;
130             }
131              
132 0           my %args = ( VERBOSE => 0, # By default don't print anything
133             @_ );
134              
135             # Inverse elements
136 0           my $inv = { B => 'D', # B is the inverse of B^(-1)
137             A => 'C', # A is the inverse of A^(-1)
138             D => 'B', # B^(-1) is the inverse of B
139             C => 'A', # A^(-1) is the inverse of A
140             };
141              
142             # Prohibited words
143             # Words of length 5
144 0           my @rel5 = (
145             'BAADC',
146             'ABCCD',
147             'AADCD',
148             'BABCC',
149             'ADCDA',
150             'CBABC',
151             'DCDAB',
152             'DCBAB',
153             'CDABC',
154             'ADCBA',
155             );
156              
157             # Words of length 6
158 0           my @rel6 = (
159             'AADCBA',
160             'DAADCB',
161             'CBAADC',
162             'BAADCD',
163             'DABCCB',
164             'CDABCC',
165             'BABCCD',
166             'ABCCDA',
167             'CDAADC',
168             'AADCDA',
169             'ABCCBA',
170             'CBABCC',
171             'CCDAAD',
172             'ADCDAB',
173             'BCCBAA',
174             'DCBABC',
175             'BCCDAA',
176             'DCDABC',
177             'CCBAAD',
178             'ADCBAB',
179             );
180              
181             # Words of length 7
182 0           my @rel7 = (
183             'CBAAADC',
184             'ABCCCDA',
185             'BAAADCC',
186             'AABCCCD',
187             'AAADCCD',
188             'BAABCCC',
189             'AADCCDA',
190             'CBAABCC',
191             'ADCCDAA',
192             'CCBAABC',
193             'DCCDAAB',
194             'DCCBAAB',
195             'CCDAABC',
196             'ADCCBAA',
197             );
198              
199             # Words of length 8
200 0           my @rel8 = (
201             'AADCCBAA',
202             'AAADCCBA',
203             'CCBAAADC',
204             'CBAAADCC',
205             'CDAABCCC',
206             'CCDAABCC',
207             'AABCCCDA',
208             'ABCCCDAA',
209             'DAAADCCB',
210             'BAAADCCD',
211             'DAABCCCB',
212             'BAABCCCD',
213             'CDAAADCC',
214             'AAADCCDA',
215             'AABCCCBA',
216             'CBAABCCC',
217             'CCDAAADC',
218             'AADCCDAA',
219             'ABCCCBAA',
220             'CCBAABCC',
221             'CCCDAAAD',
222             'ADCCDAAB',
223             'BCCCBAAA',
224             'DCCBAABC',
225             'BCCCDAAA',
226             'DCCDAABC',
227             'CCCBAAAD',
228             'ADCCBAAB',
229             );
230              
231              
232             # Define the generator set S = { A,B,A^(-1),B^(-1) }
233 0           my @generators = ('B','A','D','C');
234              
235             # Open filehandle if we have to
236 0           my $fh;
237 0 0         if($args{VERBOSE}) {
238 0 0         if($args{VERBOSE} ne '1') {
239 0   0       $fh = new FileHandle ">".$args{VERBOSE} || undef;
240             }
241             }
242              
243 0           return bless { INV => $inv, # Inverse relations
244             REL => [\@rel5,\@rel6,\@rel7,\@rel8], # Prohibited words
245             GEN => \@generators, # Generator set
246             COUNTER => 0, # Element counter
247             FIRST_ELEMENT => '', # F's element passed to the firs call of method cardBn
248             FIRST_CALL => 1, # Flag of first call to method cardBn
249             VERBOSE => $args{VERBOSE}, # Verbose mode
250             FILEHANDLE => \$fh, # Filehandler
251             }, $class;
252             }
253              
254             =item cardBn
255              
256             This method calculates #B(n) or #(gB(n) - B(n)) depending on if
257             the argument passed to the first call of cardBn is '' or not.
258              
259             Usage: my $c = $F->cardBn($radius,$g);
260              
261             where
262              
263             $radius is an integer number >= 0 and
264             $g is an element of F (word written with A,B,C or D).
265              
266             If the first time cardBn is called $g is not equal to '', then
267             cardBn returns the cardinality of the set
268              
269             gB(n) - B(n) = { w in F | w in gB(n) and w not in B(n) }
270              
271             If the firs time cardBn is callen $g is equal to '', then
272             cardBn returns #B(n).
273              
274             This algorithm runs on exponential time because
275             F is of exponential growth (more "exactly", this algorithm is
276             O(3^n) ).
277              
278             =cut
279             sub cardBn {
280 0     0 1   my ($self,$n,$g) = @_;
281 0 0         if(!defined $g) { $g = ""; }
  0            
282 0 0 0       if(!defined $self || !ref $self || !defined $n || $n < 0 || $n =~ /\D/ || $g =~ /[^ABCD]/) {
      0        
      0        
      0        
283 0           return undef;
284             }
285 0 0         if($n == 0) {
286             # We have to calculate #B(0) or #(gB(0) - B(0)). In any case is 1
287 0           return 1;
288             }
289              
290             # Check if we are in the first call of cardBn
291 0 0         if($self->{FIRST_CALL}) {
292             # The first element passed to cardBn is $g. Keep it
293 0   0       $self->{FIRST_ELEMENT} = $g || '';
294 0           $self->{FIRST_CALL} = 0;
295 0 0         if($self->{FIRST_ELEMENT} eq '') {
296 0           $self->note('e');
297             }
298             }
299              
300             # For every element A,B,A^(-1) and B^(-1)
301 0           for(0..3) {
302 0   0       my $aux_g = $self->multiply($g,$self->{GEN}->[$_]) || ''; # Multiple $g by one of the generators
303 0           my $i = 0; # Flag that say if the new word contains
304              
305             # Check if the new word is one letter larger than the previous one
306 0           my ($length_g,$length_auxg) = (0,0);
307 0 0         if($g ne '') {
308 0           $length_g += length($g);
309             }
310 0 0         if($aux_g ne '') {
311 0           $length_auxg += length($aux_g);
312             }
313 0 0         if($length_auxg == ($length_g + 1)) {
314             # Check if some prohibited word is in $aux_g
315 0           LOOP: for(5..8) {
316 0 0         if($length_auxg >= $_) {
317             # Check if the word contains any prohibited relation
318 0           foreach my $rel (@{$self->{REL}->[$_-5]}) {
  0            
319 0 0         if($aux_g =~ /$rel$/) {
320             # Prohibited word found
321 0           $i++;
322 0           last LOOP;
323             }
324             }
325             }
326             }
327              
328             # Check if we foun any prohibited word
329 0 0         if($i == 0) {
330             # Determine if we are calculating #B(n) or #(gB(n) - B(n)) where g is the first argument received by cardBn
331 0 0         if($self->{FIRST_ELEMENT} ne '') {
332             # First element wasn't ''. We are calculating #(gB(n) - B(n))
333 0 0         if(length($aux_g) < ($n + length($self->{FIRST_ELEMENT}))) {
334 0           $self->cardBn($n,$aux_g);
335             } else {
336             # Count this element
337             # Print word if VERBOSE == 1
338 0           $self->note($aux_g);
339 0           $self->{COUNTER}++;
340             }
341             } else {
342             # Count this element
343             # Print word if VERBOSE == 1
344 0           $self->note($aux_g);
345 0           $self->{COUNTER}++;
346              
347             # First element was empty. We are calculating #B(n)
348 0 0         if(length($aux_g) < $n) {
349             # Word's length is < $n, continue
350 0           $self->cardBn($n,$aux_g);
351             }
352             }
353             }
354             }
355             }
356              
357             # Return
358 0 0         if($self->{FIRST_ELEMENT} eq '') {
359 0           return $self->{COUNTER} + 1; # Returns #B(n). The 1 is for the identity element
360             } else {
361 0           return $self->{COUNTER}; # Returns #(gB(n)-B(n).
362             }
363             }
364              
365             =item reset
366              
367             Resets the counter used on cardBn method, set
368             the FIRST_ELEMENT property at '', and the FIRST_CALL
369             proporty to 1.
370              
371             Usage: $F->reset;
372              
373             =cut
374             sub reset {
375 0   0 0 1   my $self = shift || undef;
376 0 0         if(!defined $self) {
377 0           return;
378             }
379              
380 0           $self->{COUNTER} = 0;
381 0           $self->{FIRST_ELEMENT} = '';
382 0           $self->{FIRST_CALL} = 1;
383              
384 0           return 1;
385             }
386              
387             =item multiply
388              
389             Multiplication between two words of F. This method
390             considers the inverse relations stored in the attribute
391             INV.
392              
393             Usage: my $mul = $F->multiply($g,$w);
394              
395             where $g and $w are elements of F, and $mul is the
396             result of $g$w.
397              
398             =cut
399             sub multiply {
400 0     0 1   my ($self,$g,$h) = @_;
401 0 0         if(!defined $self) {
402 0           return;
403             }
404 0 0 0       if(!defined $g && !defined $h) {
    0 0        
405 0           return undef;
406             } elsif($g eq '' && $h eq '') {
407 0           return undef;
408             }
409 0 0         if(!defined $h) {
    0          
410 0           return $g;
411             } elsif ($h eq '') {
412 0           return $g;
413             }
414 0 0         if(!defined $g) {
    0          
415 0           return $h;
416             } elsif($g eq '') {
417 0           return $h;
418             }
419              
420             # Get inverse relations
421 0           my %inv = $self->get_inv;
422              
423             # Multiply
424 0           my @h = split(//,$h);
425 0           foreach my $el (@h) {
426 0           $g =~ /(.)$/;
427 0 0         if($1 ne $inv{$el}) {
428 0           return $g.$h;
429             } else {
430 0           $g =~ s/.$//;
431 0           $h =~ s/^.//;
432             }
433              
434 0 0 0       if($g eq '' && $h ne '') {
    0 0        
    0 0        
435 0           return $h
436             } elsif($h eq '' && $g ne '') {
437 0           return $g;
438             } elsif($g eq '' && $h eq '') {
439 0           return undef;
440             }
441             }
442             }
443              
444             =item rotate
445              
446             This module receives as argument a word in F and
447             puts the last letter on word in its first place.
448              
449             Usage: $w = 'ABC';
450             $W = $self->rotate($w); # $W is now equal to 'CBA'
451              
452             =cut
453             sub rotate {
454 0     0 1   my ($self,$word) = @_;
455 0 0 0       if(!defined $self || !defined $word) {
456 0           return undef;
457             }
458              
459 0           $word =~ s/(.)$//;
460 0           return $1.$word;
461             }
462              
463             =item inverse
464              
465             This method receives a word in F and returns its inverse.
466              
467             Usage: $w = 'ABC';
468             $W = $self->inverse($w); # $W == 'ADC'
469              
470             =cut
471             sub inverse {
472 0     0 1   my ($self,$word) = @_;
473 0 0 0       if(!defined $self || !defined $word) {
474 0           return undef;
475             }
476              
477 0           my %inv = $self->get_inv;
478 0           my @word = split(//,$word);
479              
480 0           for(0..$#word) {
481 0           $word[$_] = $inv{$word[$_]};
482             }
483              
484 0           $word = join('',@word);
485 0           return reverse $word;
486             }
487              
488             =item divide
489              
490             This method receives a word in F and returns a 2-dimensional
491             array where the first element is the first half
492             of the word, and the second is the inverse of the
493             second half of the word.
494              
495             Usage: $w = 'AABC';
496             ($w1,$w2) = $self->divide($w); # Now $w1 == 'AA' and $w2 == 'AD'
497              
498             =cut
499             sub divide {
500 0     0 1   my ($self,$word) = @_;
501 0 0 0       if(!defined $self || !defined $word) {
502 0           return undef;
503             }
504              
505 0           my $largo = length($word);
506 0           my @word = split(//,$word);
507 0           $word = join('',@word[0..($largo/2)-1]);
508 0           my $word2 = join('',@word[($largo/2)..$#word]);
509 0           $word2 = $self->inverse($word2);
510              
511 0           return ($word,$word2);
512             }
513              
514             =item get_inv
515              
516             This method return the hash of inverse relations
517             between the generators elements of F.
518              
519             =cut
520             sub get_inv {
521 0   0 0 1   my $self = shift || undef;
522 0 0         if(!defined $self) {
523 0           return undef;
524             }
525              
526 0           return %{$self->{INV}};
  0            
527             }
528              
529             =item note
530              
531             This method prints in STDERR the string received or
532             puts it on the correspondent file.
533              
534             Usage: $F->note('AA'); # Print AA."\n" or store it on a file.
535              
536             =cut
537             sub note {
538 0   0 0 1   my $self = shift || undef;
539 0 0         if(!defined $self) {
540 0           return undef;
541             }
542              
543 0   0       my $g = shift || return undef;
544              
545 0 0         if($self->{VERBOSE}) {
546 0 0         if($self->{VERBOSE} eq '1') {
547             # Print word to STDERR
548 0           print STDERR $g,"\n";
549             } else {
550             # Put word on the correspondent file
551 0 0 0       if($self->{FILEHANDLE} && ref(${$self->{FILEHANDLE}}) eq 'FileHandle') {
  0            
552 0           my $fh = ${$self->{FILEHANDLE}};
  0            
553 0           print $fh $g,"\n";
554             }
555             }
556             }
557             }
558              
559             # Destroy function. Closes the filehandle opened in 'new' method (if it was opened).
560             sub DESTROY {
561 0   0 0     my $self = shift || undef;
562 0 0         if(!defined $self) {
563 0           return undef;
564             }
565              
566 0 0         if($self->{VERBOSE}) {
567 0 0 0       if($self->{VERBOSE} ne '1' && ref(${$self->{FILEHANDLE}}) eq 'FileHandle') {
  0            
568 0 0         ${$self->{FILEHANDLE}}->close if $self->{FILEHANDLE};
  0            
569             }
570             }
571             }
572              
573             =back 4
574              
575             =head1 BUGS
576              
577             There isn't reported bugs yet, but that doesn't mean that there aren't ;) .
578              
579             =head1 AUTHOR
580              
581             Roberto Alamos Moreno
582              
583             Thanks to professor Juan Rivera Letelier for his support to my thesis work, and help in the design
584             of cardBn algorithm :) .
585              
586             =head1 COPYRIGHT
587              
588             Copyright (c) 2004 Roberto Alamos Moreno. All rights reserved.
589             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
590              
591             =cut
592             1;