0.1.0 Gauss

This commit is contained in:
Rob Tillaart 2023-07-07 18:31:58 +02:00
parent 26e3dff4ad
commit 8270df9d5a
19 changed files with 965 additions and 0 deletions

View File

@ -0,0 +1,34 @@
platforms:
rpipico:
board: rp2040:rp2040:rpipico
package: rp2040:rp2040
gcc:
features:
defines:
- ARDUINO_ARCH_RP2040
warnings:
flags:
packages:
rp2040:rp2040:
url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
# - due
# - zero
# - leonardo
- m4
- esp32
- esp8266
# - mega2560
- rpipico
libraries:
- MultiMap
unittest:
# These dependent libraries will be installed
libraries:
- MultiMap

4
libraries/Gauss/.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,4 @@
# These are supported funding model platforms
github: RobTillaart

View File

@ -0,0 +1,13 @@
name: Arduino-lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: arduino/arduino-lint-action@v1
with:
library-manager: update
compliance: strict

View File

@ -0,0 +1,17 @@
---
name: Arduino CI
on: [push, pull_request]
jobs:
runTest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
- run: |
gem install arduino_ci
arduino_ci.rb

View File

@ -0,0 +1,18 @@
name: JSON check
on:
push:
paths:
- '**.json'
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: json-syntax-check
uses: limitusus/json-syntax-check@v1
with:
pattern: "\\.json$"

View File

@ -0,0 +1,11 @@
# Change Log Gauss
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.1.0] - 2023-07-06
- initial version

11
libraries/Gauss/Gauss.cpp Normal file
View File

@ -0,0 +1,11 @@
//
// FILE: Gauss.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: Library for the Gauss probability math.
// DATE: 2023-07-06
// -- END OF FILE --

111
libraries/Gauss/Gauss.h Normal file
View File

@ -0,0 +1,111 @@
#pragma once
//
// FILE: Gauss.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: Library for the Gauss probability math.
// DATE: 2023-07-06
#include "Arduino.h"
#include "MultiMap.h"
#define GAUSS_LIB_VERSION (F("0.1.0"))
class Gauss
{
public:
Gauss()
{
_mean = 0;
_stddev = 1;
}
bool begin(float mean, float stddev)
{
_mean = mean;
_stddev = abs(stddev);
return true;
}
float P_smaller(float value)
{
if (_stddev == 0) return NAN;
return _P_smaller((value - _mean) / _stddev);
}
float P_larger(float value)
{
return 1.0 - P_smaller(value);
// if (_stddev == 0) return NAN;
// optimize math division?
// return _P_larger((value - _mean) / _stddev);
}
float P_between(float p, float q)
{
if (p >= q) return 0;
return P_smaller(q) - P_smaller(p);
}
float P_equal(float value)
{
if (_stddev == 0) return NAN;
float n = (value - _mean)/_stddev;
float c = 1.0 / (_stddev * sqrt(TWO_PI));
return c * exp(-0.5 * n * n);
}
float stddevs(float value)
{
return normalize(value);
}
float normalize(float value)
{
return (value - _mean)/_stddev;
}
float bellCurve(float value)
{
return P_equal(value);
}
private:
float _P_smaller(float x)
{
// TODO improve accuracy or reduce points.
float __gauss[] = {
0.5000, 0.5398, 0.5793, 0.6179, 0.6554, 0.6915, 0.7257, 0.7580,
0.7881, 0.8159, 0.8413, 0.8643, 0.8849, 0.9032, 0.9192, 0.9332,
0.9452, 0.9554, 0.9641, 0.9713, 0.9772, 0.9821, 0.9861, 0.9893,
0.9918, 0.9938, 0.9953, 0.9965, 0.9974, 0.9981, 0.9987, 1.0000
};
float __z[] = {
0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7,
0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5,
1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3,
2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 10.0
};
if (x < 0) return 1.0 - multiMap<float>(-x, __z, __gauss, 32);
return multiMap<float>(x, __z, __gauss, 32);
}
float _mean = 0;
float _stddev = 1;
};
// -- END OF FILE --

