You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
gh-39094: Fix numerical_integral with parameters
Fixes#39088.
As pointed out by @neldredge-unco in #39088, supplying a value for the
keyword argument `params` of `numerical_integral` yields a bunch of
blank lines, and then a value of `0.0` for the integral.
**Diagnosis.** The value of the integrand at each point is calculated by
the function `c_f` (lines 49-62 of `src/sage/calculus/integration.pyx`):
```
cdef double c_f(double t, void *params) noexcept:
cdef double value
cdef PyFunctionWrapper wrapper
wrapper = <PyFunctionWrapper> params
try:
if len(wrapper.the_parameters) != 0:
value = wrapper.the_function(t, wrapper.the_parameters)
else:
value = wrapper.the_function(t)
except Exception as msg:
print(msg)
return 0
return value
```
If an exception is raised while trying to calculate the value, then the
error message is printed, and 0 is returned as the value of the
function. Since parameters are not handled properly by the
`numerical_integral` function, an exception is raised every time, but
the error message is empty, so we get lots of blank lines and the
integral is 0.
**Remedy implemented in this PR.**
1. If `msg` has only whitespace, the PR instead prints an actual error
message. This replaces the blank lines with something more helpful.
2. The function `c_f` is tagged `noexcept`, so we cannot allow an
exception to be raised while printing the error message. Therefore, the
PR puts the printing in a `try...except` block to catch all exceptions.
3. `numerical_integral` converts the integrand to a `fast_callable`
function with only one variable (line 322 of
`src/sage/calculus/integration.pyx`):
```
func = fast_callable(func, vars=[v], domain=float)
```
However, `numerical_integral` still passes `params` (the parameters) to
the function `c_f`, so `c_f` is receiving more than one input value for
a one-variable function. This causes an exception every time. To fix
this, the PR executes `params = []` immediately after `func` is made
into a `fast_callable`. This means that no unwanted extra parameters
will be sent to `c_f`.
4. The doctest for `numerical_integral` was marked `# random` so it was
essentially ignored during doctesting. (I think that is why this bug was
not discovered long ago.) The PR changes this doctest to use a
tolerance, so incorrect behaviour will be detected in the future.
**Note.** The function `c_f` uses `except Exception`, instead of only
catching a specific list of exceptions. This is not good python, but I
think fixing this would belong on a different ticket, because it will
probably not be trivial (since a `noexcept` function cannot raise
exceptions).
### 📝 Checklist
<!-- Put an `x` in all the boxes that apply. -->
- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation and checked the documentation
preview.
### ⌛ Dependencies
<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - #12345: short description why this is a dependency -->
<!-- - #34567: ... -->
URL: #39094
Reported by: DaveWitteMorris
Reviewer(s): Travis Scrimshaw
0 commit comments