<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>My Own Room</title>
  <link href="/feed.xml" rel="self" />
  <link href="" />
  <updated>2026-03-26T18:30:18+00:00</updated>
  <author>
    <name>Neirth</name>
  </author>
  
    
  
    
    <entry>
      <title>NeuralAnalytics: Real-Time Brain Signal Analysis for my Bachelor&apos;s Thesis</title>
      <link href="/2025/12/01/neural-analytics-brain-computer-interface-thesis.html" />
      <id>/2025/12/01/neural-analytics-brain-computer-interface-thesis.html</id>
      <updated>2025-12-01T00:00:00+00:00</updated>
      <content type="html">&lt;p&gt;After months of hard work, I’m thrilled to finally share the culmination of my Bachelor’s Thesis at the &lt;em&gt;Universitat Politècnica de València&lt;/em&gt;. What started as a curious exploration into the intersection of neuroscience and software engineering has evolved into a fully functional brain-computer interface system capable of analyzing EEG signals in real-time using deep learning techniques.&lt;/p&gt;

&lt;p&gt;This project, which I’ve named &lt;strong&gt;NeuralAnalytics&lt;/strong&gt;, represents not just the end of my academic journey, but also the beginning of something that I believe has genuine potential to improve lives. The core idea is straightforward but ambitious: capture brain signals, process them in real-time, and translate specific neural patterns into actionable commands—like turning on a light bulb using only the power of thought.&lt;/p&gt;

&lt;h3 id=&quot;the-vision-behind-the-project&quot;&gt;The Vision Behind the Project&lt;/h3&gt;

&lt;p&gt;When I first started thinking about what my Bachelor’s Thesis should be, I knew I wanted something challenging—something that would push me beyond the comfortable boundaries of conventional software development. My passion for informatics began in adolescence, but I wanted to apply it to a domain that could make a tangible difference in people’s lives. The idea of creating a system that could interpret human brain activity felt like the perfect intersection of my interests in embedded systems, deep learning, and real-time computing. What particularly motivated me was the potential to help individuals with motor disabilities—when I read about Stephen Hawking’s communication challenges, I realized this technology could genuinely improve quality of life for people who struggle with traditional interfaces. Additionally, I was driven by the desire to contribute to the scientific community. Rather than pursuing commercial gain, I chose to publish all code and models openly, hoping other researchers could build upon this work. The project represents not just an academic requirement, but a meaningful step toward making brain-computer interface technology more accessible and practical for everyday use.&lt;/p&gt;

&lt;p&gt;The project follows the regulatory framework of &lt;strong&gt;UNE-EN 62304&lt;/strong&gt; for medical device software, which added an extra layer of complexity but also gave me invaluable experience in developing software for critical applications. This wasn’t just about writing code that worked; it was about writing code that could be trusted.&lt;/p&gt;

&lt;h3 id=&quot;understanding-the-technical-challenge&quot;&gt;Understanding the Technical Challenge&lt;/h3&gt;

&lt;p&gt;The fundamental challenge of analyzing EEG signals in real-time lies in the nature of the data itself. Electroencephalographic signals are notoriously noisy, with artifacts from eye movements, muscle contractions, and electrical interference constantly threatening to overwhelm the actual neural patterns we’re trying to detect. Additionally, the brain regions we’re interested in—specifically the occipital and temporal lobes—produce signals that vary significantly between individuals and even between sessions for the same person.&lt;/p&gt;

&lt;p&gt;To tackle this, I designed a system architecture that separates concerns cleanly. The signal acquisition layer uses the &lt;strong&gt;BrainBit&lt;/strong&gt; device, a consumer-grade EEG headband that captures data from four channels (T3, T4, O1, O2). This data flows through a preprocessing pipeline that normalizes and segments the signals before feeding them to the deep learning model for classification.&lt;/p&gt;

&lt;p&gt;The classification task itself is framed around three states:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;RED&lt;/strong&gt;: A specific mental task indicating one command&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GREEN&lt;/strong&gt;: A different mental task indicating another command&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;TRASH&lt;/strong&gt;: Everything else—noise, artifacts, or ambiguous signals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This three-class approach allows the system to be conservative, defaulting to “TRASH” when the model isn’t confident, which is crucial for a real-time control system where false positives could be problematic.&lt;/p&gt;

&lt;h3 id=&quot;deep-learning-architecture-cnn-lstm-hybrid&quot;&gt;Deep Learning Architecture: CNN-LSTM Hybrid&lt;/h3&gt;

&lt;p&gt;After extensive experimentation with different model architectures, I settled on a hybrid approach combining &lt;strong&gt;Convolutional Neural Networks (CNN)&lt;/strong&gt; for spatial feature extraction with &lt;strong&gt;Long Short-Term Memory (LSTM)&lt;/strong&gt; networks for temporal pattern recognition.&lt;/p&gt;

&lt;p&gt;The rationale behind this design is grounded in the nature of EEG data. The CNN layers excel at extracting local patterns—frequency components, amplitude variations, and cross-channel relationships—while the LSTM layers capture the temporal dynamics that distinguish one mental state from another.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NeuralAnalyticsModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NeuralAnalyticsModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# CNN Feature Extractor
&lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conv1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Conv1d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_channels&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out_channels&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kernel_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bn1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BatchNorm1d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pool1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaxPool1d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kernel_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stride&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conv2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Conv1d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_channels&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out_channels&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kernel_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bn2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BatchNorm1d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pool2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaxPool1d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kernel_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stride&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;# LSTM Temporal Encoder
&lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lstm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LSTM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hidden_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_layers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;batch_first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bidirectional&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;# Classifier
&lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sequential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Linear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReLU&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dropout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Linear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Softmax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The input data is processed in windows of 62 samples with 50% overlap, providing sufficient context for pattern detection while maintaining real-time responsiveness. Each window passes through z-score normalization per channel, ensuring consistent feature scales regardless of signal amplitude variations.&lt;/p&gt;

&lt;h3 id=&quot;the-importance-of-data-normalization&quot;&gt;The Importance of Data Normalization&lt;/h3&gt;

&lt;p&gt;One aspect that significantly impacted model performance was the normalization strategy. Initially, I experimented with various approaches, but z-score normalization per window proved to be the most robust:&lt;/p&gt;

&lt;div style=&quot;margin: 0 auto; display: grid;&quot;&gt;
    &lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;
        &lt;mrow&gt;
            &lt;msub&gt;
                &lt;mi&gt;X&lt;/mi&gt;
                &lt;mi&gt;norm&lt;/mi&gt;
            &lt;/msub&gt;
            &lt;mo&gt;=&lt;/mo&gt;
            &lt;mfrac&gt;
                &lt;mrow&gt;
                    &lt;mi&gt;X&lt;/mi&gt;
                    &lt;mo&gt;-&lt;/mo&gt;
                    &lt;mi&gt;μ&lt;/mi&gt;
                &lt;/mrow&gt;
                &lt;mi&gt;σ&lt;/mi&gt;
            &lt;/mfrac&gt;
        &lt;/mrow&gt;
    &lt;/math&gt;
&lt;/div&gt;

&lt;p&gt;Where μ is the mean and σ is the standard deviation of each channel within the window. This approach accounts for the natural drift in EEG baseline values and ensures that the model focuses on relative patterns rather than absolute amplitudes.&lt;/p&gt;

&lt;h3 id=&quot;rust-based-inference-engine&quot;&gt;Rust-Based Inference Engine&lt;/h3&gt;

&lt;p&gt;For the inference side, I chose &lt;strong&gt;Rust&lt;/strong&gt; as the implementation language. This decision was driven by several factors: deterministic memory management for real-time constraints, excellent performance characteristics, and the availability of the &lt;strong&gt;Tract&lt;/strong&gt; library for ONNX model inference.&lt;/p&gt;

&lt;p&gt;The model trained in PyTorch is exported to ONNX format, allowing clean separation between the training environment (Python with GPU acceleration) and the inference environment (Rust on a Raspberry Pi 4). This architecture mirrors what I explored in my previous post about &lt;a href=&quot;/2024/10/25/creating-a-predictive-system-in-rust-and-pytorch/&quot;&gt;creating a predictive system in Rust and PyTorch&lt;/a&gt;, though the complexity here is significantly higher due to real-time constraints.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Default&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NeuralAnalyticsService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;tract_onnx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;onnx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.model_for_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;assets/neural_analytics.onnx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Failed to load model&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.with_input_fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;InferenceFact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dt_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;datum_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;tvec!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;62&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Failed to set input shape&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.into_optimized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Failed to optimize model&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.into_runnable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Failed to create runnable model&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;NeuralAnalyticsService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The state machine architecture handles the continuous signal flow, managing the buffering, preprocessing, and inference pipeline while maintaining strict timing constraints. The system runs on a &lt;strong&gt;Raspberry Pi 4 Model B (8GB)&lt;/strong&gt; with a real-time operating system configuration to guarantee response times.&lt;/p&gt;

