File Coverage

blib/lib/AI/ParticleSwarmOptimization.pm
Criterion Covered Total %
statement 170 203 83.7
branch 51 72 70.8
condition 45 72 62.5
subroutine 24 27 88.8
pod 7 8 87.5
total 297 382 77.7


line stmt bran cond sub pod time code
1             package AI::ParticleSwarmOptimization;
2              
3 1     1   47258 use strict;
  1         4  
  1         101  
4 1     1   9 use warnings;
  1         3  
  1         52  
5 1     1   2194 use Math::Random::MT qw();
  1         2160  
  1         98  
6              
7             require Exporter;
8              
9             our @ISA = qw(Exporter);
10             our @EXPORT = qw();
11             $AI::ParticleSwarmOptimization::VERSION = '1.006';
12              
13 1     1   11 use constant kLogBetter => 1;
  1         2  
  1         103  
14 1     1   7 use constant kLogStall => 2;
  1         2  
  1         46  
15 1     1   6 use constant kLogIter => 4;
  1         2  
  1         56  
16 1     1   6 use constant kLogDetail => 8;
  1         2  
  1         71  
17 1     1   6 use constant kLogIterDetail => (kLogIter | kLogDetail);
  1         3  
  1         4332  
18              
19             sub new {
20 3     3 1 831 my ($class, %params) = @_;
21 3         10 my $self = bless {}, $class;
22              
23 3         13 $self->setParams (%params);
24 3         19 return $self;
25             }
26              
27              
28             sub setParams {
29 20     20 1 6218 my ($self, %params) = @_;
30              
31 20 100       63 if (defined $params{-fitFunc}) {
32             # Process required parameters - -fitFunc and -dimensions
33 5 100       17 if ('ARRAY' eq ref $params{-fitFunc}) {
34 1         1 ($self->{fitFunc}, @{$self->{fitParams}}) = @{$params{-fitFunc}};
  1         3  
  1         3  
35             } else {
36 4         11 $self->{fitFunc} = $params{-fitFunc};
37             }
38              
39 5   100     21 $self->{fitParams} ||= [];
40             }
41              
42 20 100 100     71 $self->{prtcls} = [] # Need to reinit if num dimensions changed
      66        
43             if defined $params{-dimensions}
44             and defined $self->{dimensions}
45             and $params{-dimensions} != $self->{dimensions};
46              
47 20         36 $self->{$_} = $params{"-$_"} for grep {exists $params{"-$_"}} qw/
  360         592  
48             dimensions
49             exitFit
50             exitPlateau
51             exitPlateauDP
52             exitPlateauWindow
53             exitPlateauBurnin
54             inertia
55             iterations
56             meWeight
57             numNeighbors
58             numParticles
59             posMax
60             posMin
61             randSeed
62             randStartVelocity
63             stallSpeed
64             themWeight
65             verbose
66             /;
67              
68 20 100 100     86 die "-dimensions must be greater than 0\n"
69             if exists $params{-dimensions} && $params{-dimensions} <= 0;
70              
71 18 50 66     95 if (defined $self->{verbose} and 'ARRAY' eq ref $self->{verbose}) {
72 0         0 my @log = map {lc} @{$self->{verbose}};
  0         0  
  0         0  
73 0         0 my %logTypes = (
74             better => kLogBetter,
75             stall => kLogStall,
76             iter => kLogIter,
77             detail => kLogDetail,
78             );
79              
80 0         0 $self->{verbose} = 0;
81 0   0     0 exists $logTypes{$_} and $self->{verbose} |= $logTypes{$_} for @log;
82             }
83              
84 18 100 66     1290 $self->{numParticles} ||= $self->{dimensions} * 10
85             if defined $self->{dimensions};
86 18 100 66     144 $self->{numNeighbors} ||= int sqrt $self->{numParticles}
87             if defined $self->{numParticles};
88 18   100     42 $self->{iterations} ||= 1000;
89 18   100     37 $self->{exitPlateauDP} ||= 10;
90 18   66     40 $self->{exitPlateauWindow} ||= $self->{iterations} * 0.1;
91 18   66     35 $self->{exitPlateauBurnin} ||= $self->{iterations} * 0.5;
92 18 100       35 $self->{posMax} = 100 unless defined $self->{posMax};
93 18 100       35 $self->{posMin} = -$self->{posMax} unless defined $self->{posMin};
94 18   100     40 $self->{meWeight} ||= 0.5;
95 18   100     39 $self->{themWeight} ||= 0.5;
96 18   100     37 $self->{inertia} ||= 0.9;
97 18   50     57 $self->{verbose} ||= 0;
98              
99 18         111 return 1;
100             }
101              
102              
103             sub init {
104 10     10 1 19 my ($self) = @_;
105              
106 10 50 33     54 die "-fitFunc must be set before init or optimize is called"
107             unless $self->{fitFunc} and 'CODE' eq ref $self->{fitFunc};
108 10 50 33     45 die
109             "-dimensions must be set to 1 or greater before init or optimize is called"
110             unless $self->{dimensions} and $self->{dimensions} >= 1;
111              
112 10 100       83 my $seed =
113             int (exists $self->{randSeed} ? $self->{randSeed} : rand (0xffffffff));
114              
115 10         35 $self->{rndGen} = Math::Random::MT->new ($seed);
116 10         223 $self->{usedRandSeed} = $seed;
117              
118 10         16 $self->{prtcls} = [];
119 10         35 $self->{bestBest} = undef;
120 10         13 $self->{bestBestByIter} = undef;
121 10         14 $self->{bestsMean} = 0;
122 10         16 $self->_initParticles ();
123 10         65 $self->{iterCount} = 0;
124              
125             # Normalise weights.
126 10         27 my $totalWeight =
127             $self->{inertia} + $self->{themWeight} + $self->{meWeight};
128              
129 10         24 $self->{inertia} /= $totalWeight;
130 10         10 $self->{meWeight} /= $totalWeight;
131 10         10 $self->{themWeight} /= $totalWeight;
132              
133 10 100       43 die "-posMax must be greater than -posMin"
134             unless $self->{posMax} > $self->{posMin};
135 8   50     30 $self->{$_} > 0 or die "-$_ must be greater then 0" for qw/numParticles/;
136              
137 8         16 $self->{deltaMax} = ($self->{posMax} - $self->{posMin}) / 100.0;
138              
139 8         39 return 1;
140             }
141              
142              
143             sub optimize {
144 1     1 1 13 my ($self, $iterations) = @_;
145              
146 1   33     6 $iterations ||= $self->{iterations};
147 1 50       7 $self->init () unless $self->{prtcls};
148 1         4 return $self->_swarm ($iterations);
149             }
150              
151              
152             sub getBestParticles {
153 0     0 1 0 my ($self, $num) = @_;
154 0         0 my @bests = 0 .. $self->{numParticles} - 1;
155 0         0 my $prtcls = $self->{prtcls};
156              
157 0         0 @bests = sort {$prtcls->[$a]{bestFit} <=> $prtcls->[$b]{bestFit}} @bests;
  0         0  
158 0   0     0 $num ||= 1;
159 0         0 return @bests[0 .. $num - 1];
160             }
161              
162              
163             sub getParticleBestPos {
164 0     0 1 0 my ($self, $prtcl) = @_;
165              
166 0 0       0 return undef if $prtcl >= $self->{numParticles};
167 0         0 $prtcl = $self->{prtcls}[$prtcl];
168              
169 0         0 return ($prtcl->{bestFit}, @{$prtcl->{bestPos}});
  0         0  
170             }
171              
172              
173             sub getIterationCount {
174 1     1 1 5 my ($self) = @_;
175              
176 1         5 return $self->{iterCount};
177             }
178              
179              
180             sub getSeed {
181 0     0 0 0 my ($self) = @_;
182              
183 0         0 return $self->{usedRandSeed};
184             }
185              
186              
187             sub _initParticles {
188 10     10   12 my ($self) = @_;
189              
190 10         27 for my $id (0 .. $self->{numParticles} - 1) {
191 39         433 $self->{prtcls}[$id]{id} = $id;
192 39         82 $self->_initParticle ($self->{prtcls}[$id]);
193             }
194             }
195              
196              
197             sub _initParticle {
198 39     39   44 my ($self, $prtcl) = @_;
199              
200             # each particle is a hash of arrays with the array sizes being the
201             # dimensionality of the problem space
202 39         76 for my $d (0 .. $self->{dimensions} - 1) {
203 99         200 $prtcl->{currPos}[$d] =
204             $self->_randInRange ($self->{posMin}, $self->{posMax});
205              
206 99 50       898 $prtcl->{velocity}[$d] =
207             $self->{randStartVelocity}
208             ? $self->_randInRange (-$self->{deltaMax}, $self->{deltaMax})
209             : 0;
210             }
211              
212 39         84 $prtcl->{currFit} = $self->_calcPosFit ($prtcl->{currPos});
213 39         474 $self->_calcNextPos ($prtcl);
214              
215 39 50       456 unless (defined $prtcl->{bestFit}) {
216             $prtcl->{bestPos}[$_] =
217             $self->_randInRange ($self->{posMin}, $self->{posMax})
218 39         112 for 0 .. $self->{dimensions} - 1;
219 39         331 $prtcl->{bestFit} = $self->_calcPosFit ($prtcl->{bestPos});
220             }
221             }
222              
223              
224             sub _calcPosFit {
225 9117     9117   10772 my ($self, $pos) = @_;
226              
227 9117         10778 return $self->{fitFunc}->(@{$self->{fitParams}}, @$pos);
  9117         28131  
228             }
229              
230              
231             sub _swarm {
232 1     1   1 my ($self, $iterations) = @_;
233              
234 1         4 for my $iter (1 .. $iterations) {
235 300         461 ++$self->{iterCount};
236 300 50       806 last if defined $self->_moveParticles ($iter);
237              
238 300         793 $self->_updateVelocities ($iter);
239 300 50 33     5318 next if !$self->{exitPlateau} || !defined $self->{bestBest};
240              
241 300 100       1040 if ($iter >= $self->{exitPlateauBurnin} - $self->{exitPlateauWindow}) {
242 261         586 my $i = $iter % $self->{exitPlateauWindow};
243              
244 261 100       1033 $self->{bestsMean} -= $self->{bestBestByIter}[$i]
245             if defined $self->{bestBestByIter}[$i];
246 261         849 $self->{bestsMean} += $self->{bestBestByIter}[$i] =
247             $self->{bestBest} / $self->{exitPlateauWindow};
248             }
249              
250 300 100       843 next if $iter <= $self->{exitPlateauBurnin};
251              
252             #Round to the specified number of d.p.
253 200         583 my $format = "%.$self->{exitPlateauDP}f";
254 200         2734 my $mean = sprintf $format, $self->{bestsMean};
255 200         681 my $current = sprintf $format, $self->{bestBest};
256              
257             #Check if there is a sufficient plateau - stopping iterations if so
258 200 100       1047 last if $mean == $current;
259             }
260              
261 1         6 return $self->{bestBest};
262             }
263              
264              
265             sub _moveParticles {
266 300     300   656 my ($self, $iter) = @_;
267              
268 300 50       964 print "Iter $iter\n" if $self->{verbose} & kLogIter;
269              
270 300         429 for my $prtcl (@{$self->{prtcls}}) {
  300         764  
271 9000         11047 @{$prtcl->{currPos}} = @{$prtcl->{nextPos}};
  9000         21932  
  9000         14341  
272 9000         14448 $prtcl->{currFit} = $prtcl->{nextFit};
273              
274 9000         9658 my $fit = $prtcl->{currFit};
275              
276 9000 100       19896 if ($self->_betterFit ($fit, $prtcl->{bestFit})) {
277             # Save position - best fit for this particle so far
278 1986         3760 $self->_saveBest ($prtcl, $fit, $iter);
279             }
280              
281 9000 50 33     23892 return $fit if defined $self->{exitFit} and $fit < $self->{exitFit};
282 9000 50       23126 next if !($self->{verbose} & kLogIterDetail);
283              
284 0 0       0 printf "Part %3d fit %8.2f", $prtcl->{id}, $fit
285             if $self->{verbose} >= 2;
286 0         0 printf " (%s @ %s)",
287 0         0 join (', ', map {sprintf '%5.3f', $_} @{$prtcl->{velocity}}),
  0         0  
288 0 0       0 join (', ', map {sprintf '%5.2f', $_} @{$prtcl->{currPos}})
  0         0  
289             if $self->{verbose} & kLogDetail;
290 0         0 print "\n";
291             }
292              
293 300         964 return undef;
294             }
295              
296              
297             sub _saveBest {
298 1986     1986   3173 my ($self, $prtcl, $fit, $iter) = @_;
299              
300             # for each dimension, set the best position as the current position
301 1986         2083 @{$prtcl->{bestPos}} = @{$prtcl->{currPos}};
  1986         5306  
  1986         2903  
302              
303 1986         2980 $prtcl->{bestFit} = $fit;
304 1986 100       4366 return if !$self->_betterFit ($fit, $self->{bestBest});
305              
306 85 50       220 if ($self->{verbose} & kLogBetter) {
307 0         0 my $velSq;
308              
309 0         0 $velSq += $_**2 for @{$prtcl->{velocity}};
  0         0  
310 0         0 printf "#%05d: Particle $prtcl->{id} best: %.4f (vel: %.3f)\n",
311             $iter, $fit, sqrt ($velSq);
312             }
313              
314 85         163 $self->{bestBest} = $fit;
315             }
316              
317              
318             sub _betterFit {
319 10986     10986   18366 my ($self, $new, $old) = @_;
320              
321 10986   100     53014 return !defined ($old) || ($new < $old);
322             }
323              
324              
325             sub _updateVelocities {
326 300     300   400 my ($self, $iter) = @_;
327              
328 300         428 for my $prtcl (@{$self->{prtcls}}) {
  300         681  
329 9000         133491 my $bestN = $self->{prtcls}[$self->_getBestNeighbour ($prtcl)];
330 9000         10037 my $velSq;
331              
332 9000         17241 for my $d (0 .. $self->{dimensions} - 1) {
333 27000         70646 my $meFactor =
334             $self->_randInRange (-$self->{meWeight}, $self->{meWeight});
335 27000         229134 my $themFactor =
336             $self->_randInRange (-$self->{themWeight}, $self->{themWeight});
337 27000         217967 my $meDelta = $prtcl->{bestPos}[$d] - $prtcl->{currPos}[$d];
338 27000         59609 my $themDelta = $bestN->{bestPos}[$d] - $prtcl->{currPos}[$d];
339              
340 27000         63256 $prtcl->{velocity}[$d] =
341             $prtcl->{velocity}[$d] * $self->{inertia} +
342             $meFactor * $meDelta +
343             $themFactor * $themDelta;
344 27000         67642 $velSq += $prtcl->{velocity}[$d]**2;
345             }
346              
347 9000         14169 my $vel = sqrt ($velSq);
348 9000 50 33     33485 if (!$vel or $self->{stallSpeed} and $vel <= $self->{stallSpeed}) {
      33        
349 0         0 $self->_initParticle ($prtcl);
350 0 0       0 printf "#%05d: Particle $prtcl->{id} stalled (%6f)\n", $iter, $vel
351             if $self->{verbose} & kLogStall;
352             }
353              
354 9000         22360 $self->_calcNextPos ($prtcl);
355             }
356             }
357              
358              
359             sub _calcNextPos {
360 9039     9039   12376 my ($self, $prtcl) = @_;
361              
362 9039         18133 for my $d (0 .. $self->{dimensions} - 1) {
363 27099         47735 $prtcl->{nextPos}[$d] = $prtcl->{currPos}[$d] + $prtcl->{velocity}[$d];
364 27099 100       98554 if ($prtcl->{nextPos}[$d] < $self->{posMin}) {
    100          
365 130         229 $prtcl->{nextPos}[$d] = $self->{posMin};
366 130         299 $prtcl->{velocity}[$d] = 0;
367             } elsif ($prtcl->{nextPos}[$d] > $self->{posMax}) {
368 96         181 $prtcl->{nextPos}[$d] = $self->{posMax};
369 96         195 $prtcl->{velocity}[$d] = 0;
370             }
371             }
372              
373 9039         21594 $prtcl->{nextFit} = $self->_calcPosFit ($prtcl->{nextPos});
374             }
375              
376              
377             sub _randInRange {
378 54198     54198   76778 my ($self, $min, $max) = @_;
379 54198         154797 return $min + $self->{rndGen}->rand ($max - $min);
380             }
381              
382              
383             sub _getBestNeighbour {
384 9000     9000   10937 my ($self, $prtcl) = @_;
385 9000         8901 my $bestNFitness;
386             my $bestNIndex;
387              
388 9000         19522 for my $neighbor (0 .. $self->{numNeighbors} - 1) {
389 45000         69654 my $prtclNIndex = ($prtcl + $neighbor) % $self->{numParticles};
390              
391 45000 100 100     202398 if (!defined ($bestNFitness)
392             || $self->{prtcls}[$prtclNIndex]{bestFit} < $bestNFitness)
393             {
394 20673         32847 $bestNFitness = $self->{prtcls}[$prtclNIndex]{bestFit};
395 20673         32361 $bestNIndex = $prtclNIndex;
396             }
397             }
398              
399 9000         18992 return $bestNIndex;
400             }
401              
402              
403             1;
404              
405              
406             =head1 NAME
407              
408             AI::ParticleSwarmOptimization - Particle Swarm Optimization (object oriented)
409              
410             =head1 SYNOPSIS
411              
412             use AI::ParticleSwarmOptimization;
413              
414             my $pso = AI::ParticleSwarmOptimization->new (
415             fitFunc => \&calcFit,
416             dimensions => 3,
417             );
418             my $fitValue = $pso->optimize ();
419             my ($best) = $pso->getBestParticles (1);
420             my ($fit, @values) = $pso->getParticleBestPos ($best);
421              
422             printf "Fit %.4f at (%s)\n",
423             $fit, join ', ', map {sprintf '%.4f', $_} @values;
424              
425              
426             sub calcFit {
427             my @values = @_;
428             my $offset = int (-@values / 2);
429             my $sum;
430              
431             $sum += ($_ - $offset++) ** 2 for @values;
432             return $sum;
433             }
434              
435             =head1 Description
436              
437             The Particle Swarm Optimization technique uses communication of the current best
438             position found between a number of particles moving over a hyper surface as a
439             technique for locating the best location on the surface (where 'best' is the
440             minimum of some fitness function). For a Wikipedia discussion of PSO see
441             http://en.wikipedia.org/wiki/Particle_swarm_optimization.
442              
443             This pure Perl module is an implementation of the Particle Swarm Optimization
444             technique for finding minima of hyper surfaces. It presents an object oriented
445             interface that facilitates easy configuration of the optimization parameters and
446             (in principle) allows the creation of derived classes to reimplement all aspects
447             of the optimization engine (a future version will describe the replaceable
448             engine components).
449              
450             This implementation allows communication of a local best point between a
451             selected number of neighbours. It does not support a single global best position
452             that is known to all particles in the swarm.
453              
454             =head1 Methods
455              
456             AI::ParticleSwarmOptimization provides the following public methods. The parameter lists shown
457             for the methods denote optional parameters by showing them in [].
458              
459             =over 4
460              
461             =item new (%parameters)
462              
463             Create an optimization object. The following parameters may be used:
464              
465             =over 4
466              
467             =item I<-dimensions>: positive number, required
468              
469             The number of dimensions of the hypersurface being searched.
470              
471             =item I<-exitFit>: number, optional
472              
473             If provided I<-exitFit> allows early termination of optimize if the
474             fitness value becomes equal or less than I<-exitFit>.
475              
476             =item I<-fitFunc>: required
477              
478             I<-fitFunc> is a reference to the fitness function used by the search. If extra
479             parameters need to be passed to the fitness function an array ref may be used
480             with the code ref as the first array element and parameters to be passed into
481             the fitness function as following elements. User provided parameters are passed
482             as the first parameters to the fitness function when it is called:
483              
484             my $pso = AI::ParticleSwarmOptimization->new (
485             fitFunc => [\&calcFit, $context],
486             dimensions => 3,
487             );
488              
489             ...
490              
491             sub calcFit {
492             my ($context, @values) = @_;
493             ...
494             return $fitness;
495             }
496              
497             In addition to any user provided parameters the list of values representing the
498             current particle position in the hyperspace is passed in. There is one value per
499             hyperspace dimension.
500              
501             =item I<-inertia>: positive or zero number, optional
502              
503             Determines what proportion of the previous velocity is carried forward to the
504             next iteration. Defaults to 0.9
505              
506             See also I<-meWeight> and I<-themWeight>.
507              
508             =item I<-iterations>: number, optional
509              
510             Number of optimization iterations to perform. Defaults to 1000.
511              
512             =item I<-meWeight>: number, optional
513              
514             Coefficient determining the influence of the current local best position on the
515             next iterations velocity. Defaults to 0.5.
516              
517             See also I<-inertia> and I<-themWeight>.
518              
519             =item I<-numNeighbors>: positive number, optional
520              
521             Number of local particles considered to be part of the neighbourhood of the
522             current particle. Defaults to the square root of the total number of particles.
523              
524             =item I<-numParticles>: positive number, optional
525              
526             Number of particles in the swarm. Defaults to 10 times the number of dimensions.
527              
528             =item I<-posMax>: number, optional
529              
530             Maximum coordinate value for any dimension in the hyper space. Defaults to 100.
531              
532             =item I<-posMin>: number, optional
533              
534             Minimum coordinate value for any dimension in the hyper space. Defaults to
535             -I<-posMax> (if I<-posMax> is negative I<-posMin> should be set more negative).
536              
537             =item I<-randSeed>: number, optional
538              
539             Seed for the random number generator. Useful if you want to rerun an
540             optimization, perhaps for benchmarking or test purposes.
541              
542             =item I<-randStartVelocity>: boolean, optional
543              
544             Set true to initialize particles with a random velocity. Otherwise particle
545             velocity is set to 0 on initalization.
546              
547             A range based on 1/100th of -I<-posMax> - I<-posMin> is used for the initial
548             speed in each dimension of the velocity vector if a random start velocity is
549             used.
550              
551             =item I<-stallSpeed>: positive number, optional
552              
553             Speed below which a particle is considered to be stalled and is repositioned to
554             a new random location with a new initial speed.
555              
556             By default I<-stallSpeed> is undefined but particles with a speed of 0 will be
557             repositioned.
558              
559             =item I<-themWeight>: number, optional
560              
561             Coefficient determining the influence of the neighbourhod best position on the
562             next iterations velocity. Defaults to 0.5.
563              
564             See also I<-inertia> and I<-meWeight>.
565              
566             =item I<-exitPlateau>: boolean, optional
567              
568             Set true to have the optimization check for plateaus (regions where the fit
569             hasn't improved much for a while) during the search. The optimization ends when
570             a suitable plateau is detected following the burn in period.
571              
572             Defaults to undefined (option disabled).
573              
574             =item I<-exitPlateauDP>: number, optional
575              
576             Specify the number of decimal places to compare between the current fitness
577             function value and the mean of the previous I<-exitPlateauWindow> values.
578              
579             Defaults to 10.
580              
581             =item I<-exitPlateauWindow>: number, optional
582              
583             Specify the size of the window used to calculate the mean for comparison to
584             the current output of the fitness function. Correlates to the minimum size of a
585             plateau needed to end the optimization.
586              
587             Defaults to 10% of the number of iterations (I<-iterations>).
588              
589             =item I<-exitPlateauBurnin>: number, optional
590              
591             Determines how many iterations to run before checking for plateaus.
592              
593             Defaults to 50% of the number of iterations (I<-iterations>).
594              
595             =item I<-verbose>: flags, optional
596              
597             If set to a non-zero value I<-verbose> determines the level of diagnostic print
598             reporting that is generated during optimization.
599              
600             The following constants may be bitwise ored together to set logging options:
601              
602             =over 4
603              
604             =item * kLogBetter
605              
606             prints particle details when its fit becomes bebtter than its previous best.
607              
608             =item * kLogStall
609              
610             prints particle details when its velocity reaches 0 or falls below the stall
611             threshold.
612              
613             =item * kLogIter
614              
615             Shows the current iteration number.
616              
617             =item * kLogDetail
618              
619             Shows additional details for some of the other logging options.
620              
621             =item * kLogIterDetail
622              
623             Shorthand for C
624              
625             =back
626              
627             =back
628              
629             =item B
630              
631             Set or change optimization parameters. See I<-new> above for a description of
632             the parameters that may be supplied.
633              
634             =item B
635              
636             Reinitialize the optimization. B will be called during the first call
637             to B if it hasn't already been called.
638              
639             =item B
640              
641             Runs the minimization optimization. Returns the fit value of the best fit
642             found. The best possible fit is negative infinity.
643              
644             B may be called repeatedly to continue the fitting process. The fit
645             processing on each subsequent call will continue from where the last call left
646             off.
647              
648             =item B
649              
650             Returns the vector of position
651              
652             =item B
653              
654             Takes an optional count.
655              
656             Returns a list containing the best $n particle numbers. If $n is not specified
657             only the best particle number is returned.
658              
659             =item B
660              
661             Returns a list containing the best value of the fit and the vector of its point
662             in hyper space.
663              
664             my ($fit, @vector) = $pso->getParticleBestPos (3)
665              
666             =item B
667              
668             Return the number of iterations performed. This may be useful when the
669             I<-exitFit> criteria has been met or where multiple calls to I have
670             been made.
671              
672             =back
673              
674             =head1 BUGS
675              
676             Please report any bugs or feature requests to C
677             at rt.cpan.org>, or through the web interface at
678             L.
679             I will be notified, and then you'll automatically be notified of progress on
680             your bug as I make changes.
681              
682             =head1 SUPPORT
683              
684             This module is supported by the author through CPAN. The following links may be
685             of assistance:
686              
687             =over 4
688              
689             =item * AnnoCPAN: Annotated CPAN documentation
690              
691             L
692              
693             =item * CPAN Ratings
694              
695             L
696              
697             =item * RT: CPAN's request tracker
698              
699             L
700              
701             =item * Search CPAN
702              
703             L
704              
705             =back
706              
707             =head1 SEE ALSO
708              
709             http://en.wikipedia.org/wiki/Particle_swarm_optimization
710              
711             =head1 ACKNOWLEDGEMENTS
712              
713             This module is an evolution of the AI::PSO module created by Kyle Schlansker.
714              
715             Plateau management code added in version 1.004 contributed by Kevin Balbi.
716              
717             =head1 AUTHOR
718              
719             Peter Jaquiery
720             CPAN ID: GRANDPA
721             grandpa@cpan.org
722              
723             =head1 COPYRIGHT AND LICENSE
724              
725             This program is free software; you can redistribute it and/or modify it under
726             the same terms as Perl itself.
727              
728             The full text of the license can be found in the LICENSE file included with this
729             module.
730              
731             =cut