feat: add support for dynamically varying algorithm paramters

This commit is contained in:
dvdrw 2024-11-23 18:41:02 +01:00
parent 3d2b41d7df
commit f5197bfc85
Signed by: dvdrw
GPG Key ID: 3ED4E5A371C20DD7
2 changed files with 65 additions and 20 deletions

View File

@ -12,7 +12,7 @@ struct Agent {
using F = std::function<float(T)>; using F = std::function<float(T)>;
virtual void move(float dt) = 0; virtual void move(float dt) = 0;
virtual void step(float dt) = 0; virtual void step() = 0;
virtual std::pair<float, T> best() const = 0; virtual std::pair<float, T> best() const = 0;
@ -21,44 +21,87 @@ protected:
F f; F f;
}; };
template<std::size_t N> template <typename A>
concept ParameterChangeAlgorithm = requires(A alg, float f, unsigned int i) {
{ alg(f, f, f, i, i) } -> std::same_as<vec<3>>;
};
namespace ParamChange {
struct Const {
vec<3> operator()(
float visc, float nostal, float peerp, int _1, int _2) {
return {visc, nostal, peerp};
}
};
struct MLinAlg {
vec<3> operator()(
float visc, float nostal, float peerp, int curr, int max) {
float pct_done = (float)curr / (float)max;
return {
visc - (0.9f - 0.4f) * pct_done,
nostal - (2.5f - 0.5f) * pct_done,
peerp + (2.5f - 0.5f) * pct_done,
};
}
};
};
template<std::size_t N, ParameterChangeAlgorithm A = ParamChange::MLinAlg>
struct Particle : public Agent<vec<N>> { struct Particle : public Agent<vec<N>> {
std::pair<float, vec<N>> best() const override { return {pb, pb_pos}; }; std::pair<float, vec<N>> best() const override { return {pb, pb_pos}; };
void move(float dt=1) override { position = position + velocity * dt; } void move(float dt=1) override { position = position + velocity * dt; }
void step(float dt=1) override { void step() override {
velocity = kviscosity * velocity velocity = viscosity * velocity
+ knostalgia * (pb_pos - position) + nostalgia * (pb_pos - position)
+ kpeer_pressure * (peer.best().second - position); + peer_pressure * (peer.best().second - position);
float y = f(position); float y = f(position);
if(y < pb) { if(y < pb) {
pb = y; pb = y;
pb_pos = position; pb_pos = position;
} }
vec<3> params = alg(kviscosity, knostalgia, kpeer_pressure,
curr_iter, max_iter);
viscosity = params.x;
nostalgia = params.y;
peer_pressure = params.z;
curr_iter++;
}; };
float kviscosity = 0.9f; const float kviscosity = 0.9f;
float knostalgia = 2.5f; const float knostalgia = 2.5f;
float kpeer_pressure = 0.5f; const float kpeer_pressure = 0.5f;
float viscosity = kviscosity;
float nostalgia = knostalgia;
float peer_pressure = kpeer_pressure;
Particle(Agent<vec<N>>::F f, const Agent<vec<N>>& peer, Particle(Agent<vec<N>>::F f, const Agent<vec<N>>& peer,
vec<N> position, vec<N> velocity) vec<N> position, vec<N> velocity, unsigned max_iter)
: Agent<vec<N>>(f), position(position), velocity(velocity), : Agent<vec<N>>(f), position(position), velocity(velocity),
pb_pos(position), peer(peer) { pb_pos(position), max_iter(max_iter), peer(peer) {
pb = f(position); pb = f(position);
} }
const vec<N> &get_position() const { return position; }; const vec<N> &get_position() const { return position; };
private: private:
A alg;
vec<N> position; vec<N> position;
vec<N> velocity; vec<N> velocity;
vec<N> pb_pos; vec<N> pb_pos;
float pb; float pb;
unsigned curr_iter = 0;
unsigned max_iter;
const Agent<vec<N>>& peer; const Agent<vec<N>>& peer;
}; };
@ -71,7 +114,7 @@ struct Swarm : public Agent<vec<N>> {
p.move(dt); p.move(dt);
} }
void step(float dt=1) override { void step() override {
if(particles.empty()) return; if(particles.empty()) return;
// find best before step() on each particle // find best before step() on each particle
@ -85,26 +128,28 @@ struct Swarm : public Agent<vec<N>> {
best_pos = b->best().second; best_pos = b->best().second;
for(auto &p : particles) for(auto &p : particles)
p.step(dt); p.step();
} }
void add_particle(std::size_t n=1) { void add_particle(std::size_t n=1) {
for(std::size_t i=0; i <= n; ++i) for(std::size_t i=0; i <= n; ++i)
particles.push_back( particles.push_back(
Particle<N>(this->_f, *this, vec<N>(0), vec<N>(10)) Particle<N>(this->_f, *this, vec<N>(0), vec<N>(10), max_iter)
); );
} }
void add_particle(const vec<N> &pos, const vec<N> &vel) { void add_particle(const vec<N> &pos, const vec<N> &vel) {
particles.push_back( particles.push_back(
Particle<N>(this->f, *this, pos, vel) Particle<N>(this->f, *this, pos, vel, max_iter)
); );
} }
const std::vector<Particle<N>>& get_particles() { return particles; }; const std::vector<Particle<N>>& get_particles() { return particles; };
Swarm(Agent<vec<N>>::F f) : Agent<vec<N>>(f) {} Swarm(Agent<vec<N>>::F f, unsigned max_iter = 150)
: Agent<vec<N>>(f), max_iter(max_iter) {}
private: private:
unsigned max_iter;
vec<N> best_pos; vec<N> best_pos;
float best_val; float best_val;
std::vector<Particle<N>> particles; std::vector<Particle<N>> particles;

View File

@ -8,8 +8,8 @@ static constexpr int kFPS = 60;
static constexpr float kDT = 1.0 / kFPS; static constexpr float kDT = 1.0 / kFPS;
float f(vec<2> x) { float f(vec<2> x) {
return 50 * (std::pow(std::sin((x.x - 10)/2),2) + std::pow(std::sin(x.y/2),2)) return 50 * (std::pow(std::sin((x.x - 10)/2),2) + std::pow(std::sin(x.y/2),2)) +
+ std::pow(std::abs(x.x - 10), 1.2) + std::pow(std::abs(x.y), 1.2); std::pow(std::abs(x.x - 3.141592653589), 1.2) + std::pow(std::abs(x.y), 1.2);
} }
int int
@ -18,7 +18,7 @@ main(int argc, char **argv) {
if(argc < 2) iter_count = 100; if(argc < 2) iter_count = 100;
else sscanf(argv[1], "%d", &iter_count); else sscanf(argv[1], "%d", &iter_count);
Swarm<2> swarm(f); Swarm<2> swarm(f, iter_count);
Screen scr(80, 24); Screen scr(80, 24);
TanhColorizer tanh_col; TanhColorizer tanh_col;
@ -148,7 +148,7 @@ main(int argc, char **argv) {
case 'p': case 'p':
auto [y, x] = swarm.best(); auto [y, x] = swarm.best();
printf("Current best: f(%.3f, %.3f) = %.3f", x.x, x.y, y); printf("Current best: f(%.10f, %.10f) = %.10f", x.x, x.y, y);
break; break;
} }