21
libraries/Gauss/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023-2023 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

124
libraries/Gauss/README.md Normal file
View File

@ -0,0 +1,124 @@
[![Arduino CI](https://github.com/RobTillaart/Gauss/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![Arduino-lint](https://github.com/RobTillaart/Gauss/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/Gauss/actions/workflows/arduino-lint.yml)
[![JSON check](https://github.com/RobTillaart/Gauss/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/Gauss/actions/workflows/jsoncheck.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/Gauss/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/Gauss.svg?maxAge=3600)](https://github.com/RobTillaart/Gauss/releases)
# Gauss
Library for the Gauss probability math.
## Description
Gauss is an experimental Arduino library to approximate the probability that a value is
smaller or larger than a given value.
These under the premises of a Gaussian distribution with parameters **mean** and **stddev**
(a.k.a. average / mu and standard deviation / sigma).
If these parameters are not given, 0 and 1 are used by default (normalized Gaussian distribution).
The values are approximated with **MultiMap()** using a 32 points interpolated lookup.
Therefore the **MultiMap** library need to be downloaded too (see related below).
The number of lookup points might chance in the future.
Return values are given as floats, if one needs percentages, just multiply by 100.0.
#### Accuracy
The lookup table used has 32 points with 4 significant digits.
Do not expect a higher accuracy / precision.
For many applications this accuracy is sufficient.
(links to a table with more significant digits is welcome).
#### Applications
- use as a filter? do not allow > 3 sigma
- compare historic data to current data
- compare population data with individual
#### Related
- https://en.wikipedia.org/wiki/Normal_distribution
- https://github.com/RobTillaart/Multimap
- https://github.com/RobTillaart/Statistic (more stat links there).
## Interface
```cpp
#include Gauss.h
```
#### Base
- **Gauss()** constructor. Uses mean = 0 and stddev = 1 by default.
- **bool begin(float mean, float stddev)** set the mean and stddev.
Returns true on success. If needed stddev is made positive.
#### Probability
- **float P_smaller(float f)** returns probability **P(x < f)**.
Multiply by 100.0 to get the value as a percentage.
- **float P_larger(float f)** returns probability **P(x > f)**.
Multiply by 100.0 to get the value as a percentage.
- **float P_between(float f, float g)** returns probability **P(f < x < g)**.
Multiply by 100.0 to get the value as a percentage.
- **float P_equal(float f)** returns probability **P(x == f)**.
This is the bell curve formula.
#### Other
- **float normalize(float f)** normalize a value to normalized distribution.
Is equal to number of **stddevs()**.
- **float stddevs(float f)** returns the number of stddevs from the mean.
E.g if mean == 50 and stddev == 14, then 71 ==> +1.5 sigma.
- **float bellCurve(float f)** returns probability **P(x == f)**.
## Future
#### Must
- documentation
- mu + sigma character
- unit tests
#### Should
- optimize performance
- remove division by stddev
- optimize accuracy
- revisit lookup of MultiMap
- (-10 .. 0) might be more accurate (significant digits)?
- double instead of floats? (good table?)
#### Could
- **void setMean(float f)**
- **float getMean()**
- **void setStddev(float f)**
- **float getStddev()**
- default values for **begin(0,1)**
- add examples
- e.g. temperature (DS18B20 or DHT22)
- e.g. loadcell (HX711)
- does the stddev needs to be positive,
- what happens if negative values are allowed?
- equality test Gauss objects
- move code to .cpp file? (rather small lib).
- embed MultiMap hardcoded instead of library dependency
- **bellCurve()** => **Z()**?
#### Won't (unless requested)

View File

@ -0,0 +1,154 @@
//
// FILE: Gauss_performance.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
#include "Gauss.h"
Gauss G;
volatile float x;
uint32_t start;
uint32_t stop;
void setup(void)
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("GAUSS_LIB_VERSION: ");
Serial.println(GAUSS_LIB_VERSION);
Serial.println();
Serial.println("Timing in micros");
Serial.println();
test_1();
test_2();
test_3();
test_4();
test_5();
test_6();
Serial.println("\ndone...");
}
void loop(void)
{
}
void test_1()
{
delay(10);
G.begin(0, 1);
start = micros();
for (float f = -5.0; f <= 5.0; f += 0.01)
{
x = G.P_smaller(f);
}
stop = micros();
Serial.print("P_smaller:\t");
Serial.print(stop - start);
Serial.println();
}
void test_2()
{
delay(10);
G.begin(0, 1);
start = micros();
for (float f = -5.0; f <= 5.0; f += 0.01)
{
x = G.P_larger(f);
}
stop = micros();
Serial.print("P_larger:\t");
Serial.print(stop - start);
Serial.println();
}
void test_3()
{
delay(10);
G.begin(0, 1);
start = micros();
for (float f = -5.0; f <= 5.0; f += 0.01)
{
x = G.P_between(0, f);
}
stop = micros();
Serial.print("P_between:\t");
Serial.print(stop - start);
Serial.println();
}
void test_4()
{
delay(10);
G.begin(0, 1);
start = micros();
for (float f = -5.0; f <= 5.0; f += 0.01)
{
x = G.normalize(f);
}
stop = micros();
Serial.print("normalize:\t");
Serial.print(stop - start);
Serial.println();
}
void test_5()
{
delay(10);
G.begin(0, 1);
start = micros();
for (float f = -5.0; f <= 5.0; f += 0.01)
{
x = G.bellCurve(f);
}
stop = micros();
Serial.print("bellCurve:\t");
Serial.print(stop - start);
Serial.println();
}
void test_6()
{
delay(10);
G.begin(0, 1);
float cf = 0.52330751; // See Gauss_test_bell_curve.ino
start = micros();
for (float f = -5.0; f <= 5.0; f += 0.01)
{
float a = G.P_smaller(f - cf);
float b = G.P_smaller(f + cf);
x = a - b;
}
stop = micros();
Serial.print("approx.bell:\t");
Serial.print(stop - start);
Serial.println();
}
// -- END OF FILE --

View File

@ -0,0 +1,16 @@
Arduino UNO
IDE 1.8.19
Gauss_performance.ino
GAUSS_LIB_VERSION: 0.1.0
Timing in micros
P_smaller: 375396
P_larger: 384368
P_between: 265624
normalize: 44172
bellCurve: 255728
approx.bell: 764028
done...

View File

@ -0,0 +1,80 @@
//
// FILE: Gauss_test.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
#include "Gauss.h"
Gauss G;
void setup(void)
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("GAUSS_LIB_VERSION: ");
Serial.println(GAUSS_LIB_VERSION);
Serial.println();
test_1();
test_2();
test_3();
test_4();
Serial.println("\ndone...");
}
void loop(void)
{
}
void test_1()
{
G.begin(0, 1);
for (float f = -5.0; f <= 5.0; f += 0.025)
{
Serial.println(100 * G.P_smaller(f));
}
Serial.println();
}
void test_2()
{
G.begin(100, 25);
for (float f = 0; f <= 200; f += 1)
{
Serial.println(100 * G.P_smaller(f));
}
Serial.println();
}
void test_3()
{
G.begin(0, 1);
for (float f = -5.0; f <= 5.0; f += 0.025)
{
Serial.println(100 * G.P_larger(f));
}
Serial.println();
}
void test_4()
{
G.begin(100, 25);
for (float f = 0; f <= 200; f += 1)
{
Serial.println(100.0 * G.P_larger(f));
}
Serial.println();
}
// -- END OF FILE --

View File

@ -0,0 +1,87 @@
//
// FILE: Gauss_test_bell_curve.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
#include "Gauss.h"
Gauss G;
volatile float x;
void setup(void)
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("GAUSS_LIB_VERSION: ");
Serial.println(GAUSS_LIB_VERSION);
Serial.println();
get_bell_curve();
approximate();
Serial.println("\ndone...");
}
void loop(void)
{
}
void get_bell_curve()
{
G.begin(0, 1);
for (float f = -5.0; f <= 5.0; f += 0.01)
{
Serial.println(100.0 * G.bellCurve(f), 6);
}
Serial.println();
}
void approximate()
{
G.begin(0, 1);
for (float f = -5.0; f <= 5.0; f += 0.01)
{
// width == 2x 0.5233 == 1.0466
// 0.0466 heuristic correction factor to match peak of bellCurve()
// not exact match but almost perfect
float cf = 0.52330751;
float a = G.P_smaller(f - cf);
float b = G.P_smaller(f + cf);
Serial.println(100.0 * (b - a), 6);
}
Serial.println();
}
// find the correction factor for the appoximate function
// so the peak matches the bell curve function.
// ==> 0.52330751
void find_correction_factor()
{
G.begin(0, 1);
float ref = 100.0 * G.bellCurve(0);
float cf = 0.5233; // 0.52330751;
do
{
cf += 0.0000001;
float a = G.P_smaller(-cf);
float b = G.P_smaller(+cf);
x = 100.0 * (b - a);
}
while (x < ref);
Serial.print("Correction Factor: ");
Serial.println(cf, 8);
Serial.println();
}
// -- END OF FILE --

View File

@ -0,0 +1,81 @@
//
// FILE: Gauss_test_dump.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
#include "Gauss.h"
Gauss G;
void setup(void)
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("GAUSS_LIB_VERSION: ");
Serial.println(GAUSS_LIB_VERSION);
test_1();
test_2();
test_3();
test_4();
Serial.println("\ndone...");
}
void loop(void)
{
}
void test_1()
{
G.begin(0, 1);
for (float f = -5.0; f <= 5.0; f += 0.025)
{
Serial.println(100 * G.P_smaller(f));
}
Serial.println();
}
void test_2()
{
G.begin(100, 25);
for (float f = 0; f <= 200; f += 1)
{
Serial.println(100 * G.P_smaller(f));
}
Serial.println();
}
void test_3()
{
G.begin(0, 1);
for (float f = -5.0; f <= 5.0; f += 0.025)
{
Serial.println(100 * G.P_larger(f));
}
Serial.println();
}
void test_4()
{
G.begin(0, 1);
for (float f = -5.0; f <= 5.0; f += 0.01)
{
Serial.println(100.0 * G.P_equal(f));
}
Serial.println();
}
// -- END OF FILE --

