// https://git.savannah.gnu.org/cgit/gsl.git/tree/integration/qk.c // GPL >= 3 #include #include // #include #include #include #define GSL_FN_EVAL(F,x) (*((F)->function))(x,(F)->params) #define GSL_DBL_EPSILON 2.2204460492503131e-16 #define GSL_DBL_MIN 2.2250738585072014e-308 #define GSL_ERROR(s) printf(s); return 1; #define GSL_ERROR_VAL(reason, value) return value ; #define RETURN_IF_NULL(x) if (!x) { return ; } struct gsl_function_struct { double (* function) (double x, void * params); void * params; }; typedef struct gsl_function_struct gsl_function ; typedef struct { size_t limit; size_t size; size_t nrmax; size_t i; size_t maximum_level; double *alist; double *blist; double *rlist; double *elist; size_t *order; size_t *level; } gsl_integration_workspace; gsl_integration_workspace * gsl_integration_workspace_alloc (const size_t n) { gsl_integration_workspace * w ; if (n == 0) { GSL_ERROR_VAL ("workspace length n must be positive integer", 0); } w = (gsl_integration_workspace *) malloc (sizeof (gsl_integration_workspace)); if (w == 0) { GSL_ERROR_VAL ("failed to allocate space for workspace struct", 0); } w->alist = (double *) malloc (n * sizeof (double)); if (w->alist == 0) { free (w); /* exception in constructor, avoid memory leak */ GSL_ERROR_VAL ("failed to allocate space for alist ranges", 0); } w->blist = (double *) malloc (n * sizeof (double)); if (w->blist == 0) { free (w->alist); free (w); /* exception in constructor, avoid memory leak */ GSL_ERROR_VAL ("failed to allocate space for blist ranges", 0); } w->rlist = (double *) malloc (n * sizeof (double)); if (w->rlist == 0) { free (w->blist); free (w->alist); free (w); /* exception in constructor, avoid memory leak */ GSL_ERROR_VAL ("failed to allocate space for rlist ranges", 0); } w->elist = (double *) malloc (n * sizeof (double)); if (w->elist == 0) { free (w->rlist); free (w->blist); free (w->alist); free (w); /* exception in constructor, avoid memory leak */ GSL_ERROR_VAL ("failed to allocate space for elist ranges", 0); } w->order = (size_t *) malloc (n * sizeof (size_t)); if (w->order == 0) { free (w->elist); free (w->rlist); free (w->blist); free (w->alist); free (w); /* exception in constructor, avoid memory leak */ GSL_ERROR_VAL ("failed to allocate space for order ranges", 0); } w->level = (size_t *) malloc (n * sizeof (size_t)); if (w->level == 0) { free (w->order); free (w->elist); free (w->rlist); free (w->blist); free (w->alist); free (w); /* exception in constructor, avoid memory leak */ GSL_ERROR_VAL ("failed to allocate space for order ranges", 0); } w->size = 0 ; w->limit = n ; w->maximum_level = 0 ; return w ; } void gsl_integration_workspace_free (gsl_integration_workspace * w) { RETURN_IF_NULL (w); free (w->level) ; free (w->order) ; free (w->elist) ; free (w->rlist) ; free (w->blist) ; free (w->alist) ; free (w) ; } /* size_t gsl_integration_workspace_limit (gsl_integration_workspace * w) { return w->limit ; } size_t gsl_integration_workspace_size (gsl_integration_workspace * w) { return w->size ; } */ static inline void initialise (gsl_integration_workspace * workspace, double a, double b); static inline void initialise (gsl_integration_workspace * workspace, double a, double b) { workspace->size = 0; workspace->nrmax = 0; workspace->i = 0; workspace->alist[0] = a; workspace->blist[0] = b; workspace->rlist[0] = 0.0; workspace->elist[0] = 0.0; workspace->order[0] = 0; workspace->level[0] = 0; workspace->maximum_level = 0; } static inline void set_initial_result (gsl_integration_workspace * workspace, double result, double error); static inline void set_initial_result (gsl_integration_workspace * workspace, double result, double error) { workspace->size = 1; workspace->rlist[0] = result; workspace->elist[0] = error; } static inline void qpsrt (gsl_integration_workspace * workspace); static inline void qpsrt (gsl_integration_workspace * workspace) { const size_t last = workspace->size - 1; const size_t limit = workspace->limit; double * elist = workspace->elist; size_t * order = workspace->order; double errmax ; double errmin ; int i, k, top; size_t i_nrmax = workspace->nrmax; size_t i_maxerr = order[i_nrmax] ; /* Check whether the list contains more than two error estimates */ if (last < 2) { order[0] = 0 ; order[1] = 1 ; workspace->i = i_maxerr ; return ; } errmax = elist[i_maxerr] ; /* This part of the routine is only executed if, due to a difficult integrand, subdivision increased the error estimate. In the normal case the insert procedure should start after the nrmax-th largest error estimate. */ while (i_nrmax > 0 && errmax > elist[order[i_nrmax - 1]]) { order[i_nrmax] = order[i_nrmax - 1] ; i_nrmax-- ; } /* Compute the number of elements in the list to be maintained in descending order. This number depends on the number of subdivisions still allowed. */ if(last < (limit/2 + 2)) { top = last ; } else { top = limit - last + 1; } /* Insert errmax by traversing the list top-down, starting comparison from the element elist(order(i_nrmax+1)). */ i = i_nrmax + 1 ; /* The order of the tests in the following line is important to prevent a segmentation fault */ while (i < top && errmax < elist[order[i]]) { order[i-1] = order[i] ; i++ ; } order[i-1] = i_maxerr ; /* Insert errmin by traversing the list bottom-up */ errmin = elist[last] ; k = top - 1 ; while (k > i - 2 && errmin >= elist[order[k]]) { order[k+1] = order[k] ; k-- ; } order[k+1] = last ; /* Set i_max and e_max */ i_maxerr = order[i_nrmax] ; workspace->i = i_maxerr ; workspace->nrmax = i_nrmax ; } static inline void update (gsl_integration_workspace * workspace, double a1, double b1, double area1, double error1, double a2, double b2, double area2, double error2); static inline void retrieve (const gsl_integration_workspace * workspace, double * a, double * b, double * r, double * e); static inline void update (gsl_integration_workspace * workspace, double a1, double b1, double area1, double error1, double a2, double b2, double area2, double error2) { double * alist = workspace->alist ; double * blist = workspace->blist ; double * rlist = workspace->rlist ; double * elist = workspace->elist ; size_t * level = workspace->level ; const size_t i_max = workspace->i ; const size_t i_new = workspace->size ; const size_t new_level = workspace->level[i_max] + 1; /* append the newly-created intervals to the list */ if (error2 > error1) { alist[i_max] = a2; /* blist[maxerr] is already == b2 */ rlist[i_max] = area2; elist[i_max] = error2; level[i_max] = new_level; alist[i_new] = a1; blist[i_new] = b1; rlist[i_new] = area1; elist[i_new] = error1; level[i_new] = new_level; } else { blist[i_max] = b1; /* alist[maxerr] is already == a1 */ rlist[i_max] = area1; elist[i_max] = error1; level[i_max] = new_level; alist[i_new] = a2; blist[i_new] = b2; rlist[i_new] = area2; elist[i_new] = error2; level[i_new] = new_level; } workspace->size++; if (new_level > workspace->maximum_level) { workspace->maximum_level = new_level; } qpsrt (workspace) ; } static inline void retrieve (const gsl_integration_workspace * workspace, double * a, double * b, double * r, double * e) { const size_t i = workspace->i; double * alist = workspace->alist; double * blist = workspace->blist; double * rlist = workspace->rlist; double * elist = workspace->elist; *a = alist[i] ; *b = blist[i] ; *r = rlist[i] ; *e = elist[i] ; } static inline double sum_results (const gsl_integration_workspace * workspace); static inline double sum_results (const gsl_integration_workspace * workspace) { const double * const rlist = workspace->rlist ; const size_t n = workspace->size; size_t k; double result_sum = 0; for (k = 0; k < n; k++) { result_sum += rlist[k]; } return result_sum; } static inline int subinterval_too_small (double a1, double a2, double b2); static inline int subinterval_too_small (double a1, double a2, double b2) { const double e = GSL_DBL_EPSILON; const double u = GSL_DBL_MIN; double tmp = (1 + 100 * e) * (fabs (a2) + 1000 * u); int status = fabs (a1) <= tmp && fabs (b2) <= tmp; return status; } static double rescale_error (double err, const double result_abs, const double result_asc) ; static double rescale_error (double err, const double result_abs, const double result_asc) { err = fabs(err) ; if (result_asc != 0 && err != 0) { double scale = pow((200 * err / result_asc), 1.5) ; if (scale < 1) { err = result_asc * scale ; } else { err = result_asc ; } } if (result_abs > GSL_DBL_MIN / (50 * GSL_DBL_EPSILON)) { double min_err = 50 * GSL_DBL_EPSILON * result_abs ; if (min_err > err) { err = min_err ; } } return err ; } void q21 (const gsl_function * f, double a, double b, double *result, double *abserr, double *resabs, double *resasc) { static const int n = 11; static const double xgk[11] = /* abscissae of the 21-point Kronrod rule */ { 0.995657163025808080735527280689003, 0.973906528517171720077964012084452, 0.930157491355708226001207180059508, 0.865063366688984510732096688423493, 0.780817726586416897063717578345042, 0.679409568299024406234327365114874, 0.562757134668604683339000099272694, 0.433395394129247190799265943165784, 0.294392862701460198131126603103866, 0.148874338981631210884826001129720, 0.000000000000000000000000000000000 }; /* xgk[1], xgk[3], ... abscissae of the 10-point Gauss rule. xgk[0], xgk[2], ... abscissae to optimally extend the 10-point Gauss rule */ static const double wg[5] = /* weights of the 10-point Gauss rule */ { 0.066671344308688137593568809893332, 0.149451349150580593145776339657697, 0.219086362515982043995534934228163, 0.269266719309996355091226921569469, 0.295524224714752870173892994651338 }; static const double wgk[11] = /* weights of the 21-point kronrod rule */ { 0.011694638867371874278064396062192, 0.032558162307964727478818972459390, 0.054755896574351996031381300244580, 0.075039674810919952767043140916190, 0.093125454583697605535065465083366, 0.109387158802297641899210590325805, 0.123491976262065851077958109831074, 0.134709217311473325928054001771707, 0.142775938577060080797094273138717, 0.147739104901338491374841515972068, 0.149445554002916905664936468389821 }; double fv1[11], fv2[11]; const double center = 0.5 * (a + b); const double half_length = 0.5 * (b - a); const double abs_half_length = fabs (half_length); const double f_center = GSL_FN_EVAL (f, center); double result_gauss = 0; double result_kronrod = f_center * wgk[n - 1]; double result_abs = fabs (result_kronrod); double result_asc = 0; double mean = 0, err = 0; int j; if (n % 2 == 0) { result_gauss = f_center * wg[n / 2 - 1]; } for (j = 0; j < (n - 1) / 2; j++) { const int jtw = j * 2 + 1; /* in original fortran j=1,2,3 jtw=2,4,6 */ const double abscissa = half_length * xgk[jtw]; const double fval1 = GSL_FN_EVAL (f, center - abscissa); const double fval2 = GSL_FN_EVAL (f, center + abscissa); const double fsum = fval1 + fval2; fv1[jtw] = fval1; fv2[jtw] = fval2; result_gauss += wg[j] * fsum; result_kronrod += wgk[jtw] * fsum; result_abs += wgk[jtw] * (fabs (fval1) + fabs (fval2)); } for (j = 0; j < n / 2; j++) { int jtwm1 = j * 2; const double abscissa = half_length * xgk[jtwm1]; const double fval1 = GSL_FN_EVAL (f, center - abscissa); const double fval2 = GSL_FN_EVAL (f, center + abscissa); fv1[jtwm1] = fval1; fv2[jtwm1] = fval2; result_kronrod += wgk[jtwm1] * (fval1 + fval2); result_abs += wgk[jtwm1] * (fabs (fval1) + fabs (fval2)); }; mean = result_kronrod * 0.5; result_asc = wgk[n - 1] * fabs (f_center - mean); for (j = 0; j < n - 1; j++) { result_asc += wgk[j] * (fabs (fv1[j] - mean) + fabs (fv2[j] - mean)); } /* scale by the width of the integration region */ err = (result_kronrod - result_gauss) * half_length; result_kronrod *= half_length; result_abs *= abs_half_length; result_asc *= abs_half_length; *result = result_kronrod; *resabs = result_abs; *resasc = result_asc; *abserr = rescale_error (err, result_abs, result_asc); } static int qag (const gsl_function * f, const double a, const double b, const double epsabs, const double epsrel, const size_t limit, gsl_integration_workspace * workspace, double *result, double *abserr) { double area, errsum; double result0, abserr0, resabs0, resasc0; double tolerance; size_t iteration = 0; int roundoff_type1 = 0, roundoff_type2 = 0, error_type = 0; double round_off; /* Initialize results */ initialise (workspace, a, b); *result = 0; *abserr = 0; if (limit > workspace->limit) { GSL_ERROR ("iteration limit exceeds available workspace") ; } if (epsabs <= 0 && (epsrel < 50 * GSL_DBL_EPSILON || epsrel < 0.5e-28)) { GSL_ERROR ("tolerance cannot be achieved with given epsabs and epsrel"); } /* perform the first integration */ q21 (f, a, b, &result0, &abserr0, &resabs0, &resasc0); set_initial_result (workspace, result0, abserr0); /* Test on accuracy */ tolerance = fmax (epsabs, epsrel * fabs (result0)); /* need IEEE rounding here to match original quadpack behavior */ round_off = 50.0 * GSL_DBL_EPSILON * resabs0; if (abserr0 <= round_off && abserr0 > tolerance) { *result = result0; *abserr = abserr0; GSL_ERROR ("cannot reach tolerance because of roundoff error " "on first attempt"); } else if ((abserr0 <= tolerance && abserr0 != resasc0) || abserr0 == 0.0) { *result = result0; *abserr = abserr0; return 0; } else if (limit == 1) { *result = result0; *abserr = abserr0; GSL_ERROR ("a maximum of one iteration was insufficient"); } area = result0; errsum = abserr0; iteration = 1; do { double a1, b1, a2, b2; double a_i, b_i, r_i, e_i; double area1 = 0, area2 = 0, area12 = 0; double error1 = 0, error2 = 0, error12 = 0; double resasc1, resasc2; double resabs1, resabs2; /* Bisect the subinterval with the largest error estimate */ retrieve (workspace, &a_i, &b_i, &r_i, &e_i); a1 = a_i; b1 = 0.5 * (a_i + b_i); a2 = b1; b2 = b_i; q21 (f, a1, b1, &area1, &error1, &resabs1, &resasc1); q21 (f, a2, b2, &area2, &error2, &resabs2, &resasc2); area12 = area1 + area2; error12 = error1 + error2; errsum += (error12 - e_i); area += area12 - r_i; if (resasc1 != error1 && resasc2 != error2) { double delta = r_i - area12; if (fabs (delta) <= 1.0e-5 * fabs (area12) && error12 >= 0.99 * e_i) { roundoff_type1++; } if (iteration >= 10 && error12 > e_i) { roundoff_type2++; } } tolerance = fmax (epsabs, epsrel * fabs (area)); if (errsum > tolerance) { if (roundoff_type1 >= 6 || roundoff_type2 >= 20) { error_type = 2; /* round off error */ } /* set error flag in the case of bad integrand behaviour at a point of the integration range */ if (subinterval_too_small (a1, a2, b2)) { error_type = 3; } } update (workspace, a1, b1, area1, error1, a2, b2, area2, error2); retrieve (workspace, &a_i, &b_i, &r_i, &e_i); iteration++; std::cout << iteration << std::endl; } while (iteration < limit && !error_type && errsum > tolerance); *result = sum_results (workspace); *abserr = errsum; if (errsum <= tolerance) { return 0; } else if (error_type == 2) { GSL_ERROR ("roundoff error prevents tolerance from being achieved"); } else if (error_type == 3) { GSL_ERROR ("bad integrand behavior found in the integration interval"); } else if (iteration == limit) { GSL_ERROR ("maximum number of subdivisions reached"); } else { GSL_ERROR ("could not integrate function"); } } double use_log(double x, void *) { double y = std::log(x); std::cout << x << " " << y << std::endl; return y; } int main() { double result = 0.0; double abserr = 0.0; gsl_function f; f.function = &use_log; gsl_integration_workspace *ws = gsl_integration_workspace_alloc(1000); qag (&f, 0.0, 1.0, 1.0e-6, 1.0e-6, 1000, ws, &result, &abserr); std::cout << result+1.0 << " " << abserr << std::endl; gsl_integration_workspace_free(ws); return 0; }