-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHopfieldNetworks.c
167 lines (155 loc) · 6.02 KB
/
HopfieldNetworks.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include "utils.h"
#define THRESHOLD 0.0
typedef struct {
int neurons;
float **weights;
} HopfieldNetwork;
HopfieldNetwork createHopfieldNetwork(int neurons) {
HopfieldNetwork network;
network.neurons = neurons;
network.weights = (float **)malloc(neurons * sizeof(float *));
for (int i = 0; i < neurons; i++) {
network.weights[i] = (float *)malloc(neurons * sizeof(float));
for (int j = 0; j < neurons; j++) {
network.weights[i][j] = 0.0;
}
}
return network;
}
void freeHopfieldNetwork(HopfieldNetwork network) {
for (int i = 0; i < network.neurons; i++) {
free(network.weights[i]);
}
free(network.weights);
}
float computeEnergy(HopfieldNetwork *network, int *state) {
float energy = 0.0;
int features = network->neurons;
for (int i = 0; i < features; i++) {
for (int j = i + 1; j < features; j++) { // Avoid double counting
energy += network->weights[i][j] * state[i] * state[j];
}
}
return -0.5 * energy;
}
void trainHopfieldNetwork(HopfieldNetwork *network, Dataset trainData) {
int features = network->neurons;
for (int inst = 0; inst < trainData.instances; inst++) {
int *state = trainData.input[inst];
// Print the state at iteration inst
#ifdef DEBUG
printf("State at iteration %d: ", inst);
for (int i = 0; i < features; i++) {
printf("%d ", state[i]);
}
printf("\n");
#endif
for (int i = 0; i < features; i++) {
for (int j = i + 1; j < features; j++) {
network->weights[i][j] += (2 * state[i] - 1) * (2 * state[j] - 1);
network->weights[j][i] = network->weights[i][j]; // Ensure symmetry
}
// Set diagonal to zero
network->weights[i][i] = 0.0;
// Normalize the weights
for (int j = 0; j < features; j++) {
network->weights[i][j] /= features;
}
}
// Print the energy of the network after each training instance
#ifdef DEBUG
float energy = computeEnergy(network, state);
printf("Energy after training instance %d: %.2f\n", inst, energy);
#endif
}
}
void updateState(HopfieldNetwork *network, int *state) {
int features = network->neurons;
for (int i = 0; i < features; i++) {
float activation = 0.0;
for (int j = 0; j < features; j++) {
activation += network->weights[i][j] * state[j];
}
state[i] = activation > THRESHOLD ? 1 : 0;
}
}
void evaluateHopfieldNetwork(HopfieldNetwork *network, Dataset testData, Dataset trainData, int sync_iterations) {
int features = network->neurons;
int *state = malloc(features * sizeof(int));
int correct_predictions = 0;
float energy;
for (int test_inst = 0; test_inst < testData.instances; test_inst++) {
// Syncronous update of the network (i.e, update all neurons at the same time until convergence)
memcpy(state, testData.input[test_inst], features * sizeof(int));
energy = computeEnergy(network, state);
for (int iter = 0; iter < sync_iterations; iter++) {
updateState(network, state);
float new_energy = computeEnergy(network, state);
if (new_energy == energy) {
break;
}
energy = new_energy;
}
/*
Make prediction and compare with ground-truth:
- If the stable state matches a specific stored pattern, assign the corresponding binary label (either 1 or 0).
- If no exact match is found, use a similarity threshold (e.g., Hamming distance) to classify the pattern based on the closest stored pattern.
*/
int match_found = 0;
int prediction = -1;
int min_distance = features;
for (int inst = 0; inst < trainData.instances; inst++) {
int distance = 0;
for (int i = 0; i < features; i++) {
distance += state[i] != trainData.input[inst][i];
}
if (distance < min_distance) {
min_distance = distance;
prediction = trainData.output[inst];
}
if (distance == 0) {
match_found = 1;
break;
}
}
#ifdef DEBUG
// Print if the prediction was made by exact match or proximity by Hamming distance
if (match_found) {
printf(">> Test instance %d - Prediction made by exact match\n", test_inst);
} else {
printf(">> Test instance %d - Prediction made by proximity\n", test_inst);
}
// Print the network prediction and the ground truth
printf("Test instance %d - Ground Truth: %d, Prediction: %d\n", test_inst, testData.output[test_inst], prediction);
#else
// Suppress unused variable warning (only for DEBUG mode)
(void)match_found;
#endif
if (testData.output[test_inst] == prediction) {
correct_predictions++;
}
}
float accuracy = ((float)correct_predictions / testData.instances) * 100.0;
printf("Accuracy on test set: %.2f%%\n", accuracy);
free(state);
}
int main(int argc, char const *argv[]) {
(void)argc, (void)argv;
Dataset trainData = readDataset("data/train.txt", TRAIN);
Dataset testData = readDataset("data/test.txt", TEST);
assert(trainData.features == testData.features && "Number of features in the training and testing datasets should be the same");
int features = trainData.features;
HopfieldNetwork network = createHopfieldNetwork(features);
trainHopfieldNetwork(&network, trainData);
int sync_iterations = 100;
evaluateHopfieldNetwork(&network, testData, trainData, sync_iterations);
freeHopfieldNetwork(network);
freeDataset(trainData);
freeDataset(testData);
return 0;
}