View File

@ -0,0 +1,23 @@
# Syntax Colouring Map For Gauss
# Data types (KEYWORD1)
Gauss KEYWORD1
# Methods and Functions (KEYWORD2)
begin KEYWORD2
P_smaller KEYWORD2
P_larger KEYWORD2
P_between KEYWORD2
P_equal KEYWORD2
stddevs KEYWORD2
normalize KEYWORD2
bellCurve KEYWORD2
# Constants (LITERAL1)
GAUSS_LIB_VERSION LITERAL1

View File

@ -0,0 +1,31 @@
{
"name": "Gauss",
"keywords": "Probability",
"description": "Library for the Gauss probability math.",
"authors":
[
{
"name": "Rob Tillaart",
"email": "Rob.Tillaart@gmail.com",
"maintainer": true
}
],
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/Gauss.git"
},
"dependencies":
[
{
"owner": "Rob Tillaart",
"name": "MultiMap",
"version": "^0.1.7"
}
],
"version": "0.1.0",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",
"headers": "Gauss.h"
}

View File

@ -0,0 +1,12 @@
name=Gauss
version=0.1.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Library for the Gauss probability math.
paragraph=
category=Data Processing
url=https://github.com/RobTillaart/Gauss
architectures=*
includes=Gauss.h
depends=MultiMap

