{"id":928,"date":"2026-03-25T21:57:16","date_gmt":"2026-03-25T10:57:16","guid":{"rendered":"https:\/\/securelang.net\/cms\/?p=928"},"modified":"2026-03-25T21:57:16","modified_gmt":"2026-03-25T10:57:16","slug":"code-from-previous-post","status":"publish","type":"post","link":"https:\/\/securelang.net\/cms\/blog\/2026\/03\/25\/code-from-previous-post\/","title":{"rendered":"Code From Previous Post"},"content":{"rendered":"\n<p>NOTE: The number of epochs in the call to nn_train needs to be increased to show reliable results.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/*\n * Simple Neural Network Library in C\n * Single-file implementation for easy compilation\n * \n * Compile: gcc -Wall -Wextra -std=c99 -O2 -o nn nn.c -lm\n * Run: .\/nn\n *\/\n\n#include &lt;stdio.h>\n#include &lt;stdlib.h>\n#include &lt;string.h>\n#include &lt;math.h>\n#include &lt;time.h>\n\n\/* ==================== Type Definitions ==================== *\/\n\n\/* Function pointer types for activation functions *\/\ntypedef double (*activation_fn)(double x);\ntypedef double (*activation_deriv_fn)(double x);\n\n\/* Layer structure - represents a fully connected layer *\/\ntypedef struct {\n    int input_size;                    \/* Number of inputs to this layer *\/\n    int output_size;                   \/* Number of neurons in this layer *\/\n    double *weights;                   \/* Weight matrix &#91;output_size]&#91;input_size] *\/\n    double *biases;                    \/* Bias vector &#91;output_size] *\/\n    double *outputs;                   \/* Activated outputs &#91;output_size] *\/\n    double *pre_activation;            \/* Pre-activation values &#91;output_size] *\/\n    double *deltas;                    \/* Error gradients &#91;output_size] *\/\n    double *weight_grads;              \/* Weight gradients &#91;output_size]&#91;input_size] *\/\n    double *bias_grads;                \/* Bias gradients &#91;output_size] *\/\n    activation_fn activate;            \/* Activation function *\/\n    activation_deriv_fn activate_deriv;\/* Activation derivative *\/\n} nn_layer_t;\n\n\/* Network structure - contains all layers *\/\ntypedef struct {\n    int num_layers;                    \/* Number of layers (excluding input) *\/\n    nn_layer_t **layers;               \/* Array of layer pointers *\/\n    double learning_rate;              \/* Learning rate for SGD *\/\n    double **layer_inputs;             \/* Store inputs for each layer *\/\n} nn_network_t;\n\n\/* ==================== Activation Functions ==================== *\/\n\n\/* Sigmoid activation: 1 \/ (1 + e^-x) *\/\nstatic double nn_sigmoid(double x) {\n    return 1.0 \/ (1.0 + exp(-x));\n}\n\n\/* Sigmoid derivative: sigmoid(x) * (1 - sigmoid(x)) *\/\nstatic double nn_sigmoid_deriv(double x) {\n    double s = nn_sigmoid(x);\n    return s * (1.0 - s);\n}\n\n\/* ReLU activation: max(0, x) *\/\nstatic double nn_relu(double x) {\n    return x > 0.0 ? x : 0.0;\n}\n\n\/* ReLU derivative: 1 if x > 0, else 0 *\/\nstatic double nn_relu_deriv(double x) {\n    return x > 0.0 ? 1.0 : 0.0;\n}\n\n\/* Tanh activation *\/\nstatic double nn_tanh(double x) {\n    return tanh(x);\n}\n\n\/* Tanh derivative: 1 - tanh(x)^2 *\/\nstatic double nn_tanh_deriv(double x) {\n    double t = tanh(x);\n    return 1.0 - t * t;\n}\n\n\/* Linear activation (identity) *\/\nstatic double nn_linear(double x) {\n    return x;\n}\n\n\/* Linear derivative (always 1) *\/\nstatic double nn_linear_deriv(double x) {\n    (void)x;  \/* Unused parameter *\/\n    return 1.0;\n}\n\n\/* ==================== Layer Functions ==================== *\/\n\n\/* Create a new layer with specified dimensions *\/\nstatic nn_layer_t* nn_create_layer(int input_size, int output_size,\n                                   activation_fn activate,\n                                   activation_deriv_fn activate_deriv) {\n    nn_layer_t *layer = (nn_layer_t*)malloc(sizeof(nn_layer_t));\n    if (!layer) {\n        fprintf(stderr, \"Error: Failed to allocate layer\\n\");\n        exit(EXIT_FAILURE);\n    }\n    \n    layer->input_size = input_size;\n    layer->output_size = output_size;\n    \n    \/* Allocate weight matrix: output_size rows, input_size columns *\/\n    layer->weights = (double*)malloc(output_size * input_size * sizeof(double));\n    layer->biases = (double*)malloc(output_size * sizeof(double));\n    layer->outputs = (double*)malloc(output_size * sizeof(double));\n    layer->pre_activation = (double*)malloc(output_size * sizeof(double));\n    layer->deltas = (double*)malloc(output_size * sizeof(double));\n    layer->weight_grads = (double*)malloc(output_size * input_size * sizeof(double));\n    layer->bias_grads = (double*)malloc(output_size * sizeof(double));\n    layer->activate = activate;\n    layer->activate_deriv = activate_deriv;\n    \n    \/* Check all allocations succeeded *\/\n    if (!layer->weights || !layer->biases || !layer->outputs || \n        !layer->pre_activation || !layer->deltas || \n        !layer->weight_grads || !layer->bias_grads) {\n        fprintf(stderr, \"Error: Failed to allocate layer arrays\\n\");\n        exit(EXIT_FAILURE);\n    }\n    \n    return layer;\n}\n\n\/* Free a layer and all its memory *\/\nstatic void nn_free_layer(nn_layer_t *layer) {\n    if (!layer) return;\n    free(layer->weights);\n    free(layer->biases);\n    free(layer->outputs);\n    free(layer->pre_activation);\n    free(layer->deltas);\n    free(layer->weight_grads);\n    free(layer->bias_grads);\n    free(layer);\n}\n\n\/* Initialize layer weights with small random values *\/\nstatic void nn_init_layer_weights(nn_layer_t *layer, unsigned int seed) {\n    srand(seed);\n    \n    \/* Xavier initialization scaled for sigmoid *\/\n    double scale = sqrt(6.0 \/ (layer->input_size + layer->output_size));\n    \n    for (int i = 0; i &lt; layer->output_size * layer->input_size; i++) {\n        layer->weights&#91;i] = ((double)rand() \/ RAND_MAX - 0.5) * 2.0 * scale;\n        layer->weight_grads&#91;i] = 0.0;\n    }\n    \n    for (int i = 0; i &lt; layer->output_size; i++) {\n        layer->biases&#91;i] = 0.0;\n        layer->bias_grads&#91;i] = 0.0;\n    }\n}\n\n\/* ==================== Network Functions ==================== *\/\n\n\/* Create a neural network with specified layer sizes *\/\nnn_network_t* nn_create_network(const int *layer_sizes, int num_layers, \n                                double learning_rate) {\n    if (num_layers &lt; 2) {\n        fprintf(stderr, \"Error: Network needs at least 2 layers (input + output)\\n\");\n        return NULL;\n    }\n    \n    nn_network_t *net = (nn_network_t*)malloc(sizeof(nn_network_t));\n    if (!net) {\n        fprintf(stderr, \"Error: Failed to allocate network\\n\");\n        return NULL;\n    }\n    \n    \/* num_layers - 1 because first layer is input layer (no computation) *\/\n    net->num_layers = num_layers - 1;\n    net->learning_rate = learning_rate;\n    \n    net->layers = (nn_layer_t**)malloc(net->num_layers * sizeof(nn_layer_t*));\n    net->layer_inputs = (double**)malloc(net->num_layers * sizeof(double*));\n    \n    if (!net->layers || !net->layer_inputs) {\n        fprintf(stderr, \"Error: Failed to allocate network arrays\\n\");\n        free(net);\n        return NULL;\n    }\n    \n    \/* Create each layer *\/\n    for (int i = 0; i &lt; net->num_layers; i++) {\n        net->layers&#91;i] = nn_create_layer(layer_sizes&#91;i], layer_sizes&#91;i + 1],\n                                         nn_sigmoid, nn_sigmoid_deriv);\n        net->layer_inputs&#91;i] = (double*)malloc(layer_sizes&#91;i] * sizeof(double));\n        \n        if (!net->layer_inputs&#91;i]) {\n            fprintf(stderr, \"Error: Failed to allocate layer inputs\\n\");\n            exit(EXIT_FAILURE);\n        }\n    }\n    \n    return net;\n}\n\n\/* Free network and all its layers *\/\nvoid nn_free_network(nn_network_t *net) {\n    if (!net) return;\n    \n    for (int i = 0; i &lt; net->num_layers; i++) {\n        nn_free_layer(net->layers&#91;i]);\n        free(net->layer_inputs&#91;i]);\n    }\n    \n    free(net->layers);\n    free(net->layer_inputs);\n    free(net);\n}\n\n\/* Initialize all network weights *\/\nvoid nn_init_weights(nn_network_t *net, unsigned int seed) {\n    for (int i = 0; i &lt; net->num_layers; i++) {\n        nn_init_layer_weights(net->layers&#91;i], seed + i);\n    }\n}\n\n\/* Set activation function for all layers *\/\nvoid nn_set_activation(nn_network_t *net, activation_fn activate, \n                       activation_deriv_fn activate_deriv) {\n    for (int i = 0; i &lt; net->num_layers; i++) {\n        net->layers&#91;i]->activate = activate;\n        net->layers&#91;i]->activate_deriv = activate_deriv;\n    }\n}\n\n\/* Set activation function for specific layer *\/\nvoid nn_set_layer_activation(nn_network_t *net, int layer_idx,\n                             activation_fn activate,\n                             activation_deriv_fn activate_deriv) {\n    if (layer_idx &lt; 0 || layer_idx >= net->num_layers) {\n        fprintf(stderr, \"Error: Invalid layer index %d\\n\", layer_idx);\n        return;\n    }\n    net->layers&#91;layer_idx]->activate = activate;\n    net->layers&#91;layer_idx]->activate_deriv = activate_deriv;\n}\n\n\/* ==================== Forward Pass ==================== *\/\n\n\/* Perform forward propagation through the network *\/\ndouble* nn_forward(nn_network_t *net, const double *input) {\n    double *current_input = (double*)input;\n    \n    for (int i = 0; i &lt; net->num_layers; i++) {\n        nn_layer_t *layer = net->layers&#91;i];\n        \n        \/* Store input for backpropagation *\/\n        memcpy(net->layer_inputs&#91;i], current_input, \n               layer->input_size * sizeof(double));\n        \n        \/* Compute weighted sum + bias for each neuron *\/\n        for (int j = 0; j &lt; layer->output_size; j++) {\n            double sum = layer->biases&#91;j];\n            \n            for (int k = 0; k &lt; layer->input_size; k++) {\n                sum += layer->weights&#91;j * layer->input_size + k] * current_input&#91;k];\n            }\n            \n            layer->pre_activation&#91;j] = sum;\n            layer->outputs&#91;j] = layer->activate(sum);\n        }\n        \n        \/* Output becomes input for next layer *\/\n        current_input = layer->outputs;\n    }\n    \n    return current_input;\n}\n\n\/* ==================== Backward Pass ==================== *\/\n\n\/* Perform backpropagation to compute gradients *\/\nvoid nn_backward(nn_network_t *net, const double *target) {\n    nn_layer_t *last_layer = net->layers&#91;net->num_layers - 1];\n    \n    \/* Output layer: compute error directly from target *\/\n    for (int i = 0; i &lt; last_layer->output_size; i++) {\n        double error = last_layer->outputs&#91;i] - target&#91;i];\n        last_layer->deltas&#91;i] = error * last_layer->activate_deriv(last_layer->pre_activation&#91;i]);\n    }\n    \n    \/* Hidden layers: propagate error backwards *\/\n    for (int i = net->num_layers - 2; i >= 0; i--) {\n        nn_layer_t *layer = net->layers&#91;i];\n        nn_layer_t *next_layer = net->layers&#91;i + 1];\n        \n        for (int j = 0; j &lt; layer->output_size; j++) {\n            double error = 0.0;\n            \n            \/* Sum weighted deltas from next layer *\/\n            for (int k = 0; k &lt; next_layer->output_size; k++) {\n                error += next_layer->deltas&#91;k] * next_layer->weights&#91;k * next_layer->input_size + j];\n            }\n            \n            layer->deltas&#91;j] = error * layer->activate_deriv(layer->pre_activation&#91;j]);\n        }\n    }\n    \n    \/* Accumulate gradients for all layers *\/\n    for (int i = 0; i &lt; net->num_layers; i++) {\n        nn_layer_t *layer = net->layers&#91;i];\n        double *inputs = net->layer_inputs&#91;i];\n        \n        for (int j = 0; j &lt; layer->output_size; j++) {\n            layer->bias_grads&#91;j] += layer->deltas&#91;j];\n            \n            for (int k = 0; k &lt; layer->input_size; k++) {\n                layer->weight_grads&#91;j * layer->input_size + k] += \n                    layer->deltas&#91;j] * inputs&#91;k];\n            }\n        }\n    }\n}\n\n\/* Update weights using accumulated gradients *\/\nstatic void nn_update_weights(nn_network_t *net, int batch_size) {\n    double lr = net->learning_rate \/ batch_size;\n    \n    for (int i = 0; i &lt; net->num_layers; i++) {\n        nn_layer_t *layer = net->layers&#91;i];\n        \n        for (int j = 0; j &lt; layer->output_size * layer->input_size; j++) {\n            layer->weights&#91;j] -= lr * layer->weight_grads&#91;j];\n            layer->weight_grads&#91;j] = 0.0;\n        }\n        \n        for (int j = 0; j &lt; layer->output_size; j++) {\n            layer->biases&#91;j] -= lr * layer->bias_grads&#91;j];\n            layer->bias_grads&#91;j] = 0.0;\n        }\n    }\n}\n\n\/* ==================== Training ==================== *\/\n\n\/* Compute Mean Squared Error loss *\/\ndouble nn_mse_loss(const double *predicted, const double *target, int size) {\n    double sum = 0.0;\n    for (int i = 0; i &lt; size; i++) {\n        double diff = predicted&#91;i] - target&#91;i];\n        sum += diff * diff;\n    }\n    return sum \/ size;\n}\n\n\/* Train the network on a dataset *\/\nvoid nn_train(nn_network_t *net, double **inputs, double **targets,\n              int num_samples, int epochs, int verbose) {\n    int output_size = net->layers&#91;net->num_layers - 1]->output_size;\n    \n    for (int epoch = 0; epoch &lt; epochs; epoch++) {\n        double total_loss = 0.0;\n        \n        \/* Forward and backward pass for each sample *\/\n        for (int s = 0; s &lt; num_samples; s++) {\n            nn_forward(net, inputs&#91;s]);\n            nn_backward(net, targets&#91;s]);\n            \n            double *output = net->layers&#91;net->num_layers - 1]->outputs;\n            total_loss += nn_mse_loss(output, targets&#91;s], output_size);\n        }\n        \n        \/* Update weights after processing all samples *\/\n        nn_update_weights(net, num_samples);\n        \n        \/* Print progress *\/\n        if (verbose &amp;&amp; (epoch + 1) % 100 == 0) {\n            printf(\"Epoch %5d, Loss: %.6f\\n\", epoch + 1, total_loss \/ num_samples);\n        }\n    }\n}\n\n\/* Get prediction for a single input *\/\ndouble* nn_predict(nn_network_t *net, const double *input) {\n    return nn_forward(net, input);\n}\n\n\/* ==================== Utility Functions ==================== *\/\n\n\/* Print network architecture *\/\nvoid nn_print_architecture(nn_network_t *net) {\n    printf(\"Network Architecture:\\n\");\n    printf(\"  Layers: %d\\n\", net->num_layers + 1);\n    printf(\"  Learning Rate: %.4f\\n\", net->learning_rate);\n    printf(\"  Structure: \");\n    \n    for (int i = 0; i &lt; net->num_layers; i++) {\n        printf(\"%d\", net->layers&#91;i]->input_size);\n        if (i &lt; net->num_layers - 1) {\n            printf(\" -> \");\n        }\n    }\n    printf(\" -> %d\\n\", net->layers&#91;net->num_layers - 1]->output_size);\n}\n\n\/* Print network outputs for debugging *\/\nvoid nn_print_outputs(nn_network_t *net) {\n    printf(\"Layer Outputs:\\n\");\n    for (int i = 0; i &lt; net->num_layers; i++) {\n        nn_layer_t *layer = net->layers&#91;i];\n        printf(\"  Layer %d: &#91;\", i);\n        for (int j = 0; j &lt; layer->output_size; j++) {\n            printf(\"%.4f\", layer->outputs&#91;j]);\n            if (j &lt; layer->output_size - 1) printf(\", \");\n        }\n        printf(\"]\\n\");\n    }\n}\n\n\/* ==================== Example: XOR Problem ==================== *\/\n\nint main(void) {\n    printf(\"=== Simple Neural Network Library Demo ===\\n\\n\");\n    \n    \/* Define network architecture: 2 inputs, 4 hidden, 1 output *\/\n    int layer_sizes&#91;] = {2, 4, 1};\n    int num_layers = sizeof(layer_sizes) \/ sizeof(layer_sizes&#91;0]);\n    \n    \/* Create network with learning rate 0.1 *\/\n    nn_network_t *net = nn_create_network(layer_sizes, num_layers, 0.1);\n    \n    if (!net) {\n        fprintf(stderr, \"Failed to create network\\n\");\n        return EXIT_FAILURE;\n    }\n    \n    nn_print_architecture(net);\n    printf(\"\\n\");\n    \n    \/* Initialize weights with seed for reproducibility *\/\n    nn_init_weights(net, 42);\n    \n    \/* XOR training data *\/\n    double inputs&#91;4]&#91;2] = {\n        {0.0, 0.0},\n        {0.0, 1.0},\n        {1.0, 0.0},\n        {1.0, 1.0}\n    };\n    \n    double targets&#91;4]&#91;1] = {\n        {0.0},\n        {1.0},\n        {1.0},\n        {0.0}\n    };\n    \n    \/* Create arrays of pointers for training function *\/\n    double *input_ptrs&#91;4];\n    double *target_ptrs&#91;4];\n    \n    for (int i = 0; i &lt; 4; i++) {\n        input_ptrs&#91;i] = inputs&#91;i];\n        target_ptrs&#91;i] = targets&#91;i];\n    }\n    \n    \/* Train the network *\/\n    printf(\"Training on XOR problem (1000 epochs)...\\n\\n\");\n    nn_train(net, input_ptrs, target_ptrs, 4, 1000, 1);\n    \n    \/* Test the trained network *\/\n    printf(\"\\n=== Test Results ===\\n\");\n    printf(\"Input          -> Output (Target)\\n\");\n    printf(\"-----------------------------------\\n\");\n    \n    for (int i = 0; i &lt; 4; i++) {\n        double *output = nn_predict(net, inputs&#91;i]);\n        printf(\"&#91;%.1f, %.1f] -> %.4f (%.1f)\\n\", \n               inputs&#91;i]&#91;0], inputs&#91;i]&#91;1], output&#91;0], targets&#91;i]&#91;0]);\n    }\n    \n    \/* Calculate final accuracy *\/\n    printf(\"\\n=== Final Metrics ===\\n\");\n    double final_loss = 0.0;\n    int correct = 0;\n    \n    for (int i = 0; i &lt; 4; i++) {\n        double *output = nn_predict(net, inputs&#91;i]);\n        final_loss += nn_mse_loss(output, targets&#91;i], 1);\n        \n        \/* Check if prediction matches target (threshold 0.5) *\/\n        double predicted_class = output&#91;0] > 0.5 ? 1.0 : 0.0;\n        if (predicted_class == targets&#91;i]&#91;0]) {\n            correct++;\n        }\n    }\n    \n    printf(\"Final MSE Loss: %.6f\\n\", final_loss \/ 4.0);\n    printf(\"Accuracy: %d\/4 (%.1f%%)\\n\", correct, (correct \/ 4.0) * 100.0);\n    \n    \/* Clean up *\/\n    nn_free_network(net);\n    \n    printf(\"\\n=== Demo Complete ===\\n\");\n    return EXIT_SUCCESS;\n}\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>NOTE: The number of epochs in the call to nn_train needs to be increased to show reliable results.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"pagelayer_contact_templates":[],"_pagelayer_content":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-928","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/securelang.net\/cms\/wp-json\/wp\/v2\/posts\/928","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/securelang.net\/cms\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/securelang.net\/cms\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/securelang.net\/cms\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/securelang.net\/cms\/wp-json\/wp\/v2\/comments?post=928"}],"version-history":[{"count":1,"href":"https:\/\/securelang.net\/cms\/wp-json\/wp\/v2\/posts\/928\/revisions"}],"predecessor-version":[{"id":929,"href":"https:\/\/securelang.net\/cms\/wp-json\/wp\/v2\/posts\/928\/revisions\/929"}],"wp:attachment":[{"href":"https:\/\/securelang.net\/cms\/wp-json\/wp\/v2\/media?parent=928"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/securelang.net\/cms\/wp-json\/wp\/v2\/categories?post=928"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/securelang.net\/cms\/wp-json\/wp\/v2\/tags?post=928"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}