Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 7 additions & 28 deletions .github/workflows/ci_meson.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,24 @@ on:
push:
branches:
- main
paths:
- '**.c'
- '**.h'
- 'meson.build'
- 'meson_options.txt'

pull_request:
branches:
- main
paths:
- '**.c'
- '**.h'
- 'meson.build'
- 'meson_options.txt'

jobs:
build:
name: Build and Test on ${{ matrix.os }} with Meson v${{ matrix.meson_version }}
name: Build and Test on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
# meson_version: ["1.2.0", "1.3.0", "1.4.0"]
meson_version: ["1.4.0"]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: python -m pip install meson==${{ matrix.meson_version }} ninja
- name: Configure Project
run: meson setup builddir/
- name: Run Tests
run: meson test -C builddir/ -v
- name: Upload Test Log
uses: actions/upload-artifact@v4
- run: python -m pip install meson==${{ matrix.meson_version }} ninja
- run: meson setup builddir/
- run: meson test -C builddir/ -v
- uses: actions/upload-artifact@v4
if: failure()
with:
name: ${{ matrix.os }}_Meson_Testlog
Expand Down
50 changes: 30 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Epsilon
Epsilon is a library with small functions for machine learning and statistics
written in plain C. The functions are decoupled and well tested.

Epsilon is a C library of small, decoupled, and well-tested functions for machine learning and statistics. It is designed for microcontrollers and resource-constrained environments.

