gsl_deriv__deriv.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /* deriv/deriv.c
  2. *
  3. * Copyright (C) 2004, 2007 Brian Gough
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 3 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. */
  19. #include "gsl__config.h"
  20. #include <stdlib.h>
  21. #include "gsl_math.h"
  22. #include "gsl_errno.h"
  23. #include "gsl_deriv.h"
  24. static void
  25. central_deriv (const gsl_function * f, double x, double h,
  26. double *result, double *abserr_round, double *abserr_trunc)
  27. {
  28. /* Compute the derivative using the 5-point rule (x-h, x-h/2, x,
  29. x+h/2, x+h). Note that the central point is not used.
  30. Compute the error using the difference between the 5-point and
  31. the 3-point rule (x-h,x,x+h). Again the central point is not
  32. used. */
  33. double fm1 = GSL_FN_EVAL (f, x - h);
  34. double fp1 = GSL_FN_EVAL (f, x + h);
  35. double fmh = GSL_FN_EVAL (f, x - h / 2);
  36. double fph = GSL_FN_EVAL (f, x + h / 2);
  37. double r3 = 0.5 * (fp1 - fm1);
  38. double r5 = (4.0 / 3.0) * (fph - fmh) - (1.0 / 3.0) * r3;
  39. double e3 = (fabs (fp1) + fabs (fm1)) * GSL_DBL_EPSILON;
  40. double e5 = 2.0 * (fabs (fph) + fabs (fmh)) * GSL_DBL_EPSILON + e3;
  41. /* The next term is due to finite precision in x+h = O (eps * x) */
  42. double dy = GSL_MAX (fabs (r3 / h), fabs (r5 / h)) *(fabs (x) / h) * GSL_DBL_EPSILON;
  43. /* The truncation error in the r5 approximation itself is O(h^4).
  44. However, for safety, we estimate the error from r5-r3, which is
  45. O(h^2). By scaling h we will minimise this estimated error, not
  46. the actual truncation error in r5. */
  47. *result = r5 / h;
  48. *abserr_trunc = fabs ((r5 - r3) / h); /* Estimated truncation error O(h^2) */
  49. *abserr_round = fabs (e5 / h) + dy; /* Rounding error (cancellations) */
  50. }
  51. int
  52. gsl_deriv_central (const gsl_function * f, double x, double h,
  53. double *result, double *abserr)
  54. {
  55. double r_0, round, trunc, error;
  56. central_deriv (f, x, h, &r_0, &round, &trunc);
  57. error = round + trunc;
  58. if (round < trunc && (round > 0 && trunc > 0))
  59. {
  60. double r_opt, round_opt, trunc_opt, error_opt;
  61. /* Compute an optimised stepsize to minimize the total error,
  62. using the scaling of the truncation error (O(h^2)) and
  63. rounding error (O(1/h)). */
  64. double h_opt = h * pow (round / (2.0 * trunc), 1.0 / 3.0);
  65. central_deriv (f, x, h_opt, &r_opt, &round_opt, &trunc_opt);
  66. error_opt = round_opt + trunc_opt;
  67. /* Check that the new error is smaller, and that the new derivative
  68. is consistent with the error bounds of the original estimate. */
  69. if (error_opt < error && fabs (r_opt - r_0) < 4.0 * error)
  70. {
  71. r_0 = r_opt;
  72. error = error_opt;
  73. }
  74. }
  75. *result = r_0;
  76. *abserr = error;
  77. return GSL_SUCCESS;
  78. }
  79. static void
  80. forward_deriv (const gsl_function * f, double x, double h,
  81. double *result, double *abserr_round, double *abserr_trunc)
  82. {
  83. /* Compute the derivative using the 4-point rule (x+h/4, x+h/2,
  84. x+3h/4, x+h).
  85. Compute the error using the difference between the 4-point and
  86. the 2-point rule (x+h/2,x+h). */
  87. double f1 = GSL_FN_EVAL (f, x + h / 4.0);
  88. double f2 = GSL_FN_EVAL (f, x + h / 2.0);
  89. double f3 = GSL_FN_EVAL (f, x + (3.0 / 4.0) * h);
  90. double f4 = GSL_FN_EVAL (f, x + h);
  91. double r2 = 2.0*(f4 - f2);
  92. double r4 = (22.0 / 3.0) * (f4 - f3) - (62.0 / 3.0) * (f3 - f2) +
  93. (52.0 / 3.0) * (f2 - f1);
  94. /* Estimate the rounding error for r4 */
  95. double e4 = 2 * 20.67 * (fabs (f4) + fabs (f3) + fabs (f2) + fabs (f1)) * GSL_DBL_EPSILON;
  96. /* The next term is due to finite precision in x+h = O (eps * x) */
  97. double dy = GSL_MAX (fabs (r2 / h), fabs (r4 / h)) * fabs (x / h) * GSL_DBL_EPSILON;
  98. /* The truncation error in the r4 approximation itself is O(h^3).
  99. However, for safety, we estimate the error from r4-r2, which is
  100. O(h). By scaling h we will minimise this estimated error, not
  101. the actual truncation error in r4. */
  102. *result = r4 / h;
  103. *abserr_trunc = fabs ((r4 - r2) / h); /* Estimated truncation error O(h) */
  104. *abserr_round = fabs (e4 / h) + dy;
  105. }
  106. int
  107. gsl_deriv_forward (const gsl_function * f, double x, double h,
  108. double *result, double *abserr)
  109. {
  110. double r_0, round, trunc, error;
  111. forward_deriv (f, x, h, &r_0, &round, &trunc);
  112. error = round + trunc;
  113. if (round < trunc && (round > 0 && trunc > 0))
  114. {
  115. double r_opt, round_opt, trunc_opt, error_opt;
  116. /* Compute an optimised stepsize to minimize the total error,
  117. using the scaling of the estimated truncation error (O(h)) and
  118. rounding error (O(1/h)). */
  119. double h_opt = h * pow (round / (trunc), 1.0 / 2.0);
  120. forward_deriv (f, x, h_opt, &r_opt, &round_opt, &trunc_opt);
  121. error_opt = round_opt + trunc_opt;
  122. /* Check that the new error is smaller, and that the new derivative
  123. is consistent with the error bounds of the original estimate. */
  124. if (error_opt < error && fabs (r_opt - r_0) < 4.0 * error)
  125. {
  126. r_0 = r_opt;
  127. error = error_opt;
  128. }
  129. }
  130. *result = r_0;
  131. *abserr = error;
  132. return GSL_SUCCESS;
  133. }
  134. int
  135. gsl_deriv_backward (const gsl_function * f, double x, double h,
  136. double *result, double *abserr)
  137. {
  138. return gsl_deriv_forward (f, x, -h, result, abserr);
  139. }