-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
364 lines (294 loc) · 11.4 KB
/
main.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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <sys/types.h>
#include <unistd.h>
///* Include the configuration header file
#include "./includes/configs.h"
#define scale(x) ((x)*PPM_SCALE_FACTOR)
#define unscale(x) ((x) / PPM_SCALE_FACTOR)
#define MIN(a, b) ((a) < (b) ? (a) : (b))
typedef float Layer[HEIGHT][WIDTH];
///* Generate random value between range min and max
int random_int_range(int min, int max) {
assert(min < max);
return min + rand() % (max - min);
}
// Save the layer to a ppm file
void save_layer_ppm(Layer layer, const char *filename) {
#if SAVE_TO_SAMPLES_FOLDER
FILE *fp = fopen(filename, "wb");
if (fp == NULL)
{
///* %m will print the error message from strerror() supplied by errno
///* Such as "No such file or directory"
///* %m is only supported by GCC (GNU Compiler Collection)
///? fprintf(stderr, "Error opening file %s: %m\n", filename);
///* Print the error message from strerror() supplied by errno
fprintf(stderr, "Error opening file %s: %s\n", filename, strerror(errno));
exit(1);
}
///* Header of the ppm file
fprintf(fp, "P6\n%d %d 255\n", scale(WIDTH), scale(HEIGHT));
///* Write the layer to the file
for (int y = 0; y < scale(HEIGHT); ++y)
{
for (int x = 0; x < scale(WIDTH); ++x)
{
float scaler = (layer[unscale(y)][unscale(x)] - PPM_RANGE) / (2.0f * PPM_RANGE);
char red = (char)random_int_range(0, 80);
char green = (char)floorf(PPM_MAX_COLOR * (1.0f - scaler));
char blue = (char)floorf(scaler * PPM_MAX_COLOR);
char pixel[3] = {red, green, blue};
///* Write the pixel to the file
fwrite(pixel, sizeof(pixel), 1, fp);
}
}
///* Close the file
fclose(fp);
#endif
}
///* Save the layer to a binary file
void save_layer_bin(Layer layer, const char *filename) {
FILE *fp = fopen(filename, "wb");
if (fp == NULL) {
///* %m will print the error message from strerror() supplied by errno
///* Such as "No such file or directory"
///* %m is only supported by GCC (GNU Compiler Collection)
///? fprintf(stderr, "Error opening file %s: %m\n", filename);
///* Print the error message from strerror() supplied by errno
fprintf(stderr, "Error opening file %s: %s\n", filename, strerror(errno));
exit(1);
}
///* Write the layer to the file
fwrite(layer, sizeof(Layer), 1, fp);
///* Close the file
fclose(fp);
}
///* Load the layer from a binary file
void load_layer_bin(Layer layer, const char *filename) {
FILE *fp = fopen(filename, "rb");
if (fp == NULL) {
///* %m will print the error message from strerror() supplied by errno
///* Such as "No such file or directory"
///* %m is only supported by GCC (GNU Compiler Collection)
///? fprintf(stderr, "Error opening file %s: %m\n", filename);
///* Print the error message from strerror() supplied by errno
fprintf(stderr, "Error opening file %s: %s\n", filename, strerror(errno));
exit(1);
}
///* Read the layer from the file
fread(layer, sizeof(Layer), 1, fp);
///* Close the file
fclose(fp);
}
///* Clamp a value between a min and max value
static inline int clamp(int value, int min, int max) {
if (value < min)
return min;
if (value > max)
return max;
return value;
}
///* Fill random rectangular area with a value
/// @param layer, Layer to fill
/// @param x, X coordinate of the top-left corner
/// @param y, Y coordinate of the top-left corner
/// @param width, Width of the area to fill
/// @param height, Height of the area to fill
/// @param value, Value to fill the area with
void fill_rectangle(Layer layer, int x, int y, int width, int height, float value) {
assert(width > 0);
assert(height > 0);
///* The area to fill is limited to the layer's dimensions
int x0 = clamp(x, 0, WIDTH - 1);
int y0 = clamp(y, 0, HEIGHT - 1);
int x1 = clamp(x0 + width - 1, 0, WIDTH - 1);
int y1 = clamp(y0 + height - 1, 0, HEIGHT - 1);
///* Fill the area with the given value
for (int i = y0; i <= y1; ++i) {
for (int j = x0; j <= x1; ++j) {
layer[i][j] = value;
}
}
}
// fill circle
void fill_circle(Layer layer, int center_x, int center_y, int radius, float value) {
assert(radius > 0);
assert(center_x >= 0);
assert(center_x < WIDTH);
assert(center_y >= 0);
assert(center_y < HEIGHT);
///* The area to fill is limited to the layer's dimensions
int x0 = clamp(center_x - radius, 0, WIDTH - 1);
int y0 = clamp(center_y - radius, 0, HEIGHT - 1);
int x1 = clamp(center_x + radius, 0, WIDTH - 1);
int y1 = clamp(center_y + radius, 0, HEIGHT - 1);
///* Fill the area with the given value
for (int i = y0; i <= y1; ++i) {
for (int j = x0; j <= x1; ++j) {
///* Calculate the distance from the center of the circle to the pixel (x, y)
///* and compare it to the radius of the circle to determine if the pixel is
///* inside the circle or not
///* (if the distance is less than the radius, the pixel is inside the circle)
int distance = (int) floorl(sqrt(pow(i - center_y, 2) + pow(j - center_x, 2)));
if (distance <= radius) {
layer[i][j] = value;
}
}
}
}
///* Transfer function
float forward(Layer inputs, Layer weights) {
float output = 0.0f;
for (int y = 0; y < HEIGHT; ++y) {
for (int x = 0; x < HEIGHT; ++x) {
output += inputs[x][y] * weights[x][y];
}
}
return output;
}
/// @param inputs, Inputs to the network
/// @param weights, Weights of the network
void add_input_to_weight(Layer inputs, Layer weights) {
for (int y = 0; y < HEIGHT; ++y) {
for (int x = 0; x < WIDTH; ++x) {
weights[y][x] += inputs[y][x];
}
}
}
/// @param inputs, Inputs to the network
/// @param weights, Weights of the network
void subtract_input_from_weight(Layer inputs, Layer weights) {
for (int y = 0; y < HEIGHT; ++y) {
for (int x = 0; x < WIDTH; ++x) {
weights[y][x] -= inputs[y][x];
}
}
}
void random_rectangle_layer(Layer layer) {
///* Fill entire layer with 0
memset(layer, 0x0, sizeof(Layer));
int x = random_int_range(0, WIDTH - 1); ///* X coordinate of the top-left corner
int y = random_int_range(0, HEIGHT - 1); ///* Y coordinate of the top-left corner
int width = random_int_range(1, WIDTH - x); ///* Width of the rectangle
int height = random_int_range(1, HEIGHT - y); ///* Height of the rectangle
fill_rectangle(layer, x, y, width, height, 1.0f);
}
void random_circle_layer(Layer layer) {
///* Fill entire layer with 0
memset(layer, 0x0, sizeof(Layer));
int center_x = random_int_range(BORDER_OFFSET_L_T,
WIDTH - 1 - BORDER_OFFSET_R_B); ///* X coordinate of the center of the circle
int center_y = random_int_range(BORDER_OFFSET_L_T,
HEIGHT - 1 - BORDER_OFFSET_R_B); ///* Y coordinate of the center of the circle
///* Radius of the circle
///* Making sure that the circle is between the layer's dimensions
///? int min_allowed_radius = (min(BORDER_OFFSET_L_T, BORDER_OFFSET_R_B) / 2) - 1;
int min_allowed_radius = 1;
int max_allowed_radius = MIN(WIDTH, HEIGHT) / 2;
///* Minimum between center_x and center_y
max_allowed_radius = MIN(MIN(center_x, center_y), max_allowed_radius);
int radius = 1;
if (max_allowed_radius > 1) {
///* Minimum between difference between center_x and center_y and the layer's dimensions
max_allowed_radius = MIN(MIN(WIDTH - center_x, HEIGHT - center_y), max_allowed_radius);
///* Generate random radius between min and max radius
radius = (min_allowed_radius != max_allowed_radius) ? random_int_range(min_allowed_radius,
max_allowed_radius + 1)
: max_allowed_radius;
}
fill_circle(layer, center_x, center_y, radius, 1.0f);
}
int train_pass(Layer inputs, Layer weights) {
static char file_path[256];
static int count = 0;
int adjusted = 0;
for (int i = 0; i < NUMBER_OF_SAMPLES; ++i) {
random_rectangle_layer(inputs);
if (forward(inputs, weights) > BIAS) {
subtract_input_from_weight(inputs, weights);
snprintf(file_path, sizeof(file_path),
SAMPLES_FOLDER_NAME
"/ppm/"
WEIGHT_SAMPLE_NAME
"s/"
WEIGHT_SAMPLE_NAME
"-%03d.ppm", count++);
///! printf("[INFO] saving %s\n", file_path);
save_layer_ppm(weights, file_path);
adjusted++;
}
random_circle_layer(inputs);
if (forward(inputs, weights) < BIAS) {
add_input_to_weight(inputs, weights);
snprintf(file_path, sizeof(file_path),
SAMPLES_FOLDER_NAME
"/ppm/"
WEIGHT_SAMPLE_NAME
"s/"
WEIGHT_SAMPLE_NAME
"-%03d.ppm", count++);
///! printf("[INFO] saving %s\n", file_path);
save_layer_ppm(weights, file_path);
adjusted++;
}
}
return adjusted;
}
int check_pass(Layer inputs, Layer weights) {
int adjusted = 0;
for (int i = 0; i < NUMBER_OF_SAMPLES; ++i) {
random_rectangle_layer(inputs);
if (forward(inputs, weights) > BIAS) {
adjusted++;
}
random_circle_layer(inputs);
if (forward(inputs, weights) < BIAS) {
adjusted++;
}
}
return adjusted;
}
///* Initialized as 0 if not initialized explicitly. (ISO C Standart)
static Layer inputs;
///* Initially Frank Rosenblatt set all the weights to 0
static Layer weights;
int main(void) {
///* Create a folder for the sample
printf("[INFO] creating %s\n", SAMPLES_FOLDER_NAME);
if (mkdir(SAMPLES_FOLDER_NAME) < 0 && errno != EEXIST) {
fprintf(stderr, "ERROR: could not create folder %s: %s", SAMPLES_FOLDER_NAME, strerror(errno));
exit(1);
}
///* Changle the current working directory to the sample folder
chdir(SAMPLES_FOLDER_NAME);
printf("[INFO] creating %s\n", "ppm");
if (mkdir("ppm") < 0 && errno != EEXIST) {
fprintf(stderr, "ERROR: could not create folder %s: %s", SAMPLES_FOLDER_NAME, strerror(errno));
exit(1);
}
printf("[INFO] creating %s\n", "weights");
if (mkdir("ppm/weights") < 0 && errno != EEXIST) {
fprintf(stderr, "ERROR: could not create folder %s: %s", SAMPLES_FOLDER_NAME, strerror(errno));
exit(1);
}
chdir("..");
srand(CHECK_SEED_VALUE);
int adj = check_pass(inputs, weights);
printf("[INFO] fail rate of untrained model is %f\n", adj / (NUMBER_OF_SAMPLES * 2.0));
for (int i = 0; i < FORWARD_TRAIN_PASSES; ++i) {
srand(TRAIN_SEED_VALUE);
int adj = train_pass(inputs, weights);
printf("[INFO] Pass %d: adjusted %d times\n", i, adj);
if (adj <= 0)
break;
}
srand(CHECK_SEED_VALUE);
adj = check_pass(inputs, weights);
printf("[INFO] fail rate of trained model is %f\n", adj / (NUMBER_OF_SAMPLES * 2.0));
return 0;
}