File Coverage

blib/lib/Math/Vector/Real/kdTree.pm
Criterion Covered Total %
statement 445 668 66.6
branch 145 270 53.7
condition 10 59 16.9
subroutine 45 68 66.1
pod 17 25 68.0
total 662 1090 60.7


line stmt bran cond sub pod time code
1             package Math::Vector::Real::kdTree;
2              
3             our $VERSION = '0.13';
4              
5 3     3   17835 use 5.010;
  3         9  
  3         98  
6 3     3   11 use strict;
  3         5  
  3         198  
7 3     3   18 use warnings;
  3         8  
  3         148  
8 3     3   18 use Carp;
  3         3  
  3         232  
9              
10 3     3   531 use Math::Vector::Real;
  3         12005  
  3         154  
11 3     3   476 use Sort::Key::Top qw(nkeypartref nhead ntail nkeyhead);
  3         1971  
  3         279  
12 3     3   1753 use Hash::Util::FieldHash qw(idhash);
  3         2525  
  3         227  
13              
14             our $max_per_pole = 12;
15             our $recommended_per_pole = 6;
16              
17 3     3   18 use constant _n => 0; # elements on subtree
  3         3  
  3         205  
18 3     3   15 use constant _c0 => 1; # corner 0
  3         5  
  3         131  
19 3     3   18 use constant _c1 => 2; # corner 1
  3         4  
  3         107  
20 3     3   12 use constant _sum => 3; # centroid * n
  3         3  
  3         96  
21 3     3   11 use constant _s0 => 4; # subtree 0
  3         3  
  3         92  
22 3     3   10 use constant _s1 => 5; # subtree 1
  3         4  
  3         109  
23 3     3   17 use constant _axis => 6; # cut axis
  3         4  
  3         107  
24 3     3   11 use constant _cut => 7; # cut point (mediam)
  3         2  
  3         98  
25              
26             # on leaf nodes:
27 3     3   11 use constant _ixs => 4;
  3         2  
  3         140  
28 3     3   11 use constant _leaf_size => _ixs + 1;
  3         3  
  3         19714  
