How to prevent MATLAB from replacing a predefined integral with besselj?
Show older comments
I am asked via a partial differential equations class to verify that
. This needs to be done by comparing the graph of besselj(0,x) over a pre-defined interval of x to the integral as stated, and see how close the graph of the integral is to MATLAB's default besselj(0,x) function.
However, MATLAB replaces the numeric computation of the integral with besselj(0,x) during the running of the code (I put the integrand in a separate function file and named it besselintegrand, then used the integral command in the file I will submit for the class), making the graphical comparison useless as it is computing besselj(0,x) twice and plotting both, resulting in the exact same graph for both the integral and besselj(0,x), instead of computing besselj(0,x) and then computing the integral and then graphing both functions as different entities. It is as if it is ignoring the integral, as something in MATLAB seems to go "Oh, this integral looks like besselj(0,x), let's just compute that instead of actually computing the integral."
Why do I want MATLAB to not default the integral to besselj(0,x)? Because I am asked to comment on the accuracy of the integral in comparison to MATLAB's default function. I can't get that if MATLAB replaces the integral with the default function that I am trying to compare the integral to!
Is there a way to prevent MATLAB from defaulting the computation of the integral to besselj, so that the actual output of the integral can be compared to besselj graphically? Or do I have to get sneaky and use some trigonometric identities to "trick" MATLAB into not recognizing the integral as besselj(0,x)? If there is a way to prevent MATLAB from doing this in the first place?
A classmate has tried using the symbolic math toolbox, and the system literally rewrites his integral in the output to besselj(0,x) so even the symbolic math toolbox has this behavior.
17 Comments
Walter Roberson
on 20 Feb 2020
I would expect this for symbolic toolbox when int() was used on a symbolic expression or on a function handle. If you are not using int() or vpaintegral() then it would not be possible for besselj to appear.
Galen Baumgartner
on 21 Feb 2020
Edited: Galen Baumgartner
on 21 Feb 2020
Walter Roberson
on 21 Feb 2020
Edited: Walter Roberson
on 21 Feb 2020
No, that explanation does not work.
I do not see how you could have the results you indicate if you do not use int() or vpaintegral(). You would not get this result with a numeric integral with integral() or the older quadgk() or similar, which is the approach that the assignment is expecting you to take.
Galen Baumgartner
on 21 Feb 2020
Edited: Galen Baumgartner
on 21 Feb 2020
Walter Roberson
on 21 Feb 2020
integral() and function handle do not generate that result.
For any given x
F = @(phi) cos(x.*sin(phi))
integral(F, 0, pi) /pi
Galen Baumgartner
on 23 Feb 2020
Edited: Galen Baumgartner
on 23 Feb 2020
Walter Roberson
on 23 Feb 2020
max(abs(J_Bessel - J_integral))
ans =
4.44089209850063e-16
That is round-off error that all numeric integration is subject to. You can adjust the 'AbsTol' and 'RelTol' parameters of, but you will get the same result. The maximum difference, shown above, is 4*eps() of the relevant entry (index 125) of J_Bessel, which is not much difference.
Indeed, if you check further, it is the besselj() evaluation that is further off.
[maxdiff, idx] = max(abs(J_Bessel - J_integral))
v = vpa(besselj(0,sym(x_bess(idx),'d')))
db = v-J_Bessel(idx)
di = v-J_integral(idx)
simplify(abs(db) > abs(di))
you will see a result of symtrue, indicating that the besselj(0, x_bess(idx)) calculation is further away from the true version calculated symbolically to (default) 32 decimal places, than the integral is away from that extended value.
David Goodmanson
on 23 Feb 2020
Edited: David Goodmanson
on 23 Feb 2020
Hi Galen,
your code appears to be doing exactly what it should.
x = (0:0.01:5);
J_Bessel = besselj(0,x)
integrand = @(phi) cos(x.*sin(phi))
J_integral = (1/pi).*integral(integrand,0,pi,'ArrayValued',true)
figure(1)
plot(x,J_integral,x,besselj(0,x)) % overlay
grid on
format compact
% show the difference, a la mathcad
JminusJ = J_integral - besselj(0,x);
JminusJ(1:10)
max(JminusJ)
ans =
1.0e-15 *
Columns 1 through 9
0 0.2220 0 -0.1110 0 0 0 0 -0.1110
Column 10
0
ans =
4.4409e-16
There are some very small differences, showing that 'integral' is working reallly well and that the residuals are probably a lot smaller than you thought.
Galen Baumgartner
on 23 Feb 2020
Edited: Galen Baumgartner
on 23 Feb 2020
Walter Roberson
on 23 Feb 2020
I understand, but the professor won't accept this!
If that is the case, then the professor is wrong. Numeric integration will never be perfect, should never be expected to be perfect, and that should be part of the lesson.
You can switch to symbolic work to show that the integral is accurate to less than one bit in floating point precision:
x = 0:0.01:5;
J_Bessel = besselj(0,sym(x,'d'));
syms phi
integrand(phi) = cos(x.*sin(phi));
J_integral = (1/pi).*vpaintegral(integrand,phi,0,pi);
subplot(2,2,1)
plot(x, J_Bessel, '-b*');
title('besselj()')
subplot(2,2,2)
plot(x, J_integral, '--k.');
title('integral')
subplot(2,2,3)
plot(x, J_Bessel, '-b*', x, J_integral, '--k.') % overlay
title('both together')
subplot(2,2,4)
JminusJ = J_integral - J_Bessel;
plot(x, JminusJ)
title('difference')
format compact
% show the difference, a la mathcad
fprintf('with differences calculated in symbolic domain:\n');
JminusJ(1:10)
[maxdiff, maxidx] = max(abs(JminusJ));
fprintf('Max symbolic difference is %.16g at index %d\n', maxdiff, maxidx)
eps_factor = JminusJ(maxidx) / eps(x(maxidx));
fprintf('Number of epsilon is %g\n', eps_factor);
fprintf('with differences calculated in floating point domain:\n');
JminusJ = double(J_integral) - double(J_Bessel);
JminusJ(1:10)
[maxdiff, maxidx] = max(abs(JminusJ));
fprintf('Max floating point difference is %.16g at index %d\n', maxdiff, maxidx)
eps_factor = JminusJ(maxidx) / eps(x(maxidx));
fprintf('Number of epsilon is %g\n', eps_factor);
Walter Roberson
on 23 Feb 2020
If he is expecting to see a larger numeric difference, then you could always use integral() with a worse 'reltol' parameter.
Galen Baumgartner
on 23 Feb 2020
Walter Roberson
on 24 Feb 2020
>> J_integral=(1/sym(pi)).*int(cos(x_bess.*sin(phi)),phi,0,sym(pi))
J_integral =
besselj(0, x_bess)
As a result, I can conclude that the system is using the default function of Jo(x) to approximate the integral
No, you cannot conclude that at all !! int() is from the symbolic toolbox and does an analytic conversion when it can. integral() is a purely numeric tool that never ever converts anything to the besselj() function !! integral() does purely numeric approximation. It just happens to be doing a good job of numeric approximation.
I just tested with much reduced reltol and abstol, and it still does a quite good job of numeric approximation.
Galen Baumgartner
on 24 Feb 2020
Edited: Galen Baumgartner
on 24 Feb 2020
Galen Baumgartner
on 24 Feb 2020
Edited: Galen Baumgartner
on 24 Feb 2020
Walter Roberson
on 24 Feb 2020
The coefficient multiple you were seeing was due to the approximation of 1/π in floating point. When the floating point number gets converted to symbolic form to multiply by the expression, the symbolic toolbox does not recognize the 1/pi floating point approximation as being the same as the reciprocal of the symbolic π that it has calculated, so what you are seeing in the output is π/(approximate pi)
The output I showed where I used 1/sym(pi) instead of 1/pi tells the symbolic calculator to use the transcendental π instead of the floating-point approximation, and that exactly balances the transcendental π that is calculated with the symbolic integral.
David Goodmanson
on 24 Feb 2020
Edited: David Goodmanson
on 24 Feb 2020
Hi Galen,
If you don't use syms, then there is no chance of defaulting to anything. 'integral' happens to be good at minimizing numerical error, hence the very small values. I don't know what point your prof is trying to make, but if he really wants to see some errors you can do
phipoints = 10;
phi = linspace(0,pi,phipoints);
x = 0:.1:10;
[xmat,phimat] = meshgrid(x,phi);
integrand = (1/pi)*(cos(xmat.*sin(phimat)));
J_integral = trapz(phi,integrand);
figure(1)
J_bessel = besselj(0,x);
plot(x,J_bessel,x,J_integral,'o')
grid on
J_err = max(abs((J_integral-J_bessel)))
J_err =
3.0488e-04
This method sets up an array with x across, phi down (the equivalent of 'arrayvalued') and integrates down each column in phi. It uses trapz, the fairly primitive trapezoidal method.
Increasing phipoints improves the accuracy. At phipoints = 20 you are already down to an error of 3e-16, which is comparable to 'integral' and a lot better than I anticipated.
Answers (0)
Categories
Find more on Call Python from MATLAB in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!