File Coverage

blib/lib/Quantum/Superpositions/Lazy/Superposition.pm
Criterion Covered Total %
statement 85 85 100.0
branch 14 16 87.5
condition 8 8 100.0
subroutine 16 16 100.0
pod 4 4 100.0
total 127 129 98.4


line stmt bran cond sub pod time code
1             package Quantum::Superpositions::Lazy::Superposition;
2              
3             our $VERSION = '1.10';
4              
5 15     15   179 use v5.24;
  15         41  
6 15     15   60 use warnings;
  15         28  
  15         307  
7 15     15   6947 use Moo;
  15         135104  
  15         66  
8 15     15   22908 use Quantum::Superpositions::Lazy::State;
  15         47  
  15         492  
9 15     15   6487 use Quantum::Superpositions::Lazy::Computation;
  15         52  
  15         552  
10 15     15   112 use Quantum::Superpositions::Lazy::Util qw(get_rand);
  15         30  
  15         748  
11 15     15   76 use Types::Standard qw(ArrayRef InstanceOf);
  15         31  
  15         126  
12 15     15   8162 use List::Util qw(sum0);
  15         31  
  15         898  
13              
14 15     15   95 use namespace::clean;
  15         37  
  15         121  
15              
16             with "Quantum::Superpositions::Lazy::Role::Collapsible";
17              
18             has "_collapsed_state" => (
19             is => "ro",
20             lazy => 1,
21             builder => "_observe",
22             clearer => "_reset",
23             predicate => "_is_collapsed",
24             init_arg => undef,
25             );
26              
27             has "_states" => (
28             is => "ro",
29             isa => ArrayRef [
30             (InstanceOf ["Quantum::Superpositions::Lazy::State"])
31             ->plus_coercions(
32             ArrayRef->where(q{@$_ == 2}),
33             q{ Quantum::Superpositions::Lazy::State->new(weight => shift @$_, value => shift @$_) },
34             ~InstanceOf ["Quantum::Superpositions::Lazy::State"],
35             q{ Quantum::Superpositions::Lazy::State->new(value => $_) },
36             )
37             ],
38             coerce => 1,
39             required => 1,
40             init_arg => "states",
41             );
42              
43             has "_weight_sum" => (
44             is => "ro",
45             lazy => 1,
46             default => sub {
47             sum0 map { $_->weight }
48             shift->_states->@*;
49             },
50             init_arg => undef,
51             clearer => 1,
52             );
53              
54             sub collapse
55             {
56 192     192 1 29373 my ($self) = @_;
57              
58 192         2926 return $self->_collapsed_state;
59             }
60              
61             sub is_collapsed
62             {
63 209     209 1 1406 my ($self) = @_;
64              
65 209         673 return $self->_is_collapsed;
66             }
67              
68             sub weight_sum
69             {
70 121     121 1 2900 my ($self) = @_;
71              
72 121         1727 return $self->_weight_sum;
73             }
74              
75             sub reset
76             {
77 130     130 1 28594 my ($self) = @_;
78              
79 130 100       314 if (!$self->{_collapsible}) {
80 20         55 $self->{_collapsible} = [grep { $_->collapsible } $self->_states->@*];
  279         6046  
81             }
82              
83 130         480 foreach my $state ($self->{_collapsible}->@*) {
84 1         5 $state->reset;
85             }
86              
87 130         2407 $self->_reset;
88              
89 130         632 return $self;
90             }
91              
92             sub _observe
93             {
94 152     152   1050 my ($self) = @_;
95              
96 152 100       355 if (!$self->{_lookup}) {
97 27         104 my @positions = $self->_states->@*;
98 27         64 my $sum = $self->weight_sum;
99 27         54 my @weights;
100 27         38 my $current = 0;
101              
102 27         49 for my $state (@positions) {
103 344         449 push @weights, $current;
104 344         568 $current += $state->weight / $sum;
105             }
106              
107 27         79 $self->{_lookup} = \@weights;
108             }
109              
110 152         351 my $prob = get_rand;
111              
112 152         33070 my @cache = $self->{_lookup}->@*;
113 152         308 my $current = int(@cache / 2);
114 152   100     357 my $step = int(@cache / 4) || 1;
115              
116             # warn "rand: $prob, all: [@cache], step: $step";
117 152         302 while ($step > 1) {
118 432 100       634 if ($cache[$current] < $prob) {
119             # warn "going up: $current + $step";
120 220         245 $current += $step;
121              
122 220 50       342 $current = @cache - 1
123             if $current >= @cache;
124             }
125              
126             else {
127             # warn "going down $current - $step";
128 212         246 $current -= $step;
129              
130 212 50       299 $current = 0
131             if $current < 0;
132             }
133              
134 432         676 $step = int($step / 2);
135             }
136              
137 152   100     474 while ($current < $#cache && $cache[$current] < $prob) {
138 136         343 $current += 1
139             }
140              
141 152   100     403 while ($current > 0 && $cache[$current] > $prob) {
142 198         464 $current -= 1
143             }
144              
145             # warn "selected: $current ($cache[$current] < $prob)";
146              
147 152         333 my $state = $self->_states->[$current];
148              
149 152 100       2393 return $state->collapsible
150             ? $state->value->collapse
151             : $state->value;
152             }
153              
154             sub _build_complete_states
155             {
156 65     65   1025 my ($self) = @_;
157              
158 65         106 my %states;
159 65         199 for my $state ($self->_states->@*) {
160 16149         19929 my @local_states;
161 16149         17680 my $coeff = 1;
162              
163 16149         28547 my $value = $state->value;
164 16149 100       201899 if ($state->collapsible) {
165              
166             # all values from this state must have their weights multiplied by $coeff
167             # this way the weight sum will stay the same
168 1         61 $coeff = $state->weight / $value->weight_sum;
169 1         5 @local_states = $value->states->@*;
170             }
171             else {
172 16148         222701 @local_states = $state;
173             }
174              
175 16149         24470 foreach my $value (@local_states) {
176 16153         23440 my $result = $value->value;
177 16153     16153   51103 my $copied = $value->clone_with(weight => sub { shift() * $coeff });
  16153         33488  
178              
179 16153 100       44693 if (exists $states{$result}) {
180 1         4 $states{$result} = $states{$result}->merge($copied);
181             }
182             else {
183 16152         38326 $states{$result} = $copied;
184             }
185             }
186             }
187              
188 65         10563 return [values %states];
189             }
190              
191             1;
192              
193             __END__