memlnaut-nisps/nisps-core/include/nisps/sample.hpp
monkey-w1n5t0n be85a5cd71 feat: extract nisps-core platform-agnostic ML library
Extract the interactive machine learning engine from MEMLNaut-NISPS
firmware into a standalone, platform-agnostic C++20 header-only library.

What is nisps-core?
-------------------
NISPS (Neural Interactive Shaping of Parameter Spaces) core is a
parameter mapping engine. It takes N input parameters (joystick,
sensors, audio features) and maps them to M output parameters through
an interactively-trained neural network.

Use it to control: synthesizers, effects, lights, robots, game
parameters, or anything that responds to continuous control data.

Key Features
------------
- Header-only: No compilation needed, just include and use
- Platform-agnostic: Pure C++20, works anywhere
- Zero dependencies: Only standard library
- Interactive learning: Train by demonstration
- Lightweight: ~3,500 lines of optimized neural network code
- Flexible: Map 1-100 inputs to 1-100 outputs

Architecture
------------
Core components:
- IML: High-level interactive ML interface
- MLP: Multi-layer perceptron (feedforward neural network)
- Dataset: Training data management with replay memory
- Layer/Node: Neural network building blocks
- Loss: MSE and categorical cross-entropy functions
- Utils: Activation functions (sigmoid, ReLU, tanh, etc.)

Transformations Applied
-----------------------
 Removed Arduino/RP2040 dependencies (Serial, SD, Pico SDK)
 Removed audio synthesis code (nisps-core is control-only)
 Added nisps namespace to all code
 Converted to header-only library with _impl.hpp pattern
 Updated to C++20 (required for std::span)
 Removed platform-specific serialization
 Replaced debug macros with no-op stubs
 Added comprehensive documentation and examples

Files Added
-----------
- nisps-core/README.md: Complete documentation and API reference
- nisps-core/CHANGELOG.md: Version history and migration guide
- nisps-core/include/nisps/*.hpp: 13 header files (~3,500 lines)
- nisps-core/test/main.cpp: XOR test demonstrating basic usage
- nisps-core/examples/simple_mapping.cpp: Interactive demo
- nisps-core/CMakeLists.txt: Build system for tests

Testing
-------
 Compiles with GCC 14.2 (C++20)
 All tests passing
 Successfully instantiates networks and runs inference

Performance
-----------
- Inference: 1-10 µs for small networks (2-10-10-4)
- Training: 10-100 ms for 100 examples, 1000 iterations
- Memory: ~1 KB per hidden neuron

Migration from Embedded IMLInterface
------------------------------------
Old (embedded):
  IMLInterface iml(n_inputs, n_outputs);

New (nisps-core):
  nisps::IML<float> iml(n_inputs, n_outputs);

All method names remain the same, just add the namespace.

Related
-------
- Implements: NISPS_CORE_EXTRACTION_PLAN.md
- Task graph: NISPS_CORE_TASKS.md
- Origin: MEMLNaut-NISPS firmware
- Docs: https://musicallyembodiedml.github.io/memlnaut/

Co-authored-by: Claude Code <claude@anthropic.com>
2026-02-08 17:47:23 +01:00

159 lines
3.8 KiB
C++

/**
* @file sample.hpp
* @brief Sample and TrainingSample class definitions for NISPS Core
* @copyright Copyright (c) 2024. Licensed under Mozilla Public License Version 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* This code is derived from David Alberto Nogueira's MLP project:
* https://github.com/davidalbertonogueira/MLP
*/
#ifndef NISPS_SAMPLE_HPP
#define NISPS_SAMPLE_HPP
#include <stdlib.h>
#include <vector>
#if defined(MLP_DEBUG_BUILD)
#include <iostream>
#endif
namespace nisps {
/**
* @brief Base class representing a sample with input features
*
* @tparam T The data type of the input features (typically float)
*/
template<typename T>
class Sample {
public:
/**
* @brief Constructs a new Sample object
*
* @param input_vector Vector containing the input features
*/
Sample(const std::vector<T> & input_vector) {
m_input_vector = input_vector;
}
/**
* @brief Get the input vector
* @return const reference to the input vector
*/
const std::vector<T> & input_vector() const {
return m_input_vector;
}
/**
* @brief Get the size of the input vector
* @return Size of input vector
*/
size_t GetInputVectorSize() const {
return m_input_vector.size();
}
/**
* @brief Add a bias value to the beginning of input vector
* @param bias_value The bias value to add
*/
void AddBiasValue(T bias_value) {
m_input_vector.insert(m_input_vector.begin(), bias_value);
}
#if defined(MLP_DEBUG_BUILD)
friend std::ostream & operator<<(std::ostream &stream, Sample const & obj) {
obj.PrintMyself(stream);
return stream;
};
#endif
protected:
#if defined(MLP_DEBUG_BUILD)
virtual void PrintMyself(std::ostream& stream) const {
stream << "Input vector: [";
for (size_t i = 0; i < m_input_vector.size(); i++) {
if (i != 0)
stream << ", ";
stream << m_input_vector[i];
}
stream << "]";
}
#endif
std::vector<T> m_input_vector;
};
/**
* @brief Class representing a training sample with both input features and expected outputs
*
* Extends the base Sample class to include output/target values for training
*
* @tparam T The data type of the input/output values (typically float)
*/
template<typename T>
class TrainingSample : public Sample<T> {
using Sample<T>::m_input_vector;
public:
/**
* @brief Constructs a new Training Sample object
*
* @param input_vector Vector containing the input features
* @param output_vector Vector containing the expected outputs/targets
*/
TrainingSample(const std::vector<T> & input_vector,
const std::vector<T> & output_vector) :
Sample<T>(input_vector) {
m_output_vector = output_vector;
}
/**
* @brief Get the output vector
* @return const reference to the output vector
*/
const std::vector<T> & output_vector() const {
return m_output_vector;
}
/**
* @brief Get the size of the output vector
* @return Size of output vector
*/
size_t GetOutputVectorSize() const {
return m_output_vector.size();
}
protected:
#if defined(MLP_DEBUG_BUILD)
virtual void PrintMyself(std::ostream& stream) const {
stream << "Input vector: [";
for (size_t i = 0; i < m_input_vector.size(); i++) {
if (i != 0)
stream << ", ";
stream << m_input_vector[i];
}
stream << "]";
stream << "; ";
stream << "Output vector: [";
for (size_t i = 0; i < m_output_vector.size(); i++) {
if (i != 0)
stream << ", ";
stream << m_output_vector[i];
}
stream << "]";
}
#endif
std::vector<T> m_output_vector;
};
} // namespace nisps
#endif // NISPS_SAMPLE_HPP