1.12.1 Why You Do Not Usually Need to Know about Internals
Most of this book is concerned with explaining what Mathematica does, not how it does it. But the purpose of this chapter is to say at least a little about how Mathematica does what it does. Appendix A.9 gives some more details.
You should realize at the outset that while knowing about the internals of Mathematica may be of intellectual interest, it is usually much less important in practice than one might at first suppose.
Indeed, one of the main points of Mathematica is that it provides an environment where you can perform mathematical and other operations without having to think in detail about how these operations are actually carried out inside your computer.
Thus, for example, if you want to factor the polynomial , you can do this just by giving Mathematica the command Factor[x^15  1]; you do not have to know the fairly complicated details of how such a factorization is actually carried out by the internal code of Mathematica.
Indeed, in almost all practical uses of Mathematica, issues about how Mathematica works inside turn out to be largely irrelevant. For most purposes it suffices to view Mathematica simply as an abstract system which performs certain specified mathematical and other operations.
You might think that knowing how Mathematica works inside would be necessary in determining what answers it will give. But this is only very rarely the case. For the vast majority of the computations that Mathematica does are completely specified by the definitions of mathematical or other operations.
Thus, for example, 3^40 will always be 12157665459056928801, regardless of how Mathematica internally computes this result.
There are some situations, however, where several different answers are all equally consistent with the formal mathematical definitions. Thus, for example, in computing symbolic integrals, there are often several different expressions which all yield the same derivative. Which of these expressions is actually generated by Integrate can then depend on how Integrate works inside.
Here is the answer generated by Integrate.
In[1]:= Integrate[1/x + 1/x^2, x]
Out[1]=
This is an equivalent expression that might have been generated if Integrate worked differently inside.
In[2]:= Together[%]
Out[2]=
In numerical computations, a similar phenomenon occurs. Thus, for example, FindRoot gives you a root of a function. But if there are several roots, which root is actually returned depends on the details of how FindRoot works inside.
This finds a particular root of .
In[3]:= FindRoot[Cos[x] + Sin[x], {x, 10.5}]
Out[3]=
With a different starting point, a different root is found. Which root is found with each starting point depends in detail on the internal algorithm used.
In[4]:= FindRoot[Cos[x] + Sin[x], {x, 10.8}]
Out[4]=
The dependence on the details of internal algorithms can be more significant if you push approximate numerical computations to the limits of their validity.
Thus, for example, if you give NIntegrate a pathological integrand, whether it yields a meaningful answer or not can depend on the details of the internal algorithm that it uses.
NIntegrate knows that this result is unreliable, and can depend on the details of the internal algorithm, so it prints warning messages.
In[5]:= NIntegrate[Sin[1/x], {x, 0, 1}]
Out[5]=
Traditional numerical computation systems have tended to follow the idea that all computations should yield results that at least nominally have the same precision. A consequence of this idea is that it is not sufficient just to look at a result to know whether it is accurate; you typically also have to analyze the internal algorithm by which the result was found. This fact has tended to make people believe that it is always important to know internal algorithms for numerical computations.
But with the approach that Mathematica takes, this is rarely the case. For Mathematica can usually use its arbitraryprecision numerical computation capabilities to give results where every digit that is generated follows the exact mathematical specification of the operation being performed.
Even though this is an approximate numerical computation, every digit is determined by the mathematical definition for .
In[6]:= N[Pi, 30]
Out[6]=
Once again, every digit here is determined by the mathematical definition for .
In[7]:= N[Sin[10^50], 20]
Out[7]=
If you use machineprecision numbers, Mathematica cannot give a reliable result, and the answer depends on the details of the internal algorithm used.
In[8]:= Sin[10.^50]
Out[8]=
It is a general characteristic that whenever the results you get can be affected by the details of internal algorithms, you should not depend on these results. For if nothing else, different versions of Mathematica may exhibit differences in these results, either because the algorithms operate slightly differently on different computer systems, or because fundamentally different algorithms are used in versions released at different times.
This is the result for on one type of computer.
In[1]:= Sin[10.^50]
Out[1]=
Here is the same calculation on another type of computer.
In[1]:= Sin[10.^50]
Out[1]= 0.0528229
And here is the result obtained in Mathematica Version 1.
In[2]:= Sin[10.^50]
Out[2]= 0.0937538
Particularly in more advanced applications of Mathematica, it may sometimes seem worthwhile to try to analyze internal algorithms in order to predict which way of doing a given computation will be the most efficient. And there are indeed occasionally major improvements that you will be able to make in specific computations as a result of such analyses.
But most often the analyses will not be worthwhile. For the internals of Mathematica are quite complicated, and even given a basic description of the algorithm used for a particular purpose, it is usually extremely difficult to reach a reliable conclusion about how the detailed implementation of this algorithm will actually behave in particular circumstances.
A typical problem is that Mathematica has many internal optimizations, and the efficiency of a computation can be greatly affected by whether the details of the computation do or do not allow a given internal optimization to be used.