&lt;h3 id=&quot;hardware-integration-and-smart-home-control&quot;&gt;Hardware Integration and Smart Home Control&lt;/h3&gt;

&lt;p&gt;One of the most satisfying aspects of this project was the tangible output: controlling a &lt;strong&gt;Tapo Smart Bulb&lt;/strong&gt; using brain signals. When the model detects a valid “GREEN” pattern with sufficient confidence, it triggers a state change in the smart bulb. The feedback loop is immediate and visceral—you think, and the light responds.&lt;/p&gt;

&lt;p&gt;The first time the system actually worked as intended was both surreal and incredibly rewarding. After weeks of debugging signal processing issues and model accuracy problems, I had one late-night session where everything finally clicked. I was wearing the BrainBit headband, focusing on associating the color green with a specific mental visualization (imagining a bright, energizing light), and when the Tapo bulb switched on reliably in response to that thought pattern, I literally jumped out of my chair.&lt;/p&gt;

&lt;p&gt;There were definitely funny moments along the way—like the time I kept getting false positives whenever I laughed, because the facial muscle movements were being misinterpreted as brain signals. Or when my cat walked across the keyboard during a recording session and somehow triggered a series of commands that made the light flicker erratically. These mishaps taught me valuable lessons about signal isolation and the importance of proper grounding.&lt;/p&gt;

&lt;p&gt;The most memorable moment came during a dry run for my thesis defense presentation. I had invited a few friends to watch, and when I successfully turned the lamp on and off three times in a row using only thought commands, the room erupted in cheers. It was validation not just of the technical work, but of the months of persistent troubleshooting and refinement that had led to that point.&lt;/p&gt;

&lt;p&gt;The BrainFlow SDK handles the Bluetooth communication with the BrainBit device, abstracting away the low-level protocol details and providing a clean streaming interface. This allowed me to focus on the signal processing and machine learning aspects without getting bogged down in hardware-specific implementation details.&lt;/p&gt;

&lt;h3 id=&quot;project-structure-and-code-organization&quot;&gt;Project Structure and Code Organization&lt;/h3&gt;

&lt;p&gt;The project follows a modular structure that separates concerns across different packages:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NeuralAnalytics/
├── packages/
│   ├── neural_analytics_core/     # Core Rust implementation
│   ├── neural_analytics_data/     # Data capture utilities
│   ├── neural_analytics_gui/      # GUI for signal visualization
│   └── neural_analytics_model/    # PyTorch model training
├── docs/                          # LaTeX thesis documentation
└── dataset/                       # Training data organized by class
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each package has clear responsibilities, and the boundaries between them are well-defined. This modular approach made iterative development much easier—I could refine the model training pipeline without touching the Rust inference code, and vice versa.&lt;/p&gt;

&lt;h3 id=&quot;challenges-and-lessons-learned&quot;&gt;Challenges and Lessons Learned&lt;/h3&gt;

&lt;p&gt;The technical journey presented several significant challenges that forced me to deepen my understanding across multiple domains.&lt;/p&gt;

&lt;p&gt;The hardest problem to solve was dealing with inter-session variability in EEG signals. Early in development, I noticed that a model trained on one day’s data would perform poorly the next day, even with the same subject and similar mental states. This wasn’t just about signal noise—it appeared to be fundamental shifts in the baseline neural patterns, possibly due to factors like fatigue, hydration levels, or even subtle changes in electrode positioning. I addressed this by implementing domain adaptation techniques and creating more robust normalization strategies that focused on relative patterns rather than absolute values.&lt;/p&gt;

&lt;p&gt;Another major challenge was meeting real-time constraints on the Raspberry Pi 4. The initial Python prototype had unacceptable latency (over 200ms), which made the system feel unresponsive. Migrating the inference engine to Rust with the Tract library reduced this to under 50ms, but required completely rethinking my approach to memory management and data buffering.&lt;/p&gt;

&lt;p&gt;If I were to start over, I would invest more time upfront in designing a subject-independent feature extraction pipeline. Rather than trying to normalize away individual differences after the fact, I’d explore techniques like Riemannian geometry for covariance matrices or transfer learning approaches that could leverage population data while adapting quickly to new users. I would also implement a more comprehensive signal quality monitoring system from the beginning, rather than adding it as an afterthought when poor signal quality ruined entire recording sessions.&lt;/p&gt;

&lt;p&gt;The journey wasn’t without its obstacles. One particularly challenging aspect was dealing with the variability in EEG signals between recording sessions. A model that performed excellently on one day’s data could struggle the next. This led me to implement more robust augmentation strategies and to be more careful about the stratification of training and validation sets.&lt;/p&gt;

&lt;p&gt;Another lesson was the importance of end-to-end testing. It’s one thing to achieve high accuracy on pre-recorded datasets, but real-time performance with a live signal stream is a different beast entirely. Latency, jitter, and the psychological pressure of a live demonstration all introduced factors that weren’t present in offline evaluation.&lt;/p&gt;

&lt;h3 id=&quot;media-coverage-and-public-recognition&quot;&gt;Media Coverage and Public Recognition&lt;/h3&gt;

&lt;p&gt;I was fortunate that this project caught the attention of major Spanish media outlets. &lt;a href=&quot;https://www.elespanol.com/reportajes/20251030/sergio-ingeniero-anos-creado-sistema-activar-bombillas-mente-puede-guiar-silla-hawking/1003743966876_0.html&quot;&gt;El Español published an article&lt;/a&gt; about the project, and I was invited to demonstrate the system live on &lt;a href=&quot;https://www.antena3.com/programas/y-ahora-sonsoles/sergio-ingeniero-que-enciende-bombillas-mente-nos-hace-demostracion-directo_2025112769288f0b55584d48fb600efa.html&quot;&gt;Antena 3’s “Y Ahora Sonsoles” program&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The media coverage was both unexpected and deeply humbling. When El Español reached out to feature the project, I was initially surprised that a technical thesis project would generate such interest, but it quickly became clear that the story resonated because it represented something tangible—technology that people could see and understand immediately.&lt;/p&gt;

&lt;p&gt;Appearing on Antena 3’s “Y Ahora Sonsoles” program was an entirely different experience. The studio environment with its bright lights, multiple cameras, and live audience created pressure I hadn’t anticipated. Unlike my controlled lab environment, I couldn’t retake failed attempts or adjust parameters on the fly. There was a moment during the live demo where the signal quality dropped due to movement artifacts, and for several tense seconds, the system wasn’t responding as expected. I had to calmly guide the host through the recalibration process while millions watched—a situation that tested both my technical knowledge and my ability to communicate under pressure.&lt;/p&gt;

&lt;p&gt;What struck me most was the genuine curiosity and enthusiasm from the audience and hosts. Rather than treating it as a magic trick, they asked thoughtful questions about how the technology actually worked, its limitations, and its potential applications. This reinforced my belief that when complex technology is explained accessibly, it can inspire meaningful conversations about innovation and its role in society.&lt;/p&gt;

&lt;p&gt;The public interest in this project has been overwhelming and humbling. It reinforced my belief that technology, when applied thoughtfully, has the potential to genuinely improve people’s lives—particularly for those with motor disabilities who could benefit from brain-computer interfaces for communication and control.&lt;/p&gt;

&lt;h3 id=&quot;technical-specifications-and-results&quot;&gt;Technical Specifications and Results&lt;/h3&gt;

&lt;p&gt;For those interested in the technical details, here’s a summary of the system specifications:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Component&lt;/th&gt;
      &lt;th&gt;Specification&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;EEG Device&lt;/td&gt;
      &lt;td&gt;BrainBit (4 channels: T3, T4, O1, O2)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Processing Platform&lt;/td&gt;
      &lt;td&gt;Raspberry Pi 4 Model B (8GB)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Model Architecture&lt;/td&gt;
      &lt;td&gt;CNN-LSTM Hybrid (16→32 conv, 32×2 LSTM)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Window Size&lt;/td&gt;
      &lt;td&gt;62 samples with 50% overlap&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Input Normalization&lt;/td&gt;
      &lt;td&gt;Z-score per channel per window&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Output Classes&lt;/td&gt;
      &lt;td&gt;3 (RED, GREEN, TRASH)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Inference Library&lt;/td&gt;
      &lt;td&gt;Tract (ONNX runtime for Rust)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Smart Device&lt;/td&gt;
      &lt;td&gt;Tapo Smart Bulb&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The model achieves reliable classification performance in controlled conditions, with the system maintaining real-time responsiveness on the constrained hardware platform.&lt;/p&gt;