View File

@ -0,0 +1,117 @@
//
// FILE: unit_test_001.cpp
// AUTHOR: Rob Tillaart
// DATE: 2023-07-07
// PURPOSE: unit tests for the Gauss library
// https://github.com/RobTillaart/Gauss
// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md
//
// supported assertions
// ----------------------------
// assertEqual(expected, actual)
// assertNotEqual(expected, actual)
// assertLess(expected, actual)
// assertMore(expected, actual)
// assertLessOrEqual(expected, actual)
// assertMoreOrEqual(expected, actual)
// assertTrue(actual)
// assertFalse(actual)
// assertNull(actual)
#include <ArduinoUnitTests.h>
#include "Arduino.h"
#include "Gauss.h"
unittest_setup()
{
fprintf(stderr, "GAUSS_LIB_VERSION: %s\n", (char *) GAUSS_LIB_VERSION);
}
unittest_teardown()
{
}
unittest(test_constructor)
{
Gauss G;
G.begin(0, 1);
assertEqualFloat(0.5, G.P_smaller(0), 0.001);
}
unittest(test_P_smaller)
{
Gauss G;
G.begin(0, 1);
assertEqualFloat(0.0013, G.P_smaller(-3.0), 0.001);
assertEqualFloat(0.0228, G.P_smaller(-2.0), 0.001);
assertEqualFloat(0.1587, G.P_smaller(-1.0), 0.001);
assertEqualFloat(0.5000, G.P_smaller(0.0), 0.001);
assertEqualFloat(0.8413, G.P_smaller(1.0), 0.001);
assertEqualFloat(0.9772, G.P_smaller(2.0), 0.001);
assertEqualFloat(0.9987, G.P_smaller(3.0), 0.001);
}
unittest(test_P_larger)
{
Gauss G;
G.begin(0, 1);
assertEqualFloat(0.9987, G.P_larger(-3.0), 0.001);
assertEqualFloat(0.9772, G.P_larger(-2.0), 0.001);
assertEqualFloat(0.8413, G.P_larger(-1.0), 0.001);
assertEqualFloat(0.5000, G.P_larger(0.0), 0.001);
assertEqualFloat(0.1587, G.P_larger(1.0), 0.001);
assertEqualFloat(0.0228, G.P_larger(2.0), 0.001);
assertEqualFloat(0.0013, G.P_larger(3.0), 0.001);
}
unittest(test_P_between)
{
Gauss G;
G.begin(0, 1);
assertEqualFloat(0.4987, G.P_between(-3.0, 0.0), 0.001);
assertEqualFloat(0.4772, G.P_between(-2.0, 0.0), 0.001);
assertEqualFloat(0.3413, G.P_between(-1.0, 0.0), 0.001);
assertEqualFloat(0.0000, G.P_between(0.0, 0.0), 0.001);
assertEqualFloat(0.3413, G.P_between(0.0, 1.0), 0.001);
assertEqualFloat(0.4772, G.P_between(0.0, 2.0), 0.001);
assertEqualFloat(0.4987, G.P_between(0.0, 3.0), 0.001);
}
unittest(test_P_equal)
{
Gauss G;
G.begin(0, 1);
assertEqualFloat(0.004432, G.P_equal(-3.0), 0.001);
assertEqualFloat(0.053991, G.P_equal(-2.0), 0.001);
assertEqualFloat(0.241971, G.P_equal(-1.0), 0.001);
assertEqualFloat(0.398942, G.P_equal(0.0), 0.001);
assertEqualFloat(0.241971, G.P_equal(1.0), 0.001);
assertEqualFloat(0.053991, G.P_equal(2.0), 0.001);
assertEqualFloat(0.004432, G.P_equal(3.0), 0.001);
}
unittest_main()
// -- END OF FILE --