File Coverage

blib/lib/Quantum/Entanglement.pm
Criterion Covered Total %
statement 231 563 41.0
branch 30 102 29.4
condition 4 36 11.1
subroutine 31 75 41.3
pod 7 17 41.1
total 303 793 38.2


line stmt bran cond sub pod time code
1             package Quantum::Entanglement;
2 1     1   8323 use strict;
  1         3  
  1         39  
3 1     1   5 use warnings;
  1         2  
  1         29  
4 1     1   6 use Carp;
  1         5  
  1         76  
5              
6             BEGIN {
7 1     1   4 use Exporter ();
  1         1  
  1         18  
8 1     1   1139 use Math::Complex;
  1         18370  
  1         328  
9 1     1   3 my @M_Complex = qw(i Re Im rho theta arg cplx cplxe);
10 1         2 our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
11 1         2 $VERSION = 0.32;
12 1         11 @ISA = qw(Exporter);
13 1         2 @EXPORT = qw(&entangle &p_op &p_func &q_logic
14             &save_state &restore_state);
15 1         8 %EXPORT_TAGS = (DEFAULT => [@EXPORT],
16             complex => [@M_Complex],
17             QFT => [qw(&QFT)],);
18 1         3353 @EXPORT_OK = (@M_Complex, '&QFT');
19             }
20             our (@EXPORT_OK, @EXPORT);
21              
22             $Quantum::Entanglement::destroy = 1; # true=> p(0) states stomped on
23             $Quantum::Entanglement::conform = 0; # true=> strives for truth when observing
24              
25             ## Contents:
26             # Constructors
27             # Utility Routines
28             # Overload table
29             # Overload routines
30             # parallel operators and functions
31             # methods for saving and restoring state
32             # pod
33              
34             # =begin pretty pictures
35             #
36             # Things look a bit like this...
37             #
38             # $variable = [ref to var which itself refs to an annon array (the universe),
39             # offset of values of variable within universe,
40             # ref to var which itself refs to an annon array (the offsets)];
41             #
42             # $offsets = [refs to all the offsets in a given universe, ...]
43             # $universe= [ [prob1,val1,prob2,val2],
44             # [prob1,val1,prob2,val2], etc. ]
45             #
46             # =cut
47              
48             # creates a new set of universes
49             sub _new {
50 6     6   11 my $universe = [];
51 6         9 my $offsets = [];
52 6         12 my $var = [\$universe,1,\$offsets];
53 6         13 $offsets->[0] = \ $var->[1];
54 6         16 while (@_) {
55 10         32 push @$universe, [shift,shift];
56             }
57 6         16 bless $var, 'Quantum::Entanglement';
58 6         17 return $var;
59             }
60              
61             # add a variable without adding values (ie. a derived value)
62             # returns the new variable
63             sub _add {
64 15     15   17 my $current = $_[0];
65 15         14 my $universe = ${ $current->[0]};
  15         25  
66 15         18 my $offset = scalar(@{$universe->[0]}) + 1;
  15         26  
67 15         18 my $var= [\$universe,$offset,\ ${$current->[2]}];
  15         35  
68 15         18 push @{${$current->[2]}} , \$var->[1];
  15         17  
  15         32  
69 15         37 bless $var, 'Quantum::Entanglement';
70 15         27 return $var;
71             }
72              
73             # joins together two previously unconnected universes
74             # takes two variables as args, gets the universes from those.
75             # should be used to modify objects in place.
76             sub _join {
77 15     15   16 my ($uni1,$uni2) = (${$_[0]->[0]},${$_[1]->[0]});
  15         37  
  15         28  
78 15 100       50 return () if $uni1 == $uni2;
79 2         4 my $universe = [];
80 2         4 foreach my $s2 (@$uni2) {
81 4         8 foreach my $s1 (@$uni1) {
82 8         23 push @$universe, [@$s1,@$s2];
83             }
84             }
85 2         3 my $offsets1 = ${$_[0]->[2]};
  2         5  
86 2         12 my $offsets2 = ${$_[1]->[2]};
  2         4  
87 2         3 my $extra = scalar(@{$uni1->[0]});
  2         4  
88 2         5 push @$offsets1, map {$$_+=$extra; $_} @$offsets2;
  2         3  
  2         4  
89 2         3 ${$_[1]->[2]} = $offsets1;
  2         3  
90 2         3 ${$_[0]->[0]} = $universe;
  2         4  
91 2         2 ${$_[1]->[0]} = $universe;
  2         4  
92 2         6 return (1);
93             }
94              
95             # exported constructor
96             sub entangle {
97 6     6 1 788 return _new(@_);
98             }
99              
100             ## Utility routines
101              
102             # a view of global state space, might still show historical states which
103             # are no longer accessable, does not count as observation
104             sub show_states {
105 0     0 1 0 my $rt;
106 0         0 my $var = shift;
107 0         0 my $universe = ${$var->[0]};
  0         0  
108 0 0       0 if ($_[0]) {
109 0         0 foreach (@$universe) { my $t;
  0         0  
110 0 0       0 $rt .= (++$t % 2) ? "$_|" : overload::StrVal($_).">\t" foreach @$_;
111 0         0 $rt .= "\n";
112             }
113             }
114             else {
115 0         0 my $os = $var->[1];
116             $rt .= $_->[$os-1]."|".overload::StrVal($_->[$os]).">\t"
117 0         0 foreach @$universe;
118 0         0 substr($rt,-1,1,"\n");
119             }
120 0         0 return $rt;
121             }
122              
123             # egads! (and don't tell anyone about the grep, it's a secret)
124             sub DESTROY {
125 21     21   3854 my ($universe, $offsets) = (${$_[0]->[0]}, ${$_[0]->[2]});
  21         57  
  21         42  
126 21         38 my $os = $_[0]->[1];
127 21         138 splice(@$_,$os-1,2) foreach @$universe;
128 21 50       44 @$offsets = grep {if ($$_ != $os) {$$_ -= 2 if $$_ > $os;1;} else {0;}}
  53 100       86  
  32         58  
  32         45  
  21         57  
129             @$offsets;
130 21 50       80 _rationalise_states([\$universe])
131             if $Quantum::Entanglement::destroy;
132             }
133              
134             # takes two non normalised probabilities and returns true with prob(1/1+2)
135             sub _sel_output {
136 0     0   0 my ($c, $d) = @_;
137 0         0 $c = abs($c)**2;
138 0         0 $d = abs($d)**2;
139 0 0       0 return rand(1) < ($c/($c+$d)) ? 1 : 0;
140             }
141              
142             # Gets a ref to a hash of complex probs, produces ref to hash of sequential
143             # probs and ref to array of ordering.
144             sub _normalise {
145 15     15   20 my $hr = $_[0];
146 15         23 my $h2 = {};
147 15         17 my $muts = [keys %{$hr}];
  15         39  
148 15         20 my $sum = 0;
149 15         16 foreach (values %{$hr}) {
  15         35  
150 15         44 $sum += abs($_)**2;
151             }
152 15 50       345 if ($sum <= 0) {
153 0         0 croak "$0: Cannot behave probabilistically with -ve probs";
154             }
155             else {
156 15         14 my $cum;
157 15         37 @{$h2}{ @{$muts} } = map {$cum +=abs($_)**2;
  15         21  
  15         38  
  15         25  
158 15         18 $cum / $sum } @{$hr}{ @{$muts} };
  15         171  
  15         22  
159 15         39 return ($h2, $muts);
160             }
161             }
162              
163             # this builds up a multi-layered hash so as to find the unique sets of
164             # states, it then uses _unravel to get them back out of the hash
165             sub _rationalise_states {
166 21     21   34 my $universe = ${$_[0]->[0]};
  21         34  
167 21         27 my $len = scalar(@{$universe->[0]})/2;
  21         40  
168 21         47 my @p_os = map {$_*2 } (0..$len-1);
  32         66  
169 21         35 my @v_os = map {$_*2+1} (0..$len-1);
  32         67  
170 21         33 my $foo = {};
171 21         59 foreach my $state (@$universe) { # build an icky data structure
172 27         573 my $tref = $foo;
173 27         44 foreach (@v_os) {
174 44 50       84 my $val = ref($state->[$_]) ? overload::StrVal($state->[$_])
175             : $state->[$_];
176 44 100       79 if ($_==2*$len-1) { # last level of the structure
177 23 100       36 if (exists $tref->{$val}) {
178 6         7 my @temp = @{$state}[@p_os];
  6         20  
179 6         10 $_+=shift @temp foreach @{$tref->{$val}}[@p_os];
  6         25  
180             }
181             else {
182 17         18 $tref->{$val} = [@{$state}];
  17         95  
183             }
184             }
185             else { # an intermediate level
186 21 100       40 if (exists $tref->{$val}) {
187 6         14 $tref = $tref->{$val};
188             }
189             else {
190 15         48 $tref = $tref->{$val} = {};
191             }
192             }
193             }
194             }
195             # do something with it...
196 21         137 @$universe =();
197 21         23 while (1) {
198 38         64 my $aref = _unravel($foo);
199 38 100       76 last unless $aref;
200 17         27 push @$universe, $aref;
201             }
202 21         209 return $universe;
203             }
204              
205             sub _unravel {
206 38     38   40 my $tref = $_[0];
207 38 100       99 return undef unless (scalar keys %$tref);
208 17         19 my @hrs;
209 17         18 my($last_ref, $val);
210 17         19 do {
211 32         34 $last_ref = $tref;
212 32         78 ($val,$tref) = %$tref;
213 32         145 unshift @hrs, $val, $last_ref;
214             } until (ref($tref) eq 'ARRAY');
215 17         18 delete ${$last_ref}{$val};
  17         30  
216 17         26 splice @hrs, 0,2;
217 17         47 while (@hrs) {
218 15         23 my $val = shift @hrs;
219 15         56 my $h = shift @hrs;
220 15 50       17 delete ${$h}{$val} if scalar(keys %{${$h}{$val}}) < 1;
  15         46  
  15         15  
  15         50  
221             }
222 17         45 return $tref;
223             }
224              
225              
226             ##
227             # Overloading. Everything except for assignment operators
228             # are overloaded specifically. Need to specifically overload a lot
229             # of stuff so that pruning of states can happen as soon as poss
230              
231             use overload
232 1     1   12 '+' => sub { binop(@_, sub{$_[0] + $_[1]} ) },
  1         64  