&lt;h3 id=&quot;future-directions&quot;&gt;Future Directions&lt;/h3&gt;

&lt;p&gt;Looking ahead, I see brain-computer interface technology evolving along several exciting trajectories. The field is moving beyond simple binary control toward more nuanced, multi-dimensional interaction paradigms. I’m particularly excited about the potential for adaptive systems that can learn from users in real-time, reducing the calibration burden and accommodating natural variations in brain signals.&lt;/p&gt;

&lt;p&gt;My personal plans involve continuing to contribute to open-source BCI tools and frameworks. I believe the key to widespread adoption lies in making these technologies more accessible—not just from a cost perspective, but also in terms of usability and setup simplicity. I’m exploring ways to simplify the signal processing pipeline while maintaining robustness, potentially leveraging edge AI accelerators for more complex feature extraction.&lt;/p&gt;

&lt;p&gt;Specific applications I’m eager to explore include:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Communication aids&lt;/strong&gt;: Developing more sophisticated text-entry systems for individuals with severe motor impairments&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Neurofeedback applications&lt;/strong&gt;: Creating tools for mental wellness, attention training, and stress management&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Augmented reality interfaces&lt;/strong&gt;: Combining BCI with AR/VR for immersive, hands-free interaction&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Passive BCI&lt;/strong&gt;: Using brain signals not for explicit commands, but for implicit feedback to adapt interfaces based on cognitive load or emotional state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The convergence of BCI with other emerging technologies—like advanced materials for more comfortable electrodes, improved signal processing algorithms, and more intuitive machine learning approaches—promises to unlock applications we can barely imagine today.&lt;/p&gt;

&lt;p&gt;While this project represents the completion of my Bachelor’s Thesis, I don’t consider it finished. There are numerous avenues for improvement and extension:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Expanded command vocabulary&lt;/strong&gt;: Moving beyond binary control to multiple distinct commands&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Personalization pipelines&lt;/strong&gt;: Real-time adaptation to individual users without extensive retraining&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Alternative output modalities&lt;/strong&gt;: Integration with wheelchair controls, computer cursors, or speech synthesis&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Edge deployment optimization&lt;/strong&gt;: Quantization and pruning for even lower latency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The field of brain-computer interfaces is evolving rapidly, and I’m excited to continue contributing to it.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;This project has been one of the most challenging and rewarding experiences of my academic career. It pushed me to learn about domains I had never explored before—neuroscience, real-time systems, regulatory compliance for medical devices—while also deepening my expertise in areas I was already passionate about, like deep learning and Rust development.&lt;/p&gt;

&lt;p&gt;Reflecting on this project, I realize it has fundamentally transformed how I view the intersection of technology and human potential. Before NeuralAnalytics, I saw software engineering primarily as a tool for building efficient systems and solving logical puzzles. This thesis revealed to me that technology’s true power lies in its ability to extend human capabilities—especially for those whose abilities are limited by circumstance.&lt;/p&gt;

&lt;p&gt;The journey taught me that meaningful innovation requires more than technical skill; it demands empathy, interdisciplinary curiosity, and the courage to venture into unfamiliar territories. Working at the boundary of neuroscience and engineering forced me to learn new languages (both literal and figurative), to respect the complexity of biological systems, and to appreciate that sometimes the most elegant solutions come from embracing rather than fighting variability.&lt;/p&gt;

&lt;p&gt;Professionally, this experience has solidified my commitment to developing technology that serves people first. Whether I’m working on biometric systems at Facephi or exploring other domains, I now constantly ask: “Who does this serve? How does it improve lives? Is it accessible and ethical?” The project also gave me confidence to tackle ambitious, ill-defined problems—knowing that persistence, systematic experimentation, and learning from failure can lead to breakthroughs even in seemingly opaque domains like brain signal interpretation.&lt;/p&gt;

&lt;p&gt;Most importantly, NeuralAnalytics reminded me that engineering excellence isn’t just about writing correct code—it’s about creating systems that dignify and empower human experience. When I see someone smile as they turn on a light with their thoughts, I’m reminded why I fell in love with engineering in the first place: to build things that matter.&lt;/p&gt;

&lt;p&gt;The complete source code is available on &lt;a href=&quot;https://github.com/Neirth/NeuralAnalytics&quot;&gt;GitHub&lt;/a&gt; under the GPL-3.0 license. The repository includes the training code, the Rust inference engine, documentation, and everything needed to replicate or extend this work. I hope it serves as a useful reference for anyone interested in exploring the fascinating intersection of neuroscience and software engineering.&lt;/p&gt;

&lt;h3 id=&quot;credits&quot;&gt;Credits&lt;/h3&gt;

&lt;p&gt;The header image of this post is made using &lt;a href=&quot;https://www.midjourney.com/&quot;&gt;Midjourney AI&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
    
  
    
    <entry>
      <title>Creating a Predictive System using PyTorch and Rust</title>
      <link href="/2024/10/25/creating-a-predictive-system-in-rust-and-pytorch.html" />
      <id>/2024/10/25/creating-a-predictive-system-in-rust-and-pytorch.html</id>
      <updated>2024-10-25T00:00:00+00:00</updated>
      <content type="html">&lt;p&gt;I hadn’t published anything here for a while. Many adventures that I will surely have to document soon.&lt;/p&gt;

&lt;p&gt;This time, I wanted to practice with a project that I had been meaning to explore for some time. Setting up my first AI model beyond a classroom and integrating it with an application (which could have been made in Rust, Golang, Flutter, or any other technology, to be honest).&lt;/p&gt;

&lt;p&gt;As a curious fact, this project is inspired by the same technological foundations as the ‘Bachellor’s Thesis’ that I will present in a few months at the &lt;em&gt;Universitat Politècnica de València&lt;/em&gt; thus ending a stage that has been a true rollercoaster of emotions.&lt;/p&gt;

&lt;h3 id=&quot;understanding-the-underlying-problem&quot;&gt;Understanding the Underlying Problem&lt;/h3&gt;

&lt;p&gt;Previously, at the &lt;em&gt;Universitat Politècnica de València&lt;/em&gt;, I had worked with &lt;em&gt;TensorFlow&lt;/em&gt; and &lt;em&gt;Scikit Learn&lt;/em&gt;, creating many small Classification and Regression models. However, I was worried about having such a high degree of dependency on &lt;em&gt;TensorFlow&lt;/em&gt; and not exploring other frameworks like &lt;em&gt;PyTorch&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, I didn’t quite understand how to design a Deep Learning model that, using a context of previous values and their respective timestamps, could predict the next value of a series. I’m not talking about a series of features inputted as different inputs to return an output; I’m referring to a matrix as a feature whose values are time-dependent.&lt;/p&gt;

&lt;p&gt;So, I decided to take a chance and venture into researching this issue. To do this, I had to investigate the topic I was going to address to solve this problem. In this case, I was inspired by models predicting electrical loads, which are quite relevant for understanding the demand that an electrical grid might receive and, consequently, for adapting to it.&lt;/p&gt;

&lt;p&gt;Honestly, before diving deep into this, I particularly like the concept of decoupling elements. This generally helps avoid introducing unnecessary runtime dependencies, thereby minimizing the overall size of the application and introducing a good degree of modularity to the project.&lt;/p&gt;

&lt;h3 id=&quot;what-is-onnx-and-why-is-it-important-for-portability&quot;&gt;What is ONNX, and Why is it Important for Portability?&lt;/h3&gt;

&lt;p&gt;To understand ONNX thoroughly, it’s essential to clarify that Machine Learning and Deep Learning models are developed in two fundamental stages: the Training stage and the Inference stage. These phases do not necessarily have to run on the same device or under the same frameworks.&lt;/p&gt;

&lt;p&gt;Based on this, it becomes crucial to have a method for serializing the model after training, so it can then be used in the inference phase. For embedded systems, for example, TensorFlow developed the .tflite format, which I have previously tested with very good results on such devices. Additionally, the inference library from Sonos, Tract, provides excellent support for this format in Rust. However, I intended to explore options beyond the TensorFlow ecosystem.&lt;/p&gt;

&lt;p&gt;Once this is explained, this is where ONNX (Open Neural Network Exchange) plays a fundamental role, standing out as a neutral format independent of any specific framework, allowing the serialization of models that can be used with different tools and environments. In fact, ONNX has established itself as the de facto standard for facilitating the flexible deployment of models, even in cases where TensorFlow Lite presents limitations.&lt;/p&gt;

