Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ParticleSwarmMinimizer.cc
Go to the documentation of this file.
1 // -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
2 // vi: set ts=2 noet:
3 //
4 // (c) Copyright Rosetta Commons Member Institutions.
5 // (c) This file is part of the Rosetta software suite and is made available under license.
6 // (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
7 // (c) For more information, see http://www.rosettacommons.org. Questions about this can be
8 // (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
9 
10 /// @file core/optimization/ParticleSwarmMinimizer.cc
11 ///
12 /// @brief
13 /// @author Ian W. Davis
14 
15 
17 
18 #include <utility/exit.hh>
19 #include <numeric/random/random.hh>
20 
21 #include <ObjexxFCL/format.hh>
22 
23 #include <algorithm>
24 
25 #include <utility/vector1.hh>
26 
27 
28 static numeric::random::RandomGenerator my_RG(6172008); // <- Magic number, do not change it!!!
29 
30 namespace core {
31 namespace optimization {
32 
33 using namespace ObjexxFCL::fmt;
34 
35 /// @brief stream output operator for Particle types
36 std::ostream &
37 operator<< ( std::ostream & os, Particle const & p ) {
38  os << " best fitness: " << ObjexxFCL::fmt::F( 9,5,-1.0 * p.fitness_pbest() )
39  << ", current fitness: " << ObjexxFCL::fmt::F( 9,6,-1.0 * p.fitness_ ) << ", current dofs: [";
40  for ( core::Size i=1; i <= p.p_.size(); ++i ) { os << ObjexxFCL::fmt::F( 8,4,p.p_[i] ) << ", "; }
41  os << " ]";
42  return os;
43 }
44 
45 
46 // Used to sort particles from best to worst
48 {
49  return a->fitness_pbest() > b->fitness_pbest();
50 }
51 
52 
54  utility::pointer::ReferenceCount(),
55  size_(p_min.size()),
56  C_inertia_start_(0.9),
57  C_inertia_end_(0.4),
58  C_pbest_(2.0),
59  C_lbest_(2.0),
60  C_gbest_(0.0),
61  first_nbr_(-2),
62  last_nbr_(2),
63  p_min_(p_min),
64  p_max_(p_max),
65  p_range_(),
66  v_max_()
67 {
68  runtime_assert(p_min_.size() == p_max_.size());
69  p_range_.resize(size_, 0.0);
70  v_max_.resize(size_, 0.0);
71  for(Size i = 1; i <= size_; ++i) {
72  runtime_assert( p_min_[i] < p_max_[i] );
73  p_range_[i] = p_max_[i] - p_min_[i];
74  v_max_[i] = 0.1 * p_range_[i];
75  }
76 }
77 
78 
80 
81 
82 ParticleOPs ParticleSwarmMinimizer::run(Size num_cycles, Multifunc & f_fitness, Size num_part /*= 50*/)
83 {
84  ParticleOPs particles;
85  for(Size i = 1; i <= num_part; ++i) {
86  ParticleOP p = new Particle(size_);
87  for(Size j = 1; j <= size_; ++j) {
88  p->p_[j] = p_min_[j] + my_RG.uniform()*p_range_[j];
89  }
90  // debugging output
91  /*std::cout << "PSM: created new particle: dofs: [ ";
92  for ( core::Size k=1; k <= size_; ++k ) {
93  std::cout << F(8,4,p->p_[k]) << ", ";
94  }
95  std::cout << " ]" << std::endl;*/
96  particles.push_back(p);
97  }
98  run(num_cycles, f_fitness, particles);
99  return particles;
100 }
101 
102 
103 ParticleOPs ParticleSwarmMinimizer::run(Size num_cycles, Multifunc & f_fitness, Size num_part, Multivec init_values )
104 {
105  ParticleOPs particles;
106  for(Size i = 1; i <= num_part; ++i) {
107  ParticleOP p = new Particle(size_);
108  for(Size j = 1; j <= size_; ++j) {
109  p->p_[j] = init_values[j] + my_RG.uniform() - my_RG.uniform(); // want to go up *and* down by a little bit
110  //p->p_[j] = init_values[j] + ( my_RG.uniform() - my_RG.uniform() ) / p_range_[j]; // want to go up *and* down by a tiny bit
111  // init values should never be outside min/max range
112  if ( p->p_[j] < p_min_[j] ) { p->p_[j] = my_RG.uniform(); }
113  else if ( p->p_[j] > p_max_[j] ) { p->p_[j] = p_max_[j]; }
114  }
115  // debugging output
116  /*std::cout << "PSM: created custom init particle: dofs: [ ";
117  for ( core::Size k=1; k <= size_; ++k ) {
118  std::cout << F(8,4,p->p_[k]) << ", ";
119  }
120  std::cout << " ]" << std::endl;*/
121  particles.push_back(p);
122  }
123  run(num_cycles, f_fitness, particles);
124  return particles;
125 }
126 
127 
128 void ParticleSwarmMinimizer::run(Size num_cycles, Multifunc & f_fitness, ParticleOPs & particles)
129 {
130  Size const N = particles.size();
131  //runtime_assert( int(N) >= last_nbr_ - first_nbr_ + 1 );
132  // Ensure particle vector sizes are consistent and long enough
133  for(Size i = 1; i <= N; ++i) {
134  particles[i]->ensure_size( size_ );
135  }
136  for(Size cycle = 1; cycle <= num_cycles; ++cycle) {
137  // linear ramp on inertial weight
138  Real const frac_done = Real(cycle) / Real(num_cycles > 1 ? num_cycles-1 : 1);
139  Real const C_inertia = (1.0-frac_done)*C_inertia_start_ + frac_done*C_inertia_end_;
140  // score everyone and update p(ersonal)best
141  score_all_particles(f_fitness,particles);
142  //
143  // TODO: apply local optimization to best particle in the swarm
144  //
145  // update p(ersonal)best again?
146  //for(Size i = 1; i <= N; ++i) {
147  // ParticleOP const & p = particles[i];
148  // if( p->pbest_.size() == 0 || p->fitness_pbest_ < p->fitness_ ) {
149  // p->pbest_ = p->p_; // make a copy
150  // p->fitness_pbest_ = p->fitness_;
151  // }
152  //}
153  // Determine l(ocal)best and g(lobal)best
154  ParticleOPs lbests;
155  for(Size i = 1; i <= N; ++i) {
156  ParticleOP const & p = particles[i];
157  lbests.push_back(p); // start by assuming each particle is best among its neighbors
158  for(int jj = int(i)+first_nbr_; jj <= int(i)+last_nbr_; ++jj) {
159  // wrap index around:
160  int j = jj;
161  if( j < 1 ) j += N;
162  else if ( j > int(N) ) j -= N;
163  if ( lbests[i]->fitness_ < particles[j]->fitness_ )
164  lbests[i] = particles[j];
165  }
166  }
167  ParticleOP gbest = particles[1];
168  for(Size i = 1; i <= N; ++i) {
169  if( gbest->fitness_ < particles[i]->fitness_ ) {
170  // debugging output
171  /*std::cout << "PSM: New global best: fitness: " << -1 * particles[i]->fitness_ << ", dofs: [ ";
172  for ( core::Size k=1; k <= size_; ++k ) {
173  std::cout << F(8,4,particles[i]->p_[k]) << ", ";
174  }
175  std::cout << " ]" << std::endl;*/
176  gbest = particles[i];
177  }
178  }
179 
180  // update velocity
181  for(Size j = 1; j <= N; ++j) {
182  ParticleOP const & p = particles[j];
183  ParticleOP const & lbest = lbests[j];
184  for(Size i = 1; i <= size_; ++i) {
185  Real const pi = p->p_[i];
186 
187  Real vi = ( C_inertia*p->v_[i]
188  + my_RG.uniform()*C_pbest_*(p->pbest()[i] - pi)
189  + my_RG.uniform()*C_lbest_*(lbest->p_[i] - pi)
190  + my_RG.uniform()*C_gbest_*(gbest->p_[i] - pi) );
191 
192  // sometimes particles react too quickly, or move too fast to their local/global best and end up
193  // getting stuck at 0.0.
194  // slow down how fast the particles move, but not by imposing a speed limit but instead by
195  // throttling them when they decide on their new speed.
196  /* Real vi = ( C_inertia*p->v_[i]
197  + my_RG.uniform()*C_pbest_*(p->pbest()[i] - pi)
198  + my_RG.uniform()*C_lbest_*(lbest->p_[i] - pi)
199  + my_RG.uniform()*C_gbest_*(gbest->p_[i] - pi) ) * ( ( p_range_[i] ) / num_cycles ); */
200 
201  Real const vmax = v_max_[i];
202  if( vi > vmax )
203  {
204  //std::cout << "PSM: particle " << I(2,j) << " DOF " << i << " velocity " << vi << " reset to " << vmax << ". v_: " << p->v_[i]
205  // << ", p_: " << p->p_[i] << ", p_best_: " << p->pbest()[i] << ", lbest: " << lbest->p_[i] << std::endl;
206  vi = vmax;
207  }
208  else if( vi < -vmax )
209  {
210  //std::cout << "PSM: particle " << I(2,j) << " DOF " << i << " velocity " << vi << " reset to " << -vmax << ". v_: " << p->v_[i]
211  // << ", p_: " << p->p_[i] << ", p_best_: " << p->pbest()[i] << ", lbest: " << lbest->p_[i] << std::endl;
212  vi = -vmax;
213  }
214  p->v_[i] = vi;
215  }
216  }
217  // update positions
218  for(Size j = 1; j <= N; ++j) {
219  ParticleOP const & p = particles[j];
220  for(Size i = 1; i <= size_; ++i) {
221  Real ppi = p->p_[i] + p->v_[i];
222  if( ppi < p_min_[i] )
223  {
224  //std::cout << "PSM: particle " << I(2,j) << " DOF " << i << " reset to minimum. p_: " << p->p_[i]
225  // << ", v_: " << p->v_[i] << ", p_best_: " << p->pbest()[i] << ", lbest: " << lbests[j]->p_[i] << ", ppi: " << ppi << std::endl;
226  ppi = p_min_[i];
227  }
228  else if( ppi > p_max_[i] )
229  {
230  //std::cout << "PSM: particle " << I(2,j) << " DOF " << i << " reset to maximum. p_: " << p->p_[i]
231  // << ", v_: " << p->v_[i] << ", p_best_: " << p->pbest()[i] << ", lbest: " << lbests[j]->p_[i] << ", ppi: " << ppi << std::endl;
232  ppi = p_max_[i];
233  }
234  p->p_[i] = ppi;
235  }
236  }
237  }
238 
239  // score everyone and update p(ersonal)best one last time
240  score_all_particles(f_fitness,particles);
241  // sort by fitness, fitest ones first
242  std::sort(particles.begin(), particles.end(), cmp_particles);
243 }
244 
245 
247  Size const N = particles.size();
248  for(Size i = 1; i <= N; ++i) {
249  particles[i]->score(f_fitness);
250  }
251 }
252 
253 
254 /// @brief helper function for displaying current particle information; calls the output operator on each particle
256  for (core::Size i=1; i <= ps.size(); ++i) {
257  ParticleOP p = ps[i];
258  std::cout << header << ", particle: " << I(2,i);
259  std::cout << *p << std::endl;
260  }
261 }
262 
263 
264 
265 } // namespace optimization
266 } // namespace core