233 1     1   22 '*' => sub { binop(@_, sub{$_[0] * $_[1]} ) },
  4         158  
234 1     1   12 '-' => sub { binop(@_, sub{$_[0] - $_[1]} ) },
  1         81  
235 1     1   14 '/' => sub { binop(@_, sub{$_[0] / $_[1]} ) },
  1         63  
236 1     1   13 '**' => sub { binop(@_, sub{$_[0] **$_[1]} ) },
  1         101  
237 1     1   9 '%' => sub { binop(@_, sub{$_[0] % $_[1]} ) },
  1         50  
238 1     1   12 'x' => sub { binop(@_, sub{$_[0] x $_[1]} ) },
  1         69  
239 1     1   11 '.' => sub { binop(@_, sub{$_[0] . $_[1]} ) },
  1         55  
240 1     1   11 '<<' => sub { binop(@_, sub{$_[0] <<$_[1]} ) },
  1         61  
241 1     1   11 '>>' => sub { binop(@_, sub{$_[0] >>$_[1]} ) },
  1         60  
242 1     1   11 '&' => sub { binop(@_, sub{$_[0] & $_[1]} ) },
  1         70  
243 1     1   11 '|' => sub { binop(@_, sub{$_[0] | $_[1]} ) },
  1         63  
244 0     0   0 '^' => sub { binop(@_, sub{$_[0] ^ $_[1]} ) },
  0         0  
