File Coverage

lib/CayleyDickson/CayleyDickson.pm
Criterion Covered Total %
statement 109 132 82.5
branch 46 60 76.6
condition 3 6 50.0
subroutine 25 35 71.4
pod 23 26 88.4
total 206 259 79.5


line stmt bran cond sub pod time code
1             #
2             # CayleyDickson.pm - Cayley-Dickson constructions and algebriac manipulations
3             #
4             # author: Jeffrey B Anderson - truejeffanderson at gmail.com
5             #
6             # reference: https://en.wikipedia.org/wiki/Cayley-Dickson_construction
7             #
8              
9              
10             package CayleyDickson;
11 2     2   54689 use strict;
  2         2  
  2         45  
12 2     2   8 no warnings;
  2         2  
  2         59  
13 2     2   1883 use overload qw(- subtract + add * multiply / divide "" as_string eq eq);
  2         1550  
  2         10  
14 2     2   342 use constant SYMBOLS => ['', 'i' .. 'z', map('a' . $_, ('a' .. 'z')), ( map('b' . $_, ('a' .. 'z')) ) x 100];
  2         2  
  2         1143  
15             our $VERSION = 0.03;
16              
17             # logic numbers ...
18 2     2   11 use constant YES => 1;
  2         3  
  2         88  
19 2     2   9 use constant NO => 0;
  2         2  
  2         77  
20              
21 2     2   10 use constant DOUBLING_PRODUCT => 'Pt0';
  2         2  
  2         96  
22             #
23             # multiplication rules for (a,b)×(c,d)
24             # valid DOUBLING PRODUCT options...
25             #
26             # P0 => ( c×a - B×d , d×A + b×c )
27             # P1 => ( c×a - d×B , A×d + c×b )
28             # P2 => ( a×c - B×d , d×A + b×c )
29             # P3 => ( a×c - d×B , A×d + c×b )
30             # Pt0 => ( c×a - b×D , a×d + C×b ) # default/tested
31             # Pt1 => ( c×a - D×b , d×a + b×C )
32             # Pt2 => ( a×c - b×D , a×d + C×b )
33             # Pt3 => ( a×c - D×b , d×a + b×C )
34             #
35             # ...where lower and upper case are conjugate vectors.
36             # ref: http://jwbales.us/cdproducts.html
37              
38 2     2   10 use constant I_SQUARED => -YES;
  2         3  
  2         2536  
