squiggle.c

Self-contained Monte Carlo estimation in C99
Log | Files | Refs | README

commit 7a2015e3e0b21813debbd6752ce2a77bd370b4ec
parent d10796cae2665a5819e16dcaae07032f90456f13
Author: NunoSempere <nuno.sempere@protonmail.com>
Date:   Sun, 16 Jul 2023 21:32:17 +0200

divide simple and complex examples into two different examples.

Diffstat:
Dexamples/04_sample_from_cdf/example | 0
Dexamples/04_sample_from_cdf/example.c | 251-------------------------------------------------------------------------------
Aexamples/04_sample_from_cdf_simple/example | 0
Aexamples/04_sample_from_cdf_simple/example.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rexamples/04_sample_from_cdf/makefile -> examples/04_sample_from_cdf_simple/makefile | 0
Aexamples/05_sample_from_cdf_beta/example | 0
Aexamples/05_sample_from_cdf_beta/example.c | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rexamples/04_sample_from_cdf/makefile -> examples/05_sample_from_cdf_beta/makefile | 0
8 files changed, 270 insertions(+), 251 deletions(-)

diff --git a/examples/04_sample_from_cdf/example b/examples/04_sample_from_cdf/example Binary files differ. diff --git a/examples/04_sample_from_cdf/example.c b/examples/04_sample_from_cdf/example.c @@ -1,251 +0,0 @@ -#include <math.h> // erf, sqrt -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include "../../squiggle.h" - -#define NUM_SAMPLES 1000000 -#define STOP_BETA 1.0e-8 -#define TINY_BETA 1.0e-30 - -// Example cdf -float cdf_uniform_0_1(float x) -{ - if (x < 0) { - return 0; - } else if (x > 1) { - return 1; - } else { - return x; - } -} - -float cdf_squared_0_1(float x) -{ - if (x < 0) { - return 0; - } else if (x > 1) { - return 1; - } else { - return x * x; - } -} - -float cdf_normal_0_1(float x) -{ - float mean = 0; - float std = 1; - return 0.5 * (1 + erf((x - mean) / (std * sqrt(2)))); // erf from math.h -} - -struct box incbeta(float a, float b, float x) -{ - // Descended from <https://github.com/codeplea/incbeta/blob/master/incbeta.c>, - // <https://codeplea.com/incomplete-beta-function-c> - // but modified to return a box struct and floats instead of doubles. - // [ ] to do: add attribution in README - // Original code under this license: - /* - * zlib License - * - * Regularized Incomplete Beta Function - * - * Copyright (c) 2016, 2017 Lewis Van Winkle - * http://CodePlea.com - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgement in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - if (x < 0.0 || x > 1.0) { - PROCESS_ERROR("x out of bounds [0, 1], in function incbeta"); - } - - /*The continued fraction converges nicely for x < (a+1)/(a+b+2)*/ - if (x > (a + 1.0) / (a + b + 2.0)) { - struct box symmetric_incbeta = incbeta(b, a, 1.0 - x); - if (symmetric_incbeta.empty) { - return symmetric_incbeta; // propagate error - } else { - struct box result = { - .empty = 0, - .content = 1 - symmetric_incbeta.content - }; - return result; - } - } - - /*Find the first part before the continued fraction.*/ - const float lbeta_ab = lgamma(a) + lgamma(b) - lgamma(a + b); - const float front = exp(log(x) * a + log(1.0 - x) * b - lbeta_ab) / a; - - /*Use Lentz's algorithm to evaluate the continued fraction.*/ - float f = 1.0, c = 1.0, d = 0.0; - - int i, m; - for (i = 0; i <= 200; ++i) { - m = i / 2; - - float numerator; - if (i == 0) { - numerator = 1.0; /*First numerator is 1.0.*/ - } else if (i % 2 == 0) { - numerator = (m * (b - m) * x) / ((a + 2.0 * m - 1.0) * (a + 2.0 * m)); /*Even term.*/ - } else { - numerator = -((a + m) * (a + b + m) * x) / ((a + 2.0 * m) * (a + 2.0 * m + 1)); /*Odd term.*/ - } - - /*Do an iteration of Lentz's algorithm.*/ - d = 1.0 + numerator * d; - if (fabs(d) < TINY_BETA) - d = TINY_BETA; - d = 1.0 / d; - - c = 1.0 + numerator / c; - if (fabs(c) < TINY_BETA) - c = TINY_BETA; - - const float cd = c * d; - f *= cd; - - /*Check for stop.*/ - if (fabs(1.0 - cd) < STOP_BETA) { - struct box result = { - .empty = 0, - .content = front * (f - 1.0) - }; - return result; - } - } - - PROCESS_ERROR("More loops needed, did not converge, in function incbeta"); -} - -struct box cdf_beta(float x) -{ - if (x < 0) { - struct box result = { .empty = 0, .content = 0 }; - return result; - } else if (x > 1) { - struct box result = { .empty = 0, .content = 1 }; - return result; - } else { - float successes = 1, failures = (2023 - 1945); - return incbeta(successes, failures, x); - } -} - -// Some testers -void test_inverse_cdf_float(char* cdf_name, float cdf_float(float)) -{ - struct box result = inverse_cdf_float(cdf_float, 0.5); - if (result.empty) { - printf("Inverse for %s not calculated\n", cdf_name); - exit(1); - } else { - printf("Inverse of %s at %f is: %f\n", cdf_name, 0.5, result.content); - } -} -void test_inverse_cdf_box(char* cdf_name, struct box cdf_box(float)) -{ - struct box result = inverse_cdf_box(cdf_box, 0.5); - if (result.empty) { - printf("Inverse for %s not calculated\n", cdf_name); - exit(1); - } else { - printf("Inverse of %s at %f is: %f\n", cdf_name, 0.5, result.content); - } -} - -void test_and_time_sampler_float(char* cdf_name, float cdf_float(float), uint32_t* seed) -{ - printf("\nGetting some samples from %s:\n", cdf_name); - clock_t begin = clock(); - for (int i = 0; i < NUM_SAMPLES; i++) { - struct box sample = sampler_float_cdf(cdf_float, seed); - if (sample.empty) { - printf("Error in sampler function for %s", cdf_name); - } else { - // printf("%f\n", sample.content); - } - } - clock_t end = clock(); - float time_spent = (float)(end - begin) / CLOCKS_PER_SEC; - printf("Time spent: %f\n", time_spent); -} - -void test_and_time_sampler_box(char* cdf_name, struct box cdf_box(float), uint32_t* seed) -{ - printf("\nGetting some samples from %s:\n", cdf_name); - clock_t begin = clock(); - for (int i = 0; i < NUM_SAMPLES; i++) { - struct box sample = sampler_box_cdf(cdf_box, seed); - if (sample.empty) { - printf("Error in sampler function for %s", cdf_name); - } else { - // printf("%f\n", sample.content); - } - } - clock_t end = clock(); - float time_spent = (float)(end - begin) / CLOCKS_PER_SEC; - printf("Time spent: %f\n", time_spent); -} - -int main() -{ - // Test inverse cdf float - test_inverse_cdf_float("cdf_uniform_0_1", cdf_uniform_0_1); - test_inverse_cdf_float("cdf_squared_0_1", cdf_squared_0_1); - test_inverse_cdf_float("cdf_normal_0_1", cdf_normal_0_1); - - // Test inverse cdf box - test_inverse_cdf_box("cdf_beta", cdf_beta); - - // Testing samplers - // set randomness seed - uint32_t* seed = malloc(sizeof(uint32_t)); - *seed = 1000; // xorshift can't start with 0 - - // Test float sampler - test_and_time_sampler_float("cdf_uniform_0_1", cdf_uniform_0_1, seed); - test_and_time_sampler_float("cdf_squared_0_1", cdf_squared_0_1, seed); - test_and_time_sampler_float("cdf_normal_0_1", cdf_normal_0_1, seed); - - // Get some normal samples using a previous approach - printf("\nGetting some samples from unit_normal\n"); - - clock_t begin_2 = clock(); - - for (int i = 0; i < NUM_SAMPLES; i++) { - float normal_sample = unit_normal(seed); - // printf("%f\n", normal_sample); - } - - clock_t end_2 = clock(); - float time_spent_2 = (float)(end_2 - begin_2) / CLOCKS_PER_SEC; - printf("Time spent: %f\n", time_spent_2); - - // Test box sampler - test_and_time_sampler_box("cdf_beta", cdf_beta, seed); - // Ok, this is slower than python!! - // Partly this is because I am using a more general algorithm, - // which applies to any cdf - // But I am also using really anal convergence conditions. - // This could be optimized. - - free(seed); - return 0; -} diff --git a/examples/04_sample_from_cdf_simple/example b/examples/04_sample_from_cdf_simple/example Binary files differ. diff --git a/examples/04_sample_from_cdf_simple/example.c b/examples/04_sample_from_cdf_simple/example.c @@ -0,0 +1,102 @@ +#include <math.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include "../../squiggle.h" + +#define NUM_SAMPLES 1000000 + +// Example cdf +float cdf_uniform_0_1(float x) +{ + if (x < 0) { + return 0; + } else if (x > 1) { + return 1; + } else { + return x; + } +} + +float cdf_squared_0_1(float x) +{ + if (x < 0) { + return 0; + } else if (x > 1) { + return 1; + } else { + return x * x; + } +} + +float cdf_normal_0_1(float x) +{ + float mean = 0; + float std = 1; + return 0.5 * (1 + erf((x - mean) / (std * sqrt(2)))); // erf from math.h +} + +// Some testers +void test_inverse_cdf_float(char* cdf_name, float cdf_float(float)) +{ + struct box result = inverse_cdf_float(cdf_float, 0.5); + if (result.empty) { + printf("Inverse for %s not calculated\n", cdf_name); + exit(1); + } else { + printf("Inverse of %s at %f is: %f\n", cdf_name, 0.5, result.content); + } +} + +void test_and_time_sampler_float(char* cdf_name, float cdf_float(float), uint32_t* seed) +{ + printf("\nGetting some samples from %s:\n", cdf_name); + clock_t begin = clock(); + for (int i = 0; i < NUM_SAMPLES; i++) { + struct box sample = sampler_float_cdf(cdf_float, seed); + if (sample.empty) { + printf("Error in sampler function for %s", cdf_name); + } else { + // printf("%f\n", sample.content); + } + } + clock_t end = clock(); + float time_spent = (float)(end - begin) / CLOCKS_PER_SEC; + printf("Time spent: %f\n", time_spent); +} + +int main() +{ + // Test inverse cdf float + test_inverse_cdf_float("cdf_uniform_0_1", cdf_uniform_0_1); + test_inverse_cdf_float("cdf_squared_0_1", cdf_squared_0_1); + test_inverse_cdf_float("cdf_normal_0_1", cdf_normal_0_1); + + // Testing samplers + // set randomness seed + uint32_t* seed = malloc(sizeof(uint32_t)); + *seed = 1000; // xorshift can't start with 0 + + // Test float sampler + test_and_time_sampler_float("cdf_uniform_0_1", cdf_uniform_0_1, seed); + test_and_time_sampler_float("cdf_squared_0_1", cdf_squared_0_1, seed); + test_and_time_sampler_float("cdf_normal_0_1", cdf_normal_0_1, seed); + + // Get some normal samples using a previous approach + printf("\nGetting some samples from unit_normal\n"); + + clock_t begin_2 = clock(); + + for (int i = 0; i < NUM_SAMPLES; i++) { + float normal_sample = unit_normal(seed); + // printf("%f\n", normal_sample); + } + + clock_t end_2 = clock(); + float time_spent_2 = (float)(end_2 - begin_2) / CLOCKS_PER_SEC; + printf("Time spent: %f\n", time_spent_2); + + free(seed); + return 0; +} diff --git a/examples/04_sample_from_cdf/makefile b/examples/04_sample_from_cdf_simple/makefile diff --git a/examples/05_sample_from_cdf_beta/example b/examples/05_sample_from_cdf_beta/example Binary files differ. diff --git a/examples/05_sample_from_cdf_beta/example.c b/examples/05_sample_from_cdf_beta/example.c @@ -0,0 +1,168 @@ +#include <math.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include "../../squiggle.h" + +#define NUM_SAMPLES 1000000 +#define STOP_BETA 1.0e-8 +#define TINY_BETA 1.0e-30 + +// Incomplete beta function +struct box incbeta(float a, float b, float x) +{ + // Descended from <https://github.com/codeplea/incbeta/blob/master/incbeta.c>, + // <https://codeplea.com/incomplete-beta-function-c> + // but modified to return a box struct and floats instead of doubles. + // [ ] to do: add attribution in README + // Original code under this license: + /* + * zlib License + * + * Regularized Incomplete Beta Function + * + * Copyright (c) 2016, 2017 Lewis Van Winkle + * http://CodePlea.com + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgement in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + if (x < 0.0 || x > 1.0) { + PROCESS_ERROR("x out of bounds [0, 1], in function incbeta"); + } + + /*The continued fraction converges nicely for x < (a+1)/(a+b+2)*/ + if (x > (a + 1.0) / (a + b + 2.0)) { + struct box symmetric_incbeta = incbeta(b, a, 1.0 - x); + if (symmetric_incbeta.empty) { + return symmetric_incbeta; // propagate error + } else { + struct box result = { + .empty = 0, + .content = 1 - symmetric_incbeta.content + }; + return result; + } + } + + /*Find the first part before the continued fraction.*/ + const float lbeta_ab = lgamma(a) + lgamma(b) - lgamma(a + b); + const float front = exp(log(x) * a + log(1.0 - x) * b - lbeta_ab) / a; + + /*Use Lentz's algorithm to evaluate the continued fraction.*/ + float f = 1.0, c = 1.0, d = 0.0; + + int i, m; + for (i = 0; i <= 200; ++i) { + m = i / 2; + + float numerator; + if (i == 0) { + numerator = 1.0; /*First numerator is 1.0.*/ + } else if (i % 2 == 0) { + numerator = (m * (b - m) * x) / ((a + 2.0 * m - 1.0) * (a + 2.0 * m)); /*Even term.*/ + } else { + numerator = -((a + m) * (a + b + m) * x) / ((a + 2.0 * m) * (a + 2.0 * m + 1)); /*Odd term.*/ + } + + /*Do an iteration of Lentz's algorithm.*/ + d = 1.0 + numerator * d; + if (fabs(d) < TINY_BETA) + d = TINY_BETA; + d = 1.0 / d; + + c = 1.0 + numerator / c; + if (fabs(c) < TINY_BETA) + c = TINY_BETA; + + const float cd = c * d; + f *= cd; + + /*Check for stop.*/ + if (fabs(1.0 - cd) < STOP_BETA) { + struct box result = { + .empty = 0, + .content = front * (f - 1.0) + }; + return result; + } + } + + PROCESS_ERROR("More loops needed, did not converge, in function incbeta"); +} + +struct box cdf_beta(float x) +{ + if (x < 0) { + struct box result = { .empty = 0, .content = 0 }; + return result; + } else if (x > 1) { + struct box result = { .empty = 0, .content = 1 }; + return result; + } else { + float successes = 1, failures = (2023 - 1945); + return incbeta(successes, failures, x); + } +} + +// Some testers +void test_inverse_cdf_box(char* cdf_name, struct box cdf_box(float)) +{ + struct box result = inverse_cdf_box(cdf_box, 0.5); + if (result.empty) { + printf("Inverse for %s not calculated\n", cdf_name); + exit(1); + } else { + printf("Inverse of %s at %f is: %f\n", cdf_name, 0.5, result.content); + } +} + +void test_and_time_sampler_box(char* cdf_name, struct box cdf_box(float), uint32_t* seed) +{ + printf("\nGetting some samples from %s:\n", cdf_name); + clock_t begin = clock(); + for (int i = 0; i < NUM_SAMPLES; i++) { + struct box sample = sampler_box_cdf(cdf_box, seed); + if (sample.empty) { + printf("Error in sampler function for %s", cdf_name); + } else { + // printf("%f\n", sample.content); + } + } + clock_t end = clock(); + float time_spent = (float)(end - begin) / CLOCKS_PER_SEC; + printf("Time spent: %f\n", time_spent); +} + +int main() +{ + // Test inverse cdf box + test_inverse_cdf_box("cdf_beta", cdf_beta); + + // Test box sampler + uint32_t* seed = malloc(sizeof(uint32_t)); + *seed = 1000; // xorshift can't start with 0 + test_and_time_sampler_box("cdf_beta", cdf_beta, seed); + // Ok, this is slower than python!! + // Partly this is because I am using a more general algorithm, + // which applies to any cdf + // But I am also using absurdly precise convergence conditions. + // This could be optimized. + + free(seed); + return 0; +} diff --git a/examples/04_sample_from_cdf/makefile b/examples/05_sample_from_cdf_beta/makefile