star-wf

First-passage time of brownian motion
git clone git://git.meso-star.fr/star-wf.git
Log | Files | Refs | README | LICENSE

commit c465bdbb86537b914653ccaa7d7073179f4c5c7b
parent 0c4e3e0eb13663e6fcf47211c27096c176701e43
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Sun, 10 Mar 2024 11:11:18 +0100

Update H table

In addition to the function value, record the first, second and third
derivatives of the tabulated x-value for later use in interpolation.

Return an error when a numerical uncertainty is detected on the
evaluation of H, i.e. when the function is not increasing. In addition,
do not register x arguments whose value is equal to the previous
tabulated x, in order to avoid stair stepping. Thus, if successful,
tabulation guarantees that H(x) is a strictly increasing normalized
function.

Diffstat:
Msrc/swf_H.c | 30+++++++++++++++++++++++-------
Msrc/swf_tabulation.c | 35+++++++++++++++++++++++++++--------
Msrc/swf_tabulation.h | 7+++++--
3 files changed, 55 insertions(+), 17 deletions(-)

diff --git a/src/swf_H.c b/src/swf_H.c @@ -163,6 +163,7 @@ swf_H3d_tabulate struct item* items = NULL; size_t nitems = 0; size_t i = 0; + size_t j = 0; double x_range = 0; double norm = 0; res_T res = RES_OK; @@ -172,8 +173,8 @@ swf_H3d_tabulate if((res = check_swf_H_tabulate_args(args)) != RES_OK) goto error; if((res = tabulation_create(args->allocator, &tab)) != RES_OK) goto error; - /* Calculate the number of arguments x to be tabulated. Note that there is one - * more argument than the number of intervals, hence the +1 */ + /* Calculate the maximum number of arguments x to be tabulated. Note that + * there is one more argument than the number of intervals, hence the +1 */ x_range = args->x_max - args->x_min; nitems = (size_t)ceil(x_range / args->delta_x) + 1; @@ -183,14 +184,29 @@ swf_H3d_tabulate /* Setup the tabulation */ FOR_EACH(i, 0, nitems) { - items[i].x = MMIN(args->x_min + (double)i*args->delta_x, args->x_max); - items[i].f_x = swf_H3d_eval(items[i].x); + /* Evaluate H(x) and its derivatives up to order 3 */ + items[j].x = MMIN(args->x_min + (double)i*args->delta_x, args->x_max); + items[j].fx = H3d(items[j].x, &items[j].dfx, &items[j].d2fx, &items[j].d3fx); + + if(j == 0) { + ++j; + } else { + /* Detect oscillations due to numerical problems. + * These oscillations occur close to 0 */ + if(items[j].fx < items[j-1].fx) { res = RES_BAD_OP; goto error; } + + /* Reject tabulation whose value is (numerically) equal to the previous + * one: it would artificially add staircase steps */ + j += items[j].fx > items[j-1].fx; + } } - ASSERT(items[nitems-1].x == args->x_max); + + CHK(j && darray_item_resize(&tab->items, j) == RES_OK); + nitems = j; /* Normalize the tabulation */ - norm = items[nitems - 1].f_x; - FOR_EACH(i, 0, nitems) items[i].f_x /= norm; + norm = items[nitems - 1].fx; + FOR_EACH(i, 0, nitems) items[i].fx /= norm; exit: if(out_tab) *out_tab = tab; diff --git a/src/swf_tabulation.c b/src/swf_tabulation.c @@ -32,13 +32,26 @@ release_tabulation(ref_T* ref) } static FINLINE int -cmp_item(const void* a, const void* b) +cmp_fx(const void* a, const void* b) { const double f_a = *((const double*)a); - const double f_b = ((const struct item*)b)->f_x; + const double f_b = ((const struct item*)b)->fx; return f_a < f_b ? -1 : (f_a > f_b ? 1 : 0); } +static INLINE double +linear_interpolation + (const double y, + const struct item* a, + const struct item* b) +{ + double u, x; + ASSERT(a && b); + u = (y - a->fx) / (b->fx - a->fx); + x = u*(b->x - a->x) + a->x; + return x; +} + /******************************************************************************* * Exported symbols ******************************************************************************/ @@ -63,7 +76,6 @@ swf_tabulation_inverse(const struct swf_tabulation* tab, const double y) { const struct item* items = NULL; const struct item* find = NULL; - double u = 0; /* Linear interpolation parameter */ double x = 0; /* Argument corresponding to input value y */ size_t nitems = 0; ASSERT(tab && y >= 0 && y < 1); @@ -72,17 +84,24 @@ swf_tabulation_inverse(const struct swf_tabulation* tab, const double y) nitems = darray_item_size_get(&tab->items); ASSERT(nitems); - find = search_lower_bound(&y, items, nitems, sizeof(*items), cmp_item); - ASSERT(find && find->f_x >= y); + find = search_lower_bound(&y, items, nitems, sizeof(*items), cmp_fx); + + if(y == 0) return 0; + + /* Input y is not in the tabulated range: returns the nearest x */ + if(!find) return items[nitems-1].x; + if(find == items) return find->x; + + ASSERT(find->fx >= y); /* The input y correspond exactly to a tabulated argument */ - if(find->f_x == y) return find->x; + if(find->fx == y) return find->x; /* Linear interpolation of tabulated arguments containing the argument * corresponding to the input value */ ASSERT(find > items && find[-1].f_x < y); - u = (y - find[-1].f_x) / (find[0].f_x - find[-1].f_x); - x = u*(find[0].x - find[-1].x) + find[-1].x; + x = linear_interpolation(y, &find[-1], &find[0]); + return x; } diff --git a/src/swf_tabulation.h b/src/swf_tabulation.h @@ -21,9 +21,12 @@ struct item { double x; /* Function argument */ - double f_x; /* Value of the function */ + double fx; /* Value of the function */ + double dfx; /* Value of the derivative of the function */ + double d2fx; /* Value of the second derivative of the function */ + double d3fx; /* Value of the third derivative of the function */ }; -#define ITEM_NULL__ {0, 0} +#define ITEM_NULL__ {0, 0, 0, 0, 0} static const struct item ITEM_NULL = ITEM_NULL__; /* Declare the dynamic array of items */