&lt;p&gt;To further understand the power of this format, we should consider that when we define a model in PyTorch, for example, this graph can ultimately be represented within the ONNX model.&lt;/p&gt;

&lt;p&gt;Let’s illustrate this with a good example; here we have the model we will use in this project, which we will explain how it works internally later on:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;torch&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;torch.nn&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;INPUT_SIZE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Number of features in the input
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HIDDEN_SIZE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Size of hidden units in the LSTM
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GridLSTMModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GridLSTMModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# LSTM with 4 layers
&lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lstm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LSTM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INPUT_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HIDDEN_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;batch_first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bidirectional&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# Using mean pooling to reduce the sequence
&lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Linear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HIDDEN_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Final fully-connected layer
&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# x shape: (batch_size, seq_length, input_size)
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;lstm_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lstm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# lstm_out: (batch_size, seq_length, hidden_size)
&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Compute the mean across the sequence dimension
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;mean_out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;torch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lstm_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# (batch_size, hidden_size)
&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Pass the reduced context through the fully-connected layer
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mean_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# (batch_size, 1)
&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And this is how the graph would look when visualized with tools like &lt;a href=&quot;https://netron.app&quot;&gt;Netron&lt;/a&gt; or similar:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2024-10-25-creating-a-predictive-system-in-rust-and-pytorch/grid_predictor.onnx.svg&quot; alt=&quot;Modelo de Red Neuronal Recurrente en ONNX&quot; style=&quot;width: 50%; margin: 0 auto; display: block;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The ability to perform an efficient conversion of models without relying on specific frameworks like &lt;em&gt;TensorFlow&lt;/em&gt; or &lt;em&gt;PyTorch&lt;/em&gt; for inference is what gives &lt;em&gt;ONNX&lt;/em&gt; its great utility. This format allows for easy and effective model exporting, facilitating implementation in production environments. This is exactly what we will need to quickly debug this project, as it means we can make changes to the model without impacting the code we write in &lt;em&gt;Rust&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;how-do-we-collect-and-preprocess-training-data&quot;&gt;How Do We Collect (and Preprocess) Training Data?&lt;/h3&gt;

&lt;p&gt;I have to be honest here; the &lt;a href=&quot;https://transparency.entsoe.eu&quot;&gt;ENTSOE Transparency Portal&lt;/a&gt; has been very helpful for retrieving the CSV dataset I used for this project. Additionally, its public API has allowed me to collect all the data related to real-time electricity demand.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;START_TIME&lt;/th&gt;
      &lt;th&gt;STOP_TIME&lt;/th&gt;
      &lt;th&gt;FORECAST_LOAD&lt;/th&gt;
      &lt;th&gt;REAL_LOAD&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;01/01/15 0:00&lt;/td&gt;
      &lt;td&gt;01/01/15 0:15&lt;/td&gt;
      &lt;td&gt;6794&lt;/td&gt;
      &lt;td&gt;6168&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;01/01/15 0:15&lt;/td&gt;
      &lt;td&gt;01/01/15 0:30&lt;/td&gt;
      &lt;td&gt;6757&lt;/td&gt;
      &lt;td&gt;6088&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;01/01/15 0:30&lt;/td&gt;
      &lt;td&gt;01/01/15 0:45&lt;/td&gt;
      &lt;td&gt;6791&lt;/td&gt;
      &lt;td&gt;6060&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;01/01/15 0:45&lt;/td&gt;
      &lt;td&gt;01/01/15 1:00&lt;/td&gt;
      &lt;td&gt;6750&lt;/td&gt;
      &lt;td&gt;5958&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;01/01/15 1:00&lt;/td&gt;
      &lt;td&gt;01/01/15 1:15&lt;/td&gt;
      &lt;td&gt;6737&lt;/td&gt;
      &lt;td&gt;6017&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;01/01/15 1:15&lt;/td&gt;
      &lt;td&gt;01/01/15 1:30&lt;/td&gt;
      &lt;td&gt;6692&lt;/td&gt;
      &lt;td&gt;5967&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;01/01/15 1:30&lt;/td&gt;
      &lt;td&gt;01/01/15 1:45&lt;/td&gt;
      &lt;td&gt;6722&lt;/td&gt;
      &lt;td&gt;5936&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;01/01/15 1:45&lt;/td&gt;
      &lt;td&gt;01/01/15 2:00&lt;/td&gt;
      &lt;td&gt;6690&lt;/td&gt;
      &lt;td&gt;5934&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;01/01/15 2:00&lt;/td&gt;
      &lt;td&gt;01/01/15 2:15&lt;/td&gt;
      &lt;td&gt;6633&lt;/td&gt;
      &lt;td&gt;5751&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;01/01/15 2:15&lt;/td&gt;
      &lt;td&gt;01/01/15 2:30&lt;/td&gt;
      &lt;td&gt;6573&lt;/td&gt;
      &lt;td&gt;5778&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;01/01/15 2:30&lt;/td&gt;
      &lt;td&gt;01/01/15 2:45&lt;/td&gt;
      &lt;td&gt;6602&lt;/td&gt;
      &lt;td&gt;5746&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;This is roughly what the dataset I’ve been using looks like, which is essentially a near-pure extract of what I received from the historical series. I say “near-pure extract” because I’ve renamed the columns for convenience. If we take a look, we can see that we have a column describing the expected load according to the model that the folks at &lt;a href=&quot;https://entsoe.eu&quot;&gt;ENTSOE&lt;/a&gt; have in production. With this, we could calculate the deviation of our model compared to their production model.&lt;/p&gt;

&lt;p&gt;If we want the model to learn based on this data, we will need to use the concept of sliding time windows so that it can predict the next value of the series conditioned by the hour and the day.&lt;/p&gt;

&lt;h3 id=&quot;how-do-we-design-and-export-the-prediction-model&quot;&gt;How Do We Design (and Export) the Prediction Model?&lt;/h3&gt;

&lt;p&gt;The model I have decided to use for this project is a Recurrent Neural Network (RNN), specifically an LSTM model. This type of model is very useful for working with time series, as it can remember information from previous steps and use it to predict the next value.&lt;/p&gt;

&lt;p&gt;In this case, the model I designed is an LSTM model that takes a sequence of values and returns a single value. To incorporate time information into the model, I decided to use a sinusoidal representation, which involves representing values such as the day of the year or the hour of the day as a sine wave.&lt;/p&gt;

&lt;p&gt;In simple terms, to understand what the LSTM operator does: an LSTM is a type of recurrent neural network that can remember information from previous steps and use it to predict the next value. In this case, the model I designed is an LSTM model that takes a sequence of values and returns a single value.&lt;/p&gt;