29              
30             sub new {
31 683     683 1 250004692 my $class = shift;
32 683         3594 my @v = map V(@$_), @_;
33 683 100       111336 my $self = { vs => \@v,
34             tree => (@v ? _build(\@v, [0..$#v]) : undef) };
35 683         30061 bless $self, $class;
36             }
37              
38             sub clone {
39 0     0 1 0 my $self = shift;
40 0         0 require Storable;
41 0         0 my $clone = { vs => [@{$self->{vs}}],
  0         0  
42             tree => Storable::dclone($self->{tree}) };
43 0 0       0 $clone->{hidden} = { %{$self->{hidden}} } if $self->{hidden};
  0         0  
44 0         0 bless $clone, ref $self;
45             }
46              
47             sub _build {
48 38437     38437   35445 my ($v, $ixs) = @_;
49 38437 100       61914 if (@$ixs > $recommended_per_pole) {
50 18686         65417 my ($b, $t) = Math::Vector::Real->box(@$v[@$ixs]);
51 18686         4494673 my $axis = ($t - $b)->max_component_index;
52 18686         534321 my $bstart = @$ixs >> 1;
53 18686     383835   102254 my ($p0, $p1) = nkeypartref { $v->[$_][$axis] } $bstart => @$ixs;
  470278         633362  
54 18686         50161 my $s0 = _build($v, $p0);
55 18686         599652 my $s1 = _build($v, $p1);
56 18686         577491 my ($c0, $c1) = Math::Vector::Real->box(@{$s0}[_c0, _c1], @{$s1}[_c0, _c1]);
  18686         22264  
  18686         37820  
57 18686         749036 my $cut = 0.5 * ($s0->[_c1][$axis] + $s1->[_c0][$axis]);
58             # [n sum s0 s1 axis cut]
59 18686         41026 [scalar(@$ixs), $c0, $c1, $s0->[_sum] + $s1->[_sum], $s0, $s1, $axis, $cut];
60             }
61             else {
62             # [n, sum, ixs]
63 19751         18046 my @vs = @{$v}[@$ixs];
  19751         34746  
64 19751         43569 my ($c0, $c1) = Math::Vector::Real->box(@vs);
65 19751         866554 [scalar(@$ixs), $c0, $c1, Math::Vector::Real->sum(@vs), $ixs];
66             }
67             }
68              
69 0     0 1 0 sub size { scalar @{shift->{vs}} }
  0         0  
70              
71             sub at {
72 38976     38976 1 1541523 my ($self, $ix) = @_;
73 38976         62091 Math::Vector::Real::clone($self->{vs}[$ix]);
74             }
75              
76             sub insert {
77 6536     6536 1 3059488 my $self = shift;
78 6536 50       15469 @_ or return;
79 6536         8273 my $vs = $self->{vs};
80 6536         7380 my $ix = @$vs;
81 6536 100       12300 if (my $tree = $self->{tree}) {
82 6495         10609 for (@_) {
83 6495         21562 my $v = V(@$_);
84 6495         32488 push @$vs, $v;
85 6495         12944 _insert($vs, $self->{tree}, $#$vs)
86             }
87             }
88             else {
89 41         278 @$vs = map V(@$_), @_;
90 41         447 $self->{tree} = _build($vs, [0..$#$vs]);
91             }
92 6536         12983 return $ix;
93             }
94              
95             # _insert does not return anything but modifies its $t argument in
96             # place. This is really ugly but done to improve performance.
97              
98             sub _insert {
99 35008     35008   38631 my ($vs, $t, $ix) = @_;
100 35008         33706 my $v = $vs->[$ix];
101              
102             # update aggregated values
103 35008         36157 my $n = $t->[_n]++;
104 35008         28971 @{$t}[_c0, _c1] = Math::Vector::Real->box($v, @{$t}[_c0, _c1]);
  35008         1162643  
  35008         82651  
105 35008         107948 $t->[_sum] += $v;
106              
107 35008 100       524595 if (defined (my $axis = $t->[_axis])) {
108 28558         28818 my $cut = $t->[_cut];
109 28558         27480 my $c = $v->[$axis];
110              
111 28558         32723 my $n0 = $t->[_s0][_n];
112 28558         28173 my $n1 = $t->[_s1][_n];
113              
114 28558 100       41257 if ($c <= $cut) {
115 14523 100       28958 if (2 * $n1 + $max_per_pole >= $n0) {
116 14498         22371 _insert($vs, $t->[_s0], $ix);
117 14498         24184 return;
118             }
119             }
120             else {
121 14035 100       29001 if (2 * $n0 + $max_per_pole >= $n1) {
122 14015         21440 _insert($vs, $t->[_s1], $ix);
123 14015         24279 return;
124             }
125             }
126              
127             # tree needs rebalancing
128 45         77 my @store;
129 45         140 $#store = $n; # preallocate space
130 45         101 @store = ($ix);
131 45         189 _push_all($t, \@store);
132 45         132 $_[1] = _build($vs, \@store);
133             }
134             else {
135 6450         6850 my $ixs = $t->[_ixs];
136 6450         10514 push @$ixs, $ix;
137 6450 100       16749 if ($n > $max_per_pole) {
138 337         932 $_[1] = _build($vs, $ixs);
139             }
140             }
141             }
142              
143             sub move {
144 0     0 1 0 my ($self, $ix, $v) = @_;
145 0         0 my $vs = $self->{vs};
146 0 0 0     0 ($ix >= 0 and $ix < @$vs) or croak "index out of range";
147 0         0 _delete($vs, $self->{tree}, $ix);
148 0         0 $vs->[$ix] = Math::Vector::Real::clone($v);
149 0         0 _insert($vs, $self->{tree}, $ix);
150             }
151              
152             sub _delete {
153 0     0   0 my ($vs, $t, $ix) = @_;
154 0 0       0 if (defined (my $axis = $t->[_axis])) {
155 0         0 my $v = $vs->[$ix];
156 0         0 my $c = $v->[$axis];
157 0         0 my ($s0, $s1, $cut) = @{$t}[_s0, _s1, _cut];
  0         0  
158 0 0 0     0 if ($c <= $cut and _delete($vs, $s0, $ix)) {
    0 0        
159 0 0       0 if ($s0->[_n]) {
160 0         0 $t->[_n]--;
161 0         0 $t->[_sum] -= $v;
162             }
163             else {
164             # when one subnode becomes empty, the other gets promoted up:
165 0         0 @$t = @$s1;
166             }
167 0         0 return 1;
168             }
169             elsif ($c >= $cut and _delete($vs, $s1, $ix)) {
170 0 0       0 if ($s1->[_n]) {
171 0         0 $t->[_n]--;
172 0         0 $t->[_sum] -= $v;
173             }
174             else {
175 0         0 @$t = @$s0;
176             }
177 0         0 return 1;
178             }
179             }
180             else {
181 0         0 my $ixs = $t->[_ixs];
182 0         0 for (0..$#$ixs) {
183 0 0       0 if ($ixs->[$_] == $ix) {
184 0         0 splice(@$ixs, $_, 1);
185 0         0 $t->[_n]--;
186 0         0 $t->[_sum] -= $vs->[$ix];
187 0         0 return 1;
188             }
189             }
190             }
191 0         0 return 0;
192             }
193              
194             sub hide {
195 0     0 0 0 my ($self, $ix) = @_;
196 0         0 my $vs = $self->{vs};
197 0 0 0     0 ($ix >= 0 and $ix < @$vs) or croak "index out of range";
198 0         0 _delete($vs, $self->{tree}, $ix);
199 0   0     0 ($self->{hidden} //= {})->{$ix} = 1;
200             }
201              
202             sub _push_all {
203 45     45   74 my ($t, $store) = @_;
204 45         144 my @q;
205 45         151 while ($t) {
206 627 100       759 if (defined $t->[_axis]) {
207 291         290 push @q, $t->[_s1];
208 291         506 $t = $t->[_s0];
209             }
210             else {
211 336         269 push @$store, @{$t->[_ixs]};
  336         513  
212 336         550 $t = pop @q;
213             }
214             }
215             }
216              
217             sub path {
218 0     0 0 0 my ($self, $ix) = @_;
219 0         0 my $p = _path($self->{vs}, $self->{tree}, $ix);
220 0         0 my $l = 1;
221 0         0 $l = (($l << 1) | $_) for @$p;
222 0         0 $l
223             }
224              
225             sub _path {
226 0     0   0 my ($vs, $t, $ix) = @_;
227 0 0       0 if (defined (my $axis = $t->[_axis])) {
228 0         0 my $v = $vs->[$ix];
229 0         0 my $c = $v->[$axis];
230 0         0 my $cut = $t->[_cut];
231 0         0 my $p;
232 0 0       0 if ($c <= $cut) {
233 0 0       0 if ($p = _path($vs, $t->[_s0], $ix)) {
234 0         0 unshift @$p, 0;
235 0         0 return $p;
236             }
237             }
238 0 0       0 if ($c >= $cut) {
239 0 0       0 if ($p = _path($vs, $t->[_s1], $ix)) {
240 0         0 unshift @$p, 1;
241 0         0 return $p;
242             }
243             }
244             }
245             else {
246 0 0       0 return [] if grep $_ == $ix, @{$t->[_ixs]}
  0         0  
247             }
248             ()
249 0         0 }
250              
251             sub find {
252 0     0 0 0 my ($self, $v) = @_;
253 0         0 _find($self->{vs}, $self->{tree}, $v);
254             }
255              
256             sub _find {
257 0     0   0 my ($vs, $t, $v) = @_;
258 0         0 while (defined (my $axis = $t->[_axis])) {
259 0         0 my $cut = $t->[_cut];
260 0         0 my $c = $v->[$axis];
261 0 0       0 if ($c < $cut) {
262 0         0 $t = $t->[_s0];
263             }
264             else {
265 0 0       0 if ($c == $cut) {
266 0         0 my $ix = _find($vs, $t->[_s0], $v);
267 0 0       0 return $ix if defined $ix;
268             }
269 0         0 $t = $t->[_s1];
270             }
271             }
272              
273 0         0 for (@{$t->[_ixs]}) {
  0         0  
274 0 0       0 return $_ if $vs->[$_] == $v;
275             }
276             ()
277 0         0 }
278              
279             sub find_nearest_vector {
280 67728     67728 1 90170 my ($self, $v, $d, @but) = @_;
281 67728 50       130062 my $t = $self->{tree} or return;
282 67728         59535 my $vs = $self->{vs};
283 67728 50       83861 my $d2 = (defined $d ? $d * $d : 'inf');
284              
285 67728         45889 my $but;
286 67728 100       100281 if (@but) {
287 12992 50 33     47107 if (@but == 1 and ref $but[0] eq 'HASH') {
288 0         0 $but = $but[0];
289             }
290             else {
291 12992         16116 my %but = map { $_ => 1 } @but;
  12992         42442  
292 12992         21794 $but = \%but;
293             }
294             }
295              
296 67728         96992 my ($rix, $rd2) = _find_nearest_vector($vs, $t, $v, $d2, undef, $but);
297 67728   50     106735 $rix // return;
298 67728 50       252612 wantarray ? ($rix, sqrt($rd2)) : $rix;
299             }
300              
301             *find_nearest_neighbor = \&find_nearest_vector; # for backwards compatibility
302              
303             sub find_nearest_vector_internal {
304 12992     12992 0 1200920 my ($self, $ix, $d) = @_;
305 12992 50       24403 $ix >= 0 or croak "index out of range";
306 12992         29327 $self->find_nearest_vector($self->{vs}[$ix], $d, $ix);
307             }
308              
309             *find_nearest_neighbor_internal = \&find_nearest_vector_internal; # for backwards compatibility
310              
311             sub _find_nearest_vector {
312 92654     92654   106358 my ($vs, $t, $v, $best_d2, $best_ix, $but) = @_;
313              
314 92654         67808 my @queue;
315             my @queue_d2;
316              
317 92654         84049 while (1) {
318 995882 100       1527731 if (defined (my $axis = $t->[_axis])) {
319             # substitute the current one by the best subtree and queue
320             # the worst for later
321 634494 100       1043464 ($t, my ($q)) = @{$t}[($v->[$axis] <= $t->[_cut]) ? (_s0, _s1) : (_s1, _s0)];
  634494         873673  
322 634494         568403 my $q_d2 = $v->dist2_to_box(@{$q}[_c0, _c1]);
  634494         1263333  
323 634494 100       29433880 if ($q_d2 <= $best_d2) {
324 411601         292495 my $j;
325 411601         765785 for ($j = $#queue_d2; $j >= 0; $j--) {
326 1193831 100       2298750 last if $queue_d2[$j] >= $q_d2;
327             }
328 411601         465408 splice @queue, ++$j, 0, $q;
329 411601         490485 splice @queue_d2, $j, 0, $q_d2;
330             }
331             }
332             else {
333 361388         304909 for (@{$t->[_ixs]}) {
  361388         549565  
334 1697120 100 100     3790746 next if $but and $but->{$_};
335 1684128         3239392 my $d21 = $vs->[$_]->dist2($v);
336 1684128 100       35779196 if ($d21 <= $best_d2) {
337 261320         197827 $best_d2 = $d21;
338 261320         313727 $best_ix = $_;
339             }
340             }
341              
342 361388 100       704387 if ($t = pop @queue) {
343 311963 100       516506 if ($best_d2 >= pop @queue_d2) {
344 268734         272510 next;
345             }
346             }
347              
348 92654         242007 return ($best_ix, $best_d2);
349             }
350             }
351             }
352              
353             sub find_nearest_vector_all_internal {
354 80     80 1 2524241 my ($self, $d) = @_;
355 80         245 my $vs = $self->{vs};
356 80 50       347 return unless @$vs > 1;
357 80 50       304 my $d2 = (defined $d ? $d * $d : 'inf');
358              
359 80         871 my @best = ((undef) x @$vs);
360 80         3266 my @d2 = (($d2) x @$vs);
361 80         399 _find_nearest_vector_all_internal($vs, $self->{tree}, \@best, \@d2);
362 80         14118 return @best;
363             }
364              
365             *find_nearest_neighbor_all_internal = \&find_nearest_vector_all_internal; # for backwards compatibility
366              
367             sub _find_nearest_vector_all_internal {
368 5440     5440   5965 my ($vs, $t, $bests, $d2s) = @_;
369 5440 100       9074 if (defined (my $axis = $t->[_axis])) {
370 2680         2198 my @all_leafs;
371 2680         2997 for my $side (0, 1) {
372 5360         10408 my @leafs = _find_nearest_vector_all_internal($vs, $t->[_s0 + $side], $bests, $d2s);
373 5360         8243 my $other = $t->[_s1 - $side];
374 5360         4905 my ($c0, $c1) = @{$other}[_c0, _c1];
  5360         8000  
375 5360         6664 for my $leaf (@leafs) {
376 17358         217948 for my $ix (@{$leaf->[_ixs]}) {
  17358         26352  
377 79456         1087537 my $v = $vs->[$ix];
378 79456 100       133915 if ($v->dist2_to_box($c0, $c1) < $d2s->[$ix]) {
379 24926         1285284 ($bests->[$ix], $d2s->[$ix]) =
380             _find_nearest_vector($vs, $other, $v, $d2s->[$ix], $bests->[$ix]);
381             }
382             }
383             }
384 5360         95885 push @all_leafs, @leafs;
385             }
386 2680         8485 return @all_leafs;
387             }
388             else {
389 2760         3175 my $ixs = $t->[_ixs];
390 2760         4390 for my $i (1 .. $#$ixs) {
391 10232         11320 my $ix_i = $ixs->[$i];
392 10232         9925 my $v_i = $vs->[$ix_i];
393 10232         13116 for my $ix_j (@{$ixs}[0 .. $i - 1]) {
  10232         14338  
394 29391         50840 my $d2 = $v_i->dist2($vs->[$ix_j]);
395 29391 100       482474 if ($d2 < $d2s->[$ix_i]) {
396 15761         16023 $d2s->[$ix_i] = $d2;
397 15761         18633 $bests->[$ix_i] = $ix_j;
398             }
399 29391 100       59366 if ($d2 < $d2s->[$ix_j]) {
400 8669         7541 $d2s->[$ix_j] = $d2;
401 8669         15329 $bests->[$ix_j] = $ix_i;
402             }
403             }
404             }
405 2760         7128 return $t;
406             }
407             }
408              
409             sub find_two_nearest_vectors {
410 418     418 1 1350931 my $self = shift;
411 418 50       1826 my $t = $self->{tree} or return;
412 418         809 my $vs = $self->{vs};
413 418 50       1585 if (my ($rix0, $rix1, $rd2) = _find_two_nearest_vectors($vs, $t)) {
414 418 50       2963 return wantarray ? ($rix0, $rix1, sqrt($rd2)) : sqrt($rd2)
415             }
416             ()
417 0         0 }
418              
419             sub _pole_id {
420 0     0   0 my ($id, $deep) = __pole_id(@_);
421 0         0 "$id/$deep";
422             }
423              
424             sub __pole_id {
425 0     0   0 my ($vs, $t) = @_;
426 0 0       0 if (defined $t->[_axis]) {
427 0         0 my ($id, $deep) = __pole_id($vs, $t->[_s0]);
428 0         0 return ($id, $deep+1);
429             }
430 0         0 return ($t->[_ixs][0], 0)
431             }
432              
433             sub _find_two_nearest_vectors {
434 418     418   684 my ($vs, $t) = @_;
435              
436 418         1007 my @best_ixs = (undef, undef);
437 418         654 my $best_d2 = 'inf' + 0;
438              
439 418         515 my @inner;
440             my @queue_t1;
441 0         0 my @queue_t2;
442 418         1118 while ($t) {
443 30970 100       43553 if (defined $t->[_axis]) {
444 15276         11928 my ($s0, $s1) = @{$t}[_s0, _s1];
  15276         21320  
445 15276         14460 push @inner, $s1;
446 15276         11901 push @queue_t1, $s0;
447 15276         11350 push @queue_t2, $s1;
448 15276         22986 $t = $s0;
449             }
450             else {
451 15694         15873 my $ixs = $t->[_ixs];
452 15694         20395 for my $i (1 .. $#$ixs) {
453 52269         50287 my $ix1 = $ixs->[$i];
454 52269         48825 my $v1 = $vs->[$ix1];
455 52269         53626 for my $j (0 .. $i - 1) {
456 122706         100562 my $ix2 = $ixs->[$j];
457 122706         187597 my $d2 = Math::Vector::Real::dist2($v1, $vs->[$ix2]);
458 122706 100       2009040 if ($d2 < $best_d2) {
459 1957         1761 $best_d2 = $d2;
460 1957         4087 @best_ixs = ($ix1, $ix2);
461             }
462             }
463             }
464 15694         31909 $t = pop @inner;
465             }
466             }
467              
468 418         2924 my @queue_d2 = (0) x @queue_t1;
469 418         1217 while (my $t1 = pop @queue_t1) {
470 124747         121029 my $t2 = pop @queue_t2;
471 124747         125537 my $d2 = pop @queue_d2;
472 124747 100       198616 if ($d2 < $best_d2) {
473 120426 100       208084 unless (defined $t1->[_axis]) {
474 38722 100       63429 unless (defined $t2->[_axis]) {
475 36490         27890 for my $ix1 (@{$t1->[_ixs]}) {
  36490         56033  
476 168934         187105 my $v1 = $vs->[$ix1];
477 168934         132910 for my $ix2 (@{$t2->[_ixs]}) {
  168934         213688  
478 787781         1342136 my $d2 = Math::Vector::Real::dist2($v1, $vs->[$ix2]);
479 787781 100       17367681 if ($d2 < $best_d2) {
480 129         155 $best_d2 = $d2;
481 129         361 @best_ixs = ($ix1, $ix2);
482             }
483             }
484             }
485 36490         99760 next;
486             }
487 2232         3309 ($t1, $t2) = ($t2, $t1);
488             }
489 83936         72046 for my $s (@{$t1}[_s0, _s1]) {
  83936         112439  
490 167872         139746 my $d2 = Math::Vector::Real->dist2_between_boxes(@{$s}[_c0, _c1], @{$t2}[_c0, _c1]);
  167872         186418  
  167872         387785  
491 167872 100       14521658 if ($d2) {
492 159883 100       343802 if ($d2 < $best_d2) {
493 101482         114950 unshift @queue_t1, $t2;
494 101482         88296 unshift @queue_t2, $s;
495 101482         234566 unshift @queue_d2, $d2;
496             }
497             }
498             else {
499 7989         7927 push @queue_t1, $t2;
500 7989         6251 push @queue_t2, $s;
501 7989         16894 push @queue_d2, 0;
502             }
503             }
504             }
505             }
506 418         2133 (@best_ixs, $best_d2)
507             }
508              
509             sub find_in_ball {
510 6496     6496 1 48328958 my ($self, $z, $d, $but) = @_;
511 6496 50 33     45687 if (defined $but and ref $but ne 'HASH') {
512 6496         27782 $but = { $but => 1 };
513             }
514 6496         31273 _find_in_ball($self->{vs}, $self->{tree}, $z, $d * $d, $but);
515             }
516              
517             sub _find_in_ball {
518 6496     6496   12989 my ($vs, $t, $z, $d2, $but) = @_;
519 6496         8042 my @queue;
520 6496         7018 my (@r, $r);
521              
522 6496         8337 while (1) {
523 281190 100       5925724 if (defined (my $axis = $t->[_axis])) {
524 158690         168658 my $c = $z->[$axis];
525 158690         163473 my $cut = $t->[_cut];
526 158690 100       212550 ($t, my ($q)) = @{$t}[$c <= $cut ? (_s0, _s1) : (_s1, _s0)];
  158690         259560  
527 158690 100       143725 push @queue, $q if $z->dist2_to_box(@{$q}[_c0, _c1]) <= $d2;
  158690         347276  
528             }
529             else {
530 122500         128732 my $ixs = $t->[_ixs];
531 122500 50       156114 if (wantarray) {
532 122500         159614 push @r, grep { $vs->[$_]->dist2($z) <= $d2 } @$ixs;
  761796         10562234  
533             }
534             else {
535 0 0       0 $r += ( $but
536 0         0 ? grep { !$but->{$_} and $vs->[$_]->dist2($z) <= $d2 } @$ixs
537 0 0       0 : grep { $vs->[$_]->dist2($z) <= $d2 } @$ixs );
538             }
539              
540 122500 100       2102319 $t = pop @queue or last;
541             }
542             }
543              
544 6496 50       16593 if (wantarray) {
545 6496 50       14225 if ($but) {
546 6496         349438 return grep !$but->{$_}, @r;
547             }
548 0         0 return @r;
549             }
550 0         0 return $r;
551             }
552              
553             sub find_farthest_vector {
554 6496     6496 1 11262 my ($self, $v, $d, @but) = @_;
555 6496 50       14948 my $t = $self->{tree} or return;
556 6496         6975 my $vs = $self->{vs};
557 6496 50       9019 my $d2 = ($d ? $d * $d : -1);
558 6496         4912 my $but;
559 6496 50       12781 if (@but) {
560 6496 50 33     24326 if (@but == 1 and ref $but[0] eq 'HASH') {
561 0         0 $but = $but[0];
562             }
563             else {
564 6496         8900 my %but = map { $_ => 1 } @but;
  6496         22949  
565 6496         10615 $but = \%but;
566             }
567             }
568              
569 6496         11921 my ($rix, $rd2) = _find_farthest_vector($vs, $t, $v, $d2, undef, $but);
570 6496   50     11541 $rix // return;
571 6496 50       43060 wantarray ? ($rix, sqrt($d2)) : $rix;
572             }
573              
574             sub find_farthest_vector_internal {
575 6496     6496 1 23013002 my ($self, $ix, $d) = @_;
576 6496 50       14224 $ix >= 0 or croak "index out of range";
577 6496         16454 $self->find_farthest_vector($self->{vs}[$ix], $d, $ix);
578             }
579              
580             sub _find_farthest_vector {
581 6496     6496   7624 my ($vs, $t, $v, $best_d2, $best_ix, $but) = @_;
582              
583 6496         6126 my @queue;
584             my @queue_d2;
585              
586 6496         5455 while (1) {
587 194815 100       354583 if (defined (my $axis = $t->[_axis])) {
588             # substitute the current one by the best subtree and queue
589             # the worst for later
590 124033 100       234779 ($t, my ($q)) = @{$t}[($v->[$axis] >= $t->[_cut]) ? (_s0, _s1) : (_s1, _s0)];
  124033         202439  
591 124033         122291 my $q_d2 = $v->max_dist2_to_box(@{$q}[_c0, _c1]);
  124033         280757  
592 124033 100       6947012 if ($q_d2 >= $best_d2) {
593 88425         69943 my $j;
594 88425         164716 for ($j = $#queue_d2; $j >= 0; $j--) {
595 490759 100       990304 last if $queue_d2[$j] <= $q_d2;
596             }
597 88425         108236 splice @queue, ++$j, 0, $q;
598 88425         113988 splice @queue_d2, $j, 0, $q_d2;
599             }
600             }
601             else {
602 70782         53090 for (@{$t->[_ixs]}) {
  70782         127569  
603 462002 100 66     1629119 next if $but and $but->{$_};
604 461806         966953 my $d21 = $vs->[$_]->dist2($v);
605 461806 100       11266978 if ($d21 >= $best_d2) {
606 56256         39322 $best_d2 = $d21;
607 56256         63017 $best_ix = $_;
608             }
609             }
610              
611 70782 100       145272 if ($t = pop @queue) {
612 69697 100       121710 if ($best_d2 <= pop @queue_d2) {
613 64286         74458 next;
614             }
615             }
616 6496         20619 return ($best_ix, $best_d2);
617             }
618             }
619             }
620              
621             sub find_random_vector {
622 0     0 0 0 my $self = shift;
623 0 0       0 my $t = $self->{tree} or return;
624 0         0 my $vs = $self->{vs};
625 0         0 my $hidden = $self->{hidden};
626 0 0 0     0 if (not $hidden or @$vs > 20 * keys(%$hidden)) {
627             # pick directly when the hidden elements are less than 5% of the total
628 0         0 while (1) {
629 0         0 my $ix = int rand @$vs;
630 0 0 0     0 return $ix unless $hidden and $hidden->{$ix};
631             }
632             }
633 0         0 _find_random_vector($vs, $t);
634             }
635              
636             sub _find_random_vector {
637 0     0   0 my ($vs, $t) = @_;
638 0         0 while (defined $t->[_axis]) {
639 0 0       0 $t = $t->[rand($t->[_n]) < $t->[_s0][_n] ? _s0 : _s1];
640             }
641 0         0 $t->[_ixs][rand $t->[_n]]
642             }
643              
644             sub k_means_seed {
645 224     224 1 21909244 my ($self, $n_req) = @_;
646 224 50       1033 $n_req = int($n_req) or return;
647 224 50       829 my $t = $self->{tree} or return;
648 224         524 my $vs = $self->{vs};
649 224         611 _k_means_seed($vs, $t, $n_req);
650             }
651              
652             *k_means_start = \&k_means_seed;
653              
654             sub _k_means_seed {
655 7464     7464   23017 my ($vs, $t, $n_req) = @_;
656 7464 100       8645 if ($n_req <= 1) {
657 2284 100       3311 return if $n_req < 1;
658             # print STDERR "returning centroid\n";
659 2263         5015 return $t->[_sum] / $t->[_n];
660             }
661             else {
662 5180         4882 my $n = $t->[_n];
663 5180 100       6868 if (defined $t->[_axis]) {
664 3620         2840 my ($s0, $s1) = @{$t}[_s0, _s1];
  3620         5561  
665 3620         4466 my $n0 = $s0->[_n];
666 3620         3707 my $n1 = $s1->[_n];
667 3620         4616 my $n0_req = int(0.5 + $n_req * ($n0 / $n));
668 3620 50       4851 $n0_req = $n0 if $n0_req > $n0;
669 3620         4684 return (_k_means_seed($vs, $s0, $n0_req),
670             _k_means_seed($vs, $s1, $n_req - $n0_req));
671             }
672             else {
673 1560         1608 my $ixs = $t->[_ixs];
674 1560         1215 my @out;
675 1560         2322 for (0..$#$ixs) {
676 10769 100       22624 push @out, $vs->[$ixs->[$_]]
677             if rand($n - $_) < ($n_req - @out);
678             }
679             # print STDERR "asked for $n_req elements, returning ".scalar(@out)."\n";
680              
681 1560         4666 return @out;
682             }
683             }
684             }
685              
686             our $k_means_seed_pp_test;
687              
688             sub _k_means_seed_pp_test {
689 0     0   0 my ($self, $err, $kms, $players, $weights) = @_;
690 0         0 my @w;
691 0         0 my $last = 0;
692 0         0 for my $i (0..$#$players) {
693 0         0 my $p = $players->[$i];
694 0         0 my $w = $weights->[$i] - $last;
695 0         0 $last = $weights->[$i];
696              
697 0         0 my @store;
698 0 0       0 if (ref $p) {
699 0         0 _push_all($p, \@store);
700             }
701             else {
702 0         0 @store = $p
703             }
704 0 0       0 if (@store) {
705 0         0 $w /= @store;
706 0         0 $w[$_] = $w for @store;
707             }
708             }
709 0         0 my $vs = $self->{vs};
710 0   0     0 $w[$_] //= 0 for 0..$#$vs;
711              
712 0         0 $k_means_seed_pp_test->($self, $err, [map $self->{vs}[$_], @$kms], \@w);
713             }
714              
715             sub k_means_seed_pp {
716 0     0 0 0 my ($self, $n_req, $err) = @_;
717 0 0       0 $n_req = int($n_req) or return;
718 0   0     0 $err ||= 0.5;
719 0 0       0 my $t = $self->{tree} or return;
720 0         0 my $vs = $self->{vs};
721 0         0 my $km = $self->find_random_vector;
722              
723 0         0 my (@km, @d2);
724 0         0 idhash my %extra; # [$min_d2, $max_d2]
725              
726             # my (@player, @weight, @queue);
727             # $#player = @$vs; # preallocate memory
728              
729 0         0 my (@weight, @queue);
730 0         0 $#weight = @$vs; # preallocate memory
731              
732 0         0 while (1) {
733 0         0 push @km, $km;
734 0 0       0 last unless @km < $n_req;
735              
736             # update distances
737 0         0 @queue = $t;
738 0         0 while (my $p = pop @queue) {
739 0         0 my $kmv = $vs->[$km];
740 0         0 my ($c0, $c1) = @{$p}[_c0, _c1];
  0         0  
741 0   0     0 my $extra = $extra{$p} //= ['inf', 'inf'];
742 0         0 my ($min_d2, $max_d2) = @$extra;
743 0         0 my $min_d2_to_box = $kmv->dist2_to_box($c0, $c1);
744 0 0       0 if ($max_d2 > $min_d2_to_box) {
745 0 0       0 if (defined $p->[_axis]) {
746 0         0 push @queue, @{$p}[_s0, _s1];
  0         0  
747             }
748             else {
749 0         0 for (@{$p->[_ixs]}) {
  0         0  
750 0         0 my $d2 = $kmv->dist2($vs->[$_]);
751 0 0 0     0 if ($d2 < ($d2[$_] //= $d2)) {
752 0         0 $d2[$_] = $d2;
753             }
754             }
755             }
756              
757 0 0       0 if ($min_d2_to_box < $min_d2) {
758 0         0 $extra->[0] = $min_d2_to_box;
759             }
760              
761 0         0 my $max_d2_to_box = $kmv->max_dist2_to_box($c0, $c1);
762 0 0       0 if ($max_d2_to_box < $max_d2) {
763 0         0 $extra->[1] = $max_d2_to_box;
764             }
765             }
766             }
767              
768             # find players and weight them
769 0         0 my $weight = 0;
770             # @player = ();
771 0         0 @weight = ();
772              
773             # @queue = $t;
774             # while (my $p = pop @queue) {
775             # my $extra = $extra{$p} or die "internal error: extra information missing for $p";
776             # my ($min_d2, $max_d2) = @$extra;
777              
778             # if ($max_d2 * $err < $min_d2) {
779             # $weight += $p->[_n] * ($min_d2 + $max_d2) * 0.5;
780             # push @weight, $weight;
781             # push @player, $p;
782             # }
783             # else {
784             # if (defined $p->[_axis]) {
785             # push @queue, @{$p}[_s0, _s1];
786             # }
787             # else {
788             # for (@{$p->[_ixs]}) {
789             # if (my $d2 = $d2[$_]) {
790             # $weight += $d2;
791             # push @weight, $weight;
792             # push @player, $_;
793             # }
794             # }
795             # }
796             # }
797             # }
798              
799 0         0 for my $ix (0..@$vs) {
800 0   0     0 $weight += $d2[$ix] // 0;
801 0         0 $weight[$ix] += $weight;
802             }
803              
804             # in order to check the algorithm we have to tap it here
805             # $k_means_seed_pp_test and @km > 1 and
806             # $self->_k_means_seed_pp_test($err, \@km, \@player, \@weight);
807              
808             # to many k-means requested?
809             # @player or last;
810              
811             # select a position on the weight queue:
812 0         0 my $dice = rand($weight);
813              
814             # and use binary search to look for it:
815 0         0 my $i = 0;
816 0         0 my $j = @weight;
817 0         0 while ($i < $j) {
818 0         0 my $pivot = (($i + $j) >> 1);
819 0 0       0 if ($weight[$pivot] < $dice) {
820 0         0 $i = $pivot + 1;
821             }
822             else {
823 0         0 $j = $pivot;
824             }
825             }
826             #my $player = $player[$i];
827             #$km = (ref $player ? _find_random_vector($vs, $player) : $player);
828 0         0 $km = $i;
829             }
830 0         0 return @{$vs}[@km];
  0         0  
831             }
832              
833             sub k_means_loop {
834 224     224 1 113832 my ($self, @k) = @_;
835 224 50       813 @k or next;
836 224 50       786 my $t = $self->{tree} or next;
837 224         386 my $vs = $self->{vs};
838 224         293 while (1) {
839 1121         1719 my $diffs;
840 1121         5305 my @n = ((0) x @k);
841 1121         3469 my @sum = ((undef) x @k);
842              
843 1121         10039 _k_means_step($vs, $t, \@k, [0..$#k], \@n, \@sum);
844              
845 1121         44691 for (0..$#k) {
846 31170 100       50261 if (my $n = $n[$_]) {
847 29609         48761 my $k = $sum[$_] / $n;
848 29609 100       237672 $diffs++ if $k != $k[$_];
849 29609         412893 $k[$_] = $k;
850             }
851             }
852 1121 100       17842 unless ($diffs) {
853 224 50       6517 return (wantarray ? @k : $k[0]);
854             }
855             }
856             }
857              
858             sub k_means_step {
859 0     0 1 0 my $self = shift;
860 0 0       0 @_ or return;
861 0 0       0 my $t = $self->{tree} or return;
862 0         0 my $vs = $self->{vs};
863              
864 0         0 my @n = ((0) x @_);
865 0         0 my @sum = ((undef) x @_);
866              
867 0         0 _k_means_step($vs, $t, \@_, [0..$#_], \@n, \@sum);
868              
869 0         0 for (0..$#n) {
870 0 0       0 if (my $n = $n[$_]) {
871 0         0 $sum[$_] /= $n;
872             }
873             else {
874             # otherwise let the original value stay
875 0         0 $sum[$_] = $_[$_];
876             }
877             }
878 0 0       0 wantarray ? @sum : $sum[0];
879             }
880              
881             sub _k_means_step {
882 102983     102983   133830 my ($vs, $t, $centers, $cixs, $ns, $sums) = @_;
883 102983         99766 my ($n, $sum, $c0, $c1) = @{$t}[_n, _sum, _c0, _c1];
  102983         217555  
884 102983 50       230940 if ($n) {
885 102983         245087 my $centroid = $sum/$n;
886 102983     0   1481364 my $best = nkeyhead { $centroid->dist2($centers->[$_]) } @$cixs;
  2121222         37536522  
887 102983         1951575 my $max_d2 = Math::Vector::Real::max_dist2_to_box($centers->[$best], $c0, $c1);
888 102983         4836869 my @down = grep { Math::Vector::Real::dist2_to_box($centers->[$_], $c0, $c1) <= $max_d2 } @$cixs;
  2121262         83203689  
889 102983 100       4096238 if (@down <= 1) {
890 9264         11892 $ns->[$best] += $n;
891             # FIXME: M::V::R objects should support this undef + vector logic natively!
892 9264 100       15816 if (defined $sums->[$best]) {
893 7725         17842 $sums->[$best] += $sum;
894             }
895             else {
896 1539         4012 $sums->[$best] = V(@$sum);
897             }
898             }
899             else {
900 93719 100       202766 if (defined (my $axis = $t->[_axis])) {
901 50931         61244 my ($s0, $s1) = @{$t}[_s0, _s1];
  50931         102125  
902 50931         134562 _k_means_step($vs, $t->[_s0], $centers, \@down, $ns, $sums);
903 50931         1069887 _k_means_step($vs, $t->[_s1], $centers, \@down, $ns, $sums);
904             }
905             else {
906 42788         46192 for my $ix (@{$t->[_ixs]}) {
  42788         91071  
907 273401         3164678 my $v = $vs->[$ix];
908 273401     0   1153514 my $best = nkeyhead { $v->dist2($centers->[$_]) } @down;
  4463686         87749334  
909 273401         5015847 $ns->[$best]++;
910 273401 100       420084 if (defined $sums->[$best]) {
911 245331         482047 $sums->[$best] += $v;
912             }
913             else {
914 28070         73651 $sums->[$best] = V(@$v);
915             }
916             }
917             }
918             }
919             }
920             }
921              
922             sub k_means_assign {
923 224     224 1 149504 my $self = shift;
924 224 50       832 @_ or return;
925 224 50       875 my $t = $self->{tree} or return;
926 224         425 my $vs = $self->{vs};
927              
928 224         4683 my @out = ((undef) x @$vs);
929 224         2159 _k_means_assign($vs, $t, \@_, [0..$#_], \@out);
930 224         11705 @out;
931             }
932              
933             sub _k_means_assign {
934 13636     13636   17074 my ($vs, $t, $centers, $cixs, $outs) = @_;
935 13636         13186 my ($n, $sum, $c0, $c1) = @{$t}[_n, _sum, _c0, _c1];
  13636         29669  
936 13636 50       28025 if ($n) {
937 13636         38419 my $centroid = $sum/$n;
938 13636     0   200397 my $best = nkeyhead { $centroid->dist2($centers->[$_]) } @$cixs;
  413778         7278209  
939 13636         243753 my $max_d2 = Math::Vector::Real::max_dist2_to_box($centers->[$best], $c0, $c1);
940 13636         572589 my @down = grep { Math::Vector::Real::dist2_to_box($centers->[$_], $c0, $c1) <= $max_d2 } @$cixs;
  413818         16022199  
941 13636 100       494942 if (@down <= 1) {
942 1347         2194 _k_means_assign_1($t, $best, $outs);
943             }
944             else {
945 12289 100       26609 if (defined (my $axis = $t->[_axis])) {
946 6706         8319 my ($s0, $s1) = @{$t}[_s0, _s1];
  6706         12839  
947 6706         18240 _k_means_assign($vs, $t->[_s0], $centers, \@down, $outs);
948 6706         28910 _k_means_assign($vs, $t->[_s1], $centers, \@down, $outs);
949             }
950             else {
951 5583         4943 for my $ix (@{$t->[_ixs]}) {
  5583         12436  
952 34381         51657 my $v = $vs->[$ix];
953 34381     0   159966 my $best = nkeyhead { $v->dist2($centers->[$_]) } @down;
  844351         16735770  
954 34381         630191 $outs->[$ix] = $best;
955             }
956             }
957             }
958             }
959             }
960              
961             sub _k_means_assign_1 {
962 6323     6323   5738 my ($t, $best, $outs) = @_;
963 6323 100       9795 if (defined (my $axis = $t->[_axis])) {
964 2488         3361 _k_means_assign_1($t->[_s0], $best, $outs);
965 2488         3549 _k_means_assign_1($t->[_s1], $best, $outs);
966             }
967             else {
968 3835         2751 $outs->[$_] = $best for @{$t->[_ixs]};
  3835         22846  
969             }
970             }
971              
972             sub ordered_by_proximity {
973 6536     6536 1 18165 my $self = shift;
974 6536         5664 my @r;
975 6536         5221 $#r = $#{$self->{vs}}; $#r = -1; # preallocate
  6536         17616  
  6536         10344  
976 6536         12243 _ordered_by_proximity($self->{tree}, \@r);
977 6536         239002 return @r;
978             }
979              
980             sub _ordered_by_proximity {
981 428468     428468   318409 my $t = shift;
982 428468         295788 my $r = shift;
983 428468 100       501973 if (defined $t->[_axis]) {
984 210966         225562 _ordered_by_proximity($t->[_s0], $r);
985 210966         255999 _ordered_by_proximity($t->[_s1], $r);
986             }
987             else {
988 217502         153401 push @$r, @{$t->[_ixs]}
  217502         443839  
989             }
990             }
991              
992             sub _dump_to_string {
993 0     0     my ($vs, $t, $indent, $opts) = @_;
994 0           my ($n, $c0, $c1, $sum) = @{$t}[_n, _c0, _c1, _sum];
  0            
995 0 0         my $id = ($opts->{pole_id} ? _pole_id($vs, $t)." " : '');
996 0 0         if (defined (my $axis = $t->[_axis])) {
997 0           my ($s0, $s1, $cut) = @{$t}[_s0, _s1, _cut];
  0            
998 0           return ( "${indent}${id}n: $n, c0: $c0, c1: $c1, sum: $sum, axis: $axis, cut: $cut\n" .
999             _dump_to_string($vs, $s0, "$indent$opts->{tab}", $opts) .
1000             _dump_to_string($vs, $s1, "$indent$opts->{tab}", $opts) );
1001             }
1002             else {
1003 0   0       my $remark = $opts->{remark} // [];
1004 0           my $o = ( "${indent}${id}n: $n, c0: $c0, c1: $c1, sum: $sum\n" .
1005             "${indent}$opts->{tab}ixs: [" );
1006 0           my @str;
1007 0           for my $ix (@{$t->[_ixs]}) {
  0            
1008 0   0       my $colored_ix = (@$remark and grep($ix == $_, @$remark)
1009             ? Term::ANSIColor::colored($ix, 'red')
1010             : $ix);
1011 0 0 0       if ($opts->{dump_vectors} // 1) {
1012 0           push @str, "$colored_ix $vs->[$ix]";
1013             }
1014             else {
1015 0           push @str, $colored_ix;
1016             }
1017             }
1018 0           return $o . join(', ', @str) . "]\n";
1019             }
1020             }
1021              
1022             sub dump_to_string {
1023 0     0 0   my ($self, %opts) = @_;
1024 0   0       my $tab = $opts{tab} //= ' ';
1025 0           my $vs = $self->{vs};
1026 0           my $nvs = @$vs;
1027 0 0         my $hidden = join ", ", keys %{$self->{hidden} || {}};
  0            
1028 0           my $o = "tree: n: $nvs, hidden: {$hidden}\n";
1029 0 0         if (my $t = $self->{tree}) {
1030 0 0         require Term::ANSIColor if $opts{remark};
1031 0           return $o . _dump_to_string($vs, $t, $tab, \%opts);
1032             }
1033             else {
1034 0           return "$o${tab}(empty)\n";
1035             }
1036             }
1037              
1038             sub dump {
1039 0     0 0   my $self = shift;
1040 0           print $self->dump_to_string(@_);
1041             }
1042              
1043             1;
1044             __END__