245 0     0   0 '~' => sub { unnop($_[0], sub { ~$_[0]} ) },
  0         0  
246 0     0   0 'neg'=> sub { unnop($_[0], sub { -$_[0]} ) },
  0         0  
247 0     0   0 '!' => sub { unnop($_[0], sub { !$_[0]} ) },
  0         0  
248 0     0   0 '++' => sub { mutop($_[0], sub {++$_[0]} ) },
  0         0  
249 0     0   0 '--' => sub { mutop($_[0], sub {--$_[0]} ) },
  0         0  
250 0     0   0 '<' => sub { bioop(@_, sub{$_[0] < $_[1]} ) },
  0         0  
251 0     0   0 '>' => sub { bioop(@_, sub{$_[0] > $_[1]} ) },
  0         0  
252 0     0   0 '<=' => sub { bioop(@_, sub{$_[0] <= $_[1]} ) },
  0         0  
253 0     0   0 '>=' => sub { bioop(@_, sub{$_[0] >= $_[1]} ) },
  0         0  
254 0     0   0 '==' => sub { bioop(@_, sub{$_[0] == $_[1]} ) },
  0         0  
255 0     0   0 '!=' => sub { bioop(@_, sub{$_[0] != $_[1]} ) },
  0         0  
256 0     0   0 'lt' => sub { bioop(@_, sub{$_[0] lt $_[1]} ) },
  0         0  
