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:
2025-08-16 00:41:28 -05:00
parent edda3761d1
commit 3265f045d1
3 changed files with 52 additions and 27 deletions

View File

@@ -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
View 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;
}

View File

@@ -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