File Coverage

blib/lib/Quantum/Superpositions.pm
Criterion Covered Total %
statement 166 246 67.4
branch 23 30 76.6
condition 6 14 42.8
subroutine 52 87 59.7
pod 0 11 0.0
total 247 388 63.6


line stmt bran cond sub pod time code
1              
2             package Quantum::Superpositions;
3              
4             ########################################################################
5             # housekeeping
6             ########################################################################
7              
8 1     1   926 use strict;
  1         1  
  1         39  
9              
10 1     1   4 use Carp;
  1         3  
  1         125  
11 1     1   791 use Class::Multimethods;
  1         15675  
  1         8  
12              
13             our $VERSION = '2.02';
14              
15             sub import
16             {
17             {
18 3     3   30 my $caller = caller;
  3         7  
19              
20 1     1   208 no strict 'refs';
  1         2  
  1         626  
21              
22 9         47 *{ $caller . '::' . $_ } = __PACKAGE__->can( $_ )
23 3         24 for qw( all any eigenstates );
24             }
25              
26 3         11 my ($class, %quantized) = @_;
27              
28 3         4 quantize_unary($_,'quop') for @{$quantized{UNARY}};
  3         11  
29 3         4 quantize_unary($_,'qulop') for @{$quantized{UNARY_LOGICAL}};
  3         7  
30              
31 3         4 quantize_binary($_,'qbop') for @{$quantized{BINARY}};
  3         8  
32 3         5 quantize_binary($_,'qblop') for @{$quantized{BINARY_LOGICAL}};
  3         9  
33              
34              
35 3         2748 1
36             }
37              
38             ########################################################################
39             # utility subroutines and package variables
40             #
41             # these are small enough to get lost in the shuffle. easier to put them
42             # up here than loose 'em...
43             ########################################################################
44              
45             # used to print intermediate results if $debug is true.
46              
47             my $debug = 0;
48              
49             sub debug
50             {
51 0     0 0 0 print +(caller(1))[3], "(";
52 0         0 print +overload::StrVal($_), "," for @_;
53 0         0 print ")\n";
54             }
55              
56             # cleans up overloaded calls.
57              
58 166 100   166 0 4607 sub swap { $_[2] ? @_[1,0] : @_[0,1] }
59              
60             # eigencache tracks objects results. destructor has to clean
61             # out the cache. due to overloading this cannot simply use
62             # the $hash{$referent} trick.
63              
64             my %eigencache;
65              
66 334     334   5321 sub DESTROY { delete $eigencache{overload::StrVal($_[0])}; }
67              
68             # replaces the cartesian product with an iterator. normal use is
69             # something like:
70             #
71             # my ( $n, $sub ) = iterator \@list1, \@list2
72             #
73             # my @result = map { somefunc @$sub->() } (1..$n );
74             #
75             # note the limit check on $j: this returns an empty list
76             # after the process has iterated once. this allows for
77             # while( @pair = $iter->() ){ ... } and gracefully handles
78             # (0..$count) also.
79              
80             sub iterator
81             {
82 4     4 0 9 my ( $a, $b ) = ( shift, shift );
83 4         8 my ( $i, $j ) = ( -1, -1 );
84              
85             # caller gets back ( iterator count, closure ).
86             # the $j test also allows for while or for(;;)
87             # loops testing the return.
88              
89             (
90             @$a * @$b,
91              
92             sub
93             {
94 38     38   67 $i = ++$i % @$a;
95 38 100       77 ++$j unless $i;
96              
97 38 50       1085 $j < @$b ? [ $a->[$i], $b->[$j] ] : ()
98             }
99             )
100              
101 4         29 }
102              
103              
104             ########################################################################
105             # what users call. the rest of this stuff is generally called
106             # indirectly via multimethods on the contents of the objects.
107              
108 203     203 0 4765 sub any { bless [@_], 'Quantum::Superpositions::Disj' }
109 124     124 0 2849 sub all { bless [@_], 'Quantum::Superpositions::Conj' }
110              
111 5     5 0 41 sub all_true { bless [@_], 'Quantum::Superpositions::Conj::True' }
112              
113              
114             ########################################################################
115             # what the hell do these really do?
116              
117             sub quantize_unary
118             {
119 1     1 0 3 my ($fullsubname, $type) = @_;
120              
121 1         8 my ($package,$subname) = m/(.+)::(.+)$/;
122              
123 1         2 my $caller = caller;
124              
125 1         4 my $original = "CORE::$subname";
126              
127 1 50       6 if( $package ne 'CORE' )
128             {
129 0         0 $original = "Quantum::Superpositions::Quantized::$fullsubname";
130              
131 1     1   6 no strict;
  1         1  
  1         217  
132              
133 0         0 *{$original} = \&$fullsubname;
  0         0  
134             }
135             else
136             {
137 1         3 $package = 'CORE::GLOBAL';
138             }
139              
140             eval
141 1 50 33 1   1655 qq{
  1 50   1   60  
  1     1   6  
  1     1   232  
  1     58   2  
  1         8  
  1         958  
  1         2  
  1         97  
  1         4  
  1         1  
  1         44  
  1         79  
  58         178  
  1         4  
  1         32  
  0         0  
142             package $package;
143              
144             use subs '$subname';
145              
146             use Class::Multimethods '$type';
147             local \$SIG{__WARN__} = sub{};
148              
149             no strict 'refs';
150              
151             *{"${package}::$subname"} =
152             sub
153             {
154             local \$^W;
155             return \$_[0]->$type(sub{$original(\$_[0])})
156             if UNIVERSAL::isa(\$_[0],'Quantum::Superpositions')
157             || UNIVERSAL::isa(\$_[1],'Quantum::Superpositions');
158              
159             no strict 'refs';
160              
161             return $original(\$_[0]);
162             };
163             }
164             || croak "Internal error: $@";
165             }
166              
167             sub quantize_binary
168             {
169 2     2 0 3 my ($fullsubname, $type) = @_;
170 2         11 my ($package,$subname) = m/(.*)::(.*)/;
171 2         23 my $caller = caller;
172 2         4 my $original = "CORE::$subname";
173 2 100       7 if ($package ne 'CORE')
174             {
175 1         2 $original = "Quantum::Superpositions::Quantized::$fullsubname";
176              
177 1     1   6 no strict;
  1         1  
  1         3450  
178              
179 1         3 *{$original} = \&$fullsubname;
  1         9  
180             }
181             else
182             {
183 1         3 $package = 'CORE::GLOBAL';
184             }
185             eval
186 1 50 100 1   6 qq{
  1 100 33 1   2  
  1 50   1   4  
  1     1   71  
  1     1   1  
  1     1   5  
  1     1   836  
  1     1   2  
  1     7   104  
  1         6  
  1         1  
  1         50  
  1         11  
  1         1  
  1         3  
  1         52  
  1         1  
  1         3  
  1         851  
  1         1  
  1         87  
  1         3  
  1         1  
  1         42  
  2         134  
  7         173  
  7         173  
  3         64  
  14         92  
  4         29  
  2         7  
  2         70  
  0         0  
187             package $package;
188             use subs '$subname';
189              
190             use Class::Multimethods '$type';
191              
192             local \$SIG{__WARN__} = sub{};
193              
194             no strict 'refs';
195              
196             *{"${package}::$subname"} =
197             sub
198             {
199             local \$^W;
200             return $type(\@_[0,1],sub{$original(\$_[0],\$_[1])})
201             if UNIVERSAL::isa(\$_[0],'Quantum::Superpositions')
202             || UNIVERSAL::isa(\$_[1],'Quantum::Superpositions');
203              
204             no strict 'refs';
205              
206             return $original(\$_[0],\$_[1]);
207             };
208             } || croak "Internal error: $@";
209             }
210              
211             ########################################################################
212             # assign the multimethods operations for various types
213              
214             multimethod qbop =>
215             ( qw(
216             Quantum::Superpositions::Conj
217             Quantum::Superpositions::Conj
218             CODE
219              
220             ) ) =>
221             sub
222             {
223             my ( $count, $iter ) = iterator @_[0,1];
224              
225             all map { qbop(@{$iter->()}, $_[2]) } (1..$count);
226             };
227              
228             multimethod qbop =>
229             ( qw(
230             Quantum::Superpositions::Disj
231             Quantum::Superpositions::Disj
232             CODE
233             ) ) =>
234             sub
235             {
236             my ( $count, $iter ) = iterator( @_[0,1] );
237              
238             any map { qbop(@{$iter->()}, $_[2]) } (1..$count);
239             };
240              
241             multimethod qbop =>
242             ( qw(
243             Quantum::Superpositions::Conj
244             Quantum::Superpositions::Disj
245             CODE
246             ) ) =>
247             sub
248             {
249             all map { qbop($_, $_[1], $_[2]) } @{$_[0]};
250             };
251              
252             multimethod qbop =>
253             ( qw(
254             Quantum::Superpositions::Disj
255             Quantum::Superpositions::Conj
256             CODE
257             ) ) =>
258             sub
259             {
260             any map { qbop($_, $_[1], $_[2]) } @{$_[0]}
261             };
262              
263             multimethod qbop =>
264             ( qw(
265             Quantum::Superpositions::Conj
266             *
267             CODE
268             ) ) =>
269             sub
270             {
271             all map { qbop($_, $_[1], $_[2]) } @{$_[0]}
272             };
273              
274             multimethod qbop =>
275             ( qw(
276             Quantum::Superpositions::Disj
277             *
278             CODE
279             ) ) =>
280             sub
281             {
282             any map { qbop($_, $_[1], $_[2]) } @{$_[0]}
283             };
284              
285             multimethod qbop =>
286             ( qw(
287             *
288             Quantum::Superpositions::Disj
289             CODE
290             ) ) =>
291             sub
292             {
293             any map { qbop($_[0], $_, $_[2]) } @{$_[1]}
294             };
295              
296             multimethod qbop =>
297             ( qw(
298             *
299             Quantum::Superpositions::Conj
300             CODE
301             ) ) =>
302             sub
303             {
304             all map { qbop($_[0], $_, $_[2]) } @{$_[1]}
305             };
306              
307             multimethod qbop =>
308             ( qw(
309             *
310             *
311             CODE
312             ) ) =>
313             sub
314             {
315             $_[2]->(@_[0..1])
316             };
317              
318             multimethod qblop =>
319             ( qw(
320             Quantum::Superpositions::Conj
321             Quantum::Superpositions::Conj
322             CODE
323             ) ) =>
324             sub
325             {
326             &debug if $debug;
327              
328             return all() unless @{$_[0]} && @{$_[1]};
329              
330             my ( $count, $iter ) = iterator @_[0,1];
331              
332             istrue( qblop(@{$iter->()}, $_[2]) ) || return all() for (1..$count);
333              
334             all_true @{$_[0]};
335             };
336              
337             multimethod qblop =>
338             ( qw(
339             Quantum::Superpositions::Conj
340             Quantum::Superpositions::Disj
341             CODE
342             ) ) =>
343             sub
344             {
345             &debug if $debug;
346              
347             return all() unless @{$_[0]} && @{$_[1]};
348              
349             my @cstates = @{$_[0]};
350              
351             my @matchstates;
352              
353             my $okay = 0;
354              
355             for my $cstate ( @cstates )
356             {
357             for my $dstate ( @{$_[1]} )
358             {
359             ++$okay && last
360             if istrue(qblop($cstate, $dstate, $_[2]));
361             }
362             }
363              
364             return all() unless $okay == @cstates;
365             return all_true @{$_[0]};
366             };
367              
368             multimethod qblop =>
369             ( qw(
370             Quantum::Superpositions::Disj
371             Quantum::Superpositions::Conj
372             CODE
373             ) ) =>
374             sub
375             {
376             &debug if $debug;
377              
378             return any() unless @{$_[0]} && @{$_[1]};
379              
380             my @dstates = @{$_[0]};
381             my @cstates = @{$_[1]};
382              
383             my @dokay = (0) x @dstates;
384             for my $cstate ( @cstates )
385             {
386             my $matched;
387             for my $d ( 0..$#dstates )
388             {
389             $matched = ++$dokay[$d]
390             if istrue(qblop($dstates[$d], $cstate, $_[2]));
391             }
392              
393             return any() unless $matched;
394             }
395              
396             return any @dstates[grep { $dokay[$_] == @cstates } (0..$#dstates)];
397             };
398              
399             multimethod qblop =>
400             ( qw(
401             Quantum::Superpositions::Conj
402             *
403             CODE
404             ) ) =>
405             sub
406             {
407             &debug if $debug;
408              
409             return all() unless @{$_[0]};
410             istrue(qblop($_, $_[1], $_[2])) || return all() for @{$_[0]};
411             return all_true @{$_[0]};
412             };
413              
414             multimethod qblop =>
415             ( qw(
416             *
417             Quantum::Superpositions::Conj
418             CODE
419             ) ) =>
420             sub
421             {
422             &debug if $debug;
423              
424             return all() unless @{$_[1]};
425             istrue(qblop($_[0], $_, $_[2])) || return all() for @{$_[1]};
426             return all_true $_[0];
427             };
428              
429             multimethod qblop =>
430             ( qw(
431             Quantum::Superpositions::Disj
432             *
433             CODE
434             ) ) =>
435             sub
436             {
437             &debug if $debug;
438              
439             return any() unless @{$_[0]};
440             return any grep { istrue(qblop($_, $_[1], $_[2])) } @{$_[0]};
441             };
442              
443             multimethod qblop =>
444             ( qw(
445             *
446             Quantum::Superpositions::Disj
447             CODE
448             ) ) =>
449             sub
450             {
451             &debug if $debug;
452              
453             return any() unless @{$_[1]};
454             return any grep { istrue(qblop($_[0], $_, $_[2])) } @{$_[1]};
455             };
456              
457             multimethod qblop =>
458             ( qw(
459             Quantum::Superpositions::Disj
460             Quantum::Superpositions::Disj
461             CODE
462             ) ) =>
463             sub
464             {
465             &debug if $debug;
466              
467             return any() unless @{$_[0]} && @{$_[1]};
468             return any grep { istrue(qblop($_[0], $_, $_[2])) } @{$_[1]};
469             };
470              
471             multimethod qblop =>
472             ( qw(
473             *
474             *
475             CODE
476             ) ) =>
477             sub
478             {
479             &debug if $debug;
480              
481             return qbop(@_) ? $_[0] : ();
482             };
483              
484             ########################################################################
485             # overload everything possible into appropraite multimethods.
486             # this is where the limitation for regexen hits.
487              
488             use overload
489              
490 0     0   0 q{+} => sub { qbop(swap(@_), sub { $_[0] + $_[1] })},
  0         0  
491 0     0   0 q{-} => sub { qbop(swap(@_), sub { $_[0] - $_[1] })},
  0         0  
492 2     21   6 q{*} => sub { qbop(swap(@_), sub { $_[0] * $_[1] })},
  21         65  
493 2     116   8 q{/} => sub { qbop(swap(@_), sub { $_[0] / $_[1] })},
  116         381  
494 0     0   0 q{%} => sub { qbop(swap(@_), sub { $_[0] % $_[1] })},
  0         0  
495 0     0   0 q{**} => sub { qbop(swap(@_), sub { $_[0] ** $_[1] })},
  0         0  
496 0     0   0 q{<<} => sub { qbop(swap(@_), sub { $_[0] << $_[1] })},
  0         0  
497 0     0   0 q{>>} => sub { qbop(swap(@_), sub { $_[0] >> $_[1] })},
  0         0  
498 0     0   0 q{x} => sub { qbop(swap(@_), sub { $_[0] x $_[1] })},
  0         0  
499 0     0   0 q{.} => sub { qbop(swap(@_), sub { $_[0] . $_[1] })},
  0         0  
500 0     0   0 q{&} => sub { qbop(swap(@_), sub { $_[0] & $_[1] })},
  0         0  
501 0     0   0 q{^} => sub { qbop(swap(@_), sub { $_[0] ^ $_[1] })},
  0         0  
502 0     0   0 q{|} => sub { qbop(swap(@_), sub { $_[0] | $_[1] })},
  0         0  
503 0     0   0 q{atan2}=> sub { qbop(swap(@_), sub { atan2($_[0],$_[1]) })},
  0         0  
504              
505 5     5   11 q{<} => sub { qblop(swap(@_), sub { $_[0] < $_[1] })},
  52         1333  
506 0     0   0 q{<=} => sub { qblop(swap(@_), sub { $_[0] <= $_[1] })},
  0         0  
507 5     5   14 q{>} => sub { qblop(swap(@_), sub { $_[0] > $_[1] })},
  36         969  
508 0     0   0 q{>=} => sub { qblop(swap(@_), sub { $_[0] >= $_[1] })},
  0         0  
509 47     3470   103 q{==} => sub { qblop(swap(@_), sub { $_[0] == $_[1] })},
  3470         91490  
510 2     2   9 q{!=} => sub { qblop(swap(@_), sub { $_[0] != $_[1] })},
  18         535  
511 0     0   0 q{<=>} => sub { qblop(swap(@_), sub { $_[0] <=> $_[1] })},
  0         0  
512 0     0   0 q{lt} => sub { qblop(swap(@_), sub { $_[0] lt $_[1] })},
  0         0  
513 0     0   0 q{le} => sub { qblop(swap(@_), sub { $_[0] le $_[1] })},
  0         0  
514 0     0   0 q{gt} => sub { qblop(swap(@_), sub { $_[0] gt $_[1] })},
  0         0  
515 0     0   0 q{ge} => sub { qblop(swap(@_), sub { $_[0] ge $_[1] })},
  0         0  
516 103     349   196 q{eq} => sub { qblop(swap(@_), sub { $_[0] eq $_[1] })},
  349         8818  
517 0     0   0 q{ne} => sub { qblop(swap(@_), sub { $_[0] ne $_[1] })},
  0         0  
518 0     0   0 q{cmp} => sub { qblop(swap(@_), sub { $_[0] cmp $_[1] })},
  0         0  
519              
520 0     0   0 q{cos} => sub { $_[0]->quop(sub { cos $_[0] })},
  0         0  
521 0     0   0 q{sin} => sub { $_[0]->quop(sub { sin $_[0] })},
  0         0  
522 0     0   0 q{exp} => sub { $_[0]->quop(sub { exp $_[0] })},
  0         0  
523 0     0   0 q{abs} => sub { $_[0]->quop(sub { abs $_[0] })},
  0         0  
524 0     0   0 q{sqrt} => sub { $_[0]->quop(sub { sqrt $_[0] })},
  0         0  
525 0     0   0 q{log} => sub { $_[0]->quop(sub { log $_[0] })},
  0         0  
526 0     0   0 q{neg} => sub { $_[0]->quop(sub { -$_[0] })},
  0         0  
527 0     0   0 q{~} => sub { $_[0]->quop(sub { ~$_[0] })},
  0         0  
528              
529             q{&{}} =>
530             sub
531             {
532 2     2   5 my $s = shift;
533 2     2   6 return sub { bless [map {$_->(@_)} @$s], ref $s }
  4         18  
534 2         10 },
535              
536 0     0   0 q{!} => sub { $_[0]->qulop(sub { !$_[0] })},
  0         0  
537              
538 1         65 q{bool} => 'qbool',
539             q{""} => 'qstr',
540             q{0+} => 'qnum',
541 1     1   9 ;
  1         1  
542              
543             ########################################################################
544             # extract results from the Q::S objects.
545              
546             multimethod collapse =>
547             ( 'Quantum::Superpositions' ) =>
548             sub { return map { collapse($_) } @{$_[0]} };
549              
550             multimethod collapse => ( '*' ) => sub { return $_[0] };
551              
552             sub eigenstates($)
553             {
554 73     73 0 95 my ($self) = @_;
555 73         193 my $eigencache_id = overload::StrVal($self);
556 73 100       430 return @{$eigencache{$eigencache_id}}
  27         108  
557             if defined $eigencache{$eigencache_id};
558 46         54 my %uniq;
559 46         1154 @uniq{collapse($self)} = ();
560 46         166 local $^W=1;
561 46         513 return @{$eigencache{$eigencache_id}} =
  103         526  
562             grep
563             {
564 46         104 my $okay=1;
565 103     8   593 local $SIG{__WARN__} = sub {$okay=0};
  8         205  
566 103 100 33     230 istrue($self eq $_) || istrue($self == $_) && $okay
567             }
568             keys %uniq;
569             }
570              
571             multimethod istrue => ( 'Quantum::Superpositions::Disj' ) =>
572             sub
573             {
574             my @states = @{$_[0]} || return 0;
575             istrue($_) && return 1 for @states; return 0;
576             };
577              
578             multimethod istrue => ( 'Quantum::Superpositions::Conj::True' ) =>
579             sub { return 1; };
580              
581             multimethod istrue => ( 'Quantum::Superpositions::Conj' ) =>
582             sub
583             {
584             my @states = @{$_[0]} || return 0;
585             istrue($_) || return 0 for @states; return 1;
586             };
587              
588             multimethod istrue => ( '*' ) => sub { return defined $_[0]; };
589              
590             multimethod istrue => () => sub { return 0; };
591              
592 15 100   15 0 151 sub qbool { $_[0]->eigenstates ? 1 : 0; }
593 0     0 0 0 sub qnum { my @states = $_[0]->eigenstates; return $states[rand @states] }
  0         0  
594              
595             ########################################################################
596             ########################################################################
597             # embedded classes.
598             #
599             # these are what the constructors bless things into.
600             ########################################################################
601              
602             package Quantum::Superpositions::Disj;
603 1     1   945 use base 'Quantum::Superpositions';
  1         1  
  1         151  
604 1     1   6 use Carp;
  1         2  
  1         273  
605              
606             sub qstr
607             {
608 35     35   173 my @eigenstates = $_[0]->eigenstates;
609 35 100       135 return "@eigenstates" if @eigenstates == 1;
610 28         134 return "any(".join(",",@eigenstates).")"
611             }
612              
613 1     1   3 sub quop { Quantum::Superpositions::any(map { $_[1]->($_) } @{$_[0]}) }
  58         1584  
  1         4  
614              
615 0     0   0 sub qulop { Quantum::Superpositions::any(grep { $_[1]->($_) } @{$_[0]}) }
  0         0  
  0         0  
616              
617              
618             package Quantum::Superpositions::Conj;
619 1     1   6 use base 'Quantum::Superpositions';
  1         2  
  1         474  
620 1     1   7 use Carp;
  1         3  
  1         268  
621              
622             sub qstr
623             {
624 22     22   226 my @eigenstate = $_[0]->eigenstates;
625              
626 22 50       59 @eigenstate ? "@eigenstate" : "all(".join(",",@{$_[0]}).")"
  22         95  
627             }
628              
629 0     0   0 sub quop { return Quantum::Superpositions::all(map { $_[1]->($_) } @{$_[0]}) }
  0         0  
  0         0  
630              
631             sub qulop
632             {
633 0   0 0   0 $_[1]->($_) || return Quantum::Superpositions::all() for @{$_[0]};
  0         0  
634              
635 0         0 Quantum::Superpositions::all(@{$_[0]})
  0         0  
636             }
637              
638              
639             package Quantum::Superpositions::Conj::True;
640 1     1   6 use base 'Quantum::Superpositions::Conj';
  1         2  
  1         612  
641              
642 5     5   49 sub qbool { 1 }
643              
644              
645             1;
646              
647             __END__