&lt;p&gt;To do this, I had to tackle the challenge of creating a function that takes the entire DataFrame and from there can create the sliding time window. To achieve this, I created a function that takes the DataFrame and the window size as input and returns a sequence of tensors with the time information encoded in sinusoidal form. Below is the code for the function:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_daily_sliding_windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalize_by_year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Convert TIMESTAMP to datetime for easier grouping
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;TIMESTAMP&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to_datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;TIMESTAMP&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;ns&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Create the sinusoidal components
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;day_of_year&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;TIMESTAMP&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dayofyear&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;minutes_of_day&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;TIMESTAMP&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hour&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;TIMESTAMP&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minute&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Sinusoidal component of the day in the year
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;day_sin&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;day_of_year&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;365&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Sinusoidal component of the minutes in the day
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;minute_sin&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;minutes_of_day&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1440&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 1440 = 60 * 24 (minutes in a day)
&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Group by day
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;daily_groups&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;groupby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;TIMESTAMP&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;windows&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;daily_groups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Check if the group has enough data to create windows
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Create sliding windows for each daily group
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window_values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;GLOBAL_LOAD_TOTAL&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;next_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;GLOBAL_LOAD_TOTAL&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;# Add the sinusoidal components to the window
&lt;/span&gt;                &lt;span class=&quot;n&quot;&gt;window_day_sin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;day_sin&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;window_minute_sin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;minute_sin&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_day_sin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_minute_sin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;next_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[!] The group for &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; has fewer records than the window size (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &amp;lt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window_size&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Check if any windows were generated
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[!] No sliding windows have been generated, check the size of your groups and the window.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Convert the list of windows to a DataFrame
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;window_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;windows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;window_values&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;day_sin&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;minute_sin&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;next_value&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[*] Data generated from the raw dataset:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[*] Preprocessed dataset to a two-column matrix...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_df&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are likely other, more effective ways to represent time in the model, but I chose to use this representation for its simplicity and effectiveness. It’s important to note that to incorporate the time information into the model, I had to scale the network’s point load values between 0 and 1 so that the model could learn more efficiently, we take later a more consideration about this. To do this, I normalized the network load values using the following function:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;normalize_by_year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Extract the year from the TIMESTAMP for grouping
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;TIMESTAMP&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to_datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;START_TIME&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;year&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;TIMESTAMP&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Initialize the scaler
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;scaler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MinMaxScaler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Create a new DataFrame with the normalized values
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;normalized_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Group by year and normalize GLOBAL_LOAD_TOTAL
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;groupby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;year&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Normalize the values for each year
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;GLOBAL_LOAD_TOTAL&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reshape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;normalized_values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scaler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit_transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# Replace the values in the original DataFrame
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;normalized_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;year&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;GLOBAL_LOAD_TOTAL&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalized_values&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Convert the TIMESTAMP to an integer for easier processing
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;normalized_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;TIMESTAMP&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;TIMESTAMP&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;astype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Drop the year column as it&apos;s no longer needed
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;normalized_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;year&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;START_TIME&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inplace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[*] Normalized GLOBAL_LOAD_TOTAL by year.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;normalized_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalized_df&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this way, the remaining model can take all this information into account to predict the next value of the time series. One not-so-obvious consideration, but which is very important for our AI models to learn efficiently, is data normalization. In this case, after normalizing the data, the model can learn effectively and make accurate predictions. For this normalization, I used the MinMaxScaler function from the Scikit Learn library. Which formula is as follows:&lt;/p&gt;

&lt;div style=&quot;margin: 0 auto; display: grid;&quot;&gt;
    &lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;
        &lt;mrow&gt;
            &lt;mi&gt;X_norm&lt;/mi&gt;
            &lt;mo&gt;=&lt;/mo&gt;
            &lt;mfrac&gt;
                &lt;mrow&gt;
                    &lt;mi&gt;X&lt;/mi&gt;
                    &lt;mo&gt;-&lt;/mo&gt;
                    &lt;mi&gt;X&lt;/mi&gt;
                    &lt;mo&gt;_&lt;/mo&gt;
                    &lt;mi&gt;min&lt;/mi&gt;
                &lt;/mrow&gt;
                &lt;mrow&gt;
                    &lt;mi&gt;X&lt;/mi&gt;
                    &lt;mo&gt;_&lt;/mo&gt;
                    &lt;mi&gt;max&lt;/mi&gt;
                    &lt;mo&gt;-&lt;/mo&gt;
                    &lt;mi&gt;X&lt;/mi&gt;
                    &lt;mo&gt;_&lt;/mo&gt;
                    &lt;mi&gt;min&lt;/mi&gt;
                &lt;/mrow&gt;
            &lt;/mfrac&gt;
        &lt;/mrow&gt;
    &lt;/math&gt;
&lt;/div&gt;

&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;(&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;X&lt;/mi&gt;&lt;mo&gt;_&lt;/mo&gt;&lt;mi&gt;norm&lt;/mi&gt;&lt;/math&gt;) is the normalized value.&lt;/li&gt;
  &lt;li&gt;(&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;X&lt;/mi&gt;&lt;/math&gt;) is the original value.&lt;/li&gt;
  &lt;li&gt;(&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;X&lt;/mi&gt;&lt;mo&gt;_&lt;/mo&gt;&lt;mi&gt;min&lt;/mi&gt;&lt;/math&gt;) is the minimum value in the dataset, in this case, the minimum in the year.&lt;/li&gt;
  &lt;li&gt;(&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;X&lt;/mi&gt;&lt;mo&gt;_&lt;/mo&gt;&lt;mi&gt;max&lt;/mi&gt;&lt;/math&gt;) is the maximum value in the dataset, in this case, the maximum in the year.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have noticed that without this normalization, the model is unable to learn efficiently and, consequently, cannot make accurate predictions. Therefore, it is essential to normalize the data before inputting it into the model so that the model can learn effectively and make precise predictions.&lt;/p&gt;

&lt;p&gt;To assess the performance of the model, I have decided to use the R² statistic (coefficient of determination) to evaluate the model’s ability to predict the electric grid load. I will also use this statistic to calculate the deviation of the model compared to the production model from ENTSOE.&lt;/p&gt;

&lt;p&gt;The formula for calculating the R² statistic is as follows:&lt;/p&gt;

&lt;div style=&quot;margin: 0 auto; display: grid;&quot;&gt;
    &lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;
        &lt;mrow&gt;
            &lt;mi&gt;R&lt;/mi&gt;
            &lt;mo&gt;^&lt;/mo&gt;
            &lt;mn&gt;2&lt;/mn&gt;
            &lt;mo&gt;=&lt;/mo&gt;
            &lt;mn&gt;1&lt;/mn&gt;
            &lt;mo&gt;-&lt;/mo&gt;
            &lt;mfrac&gt;
                &lt;mrow&gt;
                    &lt;mi&gt;SS&lt;/mi&gt;
                    &lt;mo&gt;_&lt;/mo&gt;
                    &lt;mi&gt;res&lt;/mi&gt;
                &lt;/mrow&gt;
                &lt;mrow&gt;
                    &lt;mi&gt;SS&lt;/mi&gt;
                    &lt;mo&gt;_&lt;/mo&gt;
                    &lt;mi&gt;tot&lt;/mi&gt;
                &lt;/mrow&gt;
            &lt;/mfrac&gt;
        &lt;/mrow&gt;
    &lt;/math&gt;
&lt;/div&gt;

&lt;p&gt;Where:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;(&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;SS&lt;/mi&gt;&lt;mo&gt;_&lt;/mo&gt;&lt;mi&gt;res&lt;/mi&gt;&lt;/math&gt;) is the residual sum of squares.&lt;/li&gt;
  &lt;li&gt;(&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;SS&lt;/mi&gt;&lt;mo&gt;_&lt;/mo&gt;&lt;mi&gt;tot&lt;/mi&gt;&lt;/math&gt;) is the total sum of squares.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this, we can calculate the R² statistic and evaluate the model’s performance in predicting the electric grid load. This will allow us to compare the model’s performance with the production model from ENTSOE and determine the deviation of the model compared to the production model.&lt;/p&gt;

&lt;p&gt;Finally, to export the model to ONNX, I used the ONNX library from PyTorch, which allows for easy export of PyTorch models to the ONNX format. To do this, I used the following function:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;export_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
    Exports a PyTorch model to ONNX format and simplifies the ONNX model.

    :param model: PyTorch model to be exported.
    :param device: PyTorch device being used for training.
    :param input_size: Input size of the model (e.g., (batch_size, channels, height, width)).
    :param output_path: Path where the ONNX model will be saved.
    &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Set the model to evaluation mode
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Create a dummy input tensor
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;dummy_input&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;torch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Check and create the export directory if it doesn&apos;t exist
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;output_dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makedirs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;[*] Directory created: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Export the model to ONNX format
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;torch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onnx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dummy_input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;export_params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;opset_version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;do_constant_folding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Constant optimization
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;input_names&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;input&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;output_names&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;output&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dynamic_axes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&apos;input&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;batch_size&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Dynamic axis for batch size
&lt;/span&gt;            &lt;span class=&quot;s&quot;&gt;&apos;output&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;batch_size&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Load the ONNX model for simplification
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;model_onnx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onnx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Save the simplified model
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;onnx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_onnx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;[*] Model exported and simplified to: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this, we now have the model exported to ONNX and ready to be loaded in Rust for making predictions. Next, it’s time to deploy the model in the application and enable it to make real-time predictions.&lt;/p&gt;

&lt;h3 id=&quot;deploying-the-model-in-the-application&quot;&gt;Deploying the Model in the Application&lt;/h3&gt;

&lt;p&gt;We already understand how the model works, and we have clarified how we preprocessed the information to make the learning effective. Now it’s time to make the model deployable in an application that can be consumed from production.&lt;/p&gt;

&lt;p&gt;This project, just like I would like it to be in my “Bachellor’s Thesis”, operates with a core in Rust that handles loading the ONNX model and making predictions. For this, I used the Tract library, which, as I mentioned earlier, is a neural network inference library in Rust that supports the ONNX format.&lt;/p&gt;

&lt;p&gt;The great thing about Tract is that it is written in Rust, and its low-level operations are implemented in Assembly, making it very efficient and fast. Additionally, Tract is very easy to use and has a straightforward API that allows you to load an ONNX model and make predictions with it in just a few lines of code.&lt;/p&gt;

&lt;p&gt;To load the ONNX model in Tract, I used the following function:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Default&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PredictLoadService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PredictLoadService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;PathBuf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;assets/grid_predictor.onnx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;tract_onnx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;onnx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.model_for_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OS: Failed to read model file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.with_input_fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;InferenceFact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dt_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;datum_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;tvec!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OS: Failed to set input shape&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.into_optimized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OS: Failed to optimize model&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.into_runnable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OS: Failed to create runnable model&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;PredictLoadService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And to effectively utilize the model, I created a function that takes a time window and returns the model’s prediction:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PredictLoadService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;predict_load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_data&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;19&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BUG: Input data must have 19 elements&quot;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_data&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.flat_map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;|(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;day_sin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;minute_sin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;generate_sin_components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;.map_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;format!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BUG: Error generating sin components -&amp;gt; {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

                &lt;span class=&quot;nd&quot;&gt;vec!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;day_sin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;minute_sin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.collect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Convert the values to a Tensor with the appropriate shape [1, 19, 3]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_tensor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Tensor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.map_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;format!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BUG: Error creating input tensor -&amp;gt; {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Run the model&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.model&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nd&quot;&gt;tvec!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_tensor&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.into&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.map_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;format!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BUG: Error running model -&amp;gt; {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Extract the output value&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.to_scalar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.map_err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;nd&quot;&gt;format!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BUG: Error extracting output -&amp;gt; {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As we can see, the code remains clean and easy to understand. Additionally, Tract allows us to load the model very efficiently and make predictions with it in just a few lines of code. This is what I enjoyed about working with Tract, and it has enabled me to effectively integrate the model into the application.&lt;/p&gt;

&lt;p&gt;To retrieve production data, I used the &lt;a href=&quot;https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html&quot;&gt;ENTSOE API&lt;/a&gt;, which allowed me to obtain real-time production data and make predictions with the model. In the end, it all comes down to making API calls, and I think explaining how it’s done would be a bit redundant. So, dear reader, I invite you to explore the project’s source code to see how I retrieved the production data and made predictions with the model.&lt;/p&gt;

&lt;p&gt;Out of curiosity, I wanted to extract the R² statistic to compare the deviation of my model against the ENTSOE production model. Using the Scikit Learn library, I was able to calculate it and obtained the following values after finishing the model training:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[neirth@beast-dragon electrical_grid_model] $ python3 src/main.py
[*] Training module for the &quot;Electrical_Grid&quot; model
[*] The device to be used will be &quot;mps&quot;
...
[*] Training completed in 1026.80 seconds.
[*] R^2 of the model with the evaluation set: 0.9973
[*] R^2 of the production forecast: 0.8754
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As we can see, the model achieved an R² value of 0.9973, indicating that it can predict the electrical grid load with an accuracy of 99.73%. On the other hand, the ENTSOE production model obtained an R² value of 0.8754, suggesting that the ENTSOE production model can predict the electrical grid load with an accuracy of 87.54%.&lt;/p&gt;

&lt;p&gt;This means that the model I developed is capable of predicting the electrical grid load with much higher accuracy than the ENTSOE production model. This is a very positive result, as it far exceeds the objective I had set at the beginning of the project.&lt;/p&gt;

&lt;h3 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h3&gt;

&lt;p&gt;It has been a very interesting and enriching project, allowing me to explore new technologies and learn a lot about developing AI models and deploying them in production applications. Additionally, it has enabled me to explore the use of ONNX and Tract, which are fascinating technologies with great potential for the development of AI applications in production.&lt;/p&gt;

&lt;p&gt;This project is published in the &lt;a href=&quot;https://github.com/Neirth/ElectricalGridPredictor&quot;&gt;GitHub repository&lt;/a&gt; if you want to take a look at the source code and see how I developed the model and deployed it in the application. In the repository, you will also find everything I used to calculate the results and make predictions with the model, making it all replicable.&lt;/p&gt;

&lt;h3 id=&quot;credits&quot;&gt;Credits&lt;/h3&gt;

&lt;p&gt;The header image of this post is made using &lt;a href=&quot;https://www.midjourney.com/&quot;&gt;Midjourney AI&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
    
  
    
    <entry>
      <title>Exploring OpenCL for accelerate processes in Backend Side</title>
      <link href="/2023/04/28/exploring-opencl-for-accelerate-processes.html" />
      <id>/2023/04/28/exploring-opencl-for-accelerate-processes.html</id>
      <updated>2023-04-28T00:00:00+00:00</updated>
      <content type="html">&lt;p&gt;I was interested in learning about the effective parallelism of certain heavy operations, and how these could be better leveraged in a fairly demanding environment, such as a data center. The result, therefore, was to learn more about OpenCL and how it could be exploited beyond servers to speed up processes.&lt;/p&gt;

&lt;h3 id=&quot;undestanding-the-problem-behind&quot;&gt;Undestanding the problem behind&lt;/h3&gt;

&lt;p&gt;Mainly, the issue we are going to address lies in how we could speed up certain processes that might otherwise be quite costly on the wrong device.&lt;/p&gt;

&lt;p&gt;We must understand that in the end in most servers, all computing power is being derived to the processor. This is usually shared by Hypervisor Type 1, different cores that must access in shared time with other cores of other operating systems, and then the services that make this can interact between the core and the real world, in other words, auxiliary services so that our application can run.&lt;/p&gt;

&lt;p&gt;Translated into common language, we have the processor busy with a thousand tasks which we must know how to manage very well so that our application can quickly attend to our requests. If our service also has to perform queries to external computer services, in the end we are totally wasting the capacity of our machine.&lt;/p&gt;

&lt;p&gt;This problem has been the subject of study in academia for years, where work has been ongoing on better algorithms to improve effective CPU utilization. Although a little more than a decade ago there was experimentation to introduce an additional player in this segment of computation. I am effectively talking about the GPGPU concept.&lt;/p&gt;

&lt;p&gt;In the academic world there was a lot of interest in taking advantage of the potential of these devices that were being wasted, not without criticism of course.&lt;/p&gt;

&lt;p&gt;It was realized that GPUs were very specialized devices in one type of computation, vector and matrix computation. This type of computation, largely specialized to be able to generate at an acceptable Frames per Second rate in terms of real time, could be exploited for different scientific applications, or technologies that could require a fairly high computational capacity.&lt;/p&gt;

&lt;p&gt;As these devices were relieved of the responsibility of having to manage an operating system, or in the worst case a Type 1 Hypervisor running different operating systems, they became a very interesting option to be able to launch workflows waiting for an answer very soon.&lt;/p&gt;

&lt;h3 id=&quot;how-works-opencl-and-how-coordinates-with-the-cpu&quot;&gt;How works OpenCL and how coordinates with the CPU&lt;/h3&gt;

&lt;p&gt;Before introducing OpenCL, it is worth mentioning that it can be used for GPUs, FPGAs, NPUs and even CPUs [4]. This is known as a hetereogeneous computing framework, and normally, although it is mostly exploited in servers, it is not formally linked to the server world. (Probably your mobile device has OpenCL drivers and you didn’t realize it).&lt;/p&gt;

&lt;p&gt;OpenCL works with a queuing system allowing the CPU to effectively delegate the workload to the intensive processing unit.&lt;/p&gt;

&lt;p&gt;This has a very clear advantage of being able to free up the CPU so that it is taking care of other tasks without having to worry about whether or not it will be processing our request full time. Bye bye CPU Context Changes (For now).&lt;/p&gt;

&lt;p&gt;It also allows us to manage from our application the memory regions that the processing unit will have available. This is possible thanks to the fact that complete memory arrays can be transferred to it, or even exploit those of the host itself. The latter has innumerable advantages such as random access to information, where we can avoid having to copy large blocks of memory between transactions. This of course depends on what type of application we want to develop and if it is going to be appropriate or not.&lt;/p&gt;

&lt;p&gt;Another point that makes OpenCL interesting is that its programming language is based on one already known by many. It is syntactically based on the C language (I would say that it is based on the C99 standard for some aspects that I have been seeing). Although it is based on C, it is important to take into account that we will not have access to libraries such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#import &amp;lt;strings.h&amp;gt;&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#import &amp;lt;stdio.h&amp;gt;&lt;/code&gt;, or external libraries such as Boost’s, this is because since In the beginning we have access to the limited OpenCL functions that are defined within their standard.&lt;/p&gt;

&lt;p&gt;It must be taken into account that the limitation described above is given because we are trying to make our code compatible with the largest amount of hardware that has certified drivers.&lt;/p&gt;

&lt;p&gt;In the implementation of OpenCL drivers, there is a trend towards using the SPIRV binary format, which is also used by Vulkan shaders. This trend aims to simplify graphics drivers to focus on efficient Vulkan driver development. For example, Portable Computing Language (PoCL) also allows OpenCL to be used on devices that only have Vulkan drivers, such as the Raspberry Pi 4 [5]. Intel uses SPIRV as a binary format for their OpenCL drivers [6]. However, NVIDIA uses its own PTX format [3], which is incompatible between platforms, an important aspect to consider.&lt;/p&gt;

&lt;h3 id=&quot;implementing-matrix-transpose-as-a-hello-world-example&quot;&gt;Implementing Matrix Transpose as a Hello World Example&lt;/h3&gt;

&lt;p&gt;In this example we are going to try to understand how we can speed up our calculations to transpose a matrix, in a paradigm other than parallelism, this would be developed through a succession of iterations that go through the entire matrix and copy the new result to a result matrix.&lt;/p&gt;

&lt;p&gt;In OpenCL we are going to have to think about how we are going to divide the problem into smaller problems, and how we are going to be able to solve them in parallel. In this case, we are going to divide the matrix into rows and columns and we are going to assign each row and column to a work item. This will allow us to have a fairly simple solution to the problem.&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;__kernel&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;transpose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;odata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Calculate the global index&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_global_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_global_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// Calculate the coordenates in the matrix&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// Calculate the index in the original matrix and the index in the transpose matrix&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index_in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index_out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// Copy the value from the original matrix to the transpose matrix&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;odata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;OpenCL incorporates a concept of multi-universal worker instantiation. This allows us to assign different ID’s for each dimension in which a specific worker is located. This will be very useful to further speed up certain types of matrix operations.&lt;/p&gt;

&lt;p&gt;Finally, if we come from programming in other programming languages that are similar to C, we will have noticed that the function is designated as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__kernel&lt;/code&gt; and as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void&lt;/code&gt; return value, this is because OpenCL forces it to have it structured in this way, in the same way that it needs to know what parameters we expect to introduce in the program that we are going to call.&lt;/p&gt;

&lt;p&gt;An OpenCL kernel is the sum of a program plus its respective arguments plus a queue definition so that requests to the device can be queued.&lt;/p&gt;

&lt;p&gt;The result will be sent through a result matrix to the host program, this is a common practice in OpenCL programs.&lt;/p&gt;

&lt;p&gt;For the Host Program, we will write a Rust program for catch the result from OpenCL program. In this case, we will use the &lt;a href=&quot;https://crates.io/crates/ocl&quot;&gt;ocl&lt;/a&gt; crate.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cd&quot;&gt;/// Transpose a matrix using OpenCL&lt;/span&gt;
&lt;span class=&quot;cd&quot;&gt;///&lt;/span&gt;
&lt;span class=&quot;cd&quot;&gt;/// # Arguments&lt;/span&gt;
&lt;span class=&quot;cd&quot;&gt;///&lt;/span&gt;
&lt;span class=&quot;cd&quot;&gt;/// * `width` - Width of the matrix&lt;/span&gt;
&lt;span class=&quot;cd&quot;&gt;/// * `height` - Height of the matrix&lt;/span&gt;
&lt;span class=&quot;cd&quot;&gt;/// * `matrix` - Matrix to transpose&lt;/span&gt;
&lt;span class=&quot;cd&quot;&gt;///&lt;/span&gt;
&lt;span class=&quot;cd&quot;&gt;/// # Returns&lt;/span&gt;
&lt;span class=&quot;cd&quot;&gt;///&lt;/span&gt;
&lt;span class=&quot;cd&quot;&gt;/// * `matrix_output` - Transpose matrix&lt;/span&gt;
&lt;span class=&quot;cd&quot;&gt;///&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;transpose_matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ocl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Build the program into device driver&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;program&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ProQue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accel_src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.dims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ocl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;SpatialDims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Create memory buffer between hardware accelerator and main ram&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix_buff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;program&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.buffer_builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.copy_host_slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Create memory buffer between hardware accelerator and main ram&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result_buff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;program&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.create_buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Prepare program with arguments to build kernel&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kernel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;program&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.kernel_builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;transpose&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;nf&quot;&gt;.arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result_buff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;nf&quot;&gt;.arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matrix_buff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;nf&quot;&gt;.arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;nf&quot;&gt;.arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;nf&quot;&gt;.build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// Run the kernel inside the device and wait for the result.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kernel&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.enq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Prepare output matrix&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix_output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;vec!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0f32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()];&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Transfer matrix into the main memory&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result_buff&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.enq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Return the traspose matrix&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matrix_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this way, we have already generated a wrapper that abstracts the call to OpenCL in a method that will be a black box for whoever wants to use it.&lt;/p&gt;

&lt;h3 id=&quot;implementing-shortest-path-algorithm-as-a-complete-computation-example&quot;&gt;Implementing Shortest Path Algorithm as a Complete Computation Example&lt;/h3&gt;

&lt;p&gt;In this case I was working on a small project where I would introduce operations to obtain the shortest path possible. For this I was inspired to introduce Dijkstra’s algorithm[2] for the shortest path, from that point I had to consider it so that it was parallelizable.&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;__kernel&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize_algorithm_buffers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vertex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vertex_temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Get the global id based on count of vertexs and assigned for thread&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_global_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Initialize the buffers in parallel&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FLT_MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vertex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vertex_temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;__kernel&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;shortest_path_algorithm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vertex_temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vertex_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Get the global id based on count of vertexs and assigned for thread&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_global_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Validate if the vertex is not visited&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Mark the vertex as visited&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Get the start edge&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vertex_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edge&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Get the edge from adjacent matrix&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vertex_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// Validate if the edge is valid&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FLT_MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Get the distance&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;c1&quot;&gt;// Get the result&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;vertex_temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;__kernel&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;merge_sortest_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vertex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__global&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vertex_temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Get the global id based on count of vertexs and assigned for thread&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_global_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Get the result&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;vertex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vertex_temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Reset the visited flag&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this case I had to divide the algorithm into three different cores, where it is differentiated from the initial algorithm. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize_algorithm_buffers&lt;/code&gt; kernel is formalized so that we can take advantage of the device’s time to initialize memory.&lt;/p&gt;

&lt;p&gt;The next step is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shortest_path_algorithm&lt;/code&gt;, where we will do all the comparative logic to be able to develop the algorithm as it was established in its day.&lt;/p&gt;

&lt;p&gt;Finally, we have the core of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merge_sortest_path&lt;/code&gt;, where the changes between the temporary matrix and the result matrix will be evaluated to introduce them safely. This is necessary since, for example, in a device such as an NVIDIA GPU [3] there are no concurrent access problems as usual, when trying to execute an OpenCL program within a CPU, context changes can occur that prevent the thread from executing at the same time. same time as the rest of the workers, which can cause inconsistent states in the result. And it is important to remember, your OpenCL program does not know where it will end up running, so these cases are mandatory to consider to avoid disasters.&lt;/p&gt;

&lt;p&gt;For this time I will omit some of the technical details of how the host program was structured, but for those who are interested, you can take a look at the repo where I published everything I was learning about the capabilities of this technology. Link to repo: &lt;a href=&quot;https://github.com/Neirth/PathWalker&quot;&gt;Path Walker - GitHub Repo&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Through this article we have been able to evaluate the real capabilities of this technology. In addition to discovering that it is based on a language familiar to developers, it also allows us to think from the point of view of the maximum use of the hardware that we can equip a server with.&lt;/p&gt;

&lt;p&gt;From an external point of view, it may not be possible to see where this technology can shine. But in the real world, matrices and graphs are often used for everything from finding the shortest way to drive your car to understanding what’s in front of you through computer vision. Together with CUDA, this technology is also having a second life for the world of Artificial Intelligence through, for example, Tensorflow.&lt;/p&gt;

&lt;p&gt;It is very interesting to see that OpenCL is supported by the Khronos group, precursors of OpenGL and Vulkan, and that it is in good health by the big driver vendors.&lt;/p&gt;

&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;

&lt;p&gt;[1] Midjourney. “Header image of a man walking on a trail”. https://midjourney.com.&lt;/p&gt;

&lt;p&gt;[2] T. G. Mattson, D. Ginsburg, B. Gaster, and A. Munshi, “OpenCL Programming Guide,” Pearson Education, 2011.&lt;/p&gt;

&lt;p&gt;[3] NVIDIA, “Parallel Thread Execution ISA Version 8.1”, https://docs.nvidia.com/cuda/parallel-thread-execution/&lt;/p&gt;

&lt;p&gt;[4] Khronos Group, “OpenCL Overview”, https://www.khronos.org/opencl/&lt;/p&gt;

&lt;p&gt;[5] PoCL Developers, “PoCL - Portable Computing Language”, https://github.com/pocl/pocl&lt;/p&gt;

&lt;p&gt;[6] Intel Corporation, “SPIR-V*: Default Interface to Intel® Graphics Compiler for OpenCL™ Workloads”, https://www.intel.com/content/www/us/en/developer/articles/case-study/spir-v-default-interface-to-intel-graphics-compiler-for-opencl-workloads.html&lt;/p&gt;
</content>
    </entry>
    
  
    
    <entry>
      <title>Making my own IoT backend-less vending machine</title>
      <link href="/2021/03/12/making-my.own-iot-backend-less-vending-machine.html" />
      <id>/2021/03/12/making-my.own-iot-backend-less-vending-machine.html</id>
      <updated>2021-03-12T00:00:00+00:00</updated>
      <content type="html">&lt;p&gt;I am finishing my specialty in Multi-Platform Application Development before starting my university studies. Honestly, I’ve been invited to create a project to showcase my new skills. After thinking about it and weighing a few ideas, I decided to develop a project that would let me dive into the world of IoT and serverless programming.&lt;/p&gt;

&lt;p&gt;In this case, I decided to create a ticket vending machine for events that doesn’t need a central server to operate. The idea is that the machine can issue and validate event tickets autonomously, using IoT principles and making Android the core of the user interface. Firebase became my go-to choice for managing the data layer. With it, I was able to build the database structure for tickets and advertisements without maintaining a traditional backend, and use Firestore to handle tickets, advertisements, and the entire ticket lifecycle.&lt;/p&gt;

&lt;h3 id=&quot;understanding-the-concept&quot;&gt;Understanding the Concept&lt;/h3&gt;

&lt;p&gt;The main challenge was to design a vending setup that could issue and validate event tickets autonomously. That meant building a system that could authenticate, store, and process everything on the edge, using IoT principles and making Android the center for the UI. Firebase became my go-to for managing the data layer. With it, I could build out the database structure for tickets and ads without maintaining a traditional backend, and use Firestore for handling tickets, announcements, and the entire ticket lifecycle.&lt;/p&gt;

&lt;p&gt;There’s an architectural angle here that makes this project interesting. With hexagonal architecture, the business logic can stay independent from the implementation details of specific hardware or even database choice, making it relatively easy to adapt the design for future hardware or requirements. Hexagonal architecture also allowed me to modularize the ticket validation and user interfaces without one depending directly on the other.&lt;/p&gt;

&lt;h3 id=&quot;building-out-the-core-system&quot;&gt;Building Out the Core System&lt;/h3&gt;

&lt;p&gt;With the architecture in place, the next step was diving into the setup for Firestore collections and Firebase Authentication, which would help keep things secure. I structured the data collections to capture concerts, tickets, and advertisements separately. With this, the vending machine could dynamically serve up ads in between purchases or during idle times. For tickets, I used Apple’s Passbook format to issue each one with a downloadable QR code, which the machine could validate on-site. That saved me a lot of trouble, as it meant the tickets were compatible with existing mobile wallets and minimized the data exchange needed between devices.&lt;/p&gt;

&lt;p&gt;For the UI and validation interfaces, I turned to JavaFX and Android SDK. The Android device displayed the main interface, while a second device handled QR validation and triggered the access permissions. The ticketing and validation systems both use a few simple interfaces to make calls to Firestore, and I kept it flexible so I could change the data source without affecting the overall flow.&lt;/p&gt;

&lt;h3 id=&quot;handling-real-world-constraints&quot;&gt;Handling Real-World Constraints&lt;/h3&gt;

&lt;p&gt;One of the biggest constraints was making sure the vending machine could work autonomously, with minimal need for maintenance. I had to ensure that user sessions and data like QR codes and tickets expired after a given period to avoid bloating memory and losing performance over time. Each ticket has a status field that updates once it’s used, so validation is straightforward and efficient. The whole system feels robust enough to handle event-level traffic without the kind of congestion that would come with multiple round-trips to a central server.&lt;/p&gt;

&lt;p&gt;Another core piece is the emulator I used to simulate coin insertion for testing payments, which allowed me to validate the sequence end-to-end without a physical coin interface. I kept it simple: a timer increments the “inserted” count, and it’s easy enough to switch this to a real-world currency handler later on if needed.&lt;/p&gt;

&lt;h3 id=&quot;diving-into-firebase-and-data-handling&quot;&gt;Diving into Firebase and Data Handling&lt;/h3&gt;

&lt;p&gt;Since there was no backend server, I leveraged Firebase for both user data and ticket management. Using Firestore, I created three main collections: one for tickets, another for events, and a third for advertisements. This structure allows the machine to handle ticket sales by storing a new entry in the tickets collection with all the necessary metadata, including event details, ticket status, and QR code information.&lt;/p&gt;

&lt;p&gt;For ticket generation, I went with Apple’s Passbook format since it’s widely compatible with mobile wallets and offers easy QR code support. Each ticket purchase generates a QR code, which is stored in Firebase and pulled up when scanned for validation. I configured Firebase Authentication to manage user access, allowing administrators to log in and update event or ad details directly from the device.&lt;/p&gt;

&lt;p&gt;One challenge here was data persistence and cleanup. Since the machine could end up storing a large number of tickets over time, I implemented an automated cleanup process that clears expired or validated tickets after a set period. This keeps memory usage low and ensures the system doesn’t slow down over time. With Firestore, it’s fairly straightforward to set a retention policy, so I can keep data management automatic and avoid any need for manual intervention.&lt;/p&gt;

&lt;h3 id=&quot;implementing-the-user-interface-and-device-setup&quot;&gt;Implementing the User Interface and Device Setup&lt;/h3&gt;

&lt;p&gt;The main interface runs on an Android device, while a second device handles the validation side. I used JavaFX to build the ticket validation UI, while the Android SDK powers the main sales interface. The idea was to create a seamless user experience where buyers could quickly interact with the machine, and validation staff could easily check tickets without needing direct access to the main UI.&lt;/p&gt;

&lt;p&gt;For the UI, I wanted a balance between simplicity and reliability. Using MVVM (Model View ViewModel) helped separate the presentation logic from the core business logic. This pattern lets each activity have its own ViewModel, making it easy to manage state changes without causing unexpected behavior. For instance, the sales UI could show a different screen during idle times, displaying ads from the Firebase ads collection, while the ticketing functions remain ready to jump back into action when a new user approaches.&lt;/p&gt;

&lt;p&gt;The validation device runs a JavaFX-based program that continuously listens for QR codes using a camera module. Once a QR code is detected, it triggers the Firebase API to check ticket validity. This process happens instantly, and a simple color-coded response – green for valid, red for invalid – provides feedback without delay. The idea was to minimize user wait time and make sure the entire flow felt frictionless.&lt;/p&gt;

&lt;h3 id=&quot;real-world-testing-and-performance&quot;&gt;Real-World Testing and Performance&lt;/h3&gt;

&lt;p&gt;In testing, one of my priorities was ensuring that everything would work without server latency impacting the user experience. I performed several End-to-End tests using different devices, like a Google Pixel XL, Xiaomi Mi A2, and Raspberry Pi 3 with Android Things. These tests were especially useful for checking the system’s stability in low-connectivity situations and evaluating how it handled high traffic.&lt;/p&gt;

&lt;p&gt;The Raspberry Pi setup demonstrated that the machine could run autonomously with minimal intervention. The coin-insertion emulator I used for testing was another interesting feature. By simulating coin insertion, I was able to test the entire payment and ticketing flow without a physical payment module. It worked well for prototyping, and if the machine ever gets deployed with real payment functionality, this emulator could easily be swapped out.&lt;/p&gt;

&lt;p&gt;All tests focused on maintaining performance during idle and active states, ensuring there were no memory leaks or slowdowns. Firebase’s caching proved reliable, and having offline persistence in Firestore meant that even if connectivity dropped momentarily, the machine could still function as expected.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Building an IoT vending machine without a backend server was a fun and challenging project that pushed the limits of what’s possible with edge computing. By leveraging Firebase for data management and Android for the UI, I was able to create a self-sustaining system that could handle ticket sales and validation without needing a central server. The hexagonal architecture made it easy to adapt the design for different hardware setups, and the modular approach to ticketing and validation interfaces kept the system flexible and scalable.&lt;/p&gt;

&lt;p&gt;The project is hosted on Github, and you can check it out &lt;a href=&quot;https://github.com/Neirth/ExpoSeller&quot;&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;credits&quot;&gt;Credits&lt;/h3&gt;

&lt;p&gt;The header image of this post is made using &lt;a href=&quot;https://www.midjourney.com/&quot;&gt;Midjourney AI&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
    
  
</feed>