From 4198cb75d7ff881cc55786d71c0dfd45033b5a8d Mon Sep 17 00:00:00 2001 From: Auxilus Date: Sat, 14 Jun 2025 02:10:38 -0500 Subject: [PATCH] Add STDP learning rule --- examples/stdp_example.c | 21 ++++++++ examples/test.sh | 4 +- src/brain.c | 106 +++++++++++++++++++++++++++------------- src/cbrain.h | 61 +++++++++++++---------- 4 files changed, 128 insertions(+), 64 deletions(-) create mode 100644 examples/stdp_example.c diff --git a/examples/stdp_example.c b/examples/stdp_example.c new file mode 100644 index 0000000..2fc231e --- /dev/null +++ b/examples/stdp_example.c @@ -0,0 +1,21 @@ +#include "../src/cbrain.h" +#include + +int main() +{ + srand((unsigned int)time(NULL)); + struct brain* b = brain_init(2, 0.1f); + neuron_set_type(b->neurons[0], sensory); + neuron_set_type(b->neurons[1], motor); + neuron_link(b->neurons[0], b->neurons[1], 5); + + for (int t = 0; t < 10; t++) { + if (t == 1) neuron_fire(b->neurons[0]); + if (t == 3) neuron_fire(b->neurons[1]); + neuron_update_range(0, 1, b); + printf("step %u weight=%d\n", b->step, b->neurons[0]->wts[0]); + usleep(100000); + } + return 0; +} + diff --git a/examples/test.sh b/examples/test.sh index eae3b65..2137ea2 100755 --- a/examples/test.sh +++ b/examples/test.sh @@ -5,6 +5,6 @@ make uninstall make clean make -j15 install cd examples -gcc tem_test.c -o tem_test -lSDL2 -lcbrain +gcc stdp_example.c -o stdp_example -lSDL2 -lcbrain echo -./tem_test +./stdp_example diff --git a/src/brain.c b/src/brain.c index 25b4381..46b8992 100644 --- a/src/brain.c +++ b/src/brain.c @@ -22,6 +22,8 @@ SOFTWARE. #include "cbrain.h" +pthread_mutex_t lock; + /* initialize new neuron */ struct neuron* neuron_init(uint id, float decay) { @@ -37,10 +39,11 @@ struct neuron* neuron_init(uint id, float decay) n->thisstate = 0.0; n->nextstate = 0.0; n->state_decay = decay; - n->fired = 0; - n->n_fired = 0; - n->n_type = undefined; - return n; + n->fired = 0; + n->n_fired = 0; + n->n_type = undefined; + n->last_fired = -STDP_WINDOW; + return n; } /* link neuron 'n' to neuron 'src' with given weight 'wt' */ @@ -111,10 +114,37 @@ void neuron_unlink(struct neuron* src, struct neuron* n) /* accumulate passed weight to neuron's nextstate */ void neuron_accum(struct neuron* n, uint wt) { - cbrain_print(2, "accumulating neuron %d with weight %d\n", n->id, wt); - pthread_mutex_lock(&lock); - n->nextstate += wt; - pthread_mutex_unlock(&lock); + cbrain_print(2, "accumulating neuron %d with weight %d\n", n->id, wt); + pthread_mutex_lock(&lock); + n->nextstate += wt; + pthread_mutex_unlock(&lock); +} + +void neuron_stdp(struct brain* b, struct neuron* n) +{ + /* potentiation for recently active presynaptic neurons */ + for (int i = 0; i < n->inc; i++) { + int pre_id = n->incoming[i]; + struct neuron* pre = b->neurons[pre_id]; + int dt = b->step - pre->last_fired; + if (dt > 0 && dt <= STDP_WINDOW) { + int idx = checkexist(n->id, pre->links, pre->lc); + if (idx != -1) { + pre->wts[idx] += STDP_INC; + if (pre->wts[idx] > WEIGHT_MAX) pre->wts[idx] = WEIGHT_MAX; + } + } + } + + /* depression for synapses where postsynaptic neuron fired recently */ + for (int i = 0; i < n->lc; i++) { + struct neuron* post = b->neurons[n->links[i]]; + int dt = b->step - post->last_fired; + if (dt > 0 && dt <= STDP_WINDOW) { + n->wts[i] -= STDP_DEC; + if (n->wts[i] < -WEIGHT_MAX) n->wts[i] = -WEIGHT_MAX; + } + } } /* check if neuron's thisstate exceeds the threshold, @@ -126,17 +156,19 @@ int neuron_update(struct neuron* n, struct brain* b) assert(n->id <= (b->nc - 1)); // fire neuron if thisstate exceeds THRESHOLD - if (n->thisstate >= THRESHOLD) { - n->f_type = self; - for (int i = 0; i < n->lc; i++) { - cbrain_print(4, "sending %d from %d to %d\n", n->wts[i], n->id, n->links[i]); - b->neurons[n->links[i]]->nextstate += n->wts[i]; - } - n->fired = 1; - n->thisstate = 0; - n->nextstate = 0; - n->n_fired += 1; - } else { + if (n->thisstate >= THRESHOLD) { + n->f_type = self; + for (int i = 0; i < n->lc; i++) { + cbrain_print(4, "sending %d from %d to %d\n", n->wts[i], n->id, n->links[i]); + b->neurons[n->links[i]]->nextstate += n->wts[i]; + } + n->fired = 1; + n->thisstate = 0; + n->nextstate = 0; + n->n_fired += 1; + neuron_stdp(b, n); + n->last_fired = b->step; + } else { n->thisstate += n->nextstate; // thisstate decay if next state is zero @@ -154,14 +186,15 @@ int neuron_update(struct neuron* n, struct brain* b) int neuron_update_range(uint s, uint e, struct brain* b) { - int nf = 0; - for (int i = s; i <= e; i++) { - int fired = neuron_update(b->neurons[i], b); - if (fired == 1) { - nf += 1; - } - } - return nf; + int nf = 0; + for (int i = s; i <= e; i++) { + int fired = neuron_update(b->neurons[i], b); + if (fired == 1) { + nf += 1; + } + } + b->step += 1; + return nf; } void neuron_set_type(struct neuron* n, type t) @@ -207,9 +240,10 @@ struct brain* brain_init(int s, float decay) struct brain* b = (struct brain*)malloc(sizeof(struct brain)); b->neurons = (struct neuron**)malloc(sizeof(struct neuron) * s); b->nc = s; - b->nmax = s; - b->fitness = 0.0; - b->state_decay = decay; + b->nmax = s; + b->fitness = 0.0; + b->state_decay = decay; + b->step = 0; // make neurons for (int i = 0; i < s; i++) { @@ -220,11 +254,13 @@ struct brain* brain_init(int s, float decay) void brain_reset(struct brain* b) { - for (int i = 0; i < b->nc; i++) { - b->neurons[i]->thisstate = 0; - b->neurons[i]->nextstate = 0; - b->neurons[i]->fired = 0; - } + for (int i = 0; i < b->nc; i++) { + b->neurons[i]->thisstate = 0; + b->neurons[i]->nextstate = 0; + b->neurons[i]->fired = 0; + b->neurons[i]->last_fired = -STDP_WINDOW; + } + b->step = 0; } void brain_neuron_type(struct brain* b, type t) diff --git a/src/cbrain.h b/src/cbrain.h index 9e73b0d..2128553 100644 --- a/src/cbrain.h +++ b/src/cbrain.h @@ -41,6 +41,10 @@ SOFTWARE. #define MUTATE_PROB 0.00001 #define WEIGHT_MIN 1 #define WEIGHT_MAX 20 +/* STDP parameters */ +#define STDP_WINDOW 5 +#define STDP_INC 1 +#define STDP_DEC 1 #define WIN_WIDTH 800 #define WIN_HEIGHT 400 #define E 2.71828182 @@ -51,9 +55,9 @@ typedef unsigned int uint; typedef enum { undefined, sensory, intermediate, motor } type; typedef enum { user, self } fire_type; -pthread_mutex_t lock; +extern pthread_mutex_t lock; -struct neuron { +typedef struct neuron { int id; int *links; int *incoming; @@ -68,53 +72,55 @@ struct neuron { float thisstate; float nextstate; float state_decay; - uint fired; - uint n_fired; + uint fired; + uint n_fired; + int last_fired; } neuron; -struct brain { +typedef struct brain { uint nc; uint nmax; float state_decay; - float fitness; - struct neuron** neurons; + float fitness; + struct neuron** neurons; + uint step; } brain; -struct nthread { +typedef struct nthread { pthread_t tid; uint s; uint e; uint status; } nthread; -struct thread_bank { +typedef struct thread_bank { uint tc; uint tmax; - struct nthread** threads; + struct nthread** threads; } thread_bank; -struct sdlctx { - SDL_Window* win; - SDL_Renderer* ren; - SDL_Event event; +typedef struct sdlctx { + SDL_Window* win; + SDL_Renderer* ren; + SDL_Event event; } sdlctx; -struct vect { - float x; - float y; +typedef struct vect { + float x; + float y; } vect; -struct entityctx { - int mlstart; - int mrstart; - int mlend; - int mrend; - int width; - int height; - float rot; - float x; - float y; +typedef struct entityctx { + int mlstart; + int mrstart; + int mlend; + int mrend; + int width; + int height; + float rot; + float x; + float y; } entityctx; /* src/brain.c */ @@ -129,6 +135,7 @@ void neuron_accum(struct neuron*, uint); void neuron_fire(struct neuron*); int neuron_update(struct neuron*, struct brain*); int neuron_update_range(uint, uint, struct brain*); +void neuron_stdp(struct brain*, struct neuron*); void neuron_set_type(struct neuron*, type); void neuron_add(struct brain*); void neuron_show_stat(struct neuron*);