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             $Quantum::Superpositions::Lazy::Superposition::VERSION = '1.12';
3 15     15   156 use v5.24;
  15         45  
4 15     15   64 use warnings;
  15         30  
  15         326  
5 15     15   6841 use Moo;
  15         147482  
  15         68  
6 15     15   24411 use Quantum::Superpositions::Lazy::State;
  15         47  
  15         466  
7 15     15   6718 use Quantum::Superpositions::Lazy::Computation;
  15         56  
  15         603  
8 15     15   107 use Quantum::Superpositions::Lazy::Util qw(get_rand);
  15         30  
  15         765  
9 15     15   104 use Types::Standard qw(ArrayRef InstanceOf);
  15         35  
  15         130  
10 15     15   8354 use List::Util qw(sum0);
  15         46  
  15         809  
11              
12 15     15   108 use namespace::clean;
  15         43  
  15         122  
13              
14             with "Quantum::Superpositions::Lazy::Role::Collapsible";
15              
16             has "_collapsed_state" => (
17             is => "ro",
18             lazy => 1,
19             builder => "_observe",
20             clearer => "_reset",
21             predicate => "_is_collapsed",
22             init_arg => undef,
23             );
24              
25             has "_states" => (
26             is => "ro",
27             isa => ArrayRef [
28             (InstanceOf ["Quantum::Superpositions::Lazy::State"])
29             ->plus_coercions(
30             ArrayRef->where(q{@$_ == 2}),
31             q{ Quantum::Superpositions::Lazy::State->new(weight => shift @$_, value => shift @$_) },
32             ~InstanceOf ["Quantum::Superpositions::Lazy::State"],
33             q{ Quantum::Superpositions::Lazy::State->new(value => $_) },
34             )
35             ],
36             coerce => 1,
37             required => 1,
38             init_arg => "states",
39             );
40              
41             has "_weight_sum" => (
42             is => "ro",
43             lazy => 1,
44             default => sub {
45             sum0 map { $_->weight }
46             shift->_states->@*;
47             },
48             init_arg => undef,
49             clearer => 1,
50             );
51              
52             sub collapse
53             {
54 194     194 1 35172 my ($self) = @_;
55              
56 194         3415 return $self->_collapsed_state;
57             }
58              
59             sub is_collapsed
60             {
61 209     209 1 1656 my ($self) = @_;
62              
63 209         747 return $self->_is_collapsed;
64             }
65              
66             sub weight_sum
67             {
68 122     122 1 3730 my ($self) = @_;
69              
70 122         1929 return $self->_weight_sum;
71             }
72              
73             sub reset
74             {
75 131     131 1 34083 my ($self) = @_;
76              
77 131 100       376 if (!$self->{_collapsible}) {
78 21         79 $self->{_collapsible} = [grep { $_->collapsible } $self->_states->@*];
  279         7068  
79             }
80              
81 131         604 foreach my $state ($self->{_collapsible}->@*) {
82 1         6 $state->reset;
83             }
84              
85 131         2617 $self->_reset;
86              
87 131         724 return $self;
88             }
89              
90             sub _observe
91             {
92 154     154   1160 my ($self) = @_;
93              
94 154 100       370 if (!$self->{_lookup}) {
95 28         191 my @positions = $self->_states->@*;
96 28         70 my $sum = $self->weight_sum;
97 28         61 my @weights;
98 28         41 my $current = 0;
99              
100 28         62 for my $state (@positions) {
101 344         530 push @weights, $current;
102 344         608 $current += $state->weight / $sum;
103             }
104              
105 28         83 $self->{_lookup} = \@weights;
106             }
107              
108 154         393 my $prob = get_rand;
109 154         37183 my @cache = $self->{_lookup}->@*;
110              
111 154 100       389 return undef if !@cache;
112              
113 152         360 my $current = int(@cache / 2);
114 152   100     440 my $step = int(@cache / 4) || 1;
115              
116             # warn "rand: $prob, all: [@cache], step: $step";
117 152         311 while ($step > 1) {
118 432 100       734 if ($cache[$current] < $prob) {
119             # warn "going up: $current + $step";
120 217         286 $current += $step;
121              
122 217 50       353 $current = @cache - 1
123             if $current >= @cache;
124             }
125              
126             else {
127             # warn "going down $current - $step";
128 215         284 $current -= $step;
129              
130 215 50       333 $current = 0
131             if $current < 0;
132             }
133              
134 432         749 $step = int($step / 2);
135             }
136              
137 152   100     552 while ($current < $#cache && $cache[$current] < $prob) {
138 126         308 $current += 1
139             }
140              
141 152   100     490 while ($current > 0 && $cache[$current] > $prob) {
142 198         518 $current -= 1
143             }
144              
145             # warn "selected: $current ($cache[$current] < $prob)";
146              
147 152         399 my $state = $self->_states->[$current];
148              
149 152 100       2779 return $state->collapsible
150             ? $state->value->collapse
151             : $state->value;
152             }
153              
154             sub _build_complete_states
155             {
156 66     66   1118 my ($self) = @_;
157              
158 66         130 my %states;
159 66         208 for my $state ($self->_states->@*) {
160 16149         20827 my @local_states;
161 16149         20148 my $coeff = 1;
162              
163 16149         36070 my $value = $state->value;
164 16149 100       226234 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         52 $coeff = $state->weight / $value->weight_sum;
169 1         3 @local_states = $value->states->@*;
170             }
171             else {
172 16148         258408 @local_states = $state;
173             }
174              
175 16149         26460 foreach my $value (@local_states) {
176 16153         26308 my $result = $value->value;
177 16153     16153   57604 my $copied = $value->clone_with(weight => sub { shift() * $coeff });
  16153         37771  
178              
179 16153 100       52156 if (exists $states{$result}) {
180 1         5 $states{$result} = $states{$result}->merge($copied);
181             }
182             else {
183 16152         47816 $states{$result} = $copied;
184             }
185             }
186             }
187              
188 66         12952 return [values %states];
189             }
190              
191             1;
192              
193             __END__