[![CI Meson](https://github.com/breuderink/epsilon/actions/workflows/ci_meson.yml/badge.svg)](https://github.com/breuderink/epsilon/actions/workflows/ci_meson.yml)

Expand Down Expand Up @@ -31,40 +31,50 @@ To allow machine learning to run on microcontrollers, the implementations:
- work with fixed-point math when realistic, and
- are easy to tune.

# Building
Epsilon uses Meson for building. Install Meson and Ninja, create a build
directory, and configure the project. In the repository root, configure and
build the project, then run the unit tests and examples:

# Folder Structure

```
/ # Project root
├── LICENSE
├── README.md
├── meson.build
├── docs/ # Documentation and references
├── src/ # Source code (.c/.h)
├── tests/ # Unit tests
├── examples/ # Example programs
├── subprojects/ # External dependencies (e.g., Unity)
```

# Building & Testing
Epsilon uses Meson for building. In the repository root:

```bash
$ meson setup builddir
$ meson test -C builddir
meson setup builddir
meson compile -C builddir
meson test -C builddir
```


# Algorithms

## Pseudo-random number generation
- [Xorshift](docs/marsaglia2003xrn.pdf) is a fast and simple
pseudo-random number generator by George Marsaglia that has good statistical
properties. See the [xorshift example](examples/example_rng.c).
- [Xorshift](docs/marsaglia2003xrn.pdf): Fast, simple PRNG with good statistical properties. See `examples/example_rng.c`.

## Hashing
- The [FNV hash](https://tools.ietf.org/html/draft-eastlake-fnv-17) is a fast
hash function that maps variable length input to a fixed output
([example](examples/example_hash.c)). It can be used for [feature
hashing](https://en.wikipedia.org/wiki/Feature_hashing).
- [FNV hash](https://tools.ietf.org/html/draft-eastlake-fnv-17): Fast hash for feature hashing. See `examples/example_hash.c`.

## Statistics
- Welford's method computes mean and variance in a single pass. See the
[example of Welford's method](examples/example_stats.c).
- Welford's method: Online mean and variance in one pass. See `examples/example_stats.c`.

## Transformations
- Fast Walsh-Hadamard transform (FWHT) implements the Walsh-Hadamard
transform in O(n log n) time. FWHT is similar to the fast Fourier transform
and the Haar transform. See the [FWHT example](examples/example_transform.c).
- Fast Walsh-Hadamard Transform (FWHT): O(n log n) transform, similar to FFT. See `examples/example_transform.c`.

## Passive-aggressive learning
- [Online passive-aggressive (PA)](docs/crammer2006opa.pdf) regression solves a
regression problem by only updating the model on prediction mistakes.
- [Online passive-aggressive (PA)](docs/crammer2006opa.pdf): Regression with updates only on mistakes.


# Other solutions for Tiny ML or Edge AI
- [TensorFlow Lite](https://www.tensorflow.org/lite/)
Expand Down
13 changes: 0 additions & 13 deletions epsilon/hash.h

This file was deleted.

14 changes: 0 additions & 14 deletions epsilon/pa.h

This file was deleted.

12 changes: 0 additions & 12 deletions epsilon/rng.h

This file was deleted.

29 changes: 0 additions & 29 deletions epsilon/stats.h

This file was deleted.

4 changes: 0 additions & 4 deletions epsilon/transform.h

This file was deleted.

2 changes: 1 addition & 1 deletion examples/example_hash.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "epsilon/hash.h"
#include "hash.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
Expand Down
2 changes: 1 addition & 1 deletion examples/example_rng.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "epsilon/rng.h"
#include "rng.h"
#include <inttypes.h>
#include <stdio.h>

Expand Down
2 changes: 1 addition & 1 deletion examples/example_stats.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "epsilon/stats.h"
#include "stats.h"
#include <stdio.h>

int main(void) {
Expand Down
2 changes: 1 addition & 1 deletion examples/example_transform.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "epsilon/transform.h"
#include "transform.h"
#include <stdio.h>

#define LOG2_DIMS 3
Expand Down
100 changes: 58 additions & 42 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,87 @@ project('epsilon', 'c',
default_options: ['c_std=c99', 'warning_level=3', 'werror=true']
)

# Dependencies
cc = meson.get_compiler('c')
math_dep = cc.find_library('m', required: false)
unity_dep = dependency('unity', required: true)

# Library sources
epsilon_sources = [
'epsilon/rng.c',
'epsilon/stats.c',
'epsilon/hash.c',
'epsilon/transform.c'
]
# Library configuration

# Build the library
epsilon_sources = files(
'src/rng.c',
'src/stats.c',
'src/hash.c',
'src/transform.c'
)


epsilon_headers = files(
'src/rng.h',
'src/stats.h',
'src/hash.h',
'src/transform.h',
'src/pa.h'
)

# Build library
epsilon_lib = library('epsilon',
sources: epsilon_sources,
dependencies: math_dep,
install: true,
version: meson.project_version()
)

# Dependency object for downstream use
# Declare dependency for downstream use

epsilon_dep = declare_dependency(
link_with: epsilon_lib,
include_directories: include_directories('src'),
dependencies: math_dep
)

# Install headers
epsilon_headers = [
'epsilon/rng.h',
'epsilon/stats.h',
'epsilon/hash.h',
'epsilon/transform.h',
'epsilon/pa.h'
]

install_headers(epsilon_headers, subdir: 'epsilon')

# Examples
example_exes = [
['example_hash', 'examples/example_hash.c'],
['example_transform', 'examples/example_transform.c'],
['example_stats', 'examples/example_stats.c'],
['example_rng', 'examples/example_rng.c']
]

foreach pair : example_exes
exe = executable(pair[0], pair[1], dependencies: epsilon_dep, install: false)
test(pair[0], exe)
# Examples and tests configuration
examples = {
'example_hash': 'examples/example_hash.c',
'example_transform': 'examples/example_transform.c',
'example_stats': 'examples/example_stats.c',
'example_rng': 'examples/example_rng.c'
}

tests = {
'hash_test': 'tests/hash_test.c',
'rng_test': 'tests/rng_test.c',
'stats_test': 'tests/stats_test.c',
'transform_test': 'tests/transform_test.c',
}

# Build and register examples
foreach name, source : examples
exe = executable(name, source,
dependencies: epsilon_dep,
install: false
)
test(name, exe)
endforeach

# Tests
test_exes = [
['hash_test', 'tests/hash_test.c'],
['rng_test', 'tests/rng_test.c'],
['stats_test', 'tests/stats_test.c'],
['transform_test', 'tests/transform_test.c']
]

foreach pair : test_exes
exe = executable(pair[0], pair[1], dependencies: [epsilon_dep, unity_dep], install: false)
test(pair[0], exe)
# Build and register tests
foreach name, source : tests
exe = executable(name, source,
dependencies: [epsilon_dep, unity_dep],
install: false
)
test(name, exe)
endforeach

# Documentation
install_data('README.md', install_dir: 'share/doc/epsilon')
install_data('LICENSE', install_dir: 'share/licenses/epsilon')
# Documentation installation
install_data(
files('README.md', 'LICENSE'),
install_dir: 'share/doc/epsilon'
)

# Optional metadata
# Distribution metadata
meson.add_dist_script('echo', 'Package contact: boris@cortext.nl')
2 changes: 2 additions & 0 deletions epsilon/hash.c → src/hash.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@

// FNV-1a 32-bit hash implementation.
#include <stddef.h>
#include <stdint.h>

Expand Down
12 changes: 12 additions & 0 deletions src/hash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

#ifndef EPSILON_HASH_H
#define EPSILON_HASH_H

// FNV-1a 32-bit hash interface
#include <stddef.h>
#include <stdint.h>

uint32_t FNV1a32_update(uint32_t hash, uint8_t data);
uint32_t FNV1a32_hash(const void *data, size_t n);

#endif // EPSILON_HASH_H
6 changes: 6 additions & 0 deletions src/pa.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

// Passive-aggressive regression parameters.
typedef struct {
float C;
float eps;
} PA_t;
Loading
Loading