175 lines
5.4 KiB
C++
175 lines
5.4 KiB
C++
#include "pso.h"
|
|
#include <cassert>
|
|
#include <Eigen/StdVector>
|
|
#include <vector>
|
|
#include <iostream>
|
|
|
|
template <
|
|
typename Scalar,
|
|
typename DerivedX,
|
|
typename DerivedLB,
|
|
typename DerivedUB>
|
|
IGL_INLINE Scalar igl::pso(
|
|
const std::function< Scalar (DerivedX &) > f,
|
|
const Eigen::MatrixBase<DerivedLB> & LB,
|
|
const Eigen::MatrixBase<DerivedUB> & UB,
|
|
const int max_iters,
|
|
const int population,
|
|
DerivedX & X)
|
|
{
|
|
const Eigen::Array<bool,Eigen::Dynamic,1> P =
|
|
Eigen::Array<bool,Eigen::Dynamic,1>::Zero(LB.size(),1);
|
|
return igl::pso(f,LB,UB,P,max_iters,population,X);
|
|
}
|
|
|
|
template <
|
|
typename Scalar,
|
|
typename DerivedX,
|
|
typename DerivedLB,
|
|
typename DerivedUB,
|
|
typename DerivedP>
|
|
IGL_INLINE Scalar igl::pso(
|
|
const std::function< Scalar (DerivedX &) > f,
|
|
const Eigen::MatrixBase<DerivedLB> & LB,
|
|
const Eigen::MatrixBase<DerivedUB> & UB,
|
|
const Eigen::DenseBase<DerivedP> & P,
|
|
const int max_iters,
|
|
const int population,
|
|
DerivedX & X)
|
|
{
|
|
const int dim = LB.size();
|
|
assert(UB.size() == dim && "UB should match LB size");
|
|
assert(P.size() == dim && "P should match LB size");
|
|
typedef std::vector<DerivedX,Eigen::aligned_allocator<DerivedX> > VectorList;
|
|
VectorList position(population);
|
|
VectorList best_position(population);
|
|
VectorList velocity(population);
|
|
Eigen::Matrix<Scalar,Eigen::Dynamic,1> best_f(population);
|
|
// https://en.wikipedia.org/wiki/Particle_swarm_optimization#Algorithm
|
|
//
|
|
// g → X
|
|
// p_i → best[i]
|
|
// v_i → velocity[i]
|
|
// x_i → position[i]
|
|
Scalar min_f = std::numeric_limits<Scalar>::max();
|
|
for(int p=0;p<population;p++)
|
|
{
|
|
{
|
|
const DerivedX R = DerivedX::Random(dim).array()*0.5+0.5;
|
|
position[p] = LB.array() + R.array()*(UB-LB).array();
|
|
}
|
|
best_f[p] = f(position[p]);
|
|
best_position[p] = position[p];
|
|
if(best_f[p] < min_f)
|
|
{
|
|
min_f = best_f[p];
|
|
X = best_position[p];
|
|
}
|
|
{
|
|
const DerivedX R = DerivedX::Random(dim);
|
|
velocity[p] = (UB-LB).array() * R.array();
|
|
}
|
|
}
|
|
|
|
int iter = 0;
|
|
Scalar omega = 0.98;
|
|
Scalar phi_p = 0.01;
|
|
Scalar phi_g = 0.01;
|
|
while(true)
|
|
{
|
|
//if(iter % 10 == 0)
|
|
//{
|
|
// std::cout<<iter<<":"<<std::endl;
|
|
// for(int p=0;p<population;p++)
|
|
// {
|
|
// std::cout<<" "<<best_f[p]<<", "<<best_position[p]<<std::endl;
|
|
// }
|
|
// std::cout<<std::endl;
|
|
//}
|
|
|
|
for(int p=0;p<population;p++)
|
|
{
|
|
const DerivedX R_p = DerivedX::Random(dim).array()*0.5+0.5;
|
|
const DerivedX R_g = DerivedX::Random(dim).array()*0.5+0.5;
|
|
velocity[p] =
|
|
omega * velocity[p].array() +
|
|
phi_p * R_p.array() *(best_position[p] - position[p]).array() +
|
|
phi_g * R_g.array() *( X - position[p]).array();
|
|
position[p] += velocity[p];
|
|
// Clamp to bounds
|
|
for(int d = 0;d<dim;d++)
|
|
{
|
|
//#define IGL_PSO_REFLECTION
|
|
#ifdef IGL_PSO_REFLECTION
|
|
assert(!P(d));
|
|
// Reflect velocities if exceeding bounds
|
|
if(position[p](d) < LB(d))
|
|
{
|
|
position[p](d) = LB(d);
|
|
if(velocity[p](d) < 0.0) velocity[p](d) *= -1.0;
|
|
}
|
|
if(position[p](d) > UB(d))
|
|
{
|
|
position[p](d) = UB(d);
|
|
if(velocity[p](d) > 0.0) velocity[p](d) *= -1.0;
|
|
}
|
|
#else
|
|
//#warning "trying no bounds on periodic"
|
|
// // TODO: I'm not sure this is the right thing to do/enough. The
|
|
// // velocities could be weird. Suppose the current "best" value is ε and
|
|
// // the value is -ε and the "periodic bounds" [0,2π]. Moding will send
|
|
// // the value to 2π-ε but the "velocity" term will now be huge pointing
|
|
// // all the way from 2π-ε to ε.
|
|
// //
|
|
// // Q: Would it be enough to try (all combinations) of ±(UB-LB) before
|
|
// // computing velocities to "best"s? In the example above, instead of
|
|
// //
|
|
// // v += best - p = ε - (2π-ε) = -2π+2ε
|
|
// //
|
|
// // you'd use
|
|
// //
|
|
// // v += / argmin |b - p| \ - p = (ε+2π)-(2π-ε) = 2ε
|
|
// // | |
|
|
// // \ b∈{best, best+2π, best-2π} /
|
|
// //
|
|
// // Though, for multivariate b,p,v this would seem to explode
|
|
// // combinatorially.
|
|
// //
|
|
// // Maybe periodic things just shouldn't be bounded and we hope that the
|
|
// // forces toward the current minima "regularize" them away from insane
|
|
// // values.
|
|
// if(P(d))
|
|
// {
|
|
// position[p](d) = std::fmod(position[p](d)-LB(d),UB(d)-LB(d))+LB(d);
|
|
// }else
|
|
// {
|
|
// position[p](d) = std::max(LB(d),std::min(UB(d),position[p](d)));
|
|
// }
|
|
position[p](d) = std::max(LB(d),std::min(UB(d),position[p](d)));
|
|
#endif
|
|
}
|
|
const Scalar fp = f(position[p]);
|
|
if(fp<best_f[p])
|
|
{
|
|
best_f[p] = fp;
|
|
best_position[p] = position[p];
|
|
if(best_f[p] < min_f)
|
|
{
|
|
min_f = best_f[p];
|
|
X = best_position[p];
|
|
}
|
|
}
|
|
}
|
|
iter++;
|
|
if(iter>=max_iters)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return min_f;
|
|
}
|
|
|
|
#ifdef IGL_STATIC_LIBRARY
|
|
template float igl::pso<float, Eigen::Matrix<float, 1, -1, 1, 1, -1>, Eigen::Matrix<float, 1, -1, 1, 1, -1>, Eigen::Matrix<float, 1, -1, 1, 1, -1> >(std::function<float (Eigen::Matrix<float, 1, -1, 1, 1, -1>&)>, Eigen::MatrixBase<Eigen::Matrix<float, 1, -1, 1, 1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<float, 1, -1, 1, 1, -1> > const&, int, int, Eigen::Matrix<float, 1, -1, 1, 1, -1>&);
|
|
#endif
|