diff options
author | Ian Jauslin <ian.jauslin@roma1.infn.it> | 2016-05-20 20:30:15 +0000 |
---|---|---|
committer | Ian Jauslin <ian.jauslin@roma1.infn.it> | 2016-05-20 20:30:15 +0000 |
commit | 2125f01f97abfe343fc5e0cc078bf1d081b2e441 (patch) | |
tree | 932dc60739384224be31f9e894ae63055634435e /src/integral_base.c |
Initial commitv1.0
Diffstat (limited to 'src/integral_base.c')
-rw-r--r-- | src/integral_base.c | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/src/integral_base.c b/src/integral_base.c new file mode 100644 index 0000000..da3f05a --- /dev/null +++ b/src/integral_base.c @@ -0,0 +1,506 @@ +/* +Copyright 2016 Ian Jauslin + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* + Base functions for integration + + see integral_*.h for the values taken by INTEGRAL_FUNC, etc... +*/ + + +// compute the integral of a real function of 1 variable using the Gauss-Legendre scheme +int INTEGRAL_FUNC(integrate_gauss) (INTEGRAL_FLOAT_TYPE* out, int (*func)(INTEGRAL_FLOAT_TYPE*, INTEGRAL_FLOAT_TYPE, void*), INTEGRAL_FLOAT_TYPE lower, INTEGRAL_FLOAT_TYPE upper, INTEGRAL_FLOATARRAY_TYPE abcissa, INTEGRAL_FLOATARRAY_TYPE weights, void* extra_args){ + unsigned int i; + INTEGRAL_FLOAT_TYPE valf, delta, avg, x; + int ret; + + // check arguments + if(abcissa.length != weights.length){ + return(LIBINUM_ERROR_SIZE_MISMATCH); + } + + #ifdef INTEGRAL_FLOAT_INIT + INTEGRAL_FLOAT_INIT(valf); + INTEGRAL_FLOAT_INIT(delta); + INTEGRAL_FLOAT_INIT(avg); + INTEGRAL_FLOAT_INIT(x); + #endif + + // init to 0 + INTEGRAL_FLOAT_SET_UI(*out, 0); + + // half length of interval + INTEGRAL_FLOAT_SUB(delta, upper, lower); + INTEGRAL_FLOAT_DIV_UI(delta, delta, 2); + // average of interval + INTEGRAL_FLOAT_ADD(avg, upper, lower); + INTEGRAL_FLOAT_DIV_UI(avg, avg, 2); + + for(i=0;i<abcissa.length;i++){ + // evaluate at x + INTEGRAL_FLOAT_MUL(x, delta, abcissa.values[i]); + INTEGRAL_FLOAT_ADD(x, x, avg); + + ret=(*func)(&valf, x, extra_args); + if(ret<0){ + #ifdef INTEGRAL_FLOAT_FREE + INTEGRAL_FLOAT_FREE(valf); + INTEGRAL_FLOAT_FREE(delta); + INTEGRAL_FLOAT_FREE(avg); + INTEGRAL_FLOAT_FREE(x); + #endif + return(ret); + } + // check whether valf is a number + if(! INTEGRAL_FLOAT_ISNUMBER(valf)){ + #ifdef INTEGRAL_FLOAT_FREE + INTEGRAL_FLOAT_FREE(valf); + INTEGRAL_FLOAT_FREE(delta); + INTEGRAL_FLOAT_FREE(avg); + INTEGRAL_FLOAT_FREE(x); + #endif + return(LIBINUM_ERROR_NAN); + } + + INTEGRAL_FLOAT_MUL(valf, valf, weights.values[i]); + INTEGRAL_FLOAT_ADD(*out, *out, valf); + } + + INTEGRAL_FLOAT_MUL(*out, *out, delta); + + #ifdef INTEGRAL_FLOAT_FREE + INTEGRAL_FLOAT_FREE(valf); + INTEGRAL_FLOAT_FREE(delta); + INTEGRAL_FLOAT_FREE(avg); + INTEGRAL_FLOAT_FREE(x); + #endif + + return(0); +} + +// multithreaded version +// arguments to pass to each thread +struct INTEGRAL_FUNC(pthread_integrate_gauss_args) { + // partial sum of the values assigned to the thread + INTEGRAL_FLOAT_TYPE out; + // abcissa + INTEGRAL_FLOATARRAY_TYPE x; + // weights + INTEGRAL_FLOATARRAY_TYPE weights; + // pointer to the function to evaluate + int (*func)(INTEGRAL_FLOAT_TYPE*, INTEGRAL_FLOAT_TYPE, void*); + // extra arguments passed to func + void* extra_args; + // return value + int ret; +}; +int INTEGRAL_FUNC(integrate_gauss_multithread) (INTEGRAL_FLOAT_TYPE* out, int (*func)(INTEGRAL_FLOAT_TYPE*, INTEGRAL_FLOAT_TYPE, void*), INTEGRAL_FLOAT_TYPE lower, INTEGRAL_FLOAT_TYPE upper, INTEGRAL_FLOATARRAY_TYPE abcissa, INTEGRAL_FLOATARRAY_TYPE weights, void* extra_args, unsigned int threads, array_pthread_t* thread_ids){ + unsigned int i; + INTEGRAL_FLOAT_TYPE x, delta, avg; + struct INTEGRAL_FUNC(pthread_integrate_gauss_args) args[threads]; + int ret=0; + unsigned int thread_nr; + + array_pthread_t_init(thread_ids, threads); + thread_ids->length=threads; + + // check arguments + if(abcissa.length != weights.length){ + return(LIBINUM_ERROR_SIZE_MISMATCH); + } + + #ifdef INTEGRAL_FLOAT_INIT + INTEGRAL_FLOAT_INIT(delta); + INTEGRAL_FLOAT_INIT(avg); + INTEGRAL_FLOAT_INIT(x); + #endif + + // init to 0 + INTEGRAL_FLOAT_SET_UI(*out, 0); + + // half length of interval + INTEGRAL_FLOAT_SUB(delta, upper, lower); + INTEGRAL_FLOAT_DIV_UI(delta, delta, 2); + // average of interval + INTEGRAL_FLOAT_ADD(avg, upper, lower); + INTEGRAL_FLOAT_DIV_UI(avg, avg, 2); + + // inits + for(thread_nr=0;thread_nr<threads;thread_nr++){ + INTEGRAL_FLOATARRAY_FUNC(init) (&(args[thread_nr].x),abcissa.length/threads+1); + INTEGRAL_FLOATARRAY_FUNC(init) (&(args[thread_nr].weights),abcissa.length/threads+1); + #ifdef INTEGRAL_FLOAT_INIT + INTEGRAL_FLOAT_INIT(args[thread_nr].out); + #endif + } + + // set abcissa and weights + for(i=0,thread_nr=0;i<abcissa.length;i++,thread_nr=(thread_nr+1)%threads){ + INTEGRAL_FLOAT_MUL(x, delta, abcissa.values[i]); + INTEGRAL_FLOAT_ADD(x, x, avg); + + INTEGRAL_FLOATARRAY_FUNC(append) (x, &(args[thread_nr].x)); + INTEGRAL_FLOATARRAY_FUNC(append) (weights.values[i], &(args[thread_nr].weights)); + } + + for(thread_nr=0;thread_nr<threads;thread_nr++){ + // set func + args[thread_nr].func=func; + // set extra_args + args[thread_nr].extra_args=extra_args; + // init ret + args[thread_nr].ret=0; + // run threads + pthread_create(thread_ids->values+thread_nr, NULL, INTEGRAL_FUNC(integrate_gauss_thread), (void*)(args+thread_nr)); + } + + // wait for completion and join + for(thread_nr=0;thread_nr<threads;thread_nr++){ + pthread_join(thread_ids->values[thread_nr], NULL); + + if(args[thread_nr].ret<0){ + ret=args[thread_nr].ret; + } + else{ + INTEGRAL_FLOAT_ADD(*out, *out, args[thread_nr].out); + } + } + + // multiply by size of interval + INTEGRAL_FLOAT_MUL(*out, *out, delta); + + for(thread_nr=0;thread_nr<threads;thread_nr++){ + INTEGRAL_FLOATARRAY_FUNC(free) (args[thread_nr].x); + INTEGRAL_FLOATARRAY_FUNC(free) (args[thread_nr].weights); + #ifdef INTEGRAL_FLOAT_FREE + INTEGRAL_FLOAT_FREE(args[thread_nr].out); + #endif + } + + #ifdef INTEGRAL_FLOAT_FREE + INTEGRAL_FLOAT_FREE(delta); + INTEGRAL_FLOAT_FREE(avg); + INTEGRAL_FLOAT_FREE(x); + #endif + + + return(ret); +} +// per-thread function +void* INTEGRAL_FUNC(integrate_gauss_thread) (void* args){ + unsigned int i; + INTEGRAL_FLOAT_TYPE valf; + int ret; + struct INTEGRAL_FUNC(pthread_integrate_gauss_args)* argument=((struct INTEGRAL_FUNC(pthread_integrate_gauss_args)*)args); + #ifdef INTEGRAL_FLOAT_INIT + INTEGRAL_FLOAT_INIT(valf); + #endif + + INTEGRAL_FLOAT_SET_UI(argument->out, 0); + + for(i=0;i<argument->x.length;i++){ + // evaluate + ret=(*(argument->func))(&valf, argument->x.values[i], argument->extra_args); + + if(ret<0){ + #ifdef INTEGRAL_FLOAT_FREE + INTEGRAL_FLOAT_FREE(valf); + #endif + argument->ret=ret; + return(NULL); + } + // check whether valf is a number + if(! INTEGRAL_FLOAT_ISNUMBER(valf)){ + #ifdef INTEGRAL_FLOAT_FREE + INTEGRAL_FLOAT_FREE(valf); + #endif + argument->ret=LIBINUM_ERROR_NAN; + return(NULL); + } + + INTEGRAL_FLOAT_MUL(valf, valf, argument->weights.values[i]); + INTEGRAL_FLOAT_ADD(argument->out, argument->out, valf); + } + #ifdef INTEGRAL_FLOAT_FREE + INTEGRAL_FLOAT_FREE(valf); + #endif + + return(NULL); +} + + + +// smart management of temporary variables: initialize as many as needed but allow them to be re-used instead of freeing them +#ifdef INTEGRAL_FLOAT_INIT +int INTEGRAL_FUNC(integrate_gauss_smarttmp) (INTEGRAL_FLOAT_TYPE* out, int (*func)(INTEGRAL_FLOAT_TYPE*, INTEGRAL_FLOAT_TYPE, void*), INTEGRAL_FLOAT_TYPE lower, INTEGRAL_FLOAT_TYPE upper, INTEGRAL_FLOATARRAY_TYPE abcissa, INTEGRAL_FLOATARRAY_TYPE weights, INTEGRAL_FLOATARRAY_TYPE* tmps, void* extra_args){ + unsigned int i; + int ret; + + // check arguments + if(abcissa.length != weights.length){ + return(LIBINUM_ERROR_SIZE_MISMATCH); + } + + // allocate tmps if needed + if(tmps->memory<4){ + // no need to resize since the values should not be kept + INTEGRAL_FLOATARRAY_FUNC(free)(*tmps); + INTEGRAL_FLOATARRAY_FUNC(init)(tmps, 4); + } + for (i=tmps->length;i<4;i++){ + INTEGRAL_FLOAT_INIT(tmps->values[i]); + (tmps->length)++; + } + + // init to 0 + INTEGRAL_FLOAT_SET_UI(*out, 0); + + // half length of interval + INTEGRAL_FLOAT_SUB(tmps->values[0], upper, lower); + INTEGRAL_FLOAT_DIV_UI(tmps->values[0], tmps->values[0], 2); + // average of interval + INTEGRAL_FLOAT_ADD(tmps->values[1], upper, lower); + INTEGRAL_FLOAT_DIV_UI(tmps->values[1], tmps->values[1], 2); + + for(i=0;i<abcissa.length;i++){ + // evaluation point + INTEGRAL_FLOAT_MUL(tmps->values[2], tmps->values[0], abcissa.values[i]); + INTEGRAL_FLOAT_ADD(tmps->values[2], tmps->values[2], tmps->values[1]); + + ret=(*func)(tmps->values+3, tmps->values[2], extra_args); + if(ret<0){ + return(ret); + } + // check whether tmps->values[3] is a number + if(! INTEGRAL_FLOAT_ISNUMBER(tmps->values[3])){ + return(LIBINUM_ERROR_NAN); + } + + INTEGRAL_FLOAT_MUL(tmps->values[3], tmps->values[3], weights.values[i]); + INTEGRAL_FLOAT_ADD(*out, *out, tmps->values[3]); + } + + INTEGRAL_FLOAT_MUL(*out, *out, tmps->values[0]); + + return(0); +} +#endif + +// multithreaded version with smart management of temporary variables (only for datatypes that need to be initialized) +#ifdef INTEGRAL_FLOAT_INIT +// arguments to pass to each thread +struct INTEGRAL_FUNC(pthread_integrate_gauss_smarttmp_args) { + // partial sum of the values assigned to the thread + INTEGRAL_FLOAT_TYPE* out; + // abcissa + INTEGRAL_FLOATARRAY_TYPE x; + // weights + INTEGRAL_FLOATARRAY_TYPE weights; + // tmp + INTEGRAL_FLOAT_TYPE* tmp; + // pointer to the function to evaluate + int (*func)(INTEGRAL_FLOAT_TYPE*, INTEGRAL_FLOAT_TYPE, void*); + // extra arguments passed to func + void* extra_args; + // return value + int ret; +}; +int INTEGRAL_FUNC(integrate_gauss_smarttmp_multithread) (INTEGRAL_FLOAT_TYPE* out, int (*func)(INTEGRAL_FLOAT_TYPE*, INTEGRAL_FLOAT_TYPE, void*), INTEGRAL_FLOAT_TYPE lower, INTEGRAL_FLOAT_TYPE upper, INTEGRAL_FLOATARRAY_TYPE abcissa, INTEGRAL_FLOATARRAY_TYPE weights, INTEGRAL_FLOATARRAY_TYPE* tmps, void* extra_args, unsigned int threads, array_pthread_t* thread_ids){ + unsigned int i; + struct INTEGRAL_FUNC(pthread_integrate_gauss_smarttmp_args) args[threads]; + int ret=0; + unsigned int thread_nr; + + array_pthread_t_init(thread_ids, threads); + thread_ids->length=threads; + + // check arguments + if(abcissa.length != weights.length){ + return(LIBINUM_ERROR_SIZE_MISMATCH); + } + + // allocate tmps if needed + if(tmps->memory<2+2*threads+abcissa.length){ + // no need to resize since the values should not be kept + INTEGRAL_FLOATARRAY_FUNC(free)(*tmps); + INTEGRAL_FLOATARRAY_FUNC(init)(tmps, 2+2*threads+abcissa.length); + } + for (i=tmps->length;i<2+2*threads+abcissa.length;i++){ + INTEGRAL_FLOAT_INIT(tmps->values[i]); + (tmps->length)++; + } + + + // init to 0 + INTEGRAL_FLOAT_SET_UI(*out, 0); + + // half length of interval + INTEGRAL_FLOAT_SUB(tmps->values[0], upper, lower); + INTEGRAL_FLOAT_DIV_UI(tmps->values[0], tmps->values[0], 2); + // average of interval + INTEGRAL_FLOAT_ADD(tmps->values[1], upper, lower); + INTEGRAL_FLOAT_DIV_UI(tmps->values[1], tmps->values[1], 2); + + // inits + for(thread_nr=0;thread_nr<threads;thread_nr++){ + INTEGRAL_FLOATARRAY_FUNC(init) (&(args[thread_nr].x),abcissa.length/threads+1); + INTEGRAL_FLOATARRAY_FUNC(init) (&(args[thread_nr].weights),abcissa.length/threads+1); + args[thread_nr].out=tmps->values+2+thread_nr; + args[thread_nr].tmp=tmps->values+2+threads+thread_nr; + } + + // set abcissa and weights + for(i=0,thread_nr=0;i<abcissa.length;i++,thread_nr=(thread_nr+1)%threads){ + INTEGRAL_FLOAT_MUL(tmps->values[2+2*threads+i], tmps->values[0], abcissa.values[i]); + INTEGRAL_FLOAT_ADD(tmps->values[2+2*threads+i], tmps->values[2+2*threads+i], tmps->values[1]); + + INTEGRAL_FLOATARRAY_FUNC(append_noinit) (tmps->values[2+2*threads+i], &(args[thread_nr].x)); + INTEGRAL_FLOATARRAY_FUNC(append_noinit) (weights.values[i], &(args[thread_nr].weights)); + } + + for(thread_nr=0;thread_nr<threads;thread_nr++){ + // set func + args[thread_nr].func=func; + // set extra_args + args[thread_nr].extra_args=extra_args; + // init ret + args[thread_nr].ret=0; + // run threads + pthread_create(thread_ids->values+thread_nr, NULL, INTEGRAL_FUNC(integrate_gauss_smarttmp_thread), (void*)(args+thread_nr)); + } + + // wait for completion and join + for(thread_nr=0;thread_nr<threads;thread_nr++){ + pthread_join(thread_ids->values[thread_nr], NULL); + + if(args[thread_nr].ret<0){ + ret=args[thread_nr].ret; + } + else{ + INTEGRAL_FLOAT_ADD(*out, *out, *(args[thread_nr].out)); + } + } + + // multiply by size of interval + INTEGRAL_FLOAT_MUL(*out, *out, tmps->values[0]); + + // free x and weights + for(thread_nr=0;thread_nr<threads;thread_nr++){ + INTEGRAL_FLOATARRAY_FUNC(free_vects)(args[thread_nr].x); + INTEGRAL_FLOATARRAY_FUNC(free_vects)(args[thread_nr].weights); + } + return(ret); +} +// per-thread function +void* INTEGRAL_FUNC(integrate_gauss_smarttmp_thread) (void* args){ + unsigned int i; + int ret; + struct INTEGRAL_FUNC(pthread_integrate_gauss_smarttmp_args)* argument=((struct INTEGRAL_FUNC(pthread_integrate_gauss_smarttmp_args)*)args); + + INTEGRAL_FLOAT_SET_UI(*(argument->out), 0); + + for(i=0;i<argument->x.length;i++){ + // evaluate + ret=(*(argument->func))(argument->tmp, argument->x.values[i], argument->extra_args); + + if(ret<0){ + argument->ret=ret; + return(NULL); + } + // check whether argument->tmp is a number + if(! INTEGRAL_FLOAT_ISNUMBER(*(argument->tmp))){ + argument->ret=LIBINUM_ERROR_NAN; + return(NULL); + } + + INTEGRAL_FLOAT_MUL(*(argument->tmp), *(argument->tmp), argument->weights.values[i]); + INTEGRAL_FLOAT_ADD(*(argument->out), *(argument->out), *(argument->tmp)); + } + + return(NULL); +} +#endif + + +// compute the abcissa and weights for the Gauss-Legendre numerical integration scheme +int INTEGRAL_FUNC(gauss_legendre_weights) (unsigned int order, INTEGRAL_FLOAT_TYPE tolerance, unsigned int maxiter, INTEGRAL_FLOATARRAY_TYPE* abcissa, INTEGRAL_FLOATARRAY_TYPE* weights){ + unsigned int i; + INTEGRAL_FLOAT_TYPE x, tmp; + INTEGRAL_POLYNOMIALARRAY_TYPE legendre; + int ret; + + INTEGRAL_FLOATARRAY_FUNC(init) (abcissa, order); + INTEGRAL_FLOATARRAY_FUNC(init) (weights, order); + + #ifdef INTEGRAL_FLOAT_INIT + INTEGRAL_FLOAT_INIT(x); + INTEGRAL_FLOAT_INIT(tmp); + #endif + + INTEGRAL_POLYNOMIALARRAY_FUNC(init) (&legendre, 2); + // compute the roots of the 'order'-th Legendre polynomial + INTEGRAL_POLYNOMIAL_FUNC(legendre) (order, legendre.values); + INTEGRAL_POLYNOMIAL_FUNC(derive) (legendre.values[0], legendre.values+1); + legendre.length=2; + + for(i=0;i<order;i++){ + // initial guess + INTEGRAL_FLOAT_SET_D(x, cos(3.1415926*(i+0.75)/(order+0.5))); + ret=INTEGRAL_FUNC(root_newton_inplace) (&x, &INTEGRAL_FUNC(legendre_wrapper), &INTEGRAL_FUNC(deriv_legendre_wrapper), tolerance, maxiter, &legendre); + + if(ret<0){ + #ifdef INTEGRAL_FLOAT_FREE + INTEGRAL_FLOAT_FREE(x); + INTEGRAL_FLOAT_FREE(tmp); + #endif + INTEGRAL_POLYNOMIALARRAY_FUNC(free) (legendre); + INTEGRAL_FLOATARRAY_FUNC(free) (*abcissa); + INTEGRAL_FLOATARRAY_FUNC(free) (*weights); + return(ret); + } + + INTEGRAL_FLOATARRAY_FUNC(append) (x, abcissa); + + // weight: 2/((1-x^2)*(deriv_Legendre(x)))^2 + INTEGRAL_POLYNOMIAL_FUNC(evaluate) (&tmp, x, legendre.values[1]); + INTEGRAL_FLOAT_POW_UI(tmp, tmp, 2); + INTEGRAL_FLOAT_POW_UI(x, x, 2); + INTEGRAL_FLOAT_UI_SUB(x, 1, x); + INTEGRAL_FLOAT_MUL(x, x, tmp); + INTEGRAL_FLOAT_SET_UI(tmp, 2); + INTEGRAL_FLOAT_DIV(x, tmp, x); + + INTEGRAL_FLOATARRAY_FUNC(append) (x, weights); + } + + #ifdef INTEGRAL_FLOAT_FREE + INTEGRAL_FLOAT_FREE(x); + INTEGRAL_FLOAT_FREE(tmp); + #endif + INTEGRAL_POLYNOMIALARRAY_FUNC(free) (legendre); + + return(0); +} + +// wrapper functions to evaluate the legendre polynomial and its derivative +int INTEGRAL_FUNC(legendre_wrapper) (INTEGRAL_FLOAT_TYPE* out, INTEGRAL_FLOAT_TYPE in, void* legendre){ + INTEGRAL_POLYNOMIAL_FUNC(evaluate) (out, in, ((INTEGRAL_POLYNOMIALARRAY_TYPE*)legendre)->values[0]); + return(0); +} +int INTEGRAL_FUNC(deriv_legendre_wrapper) (INTEGRAL_FLOAT_TYPE* out, INTEGRAL_FLOAT_TYPE in, void* legendre){ + INTEGRAL_POLYNOMIAL_FUNC(evaluate) (out, in, ((INTEGRAL_POLYNOMIALARRAY_TYPE*)legendre)->values[1]); + return(0); +} |