diff --git a/include/network/INetwork.hpp b/include/network/INetwork.hpp index 56f28d4..da305d6 100644 --- a/include/network/INetwork.hpp +++ b/include/network/INetwork.hpp @@ -1,8 +1,9 @@ #ifndef INETWORK #define INETWORK -#include "../src/model/optimizers.hpp" +#include "../../src/model/optimizers.hpp" #include "IvisualNetwork.hpp" +#include "tensor.hpp" namespace nn::model { class INetwork { diff --git a/src/model/ProgressBar.cpp b/src/model/ProgressBar.cpp index f55bc61..2f94bd7 100644 --- a/src/model/ProgressBar.cpp +++ b/src/model/ProgressBar.cpp @@ -1,3 +1,5 @@ + + #include "ProgressBar.hpp" #include #include @@ -5,25 +7,31 @@ namespace nn { void ProgressBar::printBar() { + // Print header on first call if (!headerPrinted) { std::cout << header << "\n"; headerPrinted = true; } + // Calculate current percentage (0-100) int percentage = (total > 0) ? (current * 100 / total) : 0; + + // Only update if percentage has changed to reduce console spam if (percentage == last_percentage) { return; } last_percentage = percentage; + // Calculate number of filled characters in the progress bar int filled = (BAR_WIDTH * percentage) / 100; + // Render the progress bar with carriage return for in-place updates std::cout << "\r["; for (int i = 0; i < BAR_WIDTH; ++i) { if (i < filled) { - std::cout << "="; + std::cout << "="; // Filled portion } else { - std::cout << " "; + std::cout << " "; // Empty portion } } std::cout << "] " << std::setw(3) << percentage << "%" << std::flush; @@ -31,31 +39,34 @@ void ProgressBar::printBar() { void ProgressBar::endPrint() { current = total; - last_percentage = -1; + last_percentage = -1; // Force update printBar(); - std::cout << std::endl; + std::cout << std::endl; // Move to next line } + ProgressBar &ProgressBar::operator++() { ++current; if (current > total) { - current = total; + current = total; // Clamp to maximum value } return *this; } + ProgressBar ProgressBar::operator++(int) { ProgressBar temp = *this; ++(*this); return temp; } + ProgressBar ProgressBar::operator=(int value) { ProgressBar temp = *this; current = value; if (current > total) { - current = total; + current = total; // Clamp to maximum value } return temp; } diff --git a/src/model/ProgressBar.hpp b/src/model/ProgressBar.hpp index c185c33..dbb521a 100644 --- a/src/model/ProgressBar.hpp +++ b/src/model/ProgressBar.hpp @@ -1,3 +1,12 @@ +/** + * @file ProgressBar.hpp + * @brief Implementation of console progress bar for neural network training + * + * This file implements a visual progress bar that displays training progress + * in the console. It provides real-time feedback to users about training + * completion status with percentage indicators and visual progress indication. + */ + #ifndef PROGRESSBAR #define PROGRESSBAR @@ -21,11 +30,55 @@ class ProgressBar { header(header_ + ": ") {} ~ProgressBar() = default; + /** + * @brief Prints the progress bar to the console + * + * Renders a visual progress bar showing current completion percentage. + * The bar is only updated when the percentage changes to avoid excessive + * console output. On first call, prints the header message. + * + * Header: + * [======== ] 75% + */ void printBar(); + + /** + * @brief Completes the progress bar and moves to next line + * + * Forces the progress bar to show 100% completion and prints a newline + * to move the cursor to the next line for subsequent output. + */ void endPrint(); + /** + * @brief Pre-increment operator for advancing progress + * + * Increments the current progress counter and ensures it doesn't + * exceed the total value. + * + * @return Reference to this ProgressBar for method chaining + */ ProgressBar &operator++(); + + /** + * @brief Post-increment operator for advancing progress + * + * Creates a copy of the current state, increments the progress, + * and returns the copy. + * + * @return Copy of the ProgressBar before incrementing + */ ProgressBar operator++(int); + + /** + * @brief Assignment operator for setting progress value + * + * Sets the current progress to a specific value, ensuring it doesn't + * exceed the total. Returns a copy of the previous state. + * + * @param value New progress value to set + * @return Copy of the ProgressBar before assignment + */ ProgressBar operator=(int value); }; } // namespace nn diff --git a/src/model/activations.cpp b/src/model/activations.cpp index 0bc619c..4670b4e 100644 --- a/src/model/activations.cpp +++ b/src/model/activations.cpp @@ -3,22 +3,6 @@ namespace nn::model { -/** - * @brief Applies the activation function to a tensor - * - * This method applies the configured activation function to the input tensor - * and stores the result in the output tensor. The operation is performed - * element-wise across the entire tensor. - * - * @param net Input tensor containing pre-activation values - * @param out Output tensor to store post-activation values - * - * @throws std::invalid_argument If input and output tensors have different sizes - * @throws std::runtime_error If the activation type is unknown - * - * @note The output tensor must have the same shape as the input tensor - * @note This method automatically chooses between CPU and GPU implementations - */ void Activation::activate(const global::Tensor &net, global::Tensor &out) const { if (net.numElements() != out.numElements()) { throw std::invalid_argument( @@ -55,22 +39,6 @@ void Activation::activate(const global::Tensor &net, global::Tensor &out) const } } -/** - * @brief Applies the derivative of the activation function to a tensor - * - * This method computes the derivative of the configured activation function - * and applies it to the input tensor. This is used during backpropagation - * to compute gradients for the previous layer. - * - * @param net Input tensor containing pre-activation values - * @param out Output tensor to store derivative values (modified in-place) - * - * @throws std::invalid_argument If input and output tensors have different sizes - * @throws std::runtime_error If the activation type is unknown - * - * @note The output tensor is modified in-place (multiplied by the derivative) - * @note This method automatically chooses between CPU and GPU implementations - */ void Activation::derivativeActivate(const nn::global::Tensor &net, nn::global::Tensor &out) const { if (net.numElements() != out.numElements()) { @@ -105,19 +73,6 @@ void Activation::derivativeActivate(const nn::global::Tensor &net, } } -/** - * @brief Finds the index of the maximum element in a tensor - * - * This utility function finds the index of the element with the maximum value - * in the given tensor. It's commonly used with softmax activation for - * classification tasks. - * - * @param metrix The input tensor to search - * @return The index of the element with the maximum value - * - * @note This function works with both CPU and GPU tensors - * @note For GPU tensors, it uses optimized CUDA kernels - */ size_t Activation::getMaxElementIndex(const global::Tensor &metrix) { if (metrix.isGpu) { return global::tensor_gpu::getMaxElementIndex(metrix.gpu_data, @@ -138,10 +93,10 @@ size_t Activation::getMaxElementIndex(const global::Tensor &metrix) { /** * @brief Gets the maximum value in a tensor - * + * * This utility function returns the maximum value in the given tensor. * It's used internally by softmax for numerical stability. - * + * * @param metrix The input tensor to search * @return The maximum value in the tensor */ @@ -233,11 +188,11 @@ global::ValueType Activation::derivativeTanh(const global::ValueType z) { /** * @brief Vectorized ReLU activation - * + * * Applies ReLU activation to all elements in the input tensor and stores * the result in the output tensor. Uses optimized implementations for * both CPU and GPU execution. - * + * * @param net Input tensor containing pre-activation values * @param out Output tensor to store post-activation values */ @@ -253,11 +208,11 @@ void Activation::relu(const global::Tensor &net, global::Tensor &out) { /** * @brief Vectorized ReLU derivative - * + * * Applies ReLU derivative to all elements in the input tensor and multiplies * the result with the output tensor (in-place operation). Used during * backpropagation for gradient computation. - * + * * @param net Input tensor containing pre-activation values * @param out Output tensor to be modified in-place with derivative values */ @@ -342,22 +297,6 @@ void Activation::derivativeTanh(const global::Tensor &net, } } -/** - * @brief Vectorized Softmax activation - * - * Applies softmax activation to the input tensor, which normalizes the values - * to create a probability distribution. The softmax function is: - * softmax(x_i) = exp(x_i - max(x)) / sum(exp(x_j - max(x))) - * - * This implementation includes numerical stability measures to prevent - * overflow by subtracting the maximum value before computing exponentials. - * - * @param net Input tensor containing pre-activation values - * @param out Output tensor to store normalized probabilities - * - * @note The output values sum to 1.0 (probability distribution) - * @note Uses numerical stability tricks to prevent overflow - */ void Activation::softmax(const global::Tensor &net, global::Tensor &out) { if (net.isGpu) { global::tensor_gpu::softmax(net.gpu_data, out.gpu_data, diff --git a/src/model/activations.hpp b/src/model/activations.hpp index 726a0b3..c0685dd 100644 --- a/src/model/activations.hpp +++ b/src/model/activations.hpp @@ -26,7 +26,7 @@ constexpr global::ValueType maxValue(const global::ValueType &a, const float &b) /** * @enum ActivationType * @brief Enumeration of available activation function types - * + * * This enum defines all the activation functions supported by the neural * network library. Each type corresponds to a specific mathematical function * used to introduce non-linearity into neural networks. @@ -43,12 +43,12 @@ enum class ActivationType { /** * @class Activation * @brief Manages activation functions for neural network layers - * + * * This class provides a unified interface for applying various activation * functions to tensors. It supports both forward propagation (activation) * and backward propagation (derivative) operations. All operations are * optimized for both CPU and GPU execution modes. - * + * * @section features Supported Activation Functions * - **ReLU**: Rectified Linear Unit for addressing vanishing gradient problem * - **Leaky ReLU**: Modified ReLU that allows small negative values @@ -56,20 +56,20 @@ enum class ActivationType { * - **Tanh**: Hyperbolic tangent for bounded outputs * - **Softmax**: Normalized exponential for multi-class classification * - **None**: Identity function (no activation) - * + * * @section usage Usage Example * ```cpp * Activation relu(ActivationType::Relu); * Tensor input({10}, 1.0f); * Tensor output({10}); - * + * * relu.activate(input, output); // Forward pass * relu.derivativeActivate(input, output); // Backward pass * ``` */ class Activation { private: - const ActivationType activationType; ///< The type of activation function + const ActivationType activationType; ///< The type of activation function // ======================================================================== // SCALAR ACTIVATION FUNCTIONS (CPU implementations) @@ -121,7 +121,22 @@ class Activation { /// Vectorized Tanh derivative static void derivativeTanh(const global::Tensor &net, global::Tensor &out); - /// Vectorized Softmax activation (normalized exponential) + /** + * @brief Vectorized Softmax activation + * + * Applies softmax activation to the input tensor, which normalizes the values + * to create a probability distribution. The softmax function is: + * softmax(x_i) = exp(x_i - max(x)) / sum(exp(x_j - max(x))) + * + * This implementation includes numerical stability measures to prevent + * overflow by subtracting the maximum value before computing exponentials. + * + * @param net Input tensor containing pre-activation values + * @param out Output tensor to store normalized probabilities + * + * @note The output values sum to 1.0 (probability distribution) + * @note Uses numerical stability tricks to prevent overflow + */ static void softmax(const global::Tensor &net, global::Tensor &out); /// Utility function to find maximum value in a tensor @@ -138,14 +153,14 @@ class Activation { */ Activation(const ActivationType activationType_) : activationType(activationType_) {} - + /** * @brief Copy constructor * @param other The activation function to copy */ Activation(const Activation &other) : activationType(other.activationType) {} - + /** * @brief Destructor (default) */ @@ -157,21 +172,37 @@ class Activation { /** * @brief Applies the activation function to a tensor - * @param net Input tensor (pre-activation values) - * @param out Output tensor (post-activation values) - * + * + * This method applies the configured activation function to the input tensor + * and stores the result in the output tensor. The operation is performed + * element-wise across the entire tensor. + * + * @param net Input tensor containing pre-activation values + * @param out Output tensor to store post-activation values + * + * @throws std::invalid_argument If input and output tensors have different sizes + * @throws std::runtime_error If the activation type is unknown + * * @note The output tensor must have the same shape as the input tensor - * @note This operation is optimized for both CPU and GPU execution + * @note This method automatically chooses between CPU and GPU implementations */ void activate(const global::Tensor &net, global::Tensor &out) const; - + /** * @brief Applies the derivative of the activation function to a tensor - * @param net Input tensor (pre-activation values) - * @param out Output tensor (derivative values) - * - * @note The output tensor must have the same shape as the input tensor - * @note This is used during backpropagation for gradient computation + * + * This method computes the derivative of the configured activation function + * and applies it to the input tensor. This is used during backpropagation + * to compute gradients for the previous layer. + * + * @param net Input tensor containing pre-activation values + * @param out Output tensor to store derivative values (modified in-place) + * + * @throws std::invalid_argument If input and output tensors have different sizes + * @throws std::runtime_error If the activation type is unknown + * + * @note The output tensor is modified in-place (multiplied by the derivative) + * @note This method automatically chooses between CPU and GPU implementations */ void derivativeActivate(const global::Tensor &net, global::Tensor &out) const; @@ -184,10 +215,16 @@ class Activation { /** * @brief Finds the index of the maximum element in a tensor + * + * This utility function finds the index of the element with the maximum value + * in the given tensor. It's commonly used with softmax activation for + * classification tasks. + * * @param metrix The input tensor to search * @return The index of the element with the maximum value - * - * @note This is commonly used with Softmax for classification + * + * @note This function works with both CPU and GPU tensors + * @note For GPU tensors, it uses optimized CUDA kernels */ static size_t getMaxElementIndex(const global::Tensor &metrix); }; diff --git a/src/model/config.cpp b/src/model/config.cpp index 046e23f..27b4da3 100644 --- a/src/model/config.cpp +++ b/src/model/config.cpp @@ -32,12 +32,15 @@ Config::Config(const std::string &config_filepath) { } void Config::initalizeJson(const nlohmann::json &j) { + // Parse training configuration (required) trainingConfig.fromJson(j.at("training config")); + // Parse visual configuration (optional) if (j.contains("visual config")) { visualConfig.fromJson(j.at("visual config")); } + // Parse network architecture configuration (required) networkConfig.fromJson(j.at("network config")); } @@ -54,7 +57,7 @@ void NetworkConfig::fromJson(const nlohmann::json &j) { std::make_shared(subNetworkConfig, prevS)); } - prevS = SubNetworksConfig[SubNetworksConfig.size()-1]->getOutputSize(); + prevS = SubNetworksConfig[SubNetworksConfig.size() - 1]->getOutputSize(); } } diff --git a/src/model/config.hpp b/src/model/config.hpp index 7f2cb8f..c7a9054 100644 --- a/src/model/config.hpp +++ b/src/model/config.hpp @@ -1,3 +1,13 @@ +/** + * @file config.hpp + * @brief Implementation of configuration management for neural network models + * + * This file implements the configuration system that reads and parses JSON + * configuration files to set up neural network parameters, training options, + * and visualization settings. It provides a centralized way to manage all + * model parameters and ensures proper validation of configuration data. + */ + #ifndef CONFIG #define CONFIG @@ -92,6 +102,17 @@ class NetworkConfig { size_t outputSize() const; std::vector> SubNetworksConfig; + + /** + * @brief Initializes network configuration from JSON data + * + * Parses the network architecture configuration section and creates + * appropriate sub-network configurations for FNN and CNN layers. + * Validates network connectivity and parameter consistency. + * + * @param j JSON object containing network configuration data + * @throws nlohmann::json::exception If network configuration is invalid + */ void fromJson(const nlohmann::json &j); }; @@ -163,9 +184,31 @@ class VisualConfig { class Config { private: + /** + * @brief Initializes configuration objects from parsed JSON data + * + * Takes a parsed JSON object and extracts configuration data for different + * components of the neural network system. Handles training configuration, + * visual configuration, and network architecture configuration. + * + * @param j Parsed JSON configuration object + * @throws nlohmann::json::exception If required configuration sections are missing + */ void initalizeJson(const nlohmann::json &j); public: + /** + * @brief Constructs a Config object by reading from a JSON configuration file + * + * Loads and parses a JSON configuration file that contains all the necessary + * parameters for setting up a neural network model, including network architecture, + * training parameters, and visualization options. + * + * @param config_filepath Path to the JSON configuration file + * @throws std::runtime_error If the configuration file cannot be opened + * @throws nlohmann::json::parse_error If the JSON is malformed + * @throws nlohmann::json::exception If required configuration fields are missing + */ Config(const std::string &config_filepath); ~Config() = default; diff --git a/src/model/model.cpp b/src/model/model.cpp index 31751f7..0317d1f 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -1,3 +1,14 @@ +/** + * @file model.cpp + * @brief Implementation of the Model class for neural network training and evaluation + * + * This file contains the core implementation of the Model class, which manages + * the complete lifecycle of neural network models including construction, + * training, evaluation, and visualization. It coordinates between different + * network types (CNN, FNN) and handles optimization, data management, and + * progress tracking. + */ + #include "../networks/cnn/CNNetwork.hpp" #include "../networks/fnn/FNNetwork.hpp" #include "ProgressBar.hpp" @@ -9,6 +20,17 @@ namespace nn::model { +/** + * @brief Constructs a Model from a configuration file + * + * Initializes a complete neural network model by reading configuration from + * a JSON file. This constructor sets up the optimizer, network architecture, + * and visualization components based on the provided configuration. + * + * @param config_filepath Path to the JSON configuration file + * @throws std::runtime_error If configuration file cannot be read or parsed + * @throws std::invalid_argument If configuration contains invalid parameters + */ Model::Model(const std::string &config_filepath) : config(config_filepath), visual(config), @@ -20,6 +42,15 @@ Model::Model(const std::string &config_filepath) } } +/** + * @brief Initializes the optimization algorithm based on configuration + * + * Creates and configures the appropriate optimizer (e.g., Constant, Adam, SGD) + * based on the optimizer type specified in the training configuration. + * The optimizer is responsible for updating network parameters during training. + * + * @throws std::runtime_error If unknown optimizer type is specified + */ void Model::initOptimizer() { const std::string &type = config.trainingConfig.getOptimizerType(); @@ -30,19 +61,39 @@ void Model::initOptimizer() { } } +/** + * @brief Initializes the visualization system for the model + * + * Sets up the visual components for real-time monitoring of training progress + * and network visualization. This includes graphs for loss tracking and + * visual representation of network layers if enabled in configuration. + * + * @note Only initializes if visualization is enabled in config + */ void Model::initVisual() { visual.start(); + // Connect network visual components if network visualization is enabled if (!config.visualConfig.enableNetwrokVisual) { return; } + // Link each sub-network's visual component to the main visualizer for (size_t i = 0; i < config.networkConfig.SubNetworksConfig.size(); ++i) { visual.addVisualSubNetwork(network[i]->getVisual()); network[i]->getVisual()->setVstate(visual.Vstate); } } +/** + * @brief Calculates the width allocation for each sub-network in visualization + * + * Determines how much horizontal space each sub-network should occupy in the + * visual representation by dividing the total available width by the number + * of sub-networks. + * + * @return Width in pixels for each sub-network, or 0 if no sub-networks exist + */ std::uint32_t Model::calculateSubNetWidth() const { const auto count = config.networkConfig.SubNetworksConfig.size(); if (count == 0) { @@ -51,13 +102,25 @@ std::uint32_t Model::calculateSubNetWidth() const { return visualizer::SUB_NETWORKS_WIDTH / static_cast(count); } +/** + * @brief Initializes the neural network architecture + * + * Constructs the complete neural network by creating and connecting all + * sub-networks (CNNs, FNNs) according to the configuration. Also calculates + * the total number of trainable parameters and prints model information. + * + * @throws std::runtime_error If network configuration is invalid + * @throws std::bad_alloc If insufficient memory for network creation + */ void Model::initModel() { const std::uint32_t WIDTH = calculateSubNetWidth(); size_t param_amount = 0; + // Create and configure each sub-network according to its type for (size_t i = 0; i < config.networkConfig.SubNetworksConfig.size(); ++i) { ISubNetworkConfig &_config = *config.networkConfig.SubNetworksConfig[i]; + // Instantiate the appropriate network type based on configuration if (_config.NNLable() == fnn::FNN_LABLE) { addFNN(WIDTH, _config); } else if (_config.NNLable() == cnn::CNN_LABLE) { @@ -67,16 +130,28 @@ void Model::initModel() { param_amount += network[i]->getParamCount(); } + // Print model summary information std::cout << "initialize model - " << param_amount << " parameters, " << config.networkConfig.SubNetworksConfig.size() << " sub networks" << std::endl; } +/** + * @brief Adds a Fully Connected Network (FNN) to the model + * + * Creates and configures a new FNN sub-network with the specified parameters. + * Optionally creates a visual component for real-time network visualization. + * + * @param width Visual width allocation for this sub-network in pixels + * @param _config Configuration object containing FNN-specific parameters + * @throws std::bad_cast If _config is not a valid FNNConfig + */ void Model::addFNN(const std::uint32_t width, ISubNetworkConfig &_config) { fnn::FNNConfig &sub_ = (fnn::FNNConfig &)(_config); std::shared_ptr visual_ = nullptr; + // Create visualizer component if network visualization is enabled if (shouldRenderNet()) { visual_ = std::make_shared( visual.Vstate, width, sub_); @@ -85,10 +160,21 @@ void Model::addFNN(const std::uint32_t width, ISubNetworkConfig &_config) { network.push_back(std::make_unique(sub_, true, visual_)); } +/** + * @brief Adds a Convolutional Neural Network (CNN) to the model + * + * Creates and configures a new CNN sub-network with the specified parameters. + * Optionally creates a visual component for real-time network visualization. + * + * @param width Visual width allocation for this sub-network in pixels + * @param _config Configuration object containing CNN-specific parameters + * @throws std::bad_cast If _config is not a valid CNNConfig + */ void Model::addCNN(const std::uint32_t width, ISubNetworkConfig &_config) { cnn::CNNConfig &sub_ = (cnn::CNNConfig &)(_config); std::shared_ptr visual_ = nullptr; + // Create visualizer component if network visualization is enabled if (shouldRenderNet()) { visual_ = std::make_shared( visual.Vstate, width, sub_); @@ -97,6 +183,14 @@ void Model::addCNN(const std::uint32_t width, ISubNetworkConfig &_config) { network.push_back(std::make_unique(sub_, true, visual_)); } +/** + * @brief Determines if network visualization should be rendered + * + * Checks if both general visualization and network-specific visualization + * are enabled in the configuration. + * + * @return true if network visualization should be rendered, false otherwise + */ bool Model::shouldRenderNet() const { return config.visualConfig.enableVisuals && config.visualConfig.enableNetwrokVisual; diff --git a/src/model/optimizers.cpp b/src/model/optimizers.cpp index b34a89a..a071249 100644 --- a/src/model/optimizers.cpp +++ b/src/model/optimizers.cpp @@ -1,8 +1,26 @@ #include "optimizers.hpp" namespace nn::model { + +/** + * @brief Performs a single optimization step using constant learning rate + * + * Updates the weight tensor by subtracting the scaled gradient. The gradient + * is scaled by the learning rate divided by batch size to ensure proper + * averaging across the batch. + * + * @param weight Weight tensor to be updated (modified in-place) + * @param grad Gradient tensor containing computed gradients + * + * @note The gradient tensor is modified in-place during computation + * @note Learning rate is divided by batch size for proper gradient averaging + */ void ConstantOptimizer::step(global::Tensor &weight, global::Tensor &grad) { + // Scale gradient by learning rate and batch size grad *= config.getLearningRate() / batchSize; + + // Update weights using gradient descent: w = w - α∇w weight -= grad; } + } // namespace nn::model diff --git a/src/model/optimizers.hpp b/src/model/optimizers.hpp index fd58f3d..e8e2da6 100644 --- a/src/model/optimizers.hpp +++ b/src/model/optimizers.hpp @@ -1,3 +1,13 @@ +/** + * @file optimizers.cpp + * @brief Implementation of optimization algorithms for neural network training + * + * This file implements various optimization algorithms used to update neural + * network parameters during training. Currently supports constant learning + * rate optimization, with infrastructure for adding more sophisticated + * optimizers like Adam, RMSprop, and momentum-based methods. + */ + #ifndef OPTIMIZERS #define OPTIMIZERS diff --git a/src/model/tensor.cpp b/src/model/tensor.cpp index 674521e..3071653 100644 --- a/src/model/tensor.cpp +++ b/src/model/tensor.cpp @@ -1,21 +1,21 @@ #include "tensor_gpu.hpp" #include +#include // Added for CUDA error checking +#include // Added for debugging #include #include -#include #include -#include // Added for debugging -#include // Added for CUDA error checking +#include namespace nn::global { /** * @brief Computes the total number of elements in a tensor from its shape - * + * * This function calculates the product of all dimensions in the shape vector, * which represents the total number of elements that can be stored in a tensor * with the given dimensions. - * + * * @param shape A vector containing the dimensions of the tensor * @return The total number of elements (product of all dimensions) * @retval 0 If the shape vector is empty @@ -33,16 +33,16 @@ size_t computeTensorSize(const std::vector &shape) { /** * @brief Converts a tensor shape into a human-readable string representation - * + * * This utility function formats a shape vector into a string that can be used * for debugging, error messages, or logging purposes. The output format is * similar to array notation: "{dim1, dim2, dim3, ...}". - * + * * @param shape A vector containing the dimensions of the tensor * @return A formatted string representation of the shape * @retval "{}" If the shape vector is empty */ -static std::string shapeToString(const std::vector &shape) { +std::string shapeToString(const std::vector &shape) { if (shape.empty()) { return "{}"; } @@ -56,21 +56,21 @@ static std::string shapeToString(const std::vector &shape) { } // Static member initialization -bool Tensor::isGpu = DEFAULT_GPU_MODE; ///< Global GPU mode flag for all tensors -size_t Tensor::tensorCount = 0; ///< Global counter for tracking active tensors +bool Tensor::isGpu = DEFAULT_GPU_MODE; ///< Global GPU mode flag for all tensors +size_t Tensor::tensorCount = 0; ///< Global counter for tracking active tensors /** * @brief Constructs a new tensor with the specified shape and initial value - * + * * This constructor creates a tensor with the given dimensions and initializes * all elements with the specified value. The tensor will be allocated either * on CPU or GPU memory depending on the current global execution mode. - * + * * @param shape_ The dimensions of the tensor (e.g., {batch_size, height, width, channels}) * @param init The initial value to fill all tensor elements with (default: 0.0) - * + * * @throws std::invalid_argument If the shape vector is empty - * + * * @note The tensor count is incremented upon successful construction * @note GPU memory allocation is performed if the global GPU mode is enabled */ @@ -108,13 +108,13 @@ Tensor::Tensor(const std::vector &shape_, ValueType init) { /** * @brief Switches the global execution mode to GPU for all future tensors - * + * * This static method changes the global execution mode to GPU, meaning all * subsequently created tensors will be allocated on GPU memory. This switch * can only be performed when no tensors currently exist in the system. - * + * * @throws std::runtime_error If tensors already exist in CPU mode - * + * * @note This is a global setting that affects all future tensor allocations * @note All existing tensors must be destroyed before switching modes */ @@ -130,13 +130,13 @@ void Tensor::toGpu() { /** * @brief Switches the global execution mode to CPU for all future tensors - * + * * This static method changes the global execution mode to CPU, meaning all * subsequently created tensors will be allocated on CPU memory. This switch * can only be performed when no tensors currently exist in the system. - * + * * @throws std::runtime_error If tensors already exist in GPU mode - * + * * @note This is a global setting that affects all future tensor allocations * @note All existing tensors must be destroyed before switching modes */ @@ -152,22 +152,22 @@ void Tensor::toCpu() { /** * @brief Copy constructor - creates a deep copy of another tensor - * + * * This constructor creates a new tensor that is an exact copy of the source tensor, * including all data, shape, and strides. The copy is performed in the same execution * mode (CPU/GPU) as the source tensor. - * + * * @param other The tensor to copy from - * + * * @throws std::runtime_error If GPU data pointer is null during GPU copy operation - * + * * @note This performs a deep copy - the new tensor has its own memory allocation * @note GPU operations are synchronized to ensure completion before proceeding * @note The tensor count is incremented upon successful construction */ Tensor::Tensor(const Tensor &other) { std::cout << "DEBUG: Entering Tensor copy constructor" << std::endl; - + // Copy shape and strides metadata shape = other.shape; strides = other.strides; @@ -176,21 +176,21 @@ Tensor::Tensor(const Tensor &other) { // GPU mode: allocate new GPU memory and copy data gpu_data_size = other.gpu_data_size; std::cout << "DEBUG: Tensor copy constructor - allocating " << gpu_data_size << " elements" << std::endl; - + // Validate source GPU data pointer if (other.gpu_data == nullptr) { std::cerr << "ERROR: other.gpu_data is null in copy constructor!" << std::endl; throw std::runtime_error("Null GPU data pointer in copy constructor"); } - + // Allocate new GPU memory gpu_data = (ValueType *)tensor_gpu::allocate(gpu_data_size * sizeof(ValueType)); - std::cout << "DEBUG: Tensor copy constructor - allocated gpu_data: " << (void*)gpu_data << std::endl; - + std::cout << "DEBUG: Tensor copy constructor - allocated gpu_data: " << (void *)gpu_data << std::endl; + // Copy data from source to destination on GPU tensor_gpu::copyDeviceToDevice(gpu_data, other.gpu_data, gpu_data_size * sizeof(ValueType)); std::cout << "DEBUG: Tensor copy constructor - copyDeviceToDevice completed" << std::endl; - + // Synchronize GPU operations and check for errors cudaDeviceSynchronize(); cudaError_t cudaError = cudaGetLastError(); @@ -201,18 +201,18 @@ Tensor::Tensor(const Tensor &other) { // CPU mode: simple vector copy cpu_data = other.cpu_data; } - + std::cout << "DEBUG: Exiting Tensor copy constructor" << std::endl; } /** * @brief Returns the total number of elements in the tensor - * + * * This method returns the total number of elements that can be stored in the tensor, * which is the product of all dimensions in the shape vector. - * + * * @return The total number of elements in the tensor - * + * * @note For GPU tensors, this returns the stored gpu_data_size * @note For CPU tensors, this returns the size of the cpu_data vector */ @@ -226,12 +226,12 @@ size_t Tensor::numElements() const { /** * @brief Copies the tensor's data into a std::vector - * + * * This method extracts all data from the tensor and copies it into the provided * vector. For GPU tensors, this involves a device-to-host memory transfer. - * + * * @param dest The destination vector that will receive the tensor data - * + * * @note The destination vector will be resized to fit the tensor data * @note For GPU tensors, this operation involves CUDA memory transfer * @note The destination vector must have sufficient capacity or will be resized @@ -248,12 +248,12 @@ void Tensor::getData(std::vector &dest) const { /** * @brief Replaces the tensor's data with data from another tensor - * + * * This method copies all data from the source tensor into this tensor. If the * tensors have different sizes, memory will be reallocated as needed. - * + * * @param other The source tensor to copy data from - * + * * @note This method performs a no-op if called with the same tensor (self-assignment) * @note For GPU tensors, this involves device-to-device memory copy * @note Memory reallocation occurs if the source tensor has different size @@ -283,15 +283,15 @@ void Tensor::setData(const Tensor &other) { /** * @brief Sets a new shape for the tensor - * + * * This method changes the shape of the tensor and recomputes the strides * for efficient multi-dimensional indexing. - * + * * @param newShape The new shape dimensions for the tensor - * + * * @warning The total number of elements defined by newShape must exactly match * the current number of elements. This function does not validate this. - * + * * @note This only modifies the shape metadata; the underlying data is unchanged * @note Strides are automatically recomputed after shape change */ @@ -302,12 +302,12 @@ void Tensor::setShape(const std::vector &newShape) { /** * @brief Fills the entire tensor with a specified scalar value - * + * * This method sets all elements in the tensor to the same value. The operation * is optimized for both CPU and GPU execution modes. - * + * * @param value The value to fill all tensor elements with - * + * * @note For GPU tensors, this uses optimized CUDA kernels * @note For CPU tensors, this uses a simple loop */ @@ -326,10 +326,10 @@ void Tensor::fill(const ValueType &value) { /** * @brief Fills the entire tensor with zeros - * + * * This is a convenience method that sets all tensor elements to zero. * It's equivalent to calling fill(0.0) but may be more efficient. - * + * * @note For GPU tensors, this uses an optimized zero kernel * @note For CPU tensors, this calls fill(0) internally */ @@ -345,7 +345,7 @@ void Tensor::zero() { Tensor &Tensor::operator=(const Tensor &other) { std::cout << "DEBUG: Entering Tensor::operator=" << std::endl; - + if (this == &other) { std::cout << "DEBUG: Self-assignment detected, returning" << std::endl; return *this; @@ -353,39 +353,39 @@ Tensor &Tensor::operator=(const Tensor &other) { if (isGpu) { std::cout << "DEBUG: operator= - current gpu_data_size: " << gpu_data_size << ", other.gpu_data_size: " << other.gpu_data_size << std::endl; - std::cout << "DEBUG: operator= - current gpu_data: " << (void*)gpu_data << ", other.gpu_data: " << (void*)other.gpu_data << std::endl; - + std::cout << "DEBUG: operator= - current gpu_data: " << (void *)gpu_data << ", other.gpu_data: " << (void *)other.gpu_data << std::endl; + if (gpu_data_size != other.gpu_data_size) { std::cout << "DEBUG: operator= - reallocating from " << gpu_data_size << " to " << other.gpu_data_size << " elements" << std::endl; - + // Check for null pointers before reallocation if (other.gpu_data == nullptr) { std::cerr << "ERROR: other.gpu_data is null during reallocation!" << std::endl; throw std::runtime_error("Null GPU data pointer in operator="); } - + ValueType *temp = (ValueType *)tensor_gpu::allocate(other.gpu_data_size * sizeof(ValueType)); - std::cout << "DEBUG: operator= - allocated temp buffer: " << (void*)temp << std::endl; - + std::cout << "DEBUG: operator= - allocated temp buffer: " << (void *)temp << std::endl; + gpu_data_size = other.gpu_data_size; std::cout << "DEBUG: operator= - about to copy " << gpu_data_size << " elements" << std::endl; - + tensor_gpu::copyDeviceToDevice(temp, other.gpu_data, gpu_data_size * sizeof(ValueType)); std::cout << "DEBUG: operator= - copyDeviceToDevice completed" << std::endl; - + cudaDeviceSynchronize(); cudaError_t cudaError = cudaGetLastError(); if (cudaError != cudaSuccess) { std::cerr << "CUDA Error after copyDeviceToDevice: " << cudaGetErrorString(cudaError) << std::endl; } - + if (gpu_data != nullptr) { - std::cout << "DEBUG: operator= - deallocating old gpu_data: " << (void*)gpu_data << std::endl; + std::cout << "DEBUG: operator= - deallocating old gpu_data: " << (void *)gpu_data << std::endl; tensor_gpu::deallocate(gpu_data); } - + gpu_data = temp; - std::cout << "DEBUG: operator= - assigned new gpu_data: " << (void*)gpu_data << std::endl; + std::cout << "DEBUG: operator= - assigned new gpu_data: " << (void *)gpu_data << std::endl; } else { std::cout << "DEBUG: operator= - same size, copying data" << std::endl; if (other.gpu_data == nullptr) { @@ -433,11 +433,11 @@ void Tensor::flatten() { /** * @brief Computes the strides for efficient multi-dimensional indexing - * + * * This method calculates the stride values for each dimension, which are used * to convert multi-dimensional indices into a single flattened index. The * strides are computed using row-major (C-style) ordering. - * + * * @note This is a private method called internally when shape changes * @note Strides are computed as: stride[i] = product of all dimensions after i */ @@ -455,17 +455,17 @@ void Tensor::computeStrides() { /** * @brief Converts multi-dimensional indices to a single flattened index - * + * * This method takes a vector of indices (one for each dimension) and converts * them into a single linear index that can be used to access the underlying * data array. The conversion uses row-major (C-style) ordering. - * + * * @param indices A vector of indices, one for each dimension * @return The corresponding flattened index - * + * * @throws std::invalid_argument If the number of indices doesn't match the tensor's rank * @throws std::out_of_range If any index is out of bounds for its dimension - * + * * @note This is a private method used internally for element access * @note The conversion formula is: index = sum(indices[i] * strides[i]) */ @@ -527,7 +527,7 @@ void Tensor::setValue(const size_t indices, const ValueType value) { Tensor &Tensor::operator+=(const Tensor &other) { std::cout << "DEBUG: Entering Tensor::operator+=" << std::endl; - + if (shape != other.shape) throw std::invalid_argument( "Shape mismatch in Tensor::operator+=. Left-hand side shape: " + @@ -535,23 +535,23 @@ Tensor &Tensor::operator+=(const Tensor &other) { if (isGpu) { std::cout << "DEBUG: Tensor::operator+= - this->gpu_data_size: " << gpu_data_size << ", other.gpu_data_size: " << other.gpu_data_size << std::endl; - std::cout << "DEBUG: Tensor::operator+= - this->gpu_data: " << (void*)gpu_data << ", other.gpu_data: " << (void*)other.gpu_data << std::endl; - + std::cout << "DEBUG: Tensor::operator+= - this->gpu_data: " << (void *)gpu_data << ", other.gpu_data: " << (void *)other.gpu_data << std::endl; + // Check for null pointers if (gpu_data == nullptr || other.gpu_data == nullptr) { std::cerr << "ERROR: Null GPU data pointer detected!" << std::endl; - std::cerr << "this->gpu_data: " << (void*)gpu_data << ", other.gpu_data: " << (void*)other.gpu_data << std::endl; + std::cerr << "this->gpu_data: " << (void *)gpu_data << ", other.gpu_data: " << (void *)other.gpu_data << std::endl; throw std::runtime_error("Null GPU data pointer in operator+="); } - + std::cout << "DEBUG: About to call tensor_gpu::add_vec" << std::endl; tensor_gpu::add_vec(gpu_data, other.gpu_data, gpu_data, gpu_data_size); std::cout << "DEBUG: tensor_gpu::add_vec completed" << std::endl; - + cudaDeviceSynchronize(); // Synchronize to catch async errors cudaError_t cudaError = cudaGetLastError(); if (cudaError != cudaSuccess) { - std::cerr << "CUDA Error after add_vec: " << cudaGetErrorString(cudaError) << std::endl; + std::cerr << "CUDA Error after add_vec: " << cudaGetErrorString(cudaError) << std::endl; } } else { for (size_t i = 0; i < cpu_data.size(); ++i) @@ -564,7 +564,7 @@ Tensor &Tensor::operator+=(const Tensor &other) { Tensor &Tensor::operator-=(const Tensor &other) { std::cout << "DEBUG: Entering Tensor::operator-=" << std::endl; - + if (shape != other.shape) throw std::invalid_argument( "Shape mismatch in Tensor::operator-=. Left-hand side shape: " + @@ -576,7 +576,7 @@ Tensor &Tensor::operator-=(const Tensor &other) { cudaDeviceSynchronize(); cudaError_t cudaError = cudaGetLastError(); if (cudaError != cudaSuccess) { - std::cerr << "CUDA Error after subtraction_vec: " << cudaGetErrorString(cudaError) << std::endl; + std::cerr << "CUDA Error after subtraction_vec: " << cudaGetErrorString(cudaError) << std::endl; } } else { for (size_t i = 0; i < cpu_data.size(); ++i) @@ -589,7 +589,7 @@ Tensor &Tensor::operator-=(const Tensor &other) { Tensor &Tensor::operator*=(const Tensor &other) { std::cout << "DEBUG: Entering Tensor::operator*=" << std::endl; - + if (shape != other.shape) throw std::invalid_argument( "Shape mismatch in Tensor::operator*=. Left-hand side shape: " + @@ -601,7 +601,7 @@ Tensor &Tensor::operator*=(const Tensor &other) { cudaDeviceSynchronize(); cudaError_t cudaError = cudaGetLastError(); if (cudaError != cudaSuccess) { - std::cerr << "CUDA Error after multiply_vec: " << cudaGetErrorString(cudaError) << std::endl; + std::cerr << "CUDA Error after multiply_vec: " << cudaGetErrorString(cudaError) << std::endl; } } else { for (size_t i = 0; i < cpu_data.size(); ++i) @@ -614,7 +614,7 @@ Tensor &Tensor::operator*=(const Tensor &other) { Tensor &Tensor::operator/=(const Tensor &other) { std::cout << "DEBUG: Entering Tensor::operator/=" << std::endl; - + if (shape != other.shape) throw std::invalid_argument( "Shape mismatch in Tensor::operator/=. Left-hand side shape: " + @@ -626,7 +626,7 @@ Tensor &Tensor::operator/=(const Tensor &other) { cudaDeviceSynchronize(); cudaError_t cudaError = cudaGetLastError(); if (cudaError != cudaSuccess) { - std::cerr << "CUDA Error after division_vec: " << cudaGetErrorString(cudaError) << std::endl; + std::cerr << "CUDA Error after division_vec: " << cudaGetErrorString(cudaError) << std::endl; } } else { for (size_t i = 0; i < cpu_data.size(); ++i) @@ -639,14 +639,14 @@ Tensor &Tensor::operator/=(const Tensor &other) { Tensor &Tensor::operator*=(ValueType scalar) { std::cout << "DEBUG: Entering Tensor::operator*=(scalar)" << std::endl; - + if (isGpu) { std::cout << "DEBUG: Tensor::operator*=(scalar) - this->gpu_data_size: " << gpu_data_size << std::endl; tensor_gpu::multiply_scalar(gpu_data, scalar, gpu_data, gpu_data_size); cudaDeviceSynchronize(); cudaError_t cudaError = cudaGetLastError(); if (cudaError != cudaSuccess) { - std::cerr << "CUDA Error after multiply_scalar: " << cudaGetErrorString(cudaError) << std::endl; + std::cerr << "CUDA Error after multiply_scalar: " << cudaGetErrorString(cudaError) << std::endl; } } else { for (auto &x : cpu_data) @@ -659,14 +659,14 @@ Tensor &Tensor::operator*=(ValueType scalar) { Tensor &Tensor::operator-=(ValueType scalar) { std::cout << "DEBUG: Entering Tensor::operator-=(scalar)" << std::endl; - + if (isGpu) { std::cout << "DEBUG: Tensor::operator-=(scalar) - this->gpu_data_size: " << gpu_data_size << std::endl; tensor_gpu::subtraction_scalar(gpu_data, scalar, gpu_data, gpu_data_size); cudaDeviceSynchronize(); cudaError_t cudaError = cudaGetLastError(); if (cudaError != cudaSuccess) { - std::cerr << "CUDA Error after subtraction_scalar: " << cudaGetErrorString(cudaError) << std::endl; + std::cerr << "CUDA Error after subtraction_scalar: " << cudaGetErrorString(cudaError) << std::endl; } } else { for (auto &x : cpu_data) @@ -679,14 +679,14 @@ Tensor &Tensor::operator-=(ValueType scalar) { Tensor &Tensor::operator+=(ValueType scalar) { std::cout << "DEBUG: Entering Tensor::operator+=(scalar)" << std::endl; - + if (isGpu) { std::cout << "DEBUG: Tensor::operator+=(scalar) - this->gpu_data_size: " << gpu_data_size << std::endl; tensor_gpu::add_scalar(gpu_data, scalar, gpu_data, gpu_data_size); cudaDeviceSynchronize(); cudaError_t cudaError = cudaGetLastError(); if (cudaError != cudaSuccess) { - std::cerr << "CUDA Error after add_scalar: " << cudaGetErrorString(cudaError) << std::endl; + std::cerr << "CUDA Error after add_scalar: " << cudaGetErrorString(cudaError) << std::endl; } } else { for (auto &x : cpu_data) @@ -699,14 +699,14 @@ Tensor &Tensor::operator+=(ValueType scalar) { Tensor &Tensor::operator/=(ValueType scalar) { std::cout << "DEBUG: Entering Tensor::operator/=(scalar)" << std::endl; - + if (isGpu) { std::cout << "DEBUG: Tensor::operator/=(scalar) - this->gpu_data_size: " << gpu_data_size << std::endl; tensor_gpu::division_scalar(gpu_data, scalar, gpu_data, gpu_data_size); cudaDeviceSynchronize(); cudaError_t cudaError = cudaGetLastError(); if (cudaError != cudaSuccess) { - std::cerr << "CUDA Error after division_scalar: " << cudaGetErrorString(cudaError) << std::endl; + std::cerr << "CUDA Error after division_scalar: " << cudaGetErrorString(cudaError) << std::endl; } } else { for (auto &x : cpu_data) @@ -778,12 +778,12 @@ void Tensor::matmulT(const Tensor &vec, Tensor &result) const { /** * @brief Destructor - cleans up tensor resources - * + * * This destructor properly cleans up all resources associated with the tensor. * For GPU tensors, it deallocates the device memory. For CPU tensors, the * std::vector destructor handles cleanup automatically. The global tensor * count is decremented to track the number of active tensors. - * + * * @note GPU memory is only deallocated if the tensor is in GPU mode and has valid data * @note The global tensor count is decremented upon destruction * @note This destructor is automatically called when the tensor goes out of scope diff --git a/src/networks/cnn/CNNetwork.hpp b/src/networks/cnn/CNNetwork.hpp index 3b4f02f..1768e0c 100644 --- a/src/networks/cnn/CNNetwork.hpp +++ b/src/networks/cnn/CNNetwork.hpp @@ -1,3 +1,13 @@ +/** + * @file CNNetwork.hpp + * @brief Header file for Convolutional Neural Network implementation + * + * This file defines the CNNetwork class, which implements a convolutional neural + * network for processing spatial data such as images. It provides 2D convolution + * operations, activation functions, and gradient computation for training CNNs + * with backpropagation. + */ + #ifndef CNNNETWORK #define CNNNETWORK @@ -6,70 +16,178 @@ namespace nn::model::cnn { +/** + * @struct Size + * @brief Simple structure to represent 2D dimensions + * + * Used to specify spatial dimensions for feature maps, filters, + * and other 2D data structures in the CNN. + */ struct Size { - size_t w; - size_t h; + size_t w; ///< Width dimension + size_t h; ///< Height dimension }; +/** + * @class CNNetwork + * @brief Convolutional Neural Network implementation + * + * The CNNetwork class implements a convolutional neural network capable of + * processing 2D spatial data through convolution operations. It supports + * multiple filter channels, bias terms, and various activation functions. + * + * Key features: + * - 2D convolution operations with configurable filter sizes + * - Multiple feature maps with independent filters + * - Bias terms for each filter channel + * - Forward and backward propagation with gradient computation + * - Integration with visualization system for real-time monitoring + * - CPU-based convolution implementation with potential GPU acceleration + * + * The network processes input data through convolution, applies activation + * functions, and computes gradients for training via backpropagation. + */ class CNNetwork : public INetwork { private: - const CNNConfig &config; - global::Tensor input; + const CNNConfig &config; ///< Configuration for CNN architecture + global::Tensor input; ///< Input tensor storage - global::Tensor filtersW; - global::Tensor filtersWGradient; + global::Tensor filtersW; ///< Convolution filter weights + global::Tensor filtersWGradient; ///< Gradients for filter weights - global::Tensor filtersB; - global::Tensor filtersBGradient; + global::Tensor filtersB; ///< Bias terms for each filter + global::Tensor filtersBGradient; ///< Gradients for bias terms - global::Tensor activationMapN; - global::Tensor activationMapO; + global::Tensor activationMapN; ///< Pre-activation feature maps (before activation function) + global::Tensor activationMapO; ///< Post-activation feature maps (after activation function) - global::Tensor activationDelta; + global::Tensor activationDelta; ///< Delta values for backpropagation - Activation activationFunction; + Activation activationFunction; ///< Activation function for feature maps + /** @brief Calculates input gradients for backpropagation to previous layers */ void calculateInputDelta(const global::Tensor &deltas); + + /** @brief Computes gradients for convolutional filter weights */ void calculateFilterGradients(const global::ValueType weight); + + /** @brief Computes gradients for bias terms */ void calculateBiasGradients(const global::ValueType weight); + + /** @brief Initializes filter weights and biases */ void initializeParameters(); - const std::shared_ptr visual; + const std::shared_ptr visual; ///< Optional visualizer component + /** @brief Creates shape vector for activation map tensors */ std::vector makeActivationMapShape(); + /** @brief Generates random initial values for filter weights */ std::vector randomFilters() const; + /** @brief Performs 2D convolution operation using CPU implementation */ void conv2d_cpu(); + /** @brief Calculates the size of output feature maps after convolution */ Size getFeatureMapSize(); public: + /** + * @brief Constructs a convolutional neural network with specified configuration + * + * @param _config CNN configuration containing filter sizes, stride, padding, etc. + * @param randomInit Whether to initialize weights randomly (true) or as zeros (false) + * @param visual_ Optional visualizer for real-time network monitoring + */ CNNetwork( const CNNConfig &_config, const bool randomInit, const std::shared_ptr visual_ = std::shared_ptr()); + + /** @brief Default virtual destructor */ ~CNNetwork() override = default; + /** + * @brief Performs forward propagation through the convolutional layer + * + * Applies 2D convolution operation to the input using learned filters, + * adds bias terms, and applies the activation function to produce + * feature maps. + * + * @param newInput Input tensor with spatial dimensions to convolve + */ void forward(const global::Tensor &newInput) override; + + /** + * @brief Performs backward propagation to compute gradients + * + * Computes gradients for filters, biases, and input based on the + * error signals from subsequent layers. Updates accumulated gradients + * for later weight updates. + * + * @param outputDeltas Gradient tensors from subsequent layers + * @param weight Scaling factor for gradients (e.g., sample weight) + */ void backward(global::Tensor **outputDeltas, const global::ValueType weight) override; + + /** + * @brief Updates network parameters using the provided optimizer + * @param optimizer Optimization algorithm to use for weight updates + */ void updateWeights(IOptimizer &optimizer) override; + + /** @brief Resets all accumulated gradients to zero */ void resetGradient() override; + /** + * @brief Calculates loss for a given prediction target + * @param index Target prediction information + * @return Computed loss value + */ global::ValueType getLoss(const global::Prediction &index) const override; + /** + * @brief Gets the size of the network's output (number of feature maps) + * @return Number of output feature maps + */ size_t outputSize() const override; + /** + * @brief Gets the network's output tensor (read-only) + * @return Reference to the output activation maps + */ const global::Tensor &getOutput() const override; + + /** + * @brief Gets pointer to the network's input tensor for modification + * @return Pointer to the input tensor + */ global::Tensor *getInput() override; + /** + * @brief Gets the visualization component for this network + * @return Shared pointer to the visualizer, or nullptr if disabled + */ std::shared_ptr getVisual() override; + /** + * @brief Extracts all network parameters as a flat vector + * @return Vector containing all filter weights and biases + */ std::vector getParams() const override; + + /** + * @brief Sets network parameters from a flat tensor + * @param params Tensor containing all weights and biases to set + */ void setParams(const global::Tensor ¶ms) override; + /** + * @brief Gets the total number of trainable parameters + * @return Total count of filter weights and biases in the network + */ size_t getParamCount() const override; void setTraining(const bool) override {} diff --git a/src/networks/fnn/FNNetwork.hpp b/src/networks/fnn/FNNetwork.hpp index ebc9085..5fb33e9 100644 --- a/src/networks/fnn/FNNetwork.hpp +++ b/src/networks/fnn/FNNetwork.hpp @@ -1,3 +1,13 @@ +/** + * @file FNNetwork.hpp + * @brief Header file for Fully Connected Neural Network implementation + * + * This file defines the FNNetwork class, which implements a fully connected + * (dense) neural network using multiple dense layers. It provides forward + * and backward propagation, parameter management, and optional visualization + * capabilities for monitoring network behavior during training. + */ + #ifndef FNNNETWORK #define FNNNETWORK @@ -5,47 +15,134 @@ #include namespace nn::model::fnn { + +/** + * @class FNNetwork + * @brief Fully Connected Neural Network implementation + * + * The FNNetwork class represents a feedforward neural network composed of + * multiple dense (fully connected) layers. It implements the INetwork interface + * to provide standardized methods for forward propagation, backpropagation, + * weight updates, and parameter management. + * + * Key features: + * - Multiple dense layers with configurable architectures + * - Forward and backward propagation with automatic gradient calculation + * - Integration with various optimization algorithms + * - Optional real-time visualization of network state + * - Parameter serialization for model saving/loading + * - Loss calculation for different prediction types + * + * The network supports various activation functions and can be configured + * through the FNNConfig object passed during construction. + */ class FNNetwork : public INetwork { private: - const FNNConfig &config; - std::vector> layers; - global::Tensor input; + const FNNConfig &config; ///< Configuration for network architecture + std::vector> layers; ///< Collection of dense layers + global::Tensor input; ///< Input tensor storage - const std::shared_ptr visual; + const std::shared_ptr visual; ///< Optional visualizer component + /** @brief Calculates input deltas for gradient propagation to previous layers */ void calculateInputDelta(global::Tensor **deltas); + /** @brief Updates visualization data if visualizer is enabled */ void vUpdate(); + /** @brief Sends new data values to visualizer for specified layer */ void sendNewVData(const size_t i) const; + + /** @brief Sends neuron activation values to visualizer for specified layer */ void sendNewVNeurons(const size_t i) const; public: + /** + * @brief Constructs a fully connected network with specified configuration + * + * @param _config Network configuration containing layer sizes, activations, etc. + * @param randomInit Whether to initialize weights randomly (true) or as zeros (false) + * @param visual_ Optional visualizer for real-time network monitoring + */ FNNetwork( const FNNConfig &_config, const bool randomInit, const std::shared_ptr visual_ = nullptr); + + /** @brief Default virtual destructor */ ~FNNetwork() override = default; + /** + * @brief Performs forward propagation through the network + * @param newInput Input tensor to process through the network + */ void forward(const global::Tensor &newInput) override; + + /** + * @brief Performs backward propagation to compute gradients + * @param outputDeltas Gradient tensors from subsequent layers + * @param weight Scaling factor for gradients (e.g., sample weight) + */ void backward(global::Tensor **outputDeltas, const global::ValueType weight) override; + + /** + * @brief Updates network weights using the provided optimizer + * @param optimizer Optimization algorithm to use for weight updates + */ void updateWeights(IOptimizer &optimizer) override; + + /** @brief Resets all accumulated gradients to zero */ void resetGradient() override; + /** + * @brief Calculates loss for a given prediction target + * @param index Target prediction information + * @return Computed loss value + */ global::ValueType getLoss(const global::Prediction &index) const override; + /** + * @brief Gets the size of the network's output layer + * @return Number of output neurons + */ size_t outputSize() const override; + /** + * @brief Gets the network's output tensor (read-only) + * @return Reference to the output tensor + */ const global::Tensor &getOutput() const override; + + /** + * @brief Gets pointer to the network's input tensor for modification + * @return Pointer to the input tensor + */ global::Tensor *getInput() override; + /** + * @brief Gets the visualization component for this network + * @return Shared pointer to the visualizer, or nullptr if disabled + */ std::shared_ptr getVisual() override { return visual; } + /** + * @brief Extracts all network parameters as a flat vector + * @return Vector containing all weights and biases + */ std::vector getParams() const override; + + /** + * @brief Sets network parameters from a flat tensor + * @param params Tensor containing all weights and biases to set + */ void setParams(const global::Tensor ¶ms) override; + /** + * @brief Gets the total number of trainable parameters + * @return Total count of weights and biases in the network + */ size_t getParamCount() const override; void setTraining(const bool state) override; diff --git a/src/visualizer/button.cpp b/src/visualizer/button.cpp index 3e2953d..57c86e8 100644 --- a/src/visualizer/button.cpp +++ b/src/visualizer/button.cpp @@ -1,7 +1,29 @@ +/** + * @file button.cpp + * @brief Implementation of interactive button components for the neural network visualizer + * + * This file implements UI button functionality for the visualization interface, + * providing interactive controls for toggling various visualization features. + * Buttons handle mouse interactions, visual state changes, and command dispatch + * to the state management system. + */ + #include "button.hpp" #include "fonts.hpp" namespace nn::visualizer { + +/** + * @brief Constructs a new Button with specified label and state binding + * + * Creates an interactive button that can toggle a specific setting in the + * state manager. The button renders with different colors based on its + * current state (active/inactive). + * + * @param _state Shared pointer to the state manager for this visualizer session + * @param lable Text label to display on the button + * @param initState The setting type this button controls (e.g., graph visibility) + */ Button::Button( const std::shared_ptr _state, const std::string_view &lable, @@ -13,12 +35,24 @@ Button::Button( renderButton(); } +/** + * @brief Renders the button with current state-appropriate appearance + * + * Updates the button's visual appearance by clearing the render texture with + * the appropriate background color and redrawing the text. This method is + * called whenever the button's state changes. + */ void Button::renderButton() { buttonRender.clear(getBgColor()); drawText(); visibleState = vstate->getState(CurrentState); } +/** + * @brief Determines the background color based on button's current state + * + * @return Active color if the associated setting is enabled, inactive color otherwise + */ sf::Color Button::getBgColor() { if (vstate->getState(CurrentState)) { return buttoncolors::ACTIVE; @@ -27,6 +61,11 @@ sf::Color Button::getBgColor() { } } +/** + * @brief Determines the text color based on button's current state + * + * @return Active text color if the associated setting is enabled, inactive text color otherwise + */ sf::Color Button::getFontColor() { if (vstate->getState(CurrentState)) { return buttoncolors::TEXT_ACTIVE; diff --git a/src/visualizer/button.hpp b/src/visualizer/button.hpp index 521583f..9023256 100644 --- a/src/visualizer/button.hpp +++ b/src/visualizer/button.hpp @@ -1,3 +1,12 @@ +/** + * @file button.hpp + * @brief Header file for interactive button components in the neural network visualizer + * + * This file defines the Button class and related constants for creating interactive + * UI elements in the visualization interface. Buttons provide visual feedback and + * allow users to toggle various visualization features during training. + */ + #ifndef BUTTON #define BUTTON @@ -5,37 +14,96 @@ #include namespace nn::visualizer { -constexpr std::uint32_t BUTTON_HEIGHT = 50; -constexpr std::uint32_t BUTTON_WIDTH = 200; -constexpr std::uint32_t BUTTON_TEXT_FONT = 30; +// Button dimension constants +constexpr std::uint32_t BUTTON_HEIGHT = 50; ///< Standard button height in pixels +constexpr std::uint32_t BUTTON_WIDTH = 200; ///< Standard button width in pixels +constexpr std::uint32_t BUTTON_TEXT_FONT = 30; ///< Font size for button text + +/** + * @namespace buttoncolors + * @brief Color scheme constants for button visual states + * + * Defines the color palette used for button backgrounds and text + * in different interaction states (active/inactive). + */ namespace buttoncolors { -constexpr sf::Color ACTIVE(0, 123, 255); -constexpr sf::Color INACTIVE(187, 187, 187); -constexpr sf::Color TEXT_ACTIVE(255, 255, 255); -constexpr sf::Color TEXT_INACTIVE(34, 34, 34); +constexpr sf::Color ACTIVE(0, 123, 255); ///< Active button background color (blue) +constexpr sf::Color INACTIVE(187, 187, 187); ///< Inactive button background color (gray) +constexpr sf::Color TEXT_ACTIVE(255, 255, 255); ///< Active button text color (white) +constexpr sf::Color TEXT_INACTIVE(34, 34, 34); ///< Inactive button text color (dark gray) } // namespace buttoncolors +/** + * @class Button + * @brief Interactive button component for the neural network visualizer + * + * The Button class provides a clickable UI element that can toggle settings + * in the visualizer state manager. It handles mouse interactions, visual state + * changes, and provides visual feedback to the user about the current state + * of the associated setting. + * + * Key features: + * - Visual state indication (active/inactive colors) + * - Mouse click detection and handling + * - Integration with state management system + * - Automatic visual updates when state changes + */ class Button : public Panel { private: - sf::RenderTexture buttonRender; - const SettingType CurrentState; - bool visibleState; - const std::string lable; + sf::RenderTexture buttonRender; ///< Render texture for button graphics + const SettingType CurrentState; ///< Setting type this button controls + bool visibleState; ///< Cached visual state for change detection + const std::string lable; ///< Text label displayed on the button + /** @brief Sends toggle command to the state manager */ void sendCommand(); + + /** @brief Renders the button with current state colors and text */ void renderButton(); + + /** @brief Displays the rendered button to its render texture */ void display(); + + /** @brief Draws the button's text label with appropriate color */ void drawText(); + + /** @brief Gets background color based on current state */ sf::Color getBgColor(); + + /** @brief Gets text color based on current state */ sf::Color getFontColor(); + + /** @brief Overridden render method from Panel base class */ void doRender() override; + + /** @brief Overridden observer method from Panel base class */ void observe() override; public: + /** + * @brief Constructs a button with specified label and state binding + * @param _state Shared state manager for the visualizer session + * @param lable Text to display on the button + * @param state_ Setting type this button will control + */ Button(const std::shared_ptr _state, const std::string_view &lable, const SettingType state_); + + /** @brief Default destructor */ ~Button() = default; + + /** + * @brief Gets the button's sprite for rendering + * @return SFML sprite object for drawing the button + */ sf::Sprite getSprite(); + + /** + * @brief Checks if a mouse position intersects with the button + * @param mousePos Mouse position in screen coordinates + * @param boxPos Button's position in screen coordinates + * @return true if the button was clicked, false otherwise + */ bool checkForClick(const sf::Vector2f mousePos, const sf::Vector2f boxPos); }; } // namespace nn::visualizer diff --git a/src/visualizer/fonts.cpp b/src/visualizer/fonts.cpp index 62943f7..12833f2 100644 --- a/src/visualizer/fonts.cpp +++ b/src/visualizer/fonts.cpp @@ -1,13 +1,37 @@ +/** + * @file fonts.cpp + * @brief Implementation of font management for the neural network visualizer + * + * This file implements font loading and management functionality for the + * visualization system. It provides a centralized way to access fonts + * for rendering text in various UI components. + */ + #include "fonts.hpp" namespace nn::visualizer { + +/** + * @brief Gets the default font for text rendering in the visualizer + * + * This function implements a singleton pattern to load and return the + * default font used throughout the visualization interface. The font + * is loaded once from the resources directory and cached for subsequent + * use across all UI components. + * + * @return Reference to the loaded font object + * @note If the font file cannot be found, an error message is printed + * and the function returns an empty font object + */ sf::Font &Fonts::getFont() { - static sf::Font font; - static bool loaded = false; + static sf::Font font; // Static font instance for singleton pattern + static bool loaded = false; // Flag to ensure font is loaded only once if (!loaded) { + // Construct path to font file in resources directory std::string path = std::string(RESOURCE_DIR) + "/Inter.ttc"; + // Attempt to load the font file if (!font.openFromFile(path)) { printf("Font not found: %s\n", path.c_str()); } @@ -16,4 +40,5 @@ sf::Font &Fonts::getFont() { } return font; } + } // namespace nn::visualizer diff --git a/src/visualizer/fonts.hpp b/src/visualizer/fonts.hpp index 411e480..d80d38f 100644 --- a/src/visualizer/fonts.hpp +++ b/src/visualizer/fonts.hpp @@ -1,15 +1,59 @@ +/** + * @file fonts.hpp + * @brief Font management interface for the neural network visualizer + * + * This file defines the Fonts utility class that provides centralized + * font loading and access for all text rendering in the visualization + * system. It implements a singleton pattern to ensure efficient resource + * management and consistent font usage across UI components. + */ + #ifndef FONTS #define FONTS #include namespace nn::visualizer { + +/** + * @class Fonts + * @brief Static utility class for font management + * + * The Fonts class provides a centralized interface for loading and accessing + * fonts used in the neural network visualizer. It implements a singleton + * pattern to ensure that fonts are loaded only once and shared across all + * UI components that need text rendering capabilities. + * + * Key features: + * - Singleton font loading to avoid resource duplication + * - Centralized font management for consistent UI appearance + * - Error handling for missing font files + * - Static interface for easy access from any UI component + * + * @note This class cannot be instantiated (constructor is deleted) + * @note All methods are static and thread-safe + */ class Fonts { public: + /** @brief Deleted constructor - this is a static utility class */ Fonts() = delete; + + /** + * @brief Gets the default font for the visualizer + * + * Returns a reference to the default font used throughout the + * visualization interface. The font is loaded on first access + * and cached for subsequent calls. + * + * @return Reference to the default SFML font object + * @note The font is loaded from the RESOURCE_DIR/Inter.ttc file + */ static sf::Font &getFont(); + + /** @brief Default destructor */ ~Fonts() = default; }; + } // namespace nn::visualizer #endif // FONTS diff --git a/src/visualizer/graph.cpp b/src/visualizer/graph.cpp index 15f0f30..709bc2c 100644 --- a/src/visualizer/graph.cpp +++ b/src/visualizer/graph.cpp @@ -1,21 +1,66 @@ +/** + * @file graph.cpp + * @brief Implementation of graph visualization components for training metrics + * + * This file implements real-time graph visualization for tracking neural network + * training progress. It provides functionality for plotting loss curves and + * evaluation metrics over time, with automatic scaling and data management. + */ + #include "graph.hpp" #include "fonts.hpp" namespace nn::visualizer { + +/** + * @brief Constructs a Graph for displaying training metrics over time + * + * Creates a graph visualization component that can display data points + * over the course of training. The graph automatically scales and manages + * data points within a fixed resolution window. + * + * @param batchCount Total number of training batches (for data gap calculation) + * @param res Resolution of the graph (maximum number of data points displayed) + * @param alpha Scaling factor for graph height visualization + */ Graph::Graph(const int batchCount, std::uint32_t res, float alpha) : graphAlpha(alpha), resolution(std::min(GRAPH_RESOLUTION, res) - 1), dataGaps(batchCount / resolution) { } +/** + * @brief Calculates the vertical position for a given value on the graph + * + * Converts a data value to screen coordinates, applying scaling and ensuring + * the point fits within the graph's height bounds. + * + * @param value The data value to convert to screen coordinates + * @return Y-coordinate position for rendering (inverted, as screen Y increases downward) + */ float Graph::getHeight(const float value) const { return GRAPH_HEIGHT - std::max(1.0, value * graphAlpha); } +/** + * @brief Calculates the 2D position for a data point at the given index + * + * Converts a data index to screen coordinates for rendering, combining + * horizontal positioning based on time/index and vertical positioning + * based on the data value. + * + * @param index Index of the data point in the graph's data array + * @return 2D position vector for rendering this data point + */ sf::Vector2f Graph::getPosition(int index) const { return {index * dataGapWidth(), getHeight(getValue(index))}; } +/** + * @brief Calculates the horizontal spacing between data points + * + * @return Width in pixels between consecutive data points on the graph + */ float Graph::dataGapWidth() const { return GRAPH_WIDTH / static_cast(resolution); } diff --git a/src/visualizer/graph.hpp b/src/visualizer/graph.hpp index 14fcd4d..8de0981 100644 --- a/src/visualizer/graph.hpp +++ b/src/visualizer/graph.hpp @@ -1,3 +1,13 @@ +/** + * @file graph.hpp + * @brief Header file for graph visualization components in the neural network visualizer + * + * This file defines classes and constants for creating real-time graphs that display + * training metrics such as loss curves and evaluation accuracy over time. The graph + * system provides automatic scaling, data management, and smooth visualization of + * training progress. + */ + #ifndef GRAPH_CORE_HPP #define GRAPH_CORE_HPP @@ -5,48 +15,100 @@ #include namespace nn::visualizer { -constexpr std::uint32_t GRAPH_WIDTH = 440; -constexpr std::uint32_t GRAPH_HEIGHT = 315; -constexpr std::uint32_t GRAPH_RESOLUTION = 100; -constexpr int VERTICAL_NUMBERS_COUNT = 10; - -constexpr std::uint32_t GRAPH_UI_WIDTH = 500; - -constexpr int GRAPH_TEXT_FONT = 30; -constexpr std::uint32_t DATA_GAP_WIDTH = GRAPH_WIDTH / GRAPH_RESOLUTION; -constexpr float GRAPH_HEIGHT_ALPHA_DEFAULT = GRAPH_HEIGHT; - -constexpr sf::Color GRAPH_LINE_COLOR_LOST(0, 0, 0); -constexpr sf::Color GRAPH_LINE_COLOR_EVALUATE(255, 0, 255); -constexpr sf::Color GRAPH_HORIZONTAL_LINE_COLOR(0, 0, 255); -constexpr sf::Color GRAPH_BG = PANELS_BG; +// Graph dimension and display constants +constexpr std::uint32_t GRAPH_WIDTH = 440; ///< Graph plotting area width in pixels +constexpr std::uint32_t GRAPH_HEIGHT = 315; ///< Graph plotting area height in pixels +constexpr std::uint32_t GRAPH_RESOLUTION = 100; ///< Maximum number of data points displayed +constexpr int VERTICAL_NUMBERS_COUNT = 10; ///< Number of vertical axis labels + +constexpr std::uint32_t GRAPH_UI_WIDTH = 500; ///< Total graph UI panel width + +// Graph styling constants +constexpr int GRAPH_TEXT_FONT = 30; ///< Font size for graph labels +constexpr std::uint32_t DATA_GAP_WIDTH = GRAPH_WIDTH / GRAPH_RESOLUTION; ///< Spacing between data points +constexpr float GRAPH_HEIGHT_ALPHA_DEFAULT = GRAPH_HEIGHT; ///< Default scaling factor + +// Graph color scheme +constexpr sf::Color GRAPH_LINE_COLOR_LOST(0, 0, 0); ///< Loss curve color (black) +constexpr sf::Color GRAPH_LINE_COLOR_EVALUATE(255, 0, 255); ///< Evaluation curve color (magenta) +constexpr sf::Color GRAPH_HORIZONTAL_LINE_COLOR(0, 0, 255); ///< Grid line color (blue) +constexpr sf::Color GRAPH_BG = PANELS_BG; ///< Graph background color + +/** + * @class Graph + * @brief Core graph component for plotting training metrics over time + * + * The Graph class manages the visualization of time-series data during neural + * network training. It handles data point storage, automatic scaling, and + * rendering of line graphs with appropriate colors and positioning. + * + * Key features: + * - Fixed-resolution data storage with automatic aggregation + * - Dynamic vertical scaling based on data range + * - Smooth line rendering between data points + * - Configurable colors and styling + */ class Graph { private: - std::array data{0}; - double graphAlpha; - std::uint32_t resolution; + std::array data{0}; ///< Data storage array + double graphAlpha; ///< Vertical scaling factor for data visualization + std::uint32_t resolution; ///< Active resolution (may be less than max) - int IndexCount{0}; - int Index{0}; - const int dataGaps; + int IndexCount{0}; ///< Count of data points at current index + int Index{0}; ///< Current data insertion index + const int dataGaps; ///< Number of raw data points per graph point + /** @brief Gets the aggregated value at a specific graph index */ global::ValueType getValue(const int index) const; + /** @brief Converts a data value to screen Y-coordinate */ float getHeight(const float value) const; + + /** @brief Gets the 2D screen position for a data point */ sf::Vector2f getPosition(const int index) const; + + /** + * @brief Renders a line segment between two consecutive data points + * @param index Starting index for the line segment + * @param target Render target to draw on + * @param position Offset position for the graph + * @param color Color for the line segment + */ void renderDot( const int index, sf::RenderTarget &target, const sf::Vector2f &position, const sf::Color &color); + + /** @brief Calculates horizontal spacing between data points */ float dataGapWidth() const; public: + /** + * @brief Constructs a graph with specified parameters + * @param batchCount Total number of training batches + * @param resolution Maximum resolution (data points) for the graph + * @param alpha Vertical scaling factor for value display + */ Graph(const int batchCount, std::uint32_t resolution = GRAPH_RESOLUTION, float alpha = GRAPH_HEIGHT); + + /** @brief Default destructor */ ~Graph() = default; + /** + * @brief Draws the complete graph to a render target + * @param rarget Render target to draw the graph on + * @param position Offset position for graph placement + * @param color Color for the graph lines + */ void drawTo(sf::RenderTarget &rarget, const sf::Vector2f position = {0.f, 0.f}, const sf::Color &color = sf::Color::Black); + + /** + * @brief Adds a new data point to the graph + * @param new_data The data value to add + * @param index The batch/time index for this data point + */ void addData(const global::ValueType new_data, const int index); void setAlpha(const float alpha); float getAlpha() const; diff --git a/src/visualizer/panel.cpp b/src/visualizer/panel.cpp index a9d9ed5..0fddc08 100644 --- a/src/visualizer/panel.cpp +++ b/src/visualizer/panel.cpp @@ -1,8 +1,30 @@ +/** + * @file panel.cpp + * @brief Implementation of the Panel base class for UI components + * + * This file implements the core functionality of the Panel base class, + * providing update management and rendering coordination for all UI + * components in the neural network visualizer. + */ + #include "panel.hpp" namespace nn::visualizer { + +/** + * @brief Renders the panel if an update is needed + * + * This method implements the rendering logic by first calling observe() to + * check for any state changes, then conditionally calling doRender() if + * the panel needs updating. After rendering, the update flag is reset. + * + * @return true if the panel was rendered, false if no update was needed + */ int Panel::render() { + // Check for state changes that might require an update observe(); + + // Only render if an update is needed if (updateStatus()) { doRender(); need_update = false; @@ -12,7 +34,17 @@ int Panel::render() { return false; } +/** + * @brief Marks the panel as needing an update + * + * Sets the internal update flag to trigger a re-render on the next + * call to render(). The wait parameter is currently unused but + * provides extensibility for future timing control features. + * + * @param wait Unused parameter for potential future timing control + */ void Panel::setUpdate(const bool) { need_update = true; } + } // namespace nn::visualizer diff --git a/src/visualizer/panel.hpp b/src/visualizer/panel.hpp index 0c5b94e..303eee5 100644 --- a/src/visualizer/panel.hpp +++ b/src/visualizer/panel.hpp @@ -1,3 +1,13 @@ +/** + * @file panel.hpp + * @brief Base class for UI panel components in the neural network visualizer + * + * This file defines the Panel base class that provides common functionality + * for all UI components in the visualization system. It handles update + * management, state observation, and provides a consistent interface for + * rendering visual components. + */ + #ifndef PANEL #define PANEL @@ -5,24 +15,72 @@ #include namespace nn::visualizer { + +/// Standard background color for all panel components constexpr sf::Color PANELS_BG(255, 255, 255); +/** + * @class Panel + * @brief Abstract base class for all UI panel components + * + * The Panel class provides the foundational structure for all visual components + * in the neural network visualizer. It manages update states, provides access + * to the shared state manager, and defines the interface that all UI components + * must implement. + * + * Key features: + * - Update management to avoid unnecessary re-rendering + * - Integration with the state management system + * - Virtual interface for custom rendering implementations + * - Observer pattern support for state changes + * + * Derived classes must implement the doRender() method to define their + * specific rendering behavior. + */ class Panel { private: + /** @brief Pure virtual method for rendering panel content - must be implemented by derived classes */ virtual void doRender() = 0; - bool need_update{true}; + + bool need_update{true}; ///< Flag indicating whether the panel needs to be re-rendered + + /** @brief Virtual method for observing state changes - can be overridden by derived classes */ virtual void observe() {} protected: - std::shared_ptr vstate; + std::shared_ptr vstate; ///< Shared state manager for this visualizer session public: + /** + * @brief Constructs a panel with access to the state manager + * @param vstate_ Shared pointer to the state manager + */ Panel(const std::shared_ptr vstate_) : vstate(vstate_) {} + + /** @brief Virtual destructor for proper cleanup of derived classes */ virtual ~Panel() = default; + /** + * @brief Renders the panel if an update is needed + * + * Checks if the panel needs updating and calls the doRender() method + * if necessary. Also calls observe() to check for state changes. + * + * @return Status code indicating render result + */ int render(); + + /** + * @brief Checks if the panel needs to be updated + * @return true if the panel needs re-rendering, false otherwise + */ virtual bool updateStatus() const { return need_update; } + + /** + * @brief Marks the panel as needing an update + * @param wait Optional parameter for update timing control + */ void setUpdate(const bool wait = false); }; } // namespace nn::visualizer