39             #
40             # I_SQUARED is the square of the first imaginary unit i. Valid options:
41             #
42             # 1 => Split numbers
43             # 0 => Dual numbers
44             # -1 => Cayley-Dickson numbers # default/tested
45              
46              
47              
48             #
49             # Conjugate: z* = (a,b)* = (a*,-b)
50             #
51             sub conjugate {
52 2030     2030 1 1892 my $m = shift;
53              
54 2030 100       2207 my $a_conjugate = $m->is_complex ? $m->a : $m->a->conjugate;
55 2030         2459 my $negative_b = -$m->b;
56              
57 2030         2935 (ref $m)->new($a_conjugate, $negative_b)
58             }
59              
60              
61              
62             #
63             # Invert: 1/z = z⁻¹ = (a,b)⁻¹ = (a,b)*/(norm(a,b)²)
64             #
65             sub inverse {
66 89     89 1 92 my $m = shift;
67              
68 89         140 my $conjugate = $m->conjugate;
69 89         130 my $norm = $m->norm;
70 89         152 $conjugate / $norm ** 2
71             }
72              
73              
74             sub d {
75 0     0 0 0 my %a = @_;
76 0         0 my @k = keys %a;
77 0         0 my $d = Data::Dumper->new([@a{@k}],[@k]); $d->Purity(1)->Deepcopy(1); print $d->Dump;
  0         0  
  0         0  
78             }
79              
80              
81              
82             #
83             # Norm: z->norm = √(norm(a)²+norm(b)²) and norm(number) = number
84             #
85             sub norm {
86 221     221 1 210 my $m = shift;
87              
88 221 100       258 my $a = $m->is_complex ? $m->a : $m->a->norm;
89 221 100       291 my $b = $m->is_complex ? $m->b : $m->b->norm;
90              
91 221         396 sqrt($a ** 2 + $b ** 2)
92             }
93              
94              
95              
96             #
97             # Addition: z1+z2 = (a,b)+(c,d) = (a+c,b+d)
98             #
99             sub add {
100 1651     1651 1 2181 my ( $m, $o ) = @_;
101              
102 1651         1789 my $a = $m->a;
103 1651         1928 my $b = $m->b;
104 1651         1830 my $c = $o->a;
105 1651         1768 my $d = $o->b;
106              
107 1651         2729 (ref $m)->new($a + $c, $b + $d)
108             }
109              
110              
111              
112             #
113             # Subtraction: (a,b)-(c,d) = (a-c,b-d)
114             #
115             sub subtract {
116 607     607 1 909 my ( $m, $o, $swap ) = @_;
117              
118 607 100       1291 $o = (ref $m)->new((my $v = $o), 0) unless ref $o;
119              
120 607 100       915 my $a = $swap ? $o->a : $m->a;
121 607 100       844 my $b = $swap ? $o->b : $m->b;
122 607 100       808 my $c = $swap ? $m->a : $o->a;
123 607 100       879 my $d = $swap ? $m->b : $o->b;
124              
125 607         997 (ref $m)->new($a - $c, $b - $d)
126             }
127              
128              
129              
130             #
131             # Divide: z1/z2 = (a,b) × (c,d)⁻¹ = (a,b) × inverse(c,d)
132             #
133             sub divide {
134 178     178 1 396 my ( $m, $o, $swap ) = @_;
135              
136 178         173 my ( $a, $b );
137 178 100       242 $a = $swap ? $m->inverse : $m;
138 178 50       343 $b = $swap ? $o : (ref $o ? $o->inverse : ($o ? 1 / $o : 0));
    100          
    100          
139              
140 178         230 $a * $b
141             }
142              
143              
144              
145             #
146             # Multiply: (a,b)×(c,d) = (a×c - d*×b, d×a + b×c*) where x* = conjugate(x) or x if x is a number
147             #
148             sub multiply {
149 3007     3007 1 4086 my ( $m, $o, $swap ) = @_;
150              
151             # Ignore $swap since n×c = c×n when n is a number and the reason why swap would be set.
152            
153 3007         2860 my ( $ii, $a, $as, $b, $bs, $c, $cs, $d, $ds );
154              
155 3007         3362 $ii = $m->i_squared;
156 3007         3496 $a = $m->a;
157 3007         3682 $b = $m->b;
158 3007 100       3579 if (ref $o) {
159 1109         1505 $c = $o->a;
160 1109         1217 $d = $o->b;
161 1109 100       1335 if ($m->is_complex) {
162 860         836 $as = $a;
163 860         774 $bs = $b;
164             }
165             else {
166 249         316 $as = $m->a->conjugate;
167 249         352 $bs = $m->b->conjugate;
168             }
169 1109 100       1323 if ($o->is_complex) {
170 853         902 $cs = $c;
171 853         758 $ds = $d;
172             }
173             else {
174 256         291 $cs = $o->a->conjugate;
175 256         335 $ds = $o->b->conjugate;
176             }
177             }
178             else {
179 1898         1820 $c = $o;
180 1898         1788 $d = 0;
181 1898 100       2029 if ($m->is_complex) {
182 1612         1487 $as = $a;
183 1612         1374 $bs = $b;
184             }
185             else {
186 286         352 $as = $a->conjugate;
187 286         382 $bs = $b->conjugate;
188             }
189 1898         2277 $cs = $o;
190 1898         1757 $ds = 0;
191             }
192              
193             # the eight ways to multiply Cayley Dickson number constructions...
194             #
195 3007         3398 my $dp = $m->doubling_product;
196 3007 50       6200 if ($dp eq 'P0' ) { (ref $m)->new($c * $a + $ii * $bs * $d, $d * $as + $b * $c) }
  0 50       0  
    50          
    50          
    50          
    0          
    0          
    0          
197 0         0 elsif ($dp eq 'P1' ) { (ref $m)->new($c * $a + $ii * $d * $bs, $as * $d + $c * $b) }
198 0         0 elsif ($dp eq 'P2' ) { (ref $m)->new($a * $c + $ii * $bs * $d, $d * $as + $b * $c) }
199 0         0 elsif ($dp eq 'P3' ) { (ref $m)->new($a * $c + $ii * $d * $bs, $as * $d + $c * $b) }
200 3007         5853 elsif ($dp eq 'Pt0') { (ref $m)->new($c * $a + $ii * $b * $ds, $a * $d + $cs * $b) } # <= default
201 0         0 elsif ($dp eq 'Pt1') { (ref $m)->new($c * $a + $ii * $ds * $b, $d * $a + $b * $cs) }
202 0         0 elsif ($dp eq 'Pt2') { (ref $m)->new($c * $a + $ii * $b * $ds, $a * $d + $cs * $b) }
203 0         0 elsif ($dp eq 'Pt3') { (ref $m)->new($a * $c + $ii * $ds * $b, $d * $a + $b * $cs) }
204             }
205              
206              
207              
208             #
209             # Tensor: $a->tensor($b) = A⊗ B = (a,b)⊗ (c,d) = (ac,ad,bc,bd)
210             #
211             sub tensor {
212 23     23 1 59 my ( $m, $o ) = @_;
213              
214 23 100       38 if ($m->is_complex) {
215 22         66 (ref $m)->new($m->a * $o, $m->b * $o)
216             }
217             else {
218 1         10 (ref $m)->new($m->a->tensor($o), $m->b->tensor($o))
219             }
220             }
221              
222              
223              
224             #
225             # Creates a new CayleyDickson object
226             # provide a list of two (or any 2^n) numbers or objects ...
227             #
228             sub new {
229 6842     6842 1 13947 my $c = shift;
230 6842         6679 my $n = scalar @_;
231 6842 100       9763 my @pair = $n > 2 ? ($c->new(@_[0 ..$n/2-1]),$c->new(@_[$n/2 ..$n-1])) : @_[0,1];
232 6842         15205 bless [@pair] => $c
233             }
234              
235              
236              
237             #
238             # hold the left number/object in a and the right number/object in b.
239             #
240 17131     17131 1 14402 sub a { ${(shift)}[NO ] }
  17131         23524  
241 10845     10845 1 9207 sub b { ${(shift)}[YES] }
  10845         13035  
242              
243              
244              
245             #
246             # flat: list of the scalar values pointed to by a,b references in the object references in order ...
247             #
248             sub flat {
249 961     961 1 1116 my $m = shift;
250 961 100       984 $m->is_complex ? ($m->a, $m->b) : ($m->a->flat, $m->b->flat);
251             }
252              
253              
254             #
255             # print the beautiful objects in terse human format ...
256             #
257             sub as_string {
258 80     80 1 253 my ( $m, $i, $swap ) = ( shift, 0, '' );
259              
260 80         121 foreach my $t ($m->flat) {
261 254 100 100     511 if ($t or not $i) {
262 146 100       218 $swap .= sprintf '%s%s%s', ($t < 0 ? '-' : '+'), abs($t), ${ SYMBOLS() }[$i]
  146         388  
263             }
264 254         291 $i ++
265             }
266             $swap
267 80         352 }
268              
269              
270              
271             #
272             # compare the string format of this object to the given string
273             #
274 43     43 0 200 sub eq { shift->as_string eq shift }
275              
276              
277              
278             #
279             # override these methods to test other algebras or the dual and split number systems ...
280             #
281             # doubling_product:See DOUBLING constant above for option choices. Override this method in your subclass if you like.
282             #
283             # i_squared: algebra selection. See I_SQUARED constant above for option choices. Override this method in your subclass if you like.
284             #
285             #
286 3007     3007 1 3046 sub doubling_product { DOUBLING_PRODUCT }
287 3007     3007 1 2851 sub i_squared { I_SQUARED }
288              
289              
290              
291             #
292             # additional meta tools ...
293             #
294 0     0 0 0 sub is_real { NO } # you could not be here if you are real
295 128220     128220 1 137460 sub is_complex { not ref (shift->a) }
296 0     0 1   sub is_quaternion { shift->_child_is('is_complex' ) }
297 0     0 1   sub is_octonion { shift->_child_is('is_quaternion' ) }
298 0     0 1   sub is_sedenion { shift->_child_is('is_octonion' ) }
299 0     0 1   sub is_trigintaduonions { shift->_child_is('is_sedenion' ) }
300 0     0 1   sub is_sexagintaquatronions { shift->_child_is('is_trigintaduonions' ) }
301 0     0 1   sub is_centumduodetrigintanions { shift->_child_is('is_sexagintaquatronions' ) }
302 0     0 1   sub is_ducentiquinquagintasexions { shift->_child_is('is_centumduodetrigintanions' ) }
303             #sub is_etc ...
304              
305              
306             #
307             # determine if the child is of the given type by common cayley dickson name ...
308             #
309             sub _child_is {
310 0     0     my $m = shift;
311 0           my $method = shift;
312 0 0 0       not $m->is_complex and $m->a->can($method) and $m->a->$method;
313             }
314              
315             =encoding utf8
316              
317             =pod
318              
319             =head1 NAME
320              
321             CayleyDickson - create and operate with hypercomplex numbers
322              
323             =head1 SYNOPSIS
324              
325             =over 4
326              
327             use Tangle;
328             my $q1 = Tangle->new(1,0);
329             print "q1 = $q1\n";
330             $q1->x_gate;
331             print "X(q1) = $q1\n";
332             $q1->hadamard;
333             print "H(X(q1)) = $q1\n";
334              
335             my $q2 = Tangle->new(1,0);
336             print "q2 = $q2\n";
337              
338             # perform CNOT($q1 ⊗ $q2)
339             $q1->cnot($q2);
340              
341             print "q1 = $q1\n";
342             print "q2 = $q2\n";
343              
344             $q1->x_gate;
345             print "X(q1) = $q1\n";
346             print "entanglement causes q2 to automatically changed: $q2\n";
347              
348             =back
349              
350             =head1 DESCRIPTION
351              
352             =over 3
353              
354             Cayley-Dickson construction and operations are defined here: https://en.wikipedia.org/wiki/Cayley–Dickson_construction
355              
356             This object provides natural and intuitive operations on these numbers by overriding the native numeric operations (+,-,/,*)
357              
358             =back
359              
360             =head1 USAGE
361              
362              
363             =head2 new()
364              
365             =over 3
366              
367             # create a new CayleyDickson number "i" ...
368             my $q1 = CayleyDickson->new(0,1);
369              
370              
371             # create a new CayleyDickson number "1+2i+3j+4k" ...
372             my $q2 = CayleyDickson->new(1,2,3,4);
373              
374              
375             # create a bigger CayleyDickson number (a Sedenion) ...
376             my $q3 = CayleyDickson->new(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);
377              
378              
379             # create a CayleyDickson number from others ...
380             my $one = CayleyDickson->new(0,1);
381             my $zero = CayleyDickson->new(1,0);
382             my $quaternion = CayleyDickson->new($one,$zero);
383              
384             =back
385              
386             =head2 conjugate()
387              
388             =over 3
389              
390             if z = (a,b)
391             then conjugate z = z* = (a,b)* = (a*,-b)
392             or conjugate(number) = number
393            
394             printf "The conjugate of q1 is: %s\n", $q1->conjugate;
395              
396             =back
397              
398             =head2 inverse()
399              
400             =over 3
401              
402             if z = (a,b)
403             then inverse z = z⁻¹ = (a,b)⁻¹ = (a,b)*/(norm(a,b)²)
404             or inverse(number) = number
405            
406             printf "The inverse of q1 is: %s\n", $q1->inverse;
407              
408             =back
409              
410             =head2 norm()
411              
412             =over 3
413              
414             if z = (a,b)
415             then norm z = norm(a,b) = √(norm(a)²+norm(b)²)
416             or norm(number) = number
417            
418             printf "The norm of q1 is: %s\n", $q1->norm;
419              
420             =back
421              
422             =head2 add()
423              
424             =over 3
425              
426             # ass z1 + z2 = (a,b)+(c,d) = (a+c,b+d)
427            
428             printf "The sum of q1 + q2 is: %s\n", $q1 + $q2;
429              
430             =back
431              
432             =head2 subtract()
433              
434             =over 3
435              
436             # subtract z1 - z2 = (a,b)-(c,d) = (a-c,b-d)
437              
438             printf "The difference of q1 - q2 is: %s\n", $q1 - $q2;
439              
440             =back
441              
442             =head2 divide()
443              
444             =over 3
445              
446             # divide z1 / z2 = z1 × inverse(z2)
447            
448             printf "The division of q1 / q2 is: %s\n", $q1 / $q2;
449              
450             =back
451              
452             =head2 multiply()
453              
454             =over 3
455              
456             # Multiply: (a,b)×(c,d) = (a×c - d*×b, d×a + b×c*) where x* = conjugate(x) or x if x is a number
457              
458             printf "The product of q1 * q2 is: %s\n", $q1 * $q2;
459              
460             =back
461              
462             =head2 new()
463              
464             =over 3
465              
466             create a new CayleyDickson number of any size ...
467              
468             # create the number 1+j-k ...
469             my $c = CayleyDickson->new( -1, 0, 1, -1 );
470              
471             # create an octonion ...
472             my $c = CayleyDickson->new( 3, 7, -2, 8, 0, 3, 3, 5 );
473              
474             # create a representation of the Horne bell state |+-> ...
475             my $c = CayleyDickson->new( 1/2, 1/2, 1/2 ,-1/2 );
476              
477             # create a 128 number construction: 1+2i+3j+4k+ .. + 128 ....
478             my $c = CayleyDickson->new(1 .. 128);
479              
480             =back
481              
482             =head2 tensor()
483              
484             =over 3
485              
486             Tensors two Cayley Dickson numbers to calculate a new number of higher dimensional construction.
487             reference: https://en.wikipedia.org/wiki/Tensor_product
488              
489             # calculate the tensor of c1 ⊗ c2 ...
490             $d = $c1->tensor($c2);
491              
492             $d will be a number of the product of the dimensions of c1 and c2.
493              
494             =back
495              
496             =head2 a()
497              
498             =head2 b()
499              
500             =over 3
501              
502             returns the two objects or numbers held by this object
503              
504             =back
505              
506             =head2 flat()
507              
508             =over 3
509              
510             return all the coefficients of the number as an array
511            
512             printf "[%s]\n", join( ', ', $q1->flat);
513              
514              
515             =back
516              
517             =head2 as_string()
518              
519             =over 3
520              
521             called automatically when this object is requested in a string form.
522             if you want to force the object to be resolved as a string ...
523              
524             printf "q1 as a string = %s\n", $q1->as_string;
525              
526             =back
527              
528             =head2 i_squared()
529              
530             =over 3
531              
532             returns the square of i: i² = -1
533              
534             normally this will be -1, but you can change it to +1 or 0 using the constant I_SQUARED
535              
536              
537              
538             =back
539              
540             =head2 doubling_product()
541              
542             =over 3
543              
544             something
545              
546             =back
547              
548             =head2 is_complex()
549              
550             =head2 is_quaternion()
551              
552             =head2 is_octonion()
553              
554             =head2 is_sedenion()
555              
556             =head2 is_trigintaduonions()
557              
558             =head2 is_sexagintaquatronions()
559              
560             =head2 is_centumduodetrigintanions()
561              
562             =head2 is_ducentiquinquagintasexions()
563              
564             returns true if the given object has depth equal to the function name
565              
566             if ($q1->is_octionion) {
567             print "q1 is an Octonion\n";
568             }
569             else {
570             print "q1 is NOT an Octonion\n";
571             }
572              
573             =back
574              
575             =head1 SUMMARY
576              
577             =over 3
578              
579             This object holds Cayley Dickson numbers and provides math operations on them.
580              
581             =back
582              
583             =head1 AUTHOR
584              
585             Jeff Anderson
586             truejeffanderson@gmail.com
587              
588             =cut
589              
590              
591             1;
592              
593             __END__