257 0     0   0 'le' => sub { bioop(@_, sub{$_[0] le $_[1]} ) },
  0         0  
258 0     0   0 'ge' => sub { bioop(@_, sub{$_[0] ge $_[1]} ) },
  0         0  
259 0     0   0 'gt' => sub { bioop(@_, sub{$_[0] gt $_[1]} ) },
  0         0  
260 0     0   0 'eq' => sub { bioop(@_, sub{$_[0] eq $_[1]} ) },
  0         0  
261 0     0   0 'ne' => sub { bioop(@_, sub{$_[0] ne $_[1]} ) },
  0         0  
262 0     0   0 '<=>'=> sub { binop(@_, sub{$_[0] <=>$_[1]} ) },
  0         0  
263 0     0   0 'cmp'=> sub { binop(@_, sub{$_[0] cmp$_[1]} ) },
  0         0  
264 0     0   0 'cos'=> sub { unnop($_[0], sub{ cos $_[0]} ) },
  0         0  
265 0     0   0 'sin'=> sub { unnop($_[0], sub{ sin $_[0]} ) },
  0         0  
266 0     0   0 'exp'=> sub { unnop($_[0], sub{ exp $_[0]} ) },
  0         0  
267 0     0   0 'abs'=> sub { unnop($_[0], sub{ abs $_[0]} ) },
  0         0  
268 0     0   0 'log'=> sub { unnop($_[0], sub{ log $_[0]} ) },
  0         0  
269 0     0   0 'sqrt'=>sub { unnop($_[0], sub{ sqrt $_[0]}) },
  0         0  
270 0     0   0 'atan2'=>sub{ binop(@_, sub{atan2($_[0], $_[1])} ) },
  0         0  
271 1         79 '&{}'=> \&sub_ent,
272             'bool'=> \&bool_ent, q{""} => \&str_ent, '0+' => \&num_ent,
273             '=' => \©_ent,
274 1     1   13 'fallback' => 1;
  1         2  
275              
276             # copying (not observation, clones states, does not increase state space)
277             sub copy_ent {
278 0     0 0 0 my $os = $_[0]->[1];
279 0         0 my $val = $_[0]->_add;
280 0         0 my $universe = ${$_[0]->[0]};
  0         0  
281 0         0 push(@$_, $_->[$os-1], $_->[$os]) foreach @$universe;
282 0         0 return $val;
283             }
284              
285             # running entangled subroutines
286             sub sub_ent {
287 0     0 0 0 my $obj = $_[0];
288 0         0 my $os = $obj->[1];
289 0         0 my $universe = ${$obj->[0]};
  0         0  
290             return sub {
291 0     0   0 my $var = $obj->_add;
292 0         0 foreach my $state (@$universe) {
293 0         0 push(@$state, $state->[$os-1],
294             scalar( $state->[$os]->(@_) ));
295             }
296 0         0 return $var;
297             }
298 0         0 }
299              
300             # stringification (observation)
301             sub str_ent {
302 15     15 0 82 my $c = $_[0];
303 15         25 my $os = $c->[1];
304 15         16 my $universe = ${$c->[0]};
  15         22  
305 15         23 my %str_vals;
306             # work out which state we want to retain
307 15         21 foreach my $state (@$universe) {
308 21   100     851 $str_vals{$state->[$os]} = $state->[$os-1] + ($str_vals{$state->[$os]}||0);
309             }
310              
311 15         1852 my ($hr, $ar) = _normalise(\%str_vals);
312 15         83 my $rand = rand(1);
313 15         14 my $rt;
314 15         29 LOOP: foreach (@$ar) {
315 15 50       13 if ( $rand < ${$hr}{$_}) {
  15         46  
316 15         18 $rt = $_;
317 15         33 last LOOP;
318             }
319             }
320             # retain only that state
321 15         25 my @retains;
322 15         32 for (0..(@$universe-1)) {
323 21         29 my $state = $universe->[$_];
324 21         28 my $foo = $state->[$os];
325 21 50       79 push(@retains, $_) if ("$foo" eq $rt);
326             }
327 15 50       35 if ($Quantum::Entanglement::destroy) {
328 15         40 @$universe = @$universe[@retains];
329 15         135 return $rt;
330             }
331              
332             # set all non retained states to zero probability, leave others alone
333 0         0 my $next_retain = shift @retains;
334 0         0 PURGE: foreach my $snum ( 0..(@$universe-1) ) {
335 0 0       0 if ($snum == $next_retain) {
336 0   0     0 $next_retain = shift(@retains) || -1;
337 0         0 next PURGE;
338             }
339 0         0 my $state = ${$universe}[$snum];
  0         0  
340 0         0 $$state[$_] = 0 foreach grep {!($_ % 2)} (0..(@$state-1))
  0         0  
341             }
342 0         0 return $rt;
343             }
344              
345             # numification (have to coerce things into numbers then strings for
346             # probability hash purposes, ick) (observation)
347             sub num_ent {
348 0     0 0 0 my $c = $_[0];
349 0         0 my $os = $c->[1];
350 0         0 my $universe = ${$c->[0]};
  0         0  
351 0         0 my %str_vals;
352             # work out which state we want to retain
353 0         0 foreach my $state (@$universe) {
354 0   0     0 $str_vals{+$state->[$os]} =
355             $state->[$os-1] + ($str_vals{+$state->[$os]}||0);
356             }
357 0         0 my ($hr, $ar) = _normalise(\%str_vals);
358 0         0 my $rand = rand(1);
359 0         0 my $rt;
360 0         0 LOOP: foreach (@$ar) {
361 0 0       0 if ( $rand < ${$hr}{$_}) {
  0         0  
362 0         0 $rt = +$_;
363 0         0 last LOOP;
364             }
365             }
366             # retain only that state
367 0         0 my @retains;
368 0         0 for (0..(@$universe-1)) {
369 0         0 my $state = $universe->[$_];
370 0         0 my $foo = +$state->[$os];
371 0 0       0 push(@retains, $_) if ($foo == $rt);
372             }
373              
374 0 0       0 if ($Quantum::Entanglement::destroy) {
375 0         0 @$universe = @$universe[@retains];
376 0         0 return $rt;
377             }
378              
379             # set probabilty to zero for each state we know can't be so
380 0         0 my $next_retain = shift @retains;
381 0         0 PURGE: foreach my $snum ( 0..(@$universe-1) ) {
382 0 0       0 if ($snum == $next_retain) {
383 0   0     0 $next_retain = shift(@retains) || -1;
384 0         0 next PURGE;
385             }
386 0         0 my $state = ${$universe}[$snum];
  0         0  
387 0         0 $$state[$_] = 0 foreach grep {!($_ % 2)} ( 0..(@$state-1) )
  0         0  
388             }
389 0         0 return $rt;
390             }
391              
392             # boolean context (observation)
393             sub bool_ent {
394 2     2 0 74 my $c = $_[0];
395 2         4 my $os = $c->[1];
396 2         3 my $universe = ${$c->[0]};
  2         5  
397 2         4 my ($rt,$ft,$p_true, $p_false) = (0,0,0,0);
398 2         3 my (@true, @false);
399              
400 2         6 foreach (0..(@$universe-1)) {
401 2         4 my $state = $universe->[$_];
402 2         3 my $c2 = $state->[$os];
403 2 50       11 if ($c2) {
404 2         4 $rt++;
405 2         3 push @true, $_;
406 2         9 $p_true += $state->[$os-1];
407             }
408             else {
409 0         0 $ft++;
410 0         0 push @false, $_;
411 0         0 $p_false += $state->[$os-1];
412             }
413             }
414              
415 2 50       193 return 0 unless $rt; # no states are true, so must be false
416 2 50       12 return $rt unless $ft; # no states are false, so must be true
417             # if it can be true, decide if it will end up being true or not
418 0         0 my @retains;
419 0 0 0     0 if ( _sel_output( $p_true,$p_false)
420             or $Quantum::Entanglement::conform) {
421 0         0 @retains = @true;
422 0         0 $rt = $rt;
423             }
424             else {
425 0         0 @retains = @false;
426 0         0 $rt = 0;
427             }
428              
429 0 0       0 if ($Quantum::Entanglement::destroy) {
430 0         0 @$universe = @$universe[@retains];
431 0         0 return $rt;
432             }
433              
434 0         0 my $next_retain = shift @retains;
435 0         0 PURGE: foreach my $snum ( 0..(@$universe-1) ) {
436 0 0       0 if ($snum == $next_retain) {
437 0   0     0 $next_retain = shift(@retains) || -1;
438 0         0 next PURGE;
439             }
440 0         0 my $state = ${$universe}[$snum];
  0         0  
441 0         0 $$state[$_] = 0 foreach grep {!($_ % 2)} (0..(@$state-1))
  0         0  
442             }
443 0         0 return $rt;
444             }
445              
446             ### any BInary, Non-observational OPeration
447             sub binop {
448 15     15 0 31 my ($c,$d,$r,$code) = @_;
449 15         20 my $var;
450             my $universe;
451 15 50 33     100 if ( ref($d)
452             && UNIVERSAL::isa($d, 'Quantum::Entanglement')) {
453 15         33 _join($c,$d);
454 15         21 my $od = $d->[1]; my $oc = $c->[1];
  15         26  
455 15         38 $var = _add($c);
456 15         16 $universe = ${$c->[0]};
  15         22  
457 15         23 foreach my $state (@$universe) {
458 21         113 push @$state, ($state->[$oc-1] * $state->[$od-1],
459             &$code($state->[$oc],$state->[$od]) );
460             }
461             }
462             else { # adding something to one state
463 0         0 my $oc = $c->[1];
464 0         0 $var = _add($c);
465 0         0 $universe = ${$c->[0]};
  0         0  
466 0 0       0 if ($r) {
467 0         0 push(@$_, ($_->[$oc-1], &$code($d,$_->[$oc]))) foreach @$universe;
468             }
469             else {
470 0         0 push(@$_, ($_->[$oc-1], &$code($_->[$oc],$d))) foreach @$universe;
471             }
472             }
473 15         69 return $var;
474             }
475              
476             # any BInary Observational OPeration
477             sub bioop {
478 0     0 0 0 my ($c, $d, $reverse, $code) = @_;
479 0         0 my $rt = 0;
480 0         0 my $ft = 0;
481 0         0 my (@true, @false);
482 0         0 my ($p_true, $p_false) = (0,0);
483 0         0 my $universe;
484 0 0 0     0 if (ref($d) && UNIVERSAL::isa($d, 'Quantum::Entanglement')) {
485 0         0 $c->_join($d);
486 0         0 $universe = ${$c->[0]};
  0         0  
487 0         0 foreach (0..(@$universe-1)) {
488 0         0 my $state = $universe->[$_];
489 0         0 my $oc = $c->[1]; my $od = $d->[1];
  0         0  
490 0         0 my $d2 = $state->[$od];
491 0         0 my $c2 = $state->[$oc];
492 0 0       0 if (&$code($c2, $d2)) {
493 0         0 $rt++;
494 0         0 push @true, $_;
495 0         0 $p_true += $state->[$oc-1]* $state->[$od-1];
496             }
497             else {
498 0         0 $ft++;
499 0         0 push @false, $_;
500 0         0 $p_false += $state->[$oc-1]* $state->[$od-1];
501             }
502             }
503             }
504             else {
505 0         0 $universe = ${$c->[0]};
  0         0  
506 0         0 foreach (0..(@$universe-1)) {
507 0         0 my $state = $universe->[$_];
508 0         0 my $d2 = $d;
509 0         0 my $os = $c->[1];
510 0         0 my $c2 = $state->[$os];
511 0 0       0 ($c2, $d2) = ($d2, $c2) if $reverse;
512 0 0       0 if (&$code($c2,$d2)) {
513 0         0 $rt++;
514 0         0 push @true, $_;
515 0         0 $p_true += $state->[$os-1];
516             }
517             else {
518 0         0 $ft++;
519 0         0 push @false, $_;
520 0         0 $p_false += $state->[$os-1];
521             }
522             }
523             }
524              
525 0 0       0 return 0 unless $rt; # no states are true, so must be false
526 0 0       0 return $rt unless $ft; # no states are false, so must be true
527 0         0 my @retains;
528             # if it can be true, decide if it will end up being true or not
529 0 0 0     0 if ( _sel_output( $p_true,$p_false)
530             or $Quantum::Entanglement::conform) {
531 0         0 @retains = @true;
532 0         0 $rt = $rt;
533             }
534             else {
535 0         0 @retains = @false;
536 0         0 $rt = 0;
537             }
538              
539 0 0       0 if ($Quantum::Entanglement::destroy) {
540 0         0 @$universe = @$universe[@retains];
541 0         0 return $rt;
542             }
543              
544 0         0 my $next_retain = shift @retains;
545 0         0 PURGE: foreach my $snum ( 0..(@$universe-1) ) {
546 0 0       0 if ($snum == $next_retain) {
547 0   0     0 $next_retain = shift(@retains) || -1;
548 0         0 next PURGE;
549             }
550 0         0 my $state = ${$universe}[$snum];
  0         0  
551 0         0 $$state[$_] = 0 foreach grep {!($_ % 2)} (0..(@$state-1))
  0         0  
552             }
553 0         0 return $rt;
554              
555             }
556              
557             # any MUTating OPerator
558             sub mutop {
559 0     0 0 0 my $c = $_[0];
560 0         0 my $code = $_[1];
561 0         0 my $os = $c->[1];
562 0         0 my $universe = ${$c->[0]};
  0         0  
563 0         0 foreach my $state (@$universe) {
564 0         0 $state->[$os] = &$code($state->[$os]);
565             }
566 0         0 return $c;
567             }
568              
569             sub unnop {
570 0     0 0 0 my $c = $_[0];
571 0         0 my $code = $_[1];
572 0         0 my $os = $c->[1];
573 0         0 my $val = $c->_add; my $universe = ${$c->[0]};
  0         0  
  0         0  
574 0         0 foreach my $state (@$universe) {
575 0         0 push(@$state, $state->[$os-1], &$code($state->[$os]) );
576             }
577 0         0 return $val;
578             }
579              
580             ##
581             # performing a conditional in paralell on the states (ie. without looking)
582             # returns a new variable
583              
584             sub p_op {
585 3     3 1 36 my ($arg1, $op, $arg2, $true_cf, $false_cf) = @_;
586 3 50   0   8 $true_cf = ref($true_cf) ? $true_cf : sub {1};
  0         0  
587 3 50   0   8 $false_cf = ref($false_cf) ? $false_cf : sub {0};
  0         0  
588 3         5 my $r = 0;
589 3 50 33     37 unless (ref($arg1) && UNIVERSAL::isa($arg1, 'Quantum::Entanglement')) {
590 0         0 $r = 1;
591 0         0 ($arg1, $arg2) = ($arg2, $arg1);
592             }
593 3         5 my $tcref;
594 3         579 eval "
595             \$tcref = sub {
596             local \*QE::arg1 = \\\$_[0];
597             local \*QE::arg2 = \\\$_[1];
598             if (\$_[0] $op \$_[1]) {
599             return \&\$true_cf;
600             }
601             else {
602             return \&\$false_cf;
603             }
604             }
605 3 50       37 "; croak "$0: something wrong in p_op $@" if $@;
606              
607 3         10 return binop($arg1, $arg2, $r, $tcref);
608             }
609              
610             # allows for other functions to be performed accross states, can take
611             # as many entangled variables as you like...
612             # can take code ref, or "symbolic" function name (eg. p_func('substr', ..))
613             sub p_func {
614 0     0 1   my $func = shift;
615 0           my $package = (caller)[0];
616             # build up the function call by shifting off
617             # entangled variables until something isn't entangled
618 0 0         my $foo = ref($func) ? "&\$func(" : "$func(";
619 0           my @temp = @_;
620 0           my $first = $temp[0];
621 0   0       do {
622 0           my $c = shift @temp;
623 0           _join($first,$c);
624             } while (ref($temp[0]) && UNIVERSAL::isa($temp[0],'Quantum::Entanglement'));
625 0           my @p_codes = ();
626 0   0       do {
627 0           my $c = shift;
628 0           $foo .= '$state->[' . $c->[1] . '],';
629 0           push @p_codes, $c->[1]-1;
630             } while ( ref($_[0]) && UNIVERSAL::isa($_[0], 'Quantum::Entanglement'));
631 0 0         $foo .= scalar(@_)? '@args);' : ');';
632 0           my @args = @_;
633             # loop over states, evaluating function in caller's package
634 0           my $var = $first->_add;
635 0           my $p_code = join('*', map {"\$state->[$_]"} @p_codes);
  0            
636 0           my $universe = ${$first->[0]};
  0            
637 0           foreach my $state (@$universe) {
638 0           my $new_prob = eval $p_code;
639 0           push(@$state, $new_prob, eval "package $package; $foo");
640 0 0         croak "Internal error: $@" if $@;
641             }
642 0           return $var;
643             }
644              
645             # This allows the introduction of new states into the system, based
646             # on the current values and probability amplitudes of current states
647             # must be given a code ref, followed by a list of entangled vars whose
648             # states will be passed to the function.
649             sub q_logic {
650 0     0 1   my $func = shift;
651 0           my (@offsets);
652 0           my $first = $_[0];
653 0           _join($first,$_) foreach @_;
654 0           @offsets = map {$_->[1]-1, $_->[1]} @_;
  0            
655 0           my $var = $first->_add;
656 0           my $universe = ${$first->[0]};
  0            
657 0           my @resultant_space;
658 0           foreach my $state (@$universe) {
659 0           my @new_states = &$func(@{$state}[@offsets]);
  0            
660 0           do {
661 0           push @resultant_space, [@$state, splice(@new_states,0,2)];
662             } while (@new_states);
663             }
664 0           @{$universe} = @resultant_space;
  0            
665 0           return $var;
666             }
667              
668             # takes ft of amplitudes of a var, creates new state with the
669             # transformed amplitudes and the values from the first state.
670             sub QFT {
671 0     0 1   my $c = $_[0];
672 0           my $var = $c->_add;
673 0           my $os = $c->[1];
674 0           my $universe = ${$c->[0]};
  0            
675 0           my @inputs = map {$_->[$os-1]} @$universe; # get current probs
  0            
676 0           my $num = scalar @inputs;
677 0           foreach my $r (0..($num-1)) {
678 0           my $prob = 0;
679 0           foreach my $x (0..($num-1)) {
680 0           $prob += cplxe(1,(-2*pi*$r*$x / $num)) * $inputs[$x];
681             }
682 0           push @{$universe->[$r]}, $prob, $universe->[$r]->[$os];
  0            
683             }
684 0           return $var;
685             }
686              
687             sub save_state{
688 0     0 1   my @os;
689 0           my $stash = [];
690              
691 0           foreach (@_) {
692 0 0 0       carp "Can only save state of Quantum::Entanglement variables"
693             unless (ref($_) && UNIVERSAL::isa($_, 'Quantum::Entanglement'));
694             }
695              
696 0           my $first = $_[0];
697 0           _join($first, $_) foreach @_;
698 0           push(@os, $_->[1]) foreach @_;
699 0           my $universe = ${$_[0]->[0]};
  0            
700 0           foreach my $state (@$universe) {
701 0           push @$stash, [ @{$state}[map {$_-1,$_} @os] ];
  0            
  0            
702             }
703 0           return bless $stash, 'Quantum::Entanglement::State';
704             }
705              
706             # completely clobbers current state with whatever was saved previously
707             sub restore_state {
708 0     0 0   my $stash = shift;
709              
710 0           my $num_saved = scalar(@{$stash->[0]}) /2;
  0            
711 0 0         carp "You don't have any states saved!" unless $num_saved;
712 0           my @newvars;
713 0           $newvars[0] = _new();
714 0           ${$newvars[0]->[0]}->[0] = ['fake','fake']; # no hackery here, no.
  0            
715 0 0         if ($num_saved > 1) {
716 0           for (2..$num_saved) {
717 0           push(@newvars, $newvars[0]->_add());
718 0           push @{${$newvars[0]->[0]}->[0]}, qw(fake fake); # or here, never
  0            
  0            
719             }
720             }
721 0           my $universe = ${$newvars[0]->[0]};
  0            
722 0           shift @$universe;
723 0           foreach (@$stash) {
724 0           push @$universe, [@$_];
725             }
726 0 0         return wantarray ? @newvars : $newvars[0];
727             }
728              
729             # this is needed for simplicity of exporting save_states
730             package Quantum::Entanglement::State;
731             @Quantum::Entanglement::State::ISA = qw(Quantum::Entanglement);
732 0     0     sub DESTROY {}
733              
734             1;
735              
736             __END__;