add in xorshift rand function. begin reworking job queue to use cell entry pointers instead of just cells so results can be posted directly to the relevant entry
This commit is contained in:
@@ -30,12 +30,15 @@ template <class T> struct Strategy {
|
||||
bool enable_mutation; // Cells may be mutated
|
||||
// before fitness evaluation
|
||||
float mutation_chance; // Chance to mutate cells before fitness evaluation
|
||||
uint64_t rand_seed;
|
||||
bool higher_fitness_is_better; // Sets whether or not to consider higher
|
||||
// fitness values better or worse. Set this to
|
||||
// false if fitness is an error function.
|
||||
|
||||
// User defined functions
|
||||
T (*make_default_cell)();
|
||||
void (*mutate)(T &cell_to_modify);
|
||||
void (*crossover)(const ReadonlySpan<T> &parents,
|
||||
const Span<T> &out_children);
|
||||
void (*crossover)(const Span<T *> parents, const Span<T *> out_children);
|
||||
float (*fitness)(const T &cell);
|
||||
};
|
||||
|
||||
@@ -44,13 +47,6 @@ template <class T> struct Stats {
|
||||
std::vector<float> average_fitness;
|
||||
};
|
||||
|
||||
template <class T> struct ReadonlySpan {
|
||||
T *_data;
|
||||
int len;
|
||||
|
||||
const T &operator[](int i);
|
||||
};
|
||||
|
||||
template <class T> struct Span {
|
||||
T *_data;
|
||||
int len;
|
||||
|
||||
18
inc/rand.h
Normal file
18
inc/rand.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// TODO: This file needs a serious audit
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
constexpr uint64_t half_max = UINT64_MAX / 2;
|
||||
|
||||
// From https://en.wikipedia.org/wiki/Xorshift
|
||||
inline void xorshift64(uint64_t &state) {
|
||||
state ^= state << 13;
|
||||
state ^= state >> 7;
|
||||
state ^= state << 17;
|
||||
}
|
||||
|
||||
// returns a random value between -1 and 1. modifies seed
|
||||
inline float norm_rand(uint64_t &state) {
|
||||
xorshift64(state);
|
||||
return (state - half_max) / half_max;
|
||||
}
|
||||
@@ -21,17 +21,17 @@ namespace genetic {
|
||||
|
||||
template <class T> struct CellEntry {
|
||||
float score;
|
||||
T cell;
|
||||
T *cell;
|
||||
bool stale;
|
||||
};
|
||||
|
||||
template <class T> struct CrossoverJob {
|
||||
const ReadonlySpan<T> &parents;
|
||||
const Span<T> &children_out;
|
||||
Span<CellEntry<T> *> &parents;
|
||||
Span<CellEntry<T> *> &children_out;
|
||||
};
|
||||
|
||||
template <class T> struct FitnessJob {
|
||||
const T &cell;
|
||||
float &result_out;
|
||||
CellEntry<T> *cell_entry;
|
||||
};
|
||||
|
||||
template <class T> struct WorkQueue {
|
||||
@@ -100,6 +100,8 @@ template <class T> struct WorkerThreadArgs {
|
||||
bool *stop_flag;
|
||||
};
|
||||
|
||||
template <class T> void do_crossover_job(CrossoverJob<T> cj) {}
|
||||
|
||||
template <class T> void *worker(void *args) {
|
||||
WorkerThreadArgs<T> *work_args = (WorkerThreadArgs<T> *)args;
|
||||
Strategy<T> &strat = work_args->strat;
|
||||
@@ -107,7 +109,10 @@ template <class T> void *worker(void *args) {
|
||||
bool *stop_flag = work_args->stop_flag;
|
||||
|
||||
auto JobDispatcher = overload{
|
||||
[strat](FitnessJob<T> fj) { fj.result_out = strat.fitness(fj.cell); },
|
||||
[strat](FitnessJob<T> fj) {
|
||||
fj.cell_entry->result_out = strat.fitness(*(fj.cell_entry->cell));
|
||||
fj.cell_entry->stale = true;
|
||||
},
|
||||
[strat](CrossoverJob<T> cj) {
|
||||
strat.crossover(cj.parents, cj.children_out);
|
||||
},
|
||||
@@ -131,21 +136,20 @@ template <class T> void *worker(void *args) {
|
||||
|
||||
template <class T> Stats<T> run(Strategy<T> strat) {
|
||||
Stats<T> stats;
|
||||
WorkQueue<T> queue = make_work_queue<T>(strat.num_cells);
|
||||
WorkQueue<T> work_queue = make_work_queue<T>(strat.num_cells);
|
||||
|
||||
vector<CellEntry<T>> cells_a, cells_b;
|
||||
T cells[strat.num_cells];
|
||||
|
||||
// Using a vector so I can use the make_heap, push_heap, etc.
|
||||
vector<CellEntry<T>> cell_queue;
|
||||
for (int i = 0; i < strat.num_cells; i++) {
|
||||
T cell = strat.make_default_cell();
|
||||
cells_a.push_back({0, cell, true});
|
||||
cells_b.push_back({0, cell, true});
|
||||
cells[i] = strat.make_default_cell();
|
||||
cell_queue.push_back({0, &cells[i], true});
|
||||
}
|
||||
|
||||
std::vector<CellEntry<T>> &cur_cells = cells_a;
|
||||
std::vector<CellEntry<T>> &next_cells = cells_b;
|
||||
|
||||
bool stop_flag = false;
|
||||
WorkerThreadArgs<T> args = {
|
||||
.strat = strat, .queue = queue, .stop_flag = &stop_flag};
|
||||
.strat = strat, .queue = work_queue, .stop_flag = &stop_flag};
|
||||
|
||||
// spawn worker threads
|
||||
pthread_t threads[strat.num_threads];
|
||||
@@ -155,20 +159,27 @@ template <class T> Stats<T> run(Strategy<T> strat) {
|
||||
|
||||
for (int i = 0; i < strat.num_generations; i++) {
|
||||
// generate fitness jobs
|
||||
if (strat.test_all) {
|
||||
|
||||
} else {
|
||||
}
|
||||
|
||||
// wait for fitness jobs to complete
|
||||
// sort cells on performance
|
||||
// generate crossover jobs
|
||||
cur_cells = cur_cells == cells_a ? cells_b : cells_a;
|
||||
next_cells = cur_cells == cells_a ? cells_b : cells_a;
|
||||
}
|
||||
|
||||
// stop worker threads
|
||||
stop_flag = true;
|
||||
pthread_cond_broadcast(queue.jobs_available_cond);
|
||||
pthread_cond_broadcast(work_queue.jobs_available_cond);
|
||||
for (int i = 0; i < strat.num_threads; i++) {
|
||||
pthread_join(threads[i], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> T &Span<T>::operator[](int i) {
|
||||
assert(i >= 0 && i < len);
|
||||
return _data[i];
|
||||
}
|
||||
|
||||
} // namespace genetic
|
||||
|
||||
Reference in New Issue
Block a user