File Coverage

blib/lib/Quantum/Superpositions/Lazy/Superposition.pm
Criterion Covered Total %
statement 86 86 100.0
branch 16 18 88.8
condition 8 8 100.0
subroutine 16 16 100.0
pod 4 4 100.0
total 130 132 98.4


line stmt bran cond sub pod time code
1             package Quantum::Superpositions::Lazy::Superposition;
2              
3             our $VERSION = '1.11';
4              
5 15     15   160 use v5.24;
  15         44  
6 15     15   73 use warnings;
  15         27  
  15         324  
7 15     15   8005 use Moo;
  15         150122  
  15         81  
8 15     15   24847 use Quantum::Superpositions::Lazy::State;
  15         56  
  15         504  
9 15     15   6992 use Quantum::Superpositions::Lazy::Computation;
  15         57  
  15         635  
10 15     15   117 use Quantum::Superpositions::Lazy::Util qw(get_rand);
  15         28  
  15         738  
11 15     15   89 use Types::Standard qw(ArrayRef InstanceOf);
  15         28  
  15         133  
12 15     15   8063 use List::Util qw(sum0);
  15         35  
  15         850  
13              
14 15     15   94 use namespace::clean;
  15         29  
  15         98  
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 194     194 1 43284 my ($self) = @_;
57              
58 194         3396 return $self->_collapsed_state;
59             }
60              
61             sub is_collapsed
62             {
63 209     209 1 1657 my ($self) = @_;
64              
65 209         742 return $self->_is_collapsed;
66             }
67              
68             sub weight_sum
69             {
70 122     122 1 3234 my ($self) = @_;
71              
72 122         1876 return $self->_weight_sum;
73             }
74              
75             sub reset
76             {
77 131     131 1 41230 my ($self) = @_;
78              
79 131 100       364 if (!$self->{_collapsible}) {
80 21         56 $self->{_collapsible} = [grep { $_->collapsible } $self->_states->@*];
  279         6888  
81             }
82              
83 131         547 foreach my $state ($self->{_collapsible}->@*) {
84 1         5 $state->reset;
85             }
86              
87 131         2507 $self->_reset;
88              
89 131         662 return $self;
90             }
91              
92             sub _observe
93             {
94 154     154   1152 my ($self) = @_;
95              
96 154 100       384 if (!$self->{_lookup}) {
97 28         104 my @positions = $self->_states->@*;
98 28         62 my $sum = $self->weight_sum;
99 28         56 my @weights;
100 28         45 my $current = 0;
101              
102 28         58 for my $state (@positions) {
103 344         516 push @weights, $current;
104 344         608 $current += $state->weight / $sum;
105             }
106              
107 28         83 $self->{_lookup} = \@weights;
108             }
109              
110 154         382 my $prob = get_rand;
111 154         35687 my @cache = $self->{_lookup}->@*;
112              
113 154 100       387 return undef if !@cache;
114              
115 152         341 my $current = int(@cache / 2);
116 152   100     400 my $step = int(@cache / 4) || 1;
117              
118             # warn "rand: $prob, all: [@cache], step: $step";
119 152         313 while ($step > 1) {
120 432 100       712 if ($cache[$current] < $prob) {
121             # warn "going up: $current + $step";
122 215         286 $current += $step;
123              
124 215 50       340 $current = @cache - 1
125             if $current >= @cache;
126             }
127              
128             else {
129             # warn "going down $current - $step";
130 217         282 $current -= $step;
131              
132 217 50       359 $current = 0
133             if $current < 0;
134             }
135              
136 432         767 $step = int($step / 2);
137             }
138              
139 152   100     518 while ($current < $#cache && $cache[$current] < $prob) {
140 137         336 $current += 1
141             }
142              
143 152   100     453 while ($current > 0 && $cache[$current] > $prob) {
144 190         452 $current -= 1
145             }
146              
147             # warn "selected: $current ($cache[$current] < $prob)";
148              
149 152         365 my $state = $self->_states->[$current];
150              
151 152 100       2675 return $state->collapsible
152             ? $state->value->collapse
153             : $state->value;
154             }
155              
156             sub _build_complete_states
157             {
158 66     66   1150 my ($self) = @_;
159              
160 66         109 my %states;
161 66         225 for my $state ($self->_states->@*) {
162 16149         22400 my @local_states;
163 16149         20655 my $coeff = 1;
164              
165 16149         35808 my $value = $state->value;
166 16149 100       226376 if ($state->collapsible) {
167              
168             # all values from this state must have their weights multiplied by $coeff
169             # this way the weight sum will stay the same
170 1         60 $coeff = $state->weight / $value->weight_sum;
171 1         4 @local_states = $value->states->@*;
172             }
173             else {
174 16148         261171 @local_states = $state;
175             }
176              
177 16149         27620 foreach my $value (@local_states) {
178 16153         26380 my $result = $value->value;
179 16153     16153   59167 my $copied = $value->clone_with(weight => sub { shift() * $coeff });
  16153         38805  
180              
181 16153 100       59147 if (exists $states{$result}) {
182 1         6 $states{$result} = $states{$result}->merge($copied);
183             }
184             else {
185 16152         45328 $states{$result} = $copied;
186             }
187             }
188             }
189              
190 66         12996 return [values %states];
191             }
192              
193             1;
194              
195             __END__