The Mathematica GuideBook for Numerics
Michael Trott
The Mathematica GuideBook for Numerics
With 1364 Illustrations
This eBook does not include ancillary media that was packaged with the printed version of the book. Michael Trott Wolfram Research Champaign, Illinois
Mathematica is a registered trademark of Wolfram Research, Inc. Library of Congress Control Number: 2005928494 ISBN-10: 0-387-95011-7 ISBN-13: 978-0387-95011-2 e-ISBN 0-387-28814-7
Printed on acid-free paper.
© 2006 Springer Science+Business Media, Inc. All rights reserved. This work may not be translated or copied in whole or in part without the written permission of the publisher (Springer Science+Business Media, Inc., 233 Spring St., New York, NY 10013, USA), except for brief excerpts in connection with reviews or scholarly analysis. Use in connection with any form of information storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now known or hereafter developed is forbidden. The use in this publication of trade names, trademarks, service marks, and similar terms, even if they are not identified as such, is not to be taken as an expression of opinion as to whether or not they are subject to proprietary rights. Printed in the United States of America. 9 8 7 6 5 4 3 2 1 springeronline.com
(HAM)
Preface Bei mathematischen Operationen kann sogar eine gänzliche Entlastung des Kopfes eintreten, indem man einmal ausgeführte Zähloperationen mit Zeichen symbolisiert und, statt die Hirnfunktion auf Wiederholung schon ausgeführter Operationen zu verschwenden, sie für wichtigere Fälle aufspart. When doing mathematics, instead of burdening the brain with the repetitive job of redoing numerical operations which have already been done before, it’s possible to save that brainpower for more important situations by using symbols, instead, to represent those numerical calculations. — Ernst Mach (1883 ) [45]
Computer Mathematics and Mathematica Computers were initially developed to expedite numerical calculations. A newer, and in the long run, very fruitful field is the manipulation of symbolic expressions. When these symbolic expressions represent mathematical entities, this field is generally called computer algebra [8]. Computer algebra begins with relatively elementary operations, such as addition and multiplication of symbolic expressions, and includes such things as factorization of integers and polynomials, exact linear algebra, solution of systems of equations, and logical operations. It also includes analysis operations, such as definite and indefinite integration, the solution of linear and nonlinear ordinary and partial differential equations, series expansions, and residue calculations. Today, with computer algebra systems, it is possible to calculate in minutes or hours the results that would (and did) take years to accomplish by paper and pencil. One classic example is the calculation of the orbit of the moon, which took the French astronomer Delaunay 20 years [12], [13], [14], [15], [11], [26], [27], [53], [16], [17], [25]. (The Mathematica GuideBooks cover the two other historic examples of calculations that, at the end of the 19th century, took researchers many years of hand calculations [1], [4], [38] and literally thousands of pages of paper.) Along with the ability to do symbolic calculations, four other ingredients of modern general-purpose computer algebra systems prove to be of critical importance for solving scientific problems: † a powerful high-level programming language to formulate complicated problems † programmable two- and three-dimensional graphics † robust, adaptive numerical methods, including arbitrary precision and interval arithmetic † the ability to numerically evaluate and symbolically deal with the classical orthogonal polynomials and special functions of mathematical physics. The most widely used, complete, and advanced general-purpose computer algebra system is Mathematica. Mathematica provides a variety of capabilities such as graphics, numerics, symbolics, standardized interfaces to other programs, a complete electronic document-creation environment (including a full-fledged mathematical typesetting system), and a variety of import and export capabilities. Most of these ingredients are necessary to coherently and exhaustively solve problems and model processes occurring in the natural sciences [41], [58], [21], [39] and other fields using constructive mathematics, as well as to properly represent the results. Conse-
Preface
vi
quently, Mathematica’s main areas of application are presently in the natural sciences, engineering, pure and applied mathematics, economics, finance, computer graphics, and computer science. Mathematica is an ideal environment for doing general scientific and engineering calculations, for investigating and solving many different mathematically expressable problems, for visualizing them, and for writing notes, reports, and papers about them. Thus, Mathematica is an integrated computing environment, meaning it is what is also called a “problem-solving environment” [40], [23], [6], [48], [43], [50], [52].
Scope and Goals The Mathematica GuideBooks are four independent books whose main focus is to show how to solve scientific problems with Mathematica. Each book addresses one of the four ingredients to solve nontrivial and real-life mathematically formulated problems: programming, graphics, numerics, and symbolics. The Programming and the Graphics volume were published in autumn 2004. The four Mathematica GuideBooks discuss programming, two-dimensional, and three-dimensional graphics, numerics, and symbolics (including special functions). While the four books build on each other, each one is self-contained. Each book discusses the definition, use, and unique features of the corresponding Mathematica functions, gives small and large application examples with detailed references, and includes an extensive set of relevant exercises and solutions. The GuideBooks have three primary goals: † to give the reader a solid working knowledge of Mathematica † to give the reader a detailed knowledge of key aspects of Mathematica needed to create the “best”, fastest, shortest, and most elegant solutions to problems from the natural sciences † to convince the reader that working with Mathematica can be a quite fruitful, enlightening, and joyful way of cooperation between a computer and a human. Realizing these goals is achieved by understanding the unifying design and philosophy behind the Mathematica system through discussing and solving numerous example-type problems. While a variety of mathematics and physics problems are discussed, the GuideBooks are not mathematics or physics books (from the point of view of content and rigor; no proofs are typically involved), but rather the author builds on Mathematica’s mathematical and scientific knowledge to explore, solve, and visualize a variety of applied problems. The focus on solving problems implies a focus on the computational engine of Mathematica, the kernel—rather than on the user interface of Mathematica, the front end. (Nevertheless, for a nicer presentation inside the electronic version, various front end features are used, but are not discussed in depth.) The Mathematica GuideBooks go far beyond the scope of a pure introduction into Mathematica. The books also present instructive implementations, explanations, and examples that are, for the most part, original. The books also discuss some “classical” Mathematica implementations, explanations, and examples, partially available only in the original literature referenced or from newsgroups threads. In addition to introducing Mathematica, the GuideBooks serve as a guide for generating fairly complicated graphics and for solving more advanced problems using graphical, numerical, and symbolical techniques in cooperative ways. The emphasis is on the Mathematica part of the solution, but the author employs examples that are not uninteresting from a content point of view. After studying the GuideBooks, the reader will be able to solve new and old scientific, engineering, and recreational mathematics problems faster and more completely with the help of Mathematica—at least, this is the author’s goal. The author also hopes that the reader will enjoy
Preface
vii
using Mathematica for visualization of the results as much as the author does, as well as just studying Mathematica as a language on its own. In the same way that computer algebra systems are not “proof machines” [46], [9], [37], [10], [54], [55], [56] such as might be used to establish the four-color theorem ([2], [22]), the Kepler [28], [19], [29], [30], [31], [32], [33], [34], [35], [36] or the Robbins ([44], [20]) conjectures, proving theorems is not the central theme of the GuideBooks. However, powerful and general proof machines [9], [42], [49], [24], [3], founded on Mathematica’ s general programming paradigms and its mathematical capabilities, have been built (one such system is Theorema [7]). And, in the GuideBooks, we occasionally prove one theorem or another theorem. In general, the author’s aim is to present a realistic portrait of Mathematica: its use, its usefulness, and its strengths, including some current weak points and sometimes unexpected, but often nevertheless quite “thought through”, behavior. Mathematica is not a universal tool to solve arbitrary problems which can be formulated mathematically—only a fraction of all mathematical problems can even be formulated in such a way to be efficiently expressed today in a way understandable to a computer. Rather, it is often necessary to do a certain amount of programming and occasionally give Mathematica some “help” instead of simply calling a single function like Solve to solve a system of equations. Because this will almost always be the case for “real-life” problems, we do not restrict ourselves only to “textbook” examples, where all goes smoothly without unexpected problems and obstacles. The reader will see that by employing Mathematica’s programming, numeric, symbolic, and graphic power, Mathematica can offer more effective, complete, straightforward, reusable, and less likely erroneous solution methods for calculations than paper and pencil, or numerical programming languages. Although the Guidebooks are large books, it is nevertheless impossible to discuss all of the 2,000+ built-in Mathematica commands. So, some simple as well as some more complicated commands have been omitted. For a full overview about Mathematica’s capabilities, it is necessary to study The Mathematica Book [60] in detail. The commands discussed in the Guidebooks are those that an scientist or research engineer needs for solving typical problems, if such a thing exists [18]. These subjects include a quite detailed discussion of the structure of Mathematica expressions, Mathematica input and output (important for the human–Mathematica interaction), graphics, numerical calculations, and calculations from classical analysis. Also, emphasis is given to the powerful algebraic manipulation functions. Interestingly, they frequently allow one to solve analysis problems in an algorithmic way [5]. These functions are typically not so well known because they are not taught in classical engineering or physics-mathematics courses, but with the advance of computers doing symbolic mathematics, their importance increases [47]. A thorough knowledge of: † machine and high-precision numbers, packed arrays, and intervals † machine, high-precision, and interval arithmetic † process of compilation, its advantages and limits † main operations of numerical analysis, such as equation solving, minimization, summation † numerical solution of ordinary and partial differential equations is needed for virtually any nontrivial numeric calculation and frequently also in symbolic computations. The Mathematica GuideBook for Numerics discusses these subjects. The current version of the Mathematica GuideBooks is tailored for Mathematica Version 5.1.
viii
Preface
Content Overview The Mathematica GuideBook for Numerics has two chapters. Each chapter is subdivided into sections (which occasionally have subsections), exercises, solutions to the exercises, and references. This volume deals with Mathematica’s numerical mathematics capabilities, the indispensable tools for dealing with virtually any “real life” problem. Fast machine, exact integer, and rational and verified high-precision arithmetic is applied to a large number of examples in the main text and in the solutions to the exercises. Chapter 1 deals with numerical calculations, which are important for virtually all Mathematica users. This volume starts with calculations involving real and complex numbers with an “arbitrary” number of digits. (Well, not really an "arbitrary" number of digits, but on present-day computers, many calculations involving a few million digits are easily feasible.). Then follows a discussion of significance arithmetic, which automatically keeps track of the digits which are correct in calculations with high-precision numbers. Also discussed is the use of interval arithmetic. (Despite being slow, exact and/or inexact, interval arithmetic allows one to carry out validated numerical calculations.) The next important subject is the (pseudo)compilation of Mathematica code. Because Mathematica is an interpreted language that allows for “unforeseeable” actions and arbitrary side effects at runtime, it generally cannot be compiled. Strictly numerical calculations can, of course, be compiled. Then, the main numerical functions are discussed: interpolation, Fourier transforms, numerical summation, and integration, solution of equations (root finding), minimization of functions, and the solution of ordinary and partial differential equations. To illustrate Mathematica’s differential equation solving capabilities, many ODEs and PDEs are discussed. Many medium-sized examples are given for the various numerical procedures. In addition, Mathematica is used to monitor and visualize various numerical algorithms. The main part of Chapter 1 culminates with two larger applications, the construction of Riemann surfaces of algebraic functions and the visualization of electric and magnetic field lines of some more complicated two- and three-dimensional charge and current distributions. A large, diverse set of exercises and detailed solutions ends the first chapter. Chapter 2 deals with exact integer calculations and integer-valued functions while concentrating on topics that are important in classical analysis. Number theory functions and modular polynomial functions, currently little used in most natural science applications, are intentionally given less detailed treatment. While some of the functions of this chapter have analytic continuations to complex arguments and could so be considered as belonging to Chapter 3 of the Symbolics volume, emphasis is given to their combinatorial use in this chapter. This volume explains and demonstrates the use of the numerical functions of Mathematica. It only rarely discusses the underlying numerical algorithms themselves. But occasionally Mathematica is used to monitor how the algorithms work and progress.
The Books and the Accompanying DVDs Each of the GuideBooks comes with a multiplatform DVD. Each DVD contains the fourteen main notebooks, the hyperlinked table of contents and index, a navigation palette, and some utility notebooks and files. All notebooks are tailored for Mathematica 5.1. Each of the main notebooks corresponds to a chapter from the printed books. The notebooks have the look and feel of a printed book, containing structured units, typeset formulas, Mathemat-
Preface
ix
ica code, and complete solutions to all exercises. The DVDs contain the fully evaluated notebooks corresponding to the chapters of the corresponding printed book (meaning these notebooks have text, inputs, outputs and graphics). The DVDs also include the unevaluated versions of the notebooks of the other three GuideBooks (meaning they contain all text and Mathematica code, but no outputs and graphics). Although the Mathematica GuideBooks are printed, Mathematica is “a system for doing mathematics by computer” [59]. This was the lovely tagline of earlier versions of Mathematica, but because of its growing breadth (like data import, export and handling, operating system-independent file system operations, electronic publishing capabilities, web connectivity), nowadays Mathematica is called a “system for technical computing”. The original tagline (that is more than ever valid today!) emphasized two points: doing mathematics and doing it on a computer. The approach and content of the GuideBooks are fully in the spirit of the original tagline: They are centered around doing mathematics. The second point of the tagline expresses that an electronic version of the GuideBooks is the more natural medium for Mathematica-related material. Long outputs returned by Mathematica, sequences of animations, thousands of web-retrievable references, a 10,000-entry hyperlinked index (that points more precisely than a printed index does) are space-consuming, and therefore not well suited for the printed book. As an interactive program, Mathematica is best learned, used, challenged, and enjoyed while sitting in front of a powerful computer (or by having a remote kernel connection to a powerful computer). In addition to simply showing the printed book’s text, the notebooks allow the reader to: † experiment with, reuse, adapt, and extend functions and code † investigate parameter dependencies † annotate text, code, and formulas † view graphics in color † run animations.
The Accompanying Web Site Why does a printed book need a home page? There are (in addition to being just trendy) two reasons for a printed book to have its fingerprints on the web. The first is for (Mathematica) users who have not seen the book so far. Having an outline and content sample on the web is easily accomplished, and shows the look and feel of the notebooks (including some animations). This is something that a printed book actually cannot do. The second reason is for readers of the book: Mathematica is a large modern software system. As such, it ages quickly in the sense that in the timescale of 101. smallInteger months, a new version will likely be available. The overwhelmingly large majority of Mathematica functions and programs will run unchanged in a new version. But occasionally, changes and adaptations might be needed. To accommodate this, the web site of this book—http://www.MathematicaGuideBooks.org—contains a list of changes relevant to the GuideBooks. In addition, like any larger software project, unavoidably, the GuideBooks will contain suboptimal implementations, mistakes, omissions, imperfections, and errors. As they come to his attention, the author will list them at the book’s web site. Updates to references, corrections [51], hundreds of pages of additional exercises and solutions, improved code segments, and other relevant information will be on the web site as well. Also, information about OS-dependent and Mathematica version-related changes of the given Mathematica code will be available there.
Preface
x
Evolution of the Mathematica GuideBooks A few words about the history and the original purpose of the GuideBooks: They started from lecture notes of an Introductory Course in Mathematica 2 and an advanced course on the Efficient Use of the Mathematica Programming System, given in 1991/1992 at the Technical University of Ilmenau, Germany. Since then, after each release of a new version of Mathematica, the material has been updated to incorporate additional functionality. This electronic/printed publication contains text, unique graphics, editable formulas, runable, and modifiable programs, all made possible by the electronic publishing capabilities of Mathematica. However, because the structure, functions and examples of the original lecture notes have been kept, an abbreviated form of the GuideBooks is still suitable for courses. Since 1992 the manuscript has grown in size from 1,600 pages to more than three times its original length, finally “weighing in” at nearly 5,000 printed book pages with more than: † 18 gigabytes of accompanying Mathematica notebooks † 22,000 Mathematica inputs with more than 13,000 code comments † 11,000 references † 4,000 graphics † 1,000 fully solved exercises † 150 animations. This first edition of this book is the result of more than eleven years of writing and daily work with Mathematica. In these years, Mathematica gained hundreds of functions with increased functionality and power. A modern year-2005 computer equipped with Mathematica represents a computational power available only a few years ago to a select number of people [57] and allows one to carry out recreational or new computations and visualizations—unlimited in nature, scope, and complexity— quickly and easily. Over the years, the author has learned a lot of Mathematica and its current and potential applications, and has had a lot of fun, enlightening moments and satisfaction applying Mathematica to a variety of research and recreational areas, especially graphics. The author hopes the reader will have a similar experience.
Disclaimer In addition to the usual disclaimer that neither the author nor the publisher guarantees the correctness of any formula, fitness, or reliability of any of the code pieces given in this book, another remark should be made. No guarantee is given that running the Mathematica code shown in the GuideBooks will give identical results to the printed ones. On the contrary, taking into account that Mathematica is a large and complicated software system which evolves with each released version, running the code with another version of Mathematica (or sometimes even on another operating system) will very likely result in different outputs for some inputs. And, as a consequence, if different outputs are generated early in a longer calculation, some functions might hang or return useless results. The interpretations of Mathematica commands, their descriptions, and uses belong solely to the author. They are not claimed, supported, validated, or enforced by Wolfram Research. The reader will find that the author’s view on Mathematica deviates sometimes considerably from those found in other books. The author’s view is more on
Preface
xi
the formal than on the pragmatic side. The author does not hold the opinion that any Mathematica input has to have an immediate semantic meaning. Mathematica is an extremely rich system, especially from the language point of view. It is instructive, interesting, and fun to study the behavior of built-in Mathematica functions when called with a variety of arguments (like unevaluated, hold, including undercover zeros, etc.). It is the author’s strong belief that doing this and being able to explain the observed behavior will be, in the long term, very fruitful for the reader because it develops the ability to recognize the uniformity of the principles underlying Mathematica and to make constructive, imaginative, and effective use of this uniformity. Also, some exercises ask the reader to investigate certain “unusual” inputs. From time to time, the author makes use of undocumented features and/or functions from the Developer` and Experimental` contexts (in later versions of Mathematica these functions could exist in the System` context or could have different names). However, some such functions might no longer be supported or even exist in later versions of Mathematica.
Acknowledgements Over the decade, the GuideBooks were in development, many people have seen parts of them and suggested useful changes, additions, and edits. I would like to thank Horst Finsterbusch, Gottfried Teichmann, Klaus Voss, Udo Krause, Jerry Keiper, David Withoff, and Yu He for their critical examination of early versions of the manuscript and their useful suggestions, and Sabine Trott for the first proofreading of the German manuscript. I also want to thank the participants of the original lectures for many useful discussions. My thanks go to the reviewers of this book: John Novak, Alec Schramm, Paul Abbott, Jim Feagin, Richard Palmer, Ward Hanson, Stan Wagon, and Markus van Almsick, for their suggestions and ideas for improvement. I thank Richard Crandall, Allan Hayes, Andrzej Kozlowski, Hartmut Wolf, Stephan Leibbrandt, George Kambouroglou, Domenico Minunni, Eric Weisstein, Andy Shiekh, Arthur G. Hubbard, Jay Warrendorff, Allan Cortzen, Ed Pegg, and Udo Krause for comments on the prepublication version of the GuideBooks. I thank Bobby R. Treat, Arthur G. Hubbard, Murray Eisenberg, Marvin Schaefer, Marek Duszynski, Daniel Lichtblau, Devendra Kapadia, Adam Strzebonski, Anton Antonov, and Brett Champion for useful comments on the Mathematica Version 5.1 tailored version of the GuideBooks. My thanks are due to Gerhard Gobsch of the Institute for Physics of the Technical University in Ilmenau for the opportunity to develop and give these original lectures at the Institute, and to Stephen Wolfram who encouraged and supported me on this project. Concerning the process of making the Mathematica GuideBooks from a set of lecture notes, I thank Glenn Scholebo for transforming notebooks to T E X files, and Joe Kaiping for T E X work related to the printed book. I thank John Novak and Jan Progen for putting all the material into good English style and grammar, John Bonadies for the chapter-opener graphics of the book, and Jean Buck for library work. I especially thank John Novak for the creation of Mathematica 3 notebooks from the T E X files, and Andre Kuzniarek for his work on the stylesheet to give the notebooks a pleasing appearance. My thanks go to Andy Hunt who created a specialized stylesheet for the actual book printout and printed and formatted the 4×1000+ pages of the Mathematica GuideBooks. I thank Andy Hunt for making a first version of the homepage of the GuideBooks and Amy Young for creating the current version of the homepage of the GuideBooks. I thank Sophie Young for a final check of the English. My largest thanks go to Amy Young, who encouraged me to update the whole book over the years and who had a close look at all of my English writing and often improved it considerably. Despite reviews by many individuals any remaining mistakes or omissions, in the Mathematica code, in the mathematics, in the description of the Mathematica functions, in the English, or in the references, etc. are, of course, solely mine.
xii
Preface
Let me take the opportunity to thank members of the Research and Development team of Wolfram Research whom I have met throughout the years, especially Victor Adamchik, Anton Antonov, Alexei Bocharov, Arnoud Buzing, Brett Champion, Matthew Cook, Todd Gayley, Darren Glosemeyer, Roger Germundsson, Unal Goktas, Yifan Hu, Devendra Kapadia, Zbigniew Leyk, David Librik, Daniel Lichtblau, Jerry Keiper, Robert Knapp, Roman Mäder, Oleg Marichev, John Novak, Peter Overmann, Oleksandr Pavlyk, Ulises Cervantes–Pimentel, Mark Sofroniou, Adam Strzebonski, Oyvind Tafjord, Robby Villegas, Tom Wickham–Jones, David Withoff, and Stephen Wolfram for numerous discussions about design principles, various small details, underlying algorithms, efficient implementation of various procedures, and tricks concerning Mathematica. The appearance of the notebooks profited from discussions with John Fultz, Paul Hinton, John Novak, Lou D’Andria, Theodore Gray, Andre Kuzniarek, Jason Harris, Andy Hunt, Christopher Carlson, Robert Raguet–Schofield, George Beck, Kai Xin, Chris Hill, and Neil Soiffer about front end, button, and typesetting issues. I'm grateful to Jeremy Hilton from the Corporation for National Research Initiatives for allowing the use of the text of Shakespeare's Hamlet (to be used in Chapter 1 of The Mathematica GuideBook to Numerics). It was an interesting and unique experience to work over the last 12 years with five editors: Allan Wylde, Paul Wellin, Maria Taylor, Wayne Yuhasz, and Ann Kostant, with whom the GuideBooks were finally published. Many book-related discussions that ultimately improved the GuideBooks, have been carried out with Jan Benes from TELOS and associates, Steven Pisano, Jenny Wolkowicki, Henry Krell, Fred Bartlett, Vaishali Damle, Ken Quinn, Jerry Lyons, and Rüdiger Gebauer from Springer New York. The author hopes the Mathematica GuideBooks help the reader to discover, investigate, urbanize, and enjoy the computational paradise offered by Mathematica.
Wolfram Research, Inc. April 2005
Michael Trott
Preface
xiii
References 1
A. Amthor. Z. Math. Phys. 25, 153 (1880).
2
K. Appel, W. Haken. J. Math. 21, 429 (1977).
3
A. Bauer, E. Clarke, X. Zhao. J. Automat. Reasoning 21, 295 (1998).
4
A. H. Bell. Am. Math. Monthly 2, 140 (1895).
5
M. Berz. Adv. Imaging Electron Phys. 108, 1 (2000).
6
R. F. Boisvert. arXiv:cs.MS/0004004 (2000).
7
B. Buchberger. Theorema Project (1997). ftp://ftp.risc.uni-linz.ac.at/pub/techreports/1997/97-34/ed-media.nb
8
B. Buchberger. SIGSAM Bull. 36, 3 (2002).
9
S.-C. Chou, X.-S. Gao, J.-Z. Zhang. Machine Proofs in Geometry, World Scientific, Singapore, 1994.
10
A. M. Cohen. Nieuw Archief Wiskunde 14, 45 (1996).
11
A. Cook. The Motion of the Moon, Adam-Hilger, Bristol, 1988.
12
C. Delaunay. Théorie du Mouvement de la Lune, Gauthier-Villars, Paris, 1860.
13
C. Delaunay. Mem. de l’ Acad. des Sc. Paris 28 (1860).
14
C. Delaunay. Mem. de l’ Acad. des Sc. Paris 29 (1867).
15
A. Deprit, J. Henrard, A. Rom. Astron. J. 75, 747 (1970).
16
A. Deprit. Science 168, 1569 (1970).
17
A. Deprit, J. Henrard, A. Rom. Astron. J. 76, 273 (1971).
18
P. J. Dolan, Jr., D. S. Melichian. Am. J. Phys. 66, 11 (1998).
19
S. P. Ferguson, T. C. Hales. arXiv:math.MG/ 9811072 (1998).
20
B. Fitelson. Mathematica Educ. Res. 7, n1, 17 (1998).
21
A. C. Fowler. Mathematical Models in the Applied Sciences, Cambridge University Press, Cambridge, 1997.
22
H. Fritsch, G. Fritsch. The Four-Color Theorem, Springer-Verlag, New York, 1998.
23
E. Gallopoulus, E. Houstis, J. R. Rice (eds.). Future Research Directions in Problem Solving Environments for Computational Science: Report of a Workshop on Research Directions in Integrating Numerical Analysis, Symbolic Computing, Computational Geometry, and Artificial Intelligence for Computational Science, 1991. http://www.cs.purdue.edu/research/cse/publications/tr/92/92-032.ps.gz
24
V. Gerdt, S. A. Gogilidze in V. G. Ganzha, E. W. Mayr, E. V. Vorozhtsov (eds.). Computer Algebra in Scientific Computing, Springer-Verlag, Berlin, 1999.
25
M. C. Gutzwiller, D. S. Schmidt. Astronomical Papers: The Motion of the Moon as Computed by the Method of Hill, Brown, and Eckert, U.S. Government Printing Office, Washington, 1986.
26
M. C. Gutzwiller. Rev. Mod. Phys. 70, 589 (1998).
27
Y. Hagihara. Celestial Mechanics vII/1, MIT Press, Cambridge, 1972.
28
T. C. Hales. arXiv:math.MG/ 9811071 (1998).
29
T. C. Hales. arXiv:math.MG/ 9811073 (1998).
30
T. C. Hales. arXiv:math.MG/ 9811074 (1998).
31
T. C. Hales. arXiv:math.MG/ 9811075 (1998).
32
T. C. Hales. arXiv:math.MG/ 9811076 (1998).
33
T. C. Hales. arXiv:math.MG/ 9811077 (1998).
Preface
xiv 34
T. C. Hales. arXiv:math.MG/ 9811078 (1998).
35
T. C. Hales. arXiv:math.MG/0205208 (2002).
36
T. C. Hales in L. Tatsien (ed.). Proceedings of the International Congress of Mathematicians v. 3, Higher Education Press, Beijing, 2002.
37
J. Harrison. Theorem Proving with the Real Numbers, Springer-Verlag, London, 1998.
38
J. Hermes. Nachrichten Königl. Gesell. Wiss. Göttingen 170 (1894).
39 40
E. N. Houstis, J. R. Rice, E. Gallopoulos, R. Bramley (eds.). Enabling Technologies for Computational Science, Kluwer, Boston, 2000. E. N. Houstis, J. R. Rice. Math. Comput. Simul. 54, 243 (2000).
41
M. S. Klamkin (eds.). Mathematical Modelling, SIAM, Philadelphia, 1996.
42
H. Koch, A. Schenkel, P. Wittwer. SIAM Rev. 38, 565 (1996).
43
Y. N. Lakshman, B. Char, J. Johnson in O. Gloor (ed.). ISSAC 1998, ACM Press, New York, 1998.
44
W. McCune. Robbins Algebras Are Boolean, 1997. http://www.mcs.anl.gov/home/mccune/ar/robbins/
45
E. Mach (R. Wahsner, H.-H. von Borszeskowski eds.). Die Mechanik in ihrer Entwicklung, Akademie-Verlag, Berlin, 1988. D. A. MacKenzie. Mechanizing Proof: Computing, Risk, and Trust, MIT Press, Cambridge, 2001.
46 47
B. M. McCoy. arXiv:cond-mat/0012193 (2000).
48
K. J. M. Moriarty, G. Murdeshwar, S. Sanielevici. Comput. Phys. Commun. 77, 325 (1993).
49
I. Nemes, M. Petkovšek, H. S. Wilf, D. Zeilberger. Am. Math. Monthly 104, 505 (1997).
50
W. H. Press, S. A. Teukolsky. Comput. Phys. 11, 417 (1997).
51
D. Rawlings. Am. Math. Monthly 108, 713 (2001).
52
Problem Solving Environments Home Page. http://www.cs.purdue.edu/research/cse/pses
53
D. S. Schmidt in H. S. Dumas, K. R. Meyer, D. S. Schmidt (eds.). Hamiltonian Dynamical Systems, Springer-Verlag, New York, 1995. S. Seiden. SIGACT News 32, 111 (2001).
54 55
S. Seiden. Theor. Comput. Sc. 282, 381 (2002).
56
C. Simpson. arXiv:math.HO/0311260 (2003).
57
A. M. Stoneham. Phil. Trans. R. Soc. Lond. A 360, 1107 (2002).
58
M. Tegmark. Ann. Phys. 270, 1 (1999).
59
S. Wolfram. Mathematica: A System for Doing Mathematics by Computer, Addison-Wesley, Redwood City, 1992.
60
S. Wolfram. The Mathematica Book, Wolfram Media, Champaign, 2003.
Contents 0.
Introduction and Orientation xvii
CHAPTER 1
Numerical Computations 1.0
Remarks 1
1.1
Approximate Numbers 15 1.1.0 Remarks 15 1.1.1
Numbers with an Arbitrary Number of Digits 15
1.1.2
Interval Arithmetic 54
1.1.3
Converting Approximate Numbers to Exact Numbers 66
1.1.4
When N Does Not Succeed 94
1.1.5
Packed Arrays 101
1.2
Fitting and Interpolating Functions 124
1.3
Compiled Programs 159
1.4
Linear Algebra 203
1.5
Fourier Transforms 225
1.6
Numerical Functions and Their Options 264
1.7
Sums and Products 272
1.8
Integration 289
1.9
Solution of Equations 316
1.10
Minimization 347
1.11
Solution of Differential Equations 368 1.11.1 Ordinary Differential Equations 368 1.11.2
Partial Differential Equations 451
xvi 1.12
Two Applications 481 1.12.0 Remarks 481 1.12.1
Visualizing Electric and Magnetic Field Lines 481
1.12.2
Riemann Surfaces of Algebraic Functions 508
Exercises 521 Solutions 703 References 915
CHAPTER 2
Computations with Exact Numbers 2.0
Remarks 969
2.1
Divisors and Multiples 974
2.2
Number Theory Functions 1009
2.3
Combinatorial Functions 1030
2.4
Euler, Bernoulli, and Fibonacci Numbers 1046 Exercises 1060 Solutions 1076 References 1173 Index 1191
Introduction and Orientation to The Mathematica GuideBooks 0.1 Overview 0.1.1 Content Summaries The Mathematica GuideBooks are published as four independent books: The Mathematica GuideBook to Programming, The Mathematica GuideBook to Graphics, The Mathematica GuideBook to Numerics, and The Mathematica GuideBook to Symbolics. † The Programming volume deals with the structure of Mathematica expressions and with Mathematica as a programming language. This volume includes the discussion of the hierarchical construction of all Mathematica objects out of symbolic expressions (all of the form head[argument]), the ultimate building blocks of expressions (numbers, symbols, and strings), the definition of functions, the application of rules, the recognition of patterns and their efficient application, the order of evaluation, program flows and program structure, the manipulation of lists (the universal container for Mathematica expressions of all kinds), as well as a number of topics specific to the Mathematica programming language. Various programming styles, especially Mathematica’ s powerful functional programming constructs, are covered in detail. † The Graphics volume deals with Mathematica’s two-dimensional (2D) and three-dimensional (3D) graphics. The chapters of this volume give a detailed treatment on how to create images from graphics primitives, such as points, lines, and polygons. This volume also covers graphically displaying functions given either analytically or in discrete form. A number of images from the Mathematica Graphics Gallery are also reconstructed. Also discussed is the generation of pleasing scientific visualizations of functions, formulas, and algorithms. A variety of such examples are given. † The Numerics volume deals with Mathematica’s numerical mathematics capabilities—the indispensable sledgehammer tools for dealing with virtually any “real life” problem. The arithmetic types (fast machine, exact integer and rational, verified high-precision, and interval arithmetic) are carefully analyzed. Fundamental numerical operations, such as compilation of programs, numerical Fourier transforms, minimization, numerical solution of equations, and ordinary/partial differential equations are analyzed in detail and are applied to a large number of examples in the main text and in the solutions to the exercises. † The Symbolics volume deals with Mathematica’s symbolic mathematical capabilities—the real heart of Mathematica and the ingredient of the Mathematica software system that makes it so unique and powerful. Structural and mathematical operations on systems of polynomials are fundamental to many symbolic calculations and are covered in detail. The solution of equations and differential equations, as well as the classical calculus operations, are exhaustively treated. In addition, this volume discusses and employs the classical
xviii
Introduction
orthogonal polynomials and special functions of mathematical physics. To demonstrate the symbolic mathematics power, a variety of problems from mathematics and physics are discussed. The four GuideBooks contain about 25,000 Mathematica inputs, representing more than 75,000 lines of commented Mathematica code. (For the reader already familiar with Mathematica, here is a more precise measure: The LeafCount of all inputs would be about 900,000 when collected in a list.) The GuideBooks also have more than 4,000 graphics, 150 animations, 11,000 references, and 1,000 exercises. More than 10,000 hyperlinked index entries and hundreds of hyperlinks from the overview sections connect all parts in a convenient way. The evaluated notebooks of all four volumes have a cumulative file size of about 20 GB. Although these numbers may sound large, the Mathematica GuideBooks actually cover only a portion of Mathematica’s functionality and features and give only a glimpse into the possibilities Mathematica offers to generate graphics, solve problems, model systems, and discover new identities, relations, and algorithms. The Mathematica code is explained in detail throughout all chapters. More than 13,000 comments are scattered throughout all inputs and code fragments.
0.1.2 Relation of the Four Volumes The four volumes of the GuideBooks are basically independent, in the sense that readers familiar with Mathematica programming can read any of the other three volumes. But a solid working knowledge of the main topics discussed in The Mathematica GuideBook to Programming—symbolic expressions, pure functions, rules and replacements, and list manipulations—is required for the Graphics, Numerics, and Symbolics volumes. Compared to these three volumes, the Programming volume might appear to be a bit “dry”. But similar to learning a foreign language, before being rewarded with the beauty of novels or a poem, one has to sweat and study. The whole suite of graphical capabilities and all of the mathematical knowledge in Mathematica are accessed and applied through lists, patterns, rules, and pure functions, the material discussed in the Programming volume. Naturally, graphics are the center of attention of the The Mathematica GuideBook to Graphics. While in the Programming volume some plotting and graphics for visualization are used, graphics are not crucial for the Programming volume. The reader can safely skip the corresponding inputs to follow the main programming threads. The Numerics and Symbolics volumes, on the other hand, make heavy use of the graphics knowledge acquired in the Graphics volume. Hence, the prerequisites for the Numerics and Symbolics volumes are a good knowledge of Mathematica’s programming language and of its graphics system. The Programming volume contains only a few percent of all graphics, the Graphics volume contains about two-thirds, and the Numerics and Symbolics volume, about one-third of the overall 4,000+ graphics. The Programming and Graphics volumes use some mathematical commands, but they restrict the use to a relatively small number (especially Expand, Factor, Integrate, Solve). And the use of the function N for numerical ization is unavoidable for virtually any “real life” application of Mathematica. The last functions allow us to treat some mathematically not uninteresting examples in the Programming and Graphics volumes. In addition to putting these functions to work for nontrivial problems, a detailed discussion of the mathematics functions of Mathematica takes place exclusively in the Numerics and Symbolics volumes. The Programming and Graphics volumes contain a moderate amount of mathematics in the examples and exercises, and focus on programming and graphics issues. The Numerics and Symbolics volumes contain a substantially larger amount of mathematics. Although printed as four books, the fourteen individual chapters (six in the Programming volume, three in the Graphics volume, two in the Numerics volume, and three in the Symbolics volume) of the Mathematica GuideBooks form one organic whole, and the author recommends a strictly sequential reading, starting from Chapter 1 of the Programming volume and ending with Chapter 3 of the Symbolics volume for gaining the maximum
Introduction
xix
benefit. The electronic component of each book contains the text and inputs from all the four GuideBooks, together with a comprehensive hyperlinked index. The four volumes refer frequently to one another.
0.1.3 Chapter Structure A rough outline of the content of a chapter is the following: † The main body discusses the Mathematica functions belonging to the chapter subject, as well their options and attributes. Generically, the author has attempted to introduce the functions in a “natural order”. But surely, one cannot be axiomatic with respect to the order. (Such an order of the functions is not unique, and the author intentionally has “spread out” the introduction of various Mathematica functions across the four volumes.) With the introduction of a function, some small examples of how to use the functions and comparisons of this function with related ones are given. These examples typically (with the exception of some visualizations in the Programming volume) incorporate functions already discussed. The last section of a chapter often gives a larger example that makes heavy use of the functions discussed in the chapter. † A programmatically constructed overview of each chapter functions follows. The functions listed in this section are hyperlinked to their attributes and options, as well as to the corresponding reference guide entries of The Mathematica Book. † A set of exercises and potential solutions follow. Because learning Mathematica through examples is very efficient, the proposed solutions are quite detailed and form up to 50% of the material of a chapter. † References end the chapter. Note that the first few chapters of the Programming volume deviate slightly from this structure. Chapter 1 of the Programming volume gives a general overview of the kind of problems dealt with in the four GuideBooks. The second, third, and fourth chapters of the Programming volume introduce the basics of programming in Mathematica. Starting with Chapters 5 of the Programming volume and throughout the Graphics, Numerics, and Symbolics volumes, the above-described structure applies. In the 14 chapters of the GuideBooks the author has chosen a “we” style for the discussions of how to proceed in constructing programs and carrying out calculations to include the reader intimately.
0.1.4 Code Presentation Style The typical style of a unit of the main part of a chapter is: Define a new function, discuss its arguments, options, and attributes, and then give examples of its usage. The examples are virtually always Mathematica inputs and outputs. The majority of inputs is in InputForm are the notebooks. On occasion StandardForm is also used. Although StandardForm mimics classical mathematics notation and makes short inputs more readable, for “program-like” inputs, InputForm is typically more readable and easier and more natural to align. For the outputs, StandardForm is used by default and occasionally the author has resorted to InputForm or FullForm to expose digits of numbers and to TraditionalForm for some formulas. Outputs are mostly not programs, but nearly always “results” (often mathematical expressions, formulas, identities, or lists of numbers rather than program constructs). The world of Mathematica users is divided into three groups, and each of them has a nearly religious opinion on how to format Mathematica code [1], [2]. The author follows the InputForm
xx
Introduction
cult(ure) and hopes that the Mathematica users who do everything in either StandardForm or Tradition alForm will bear with him. If the reader really wants to see all code in either StandardForm or Tradition alForm, this can easily be done with the Convert To item from the Cell menu. (Note that the relation between InputForm and StandardForm is not symmetric. The InputForm cells of this book have been line-broken and aligned by hand. Transforming them into StandardForm or TraditionalForm cells works well because one typically does not line-break manually and align Mathematica code in these cell types. But converting StandardForm or TraditionalForm cells into InputForm cells results in much less pleasing results.) In the inputs, special typeset symbols for Mathematica functions are typically avoided because they are not monospaced. But the author does occasionally compromise and use Greek, script, Gothic, and doublestruck characters. In a book about a programming language, two other issues come always up: indentation and placement of the code. † The code of the GuideBooks is largely consistently formatted and indented. There are no strict guidelines or even rules on how to format and indent Mathematica code. The author hopes the reader will find the book’s formatting style readable. It is a compromise between readability (mental parsabililty) and space conservation, so that the printed version of the Mathematica GuideBook matches closely the electronic version. † Because of the large number of examples, a rather imposing amount of Mathematica code is presented. Should this code be present only on the disk, or also in the printed book? If it is in the printed book, should it be at the position where the code is used or at the end of the book in an appendix? Many authors of Mathematica articles and books have strong opinions on this subject. Because the main emphasis of the Mathematica GuideBooks is on solving problems with Mathematica and not on the actual problems, the GuideBooks give all of the code at the point where it is needed in the printed book, rather than “hiding” it in packages and appendices. In addition to being more straightforward to read and conveniently allowing us to refer to elements of the code pieces, this placement makes the correspondence between the printed book and the notebooks close to 1:1, and so working back and forth between the printed book and the notebooks is as straightforward as possible.
0.2 Requirements 0.2.1 Hardware and Software Throughout the GuideBooks, it is assumed that the reader has access to a computer running a current version of Mathematica (version 5.0/5.1 or newer). For readers without access to a licensed copy of Mathematica, it is possible to view all of the material on the disk using a trial version of Mathematica. (A trial version is downloadable from http://www.wolfram.com/products/mathematica/trial.cgi.) The files of the GuideBooks are relatively large, altogether more than 20 GB. This is also the amount of hard disk space needed to store uncompressed versions of the notebooks. To view the notebooks comfortably, the reader’s computer needs 128 MB RAM; to evaluate the evaluation units of the notebooks 1 GB RAM or more is recommended. In the GuideBooks, a large number of animations are generated. Although they need more memory than single pictures, they are easy to create, to animate, and to store on typical year-2005 hardware, and they provide a lot of joy.
Introduction
xxi
0.2.2 Reader Prerequisites Although prior Mathematica knowledge is not needed to read The Mathematica GuideBook to Programming, it is assumed that the reader is familiar with basic actions in the Mathematica front end, including entering Greek characters using the keyboard, copying and pasting cells, and so on. Freely available tutorials on these (and other) subjects can be found at http://library.wolfram.com. For a complete understanding of most of the GuideBooks examples, it is desirable to have a background in mathematics, science, or engineering at about the bachelor’s level or above. Familiarity with mechanics and electrodynamics is assumed. Some examples and exercises are more specialized, for instance, from quantum mechanics, finite element analysis, statistical mechanics, solid state physics, number theory, and other areas. But the GuideBooks avoid very advanced (but tempting) topics such as renormalization groups [6], parquet approximations [27], and modular moonshines [14]. (Although Mathematica can deal with such topics, they do not fit the character of the Mathematica GuideBooks but rather the one of a Mathematica Topographical Atlas [a monumental work to be carried out by the Mathematica–Bourbakians of the 21st century]). Each scientific application discussed has a set of references. The references should easily give the reader both an overview of the subject and pointers to further references.
0.3 What the GuideBooks Are and What They Are Not 0.3.1 Doing Computer Mathematics As discussed in the Preface, the main goal of the GuideBooks is to demonstrate, showcase, teach, and exemplify scientific problem solving with Mathematica. An important step in achieving this goal is the discussion of Mathematica functions that allow readers to become fluent in programming when creating complicated graphics or solving scientific problems. This again means that the reader must become familiar with the most important programming, graphics, numerics, and symbolics functions, their arguments, options, attributes, and a few of their time and space complexities. And the reader must know which functions to use in each situation. The GuideBooks treat only aspects of Mathematica that are ultimately related to “doing mathematics”. This means that the GuideBooks focus on the functionalities of the kernel rather than on those of the front end. The knowledge required to use the front end to work with the notebooks can easily be gained by reading the corresponding chapters of the online documentation of Mathematica. Some of the subjects that are treated either lightly or not at all in the GuideBooks include the basic use of Mathematica (starting the program, features, and special properties of the notebook front end [16]), typesetting, the preparation of packages, external file operations, the communication of Mathematica with other programs via MathLink, special formatting and string manipulations, computer- and operating system-specific operations, audio generation, and commands available in various packages. “Packages” includes both, those distributed with Mathematica as well as those available from the Mathematica Information Center (http://library.wolfram.com/infocenter) and commercial sources, such as MathTensor for doing general relativity calculations (http://smc.vnet.net/MathTensor.html) or FeynCalc for doing high-energy physics calculations (http://www.feyncalc.org). This means, in particular, that probability and statistical calculations are barely touched on because most of the relevant commands are contained in the packages. The GuideBooks make little or no mention of the machine-dependent possibilities offered by the various Mathematica implementations. For this information, see the Mathematica documentation.
xxii
Introduction
Mathematical and physical remarks introduce certain subjects and formulas to make the associated Mathematica implementations easier to understand. These remarks are not meant to provide a deep understanding of the (sometimes complicated) physical model or underlying mathematics; some of these remarks intentionally oversimplify matters. The reader should examine all Mathematica inputs and outputs carefully. Sometimes, the inputs and outputs illustrate little-known or seldom-used aspects of Mathematica commands. Moreover, for the efficient use of Mathematica, it is very important to understand the possibilities and limits of the built-in commands. Many commands in Mathematica allow different numbers of arguments. When a given command is called with fewer than the maximum number of arguments, an internal (or user-defined) default value is used for the missing arguments. For most of the commands, the maximum number of arguments and default values are discussed. When solving problems, the GuideBooks generically use a “straightforward” approach. This means they are not using particularly clever tricks to solve problems, but rather direct, possibly computationally more expensive, approaches. (From time to time, the GuideBooks even make use of a “brute force” approach.) The motivation is that when solving new “real life” problems a reader encounters in daily work, the “right mathematical trick” is seldom at hand. Nevertheless, the reader can more often than not rely on Mathematica being powerful enough to often succeed in using a straightforward approach. But attention is paid to Mathematica-specific issues to find time- and memory-efficient implementations—something that should be taken into account for any larger program. As already mentioned, all larger pieces of code in this book have comments explaining the individual steps carried out in the calculations. Many smaller pieces of code have comments when needed to expedite the understanding of how they work. This enables the reader to easily change and adapt the code pieces. Sometimes, when the translation from traditional mathematics into Mathematica is trivial, or when the author wants to emphasize certain aspects of the code, we let the code “speak for itself”. While paying attention to efficiency, the GuideBooks only occasionally go into the computational complexity ([8], [40], and [7]) of the given implementations. The implementation of very large, complicated suites of algorithms is not the purpose of the GuideBooks. The Mathematica packages included with Mathematica and the ones at MathSource (http://library.wolfram.com/ database/MathSource) offer a rich variety of self-study material on building large programs. Most general guidelines for writing code for scientific calculations (like descriptive variable names and modularity of code; see, e.g., [19] for a review) apply also to Mathematica programs. The programs given in a chapter typically make use of Mathematica functions discussed in earlier chapters. Using commands from later chapters would sometimes allow for more efficient techniques. Also, these programs emphasize the use of commands from the current chapter. So, for example, instead of list operation, from a complexity point of view, hashing techniques or tailored data structures might be preferable. All subsections and sections are “self-contained” (meaning that no other code than the one presented is needed to evaluate the subsections and sections). The price for this “self-containedness” is that from time to time some code has to be repeated (such as manipulating polygons or forming random permutations of lists) instead of delegating such programming constructs to a package. Because this repetition could be construed as boring, the author typically uses a slightly different implementation to achieve the same goal.
Introduction
xxiii
0.3.2 Programming Paradigms In the GuideBooks, the author wants to show the reader that Mathematica supports various programming paradigms and also show that, depending on the problem under consideration and the goal (e.g., solution of a problem, test of an algorithm, development of a program), each style has its advantages and disadvantages. (For a general discussion concerning programming styles, see [3], [41], [23], [32], [15], and [9].) Mathematica supports a functional programming style. Thus, in addition to classical procedural programs (which are often less efficient and less elegant), programs using the functional style are also presented. In the first volume of the Mathematica GuideBooks, the programming style is usually dictated by the types of commands that have been discussed up to that point. A certain portion of the programs involve recursive, rule-based programming. The choice of programming style is, of course, partially (ultimately) a matter of personal preference. The GuideBooks’ main aim is to explain the operation, limits, and efficient application of the various Mathematica commands. For certain commands, this dictates a certain style of programming. However, the various programming styles, with their advantages and disadvantages, are not the main concern of the GuideBooks. In working with Mathematica, the reader is likely to use different programming styles depending if one wants a quick one-time calculation or a routine that will be used repeatedly. So, for a given implementation, the program structure may not always be the most elegant, fastest, or “prettiest”. The GuideBooks are not a substitute for the study of The Mathematica Book [45] http://documents. wolfram.com/mathematica). It is impossible to acquire a deeper (full) understanding of Mathematica without a thorough study of this book (reading it twice from the first to the last page is highly recommended). It defines the language and the spirit of Mathematica. The reader will probably from time to time need to refer to parts of it, because not all commands are discussed in the GuideBooks. However, the story of what can be done with Mathematica does not end with the examples shown in The Mathematica Book. The Mathematica GuideBooks go beyond The Mathematica Book. They present larger programs for solving various problems and creating complicated graphics. In addition, the GuideBooks discuss a number of commands that are not or are only fleetingly mentioned in the manual (e.g., some specialized methods of mathematical functions and functions from the Developer` and Experimental` contexts), but which the author deems important. In the notebooks, the author gives special emphasis to discussions, remarks, and applications relating to several commands that are typical for Mathematica but not for most other programming languages, e.g., Map, MapAt, MapIndexed, Distribute, Apply, Replace, ReplaceAll, Inner, Outer, Fold, Nest, Nest List, FixedPoint, FixedPointList, and Function. These commands allow to write exceptionally elegant, fast, and powerful programs. All of these commands are discussed in The Mathematica Book and others that deal with programming in Mathematica (e.g., [33], [34], and [42]). However, the author’s experience suggests that a deeper understanding of these commands and their optimal applications comes only after working with Mathematica in the solution of more complicated problems. Both the printed book and the electronic component contain material that is meant to teach in detail how to use Mathematica to solve problems, rather than to present the underlying details of the various scientific examples. It cannot be overemphasized that to master the use of Mathematica, its programming paradigms and individual functions, the reader must experiment; this is especially important, insightful, easily verifiable, and satisfying with graphics, which involve manipulating expressions, making small changes, and finding different approaches. Because the results can easily be visually checked, generating and modifying graphics is an ideal method to learn programming in Mathematica.
xxiv
Introduction
0.4 Exercises and Solutions 0.4.1 Exercises Each chapter includes a set of exercises and a detailed solution proposal for each exercise. When possible, all of the purely Mathematica-programming related exercises (these are most of the exercises of the Programming volume) should be solved by every reader. The exercises coming from mathematics, physics, and engineering should be solved according to the reader’s interest. The most important Mathematica functions needed to solve a given problem are generally those of the associated chapter. For a rough orientation about the content of an exercise, the subject is included in its title. The relative degree of difficulty is indicated by level superscript of the exercise number ( L1 indicates easy, L2 indicates medium, and L3 indicates difficult). The author’s aim was to present understandable interesting examples that illustrate the Mathematica material discussed in the corresponding chapter. Some exercises were inspired by recent research problems; the references given allow the interested reader to dig deeper into the subject. The exercises are intentionally not hyperlinked to the corresponding solution. The independent solving of the exercises is an important part of learning Mathematica.
0.4.2 Solutions The GuideBooks contain solutions to each of the more than 1,000 exercises. Many of the techniques used in the solutions are not just one-line calls to built-in functions. It might well be that with further enhancements, a future version of Mathematica might be able to solve the problem more directly. (But due to different forms of some results returned by Mathematica, some problems might also become more challenging.) The author encourages the reader to try to find shorter, more clever, faster (in terms of runtime as well complexity), more general, and more elegant solutions. Doing various calculations is the most effective way to learn Mathematica. A proper Mathematica implementation of a function that solves a given problem often contains many different elements. The function(s) should have sensibly named and sensibly behaving options; for various (machine numeric, high-precision numeric, symbolic) inputs different steps might be required; shielding against inappropriate input might be needed; different parameter values might require different solution strategies and algorithms, helpful error and warning messages should be available. The returned data structure should be intuitive and easy to reuse; to achieve a good computational complexity, nontrivial data structures might be needed, etc. Most of the solutions do not deal with all of these issues, but only with selected ones and thereby leave plenty of room for more detailed treatments; as far as limit, boundary, and degenerate cases are concerned, they represent an outline of how to tackle the problem. Although the solutions do their job in general, they often allow considerable refinement and extension by the reader. The reader should consider the given solution to a given exercise as a proposal; quite different approaches are often possible and sometimes even more efficient. The routines presented in the solutions are not the most general possible, because to make them foolproof for every possible input (sensible and nonsensical, evaluated and unevaluated, numerical and symbolical), the books would have had to go considerably beyond the mathematical and physical framework of the GuideBooks. In addition, few warnings are implemented for improper or improperly used arguments. The graphics provided in the solutions are mostly subject to a long list of refinements. Although the solutions do work, they are often sketchy and can be considerably refined and extended by the reader. This also means that the provided solutions to the exercises programs are not always very suitable for
Introduction
xxv
solving larger classes of problems. To increase their applicability would require considerably more code. Thus, it is not guaranteed that given routines will work correctly on related problems. To guarantee this generality and scalability, one would have to protect the variables better, implement formulas for more general or specialized cases, write functions to accept different numbers of variables, add type-checking and error-checking functions, and include corresponding error messages and warnings. To simplify working through the solutions, the various steps of the solution are commented and are not always packed in a Module or Block. In general, only functions that are used later are packed. For longer calculations, such as those in some of the exercises, this was not feasible and intended. The arguments of the functions are not always checked for their appropriateness as is desirable for robust code. But, this makes it easier for the user to test and modify the code.
0.5 The Books Versus the Electronic Components 0.5.1 Working with the Notebooks Each volume of the GuideBooks comes with a multiplatform DVD, containing fourteen main notebooks tailored for Mathematica 4 and compatible with Mathematica 5. Each notebook corresponds to a chapter from the printed books. (To avoid large file sizes of the notebooks, all animations are located in the Animations directory and not directly in the chapter notebooks.) The chapters (and so the corresponding notebooks) contain a detailed description and explanation of the Mathematica commands needed and used in applications of Mathematica to the sciences. Discussions on Mathematica functions are supplemented by a variety of mathematics, physics, and graphics examples. The notebooks also contain complete solutions to all exercises. Forming an electronic book, the notebooks also contain all text, as well as fully typeset formulas, and reader-editable and reader-changeable input. (Readers can copy, paste, and use the inputs in their notebooks.) In addition to the chapter notebooks, the DVD also includes a navigation palette and fully hyperlinked table of contents and index notebooks. The Mathematica notebooks corresponding to the printed book are fully evaluated. The evaluated chapter notebooks also come with hyperlinked overviews; these overviews are not in the printed book. When reading the printed books, it might seem that some parts are longer than needed. The reader should keep in mind that the primary tool for working with the Mathematica kernel are Mathematica notebooks and that on a computer screen and there “length does not matter much”. The GuideBooks are basically a printout of the notebooks, which makes going back and forth between the printed books and the notebooks very easy. The GuideBooks give large examples to encourage the reader to investigate various Mathematica functions and to become familiar with Mathematica as a system for doing mathematics, as well as a programming language. Investigating Mathematica in the accompanying notebooks is the best way to learn its details. To start viewing the notebooks, open the table of contents notebook TableOfContents.nb. Mathematica notebooks can contain hyperlinks, and all entries of the table of contents are hyperlinked. Navigating through one of the chapters is convenient when done using the navigator palette GuideBooksNavigator.nb. When opening a notebook, the front end minimizes the amount of memory needed to display the notebook by loading it incrementally. Depending on the reader’s hardware, this might result in a slow scrolling speed. Clicking the “Load notebook cache” button of the GuideBooksNavigator palette speeds this up by loading the complete notebook into the front end. For the vast majority of sections, subsections, and solutions of the exercises, the reader can just select such a structural unit and evaluate it (at once) on a year-2005 computer (¥512 MB RAM) typically in a matter of
xxvi
Introduction
minutes. Some sections and solutions containing many graphics may need hours of computation time. Also, more than 50 pieces of code run hours, even days. The inputs that are very memory intensive or produce large outputs and graphics are in inactive cells which can be activated by clicking the adjacent button. Because of potentially overlapping variable names between various sections and subsections, the author advises the reader not to evaluate an entire chapter at once. Each smallest self-contained structural unit (a subsection, a section without subsections, or an exercise) should be evaluated within one Mathematica session starting with a freshly started kernel. At the end of each unit is an input cell. After evaluating all input cells of a unit in consecutive order, the input of this cell generates a short summary about the entire Mathematica session. It lists the number of evaluated inputs, the kernel CPU time, the wall clock time, and the maximal memory used to evaluate the inputs (excluding the resources needed to evaluate the Program cells). These numbers serve as a guide for the reader about the to-be-expected running times and memory needs. These numbers can deviate from run to run. The wall clock time can be substantially larger than the CPU time due to other processes running on the same computer and due to time needed to render graphics. The data shown in the evaluated notebooks came from a 2.5 GHz Linux computer. The CPU times are generically proportional to the computer clock speed, but can deviate within a small factor from operating system to operating system. In rare, randomly occurring cases slower computers can achieve smaller CPU and wall clock times than faster computers, due to internal time-constrained simplification processes in various symbolic mathematics functions (such as Integrate, Sum, DSolve, …). The Overview Section of the chapters is set up for a front end and kernel running on the same computer and having access to the same file system. When using a remote kernel, the directory specification for the package Overview.m must be changed accordingly. References can be conveniently extracted from the main text by selecting the cell(s) that refer to them (or parts of a cell) and then clicking the “Extract References” button. A new notebook with the extracted references will then appear. The notebooks contain color graphics. (To rerender the pictures with a greater color depth or at a larger size, choose Rerender Graphics from the Cell menu.) With some of the colors used, black-and-white printouts occasionally give low-contrast results. For better black-and-white printouts of these graphics, the author recommends setting the ColorOutput option of the relevant graphics function to GrayLevel. The notebooks with animations (in the printed book, animations are typically printed as an array of about 10 to 20 individual graphics) typically contain between 60 and 120 frames. Rerunning the corresponding code with a large number of frames will allow the reader to generate smoother and longer-running animations. Because many cell styles used in the notebooks are unique to the GuideBooks, when copying expressions and cells from the GuideBooks notebooks to other notebooks, one should first attach the style sheet notebook GuideBooksStylesheet.nb to the destination notebook, or define the needed styles in the style sheet of the destination notebook.
0.5.2 Reproducibility of the Results The 14 chapter notebooks contained in the electronic version of the GuideBooks were run mostly with Mathematica 5.1 on a 2 GHz Intel Linux computer with 2 GB RAM. They need more than 100 hours of evaluation time. (This does not include the evaluation of the currently unevaluatable parts of code after the Make Input buttons.) For most subsections and sections, 512 MB RAM are recommended for a fast and smooth evaluation “at once” (meaning the reader can select the section or subsection, and evaluate all inputs without running out of memory or clearing variables) and the rendering of the generated graphic in the front end. Some subsections and sections
Introduction
xxvii
need more memory when run. To reduce these memory requirements, the author recommends restarting the Mathematica kernel inside these subsections and sections, evaluating the necessary definitions, and then continuing. This will allow the reader to evaluate all inputs. In general, regardless of the computer, with the same version of Mathematica, the reader should get the same results as shown in the notebooks. (The author has tested the code on Sun and Intel-based Linux computers, but this does not mean that some code might not run as displayed (because of different configurations, stack size settings, etc., but the disclaimer from the Preface applies everywhere). If an input does not work on a particular machine, please inform the author. Some deviations from the results given may appear because of the following: † Inputs involving the function Random[…] in some form. (Often SeedRandom to allow for some kind of reproducibility and randomness at the same time is employed.) † Mathematica commands operating on the file system of the computer, or make use of the type of computer (such inputs need to be edited using the appropriate directory specifications). † Calculations showing some of the differences of floating-point numbers and the machine-dependent representation of these on various computers. † Pictures using various fonts and sizes because of their availability (or lack thereof) and shape on different computers. † Calculations involving Timing because of different clock speeds, architectures, operating systems, and libraries. † Formats of results depending on the actual window width and default font size. (Often, the corresponding inputs will contain Short.) Using anything other than Mathematica Version 5.1 might also result in different outputs. Examples of results that change form, but are all mathematically correct and equivalent, are the parameter variables used in underdetermined systems of linear equations, the form of the results of an integral, and the internal form of functions like InterpolatingFunction and CompiledFunction. Some inputs might no longer evaluate the same way because functions from a package were used and these functions are potentially built-in functions in a later Mathematica version. Mathematica is a very large and complicated program that is constantly updated and improved. Some of these changes might be design changes, superseded functionality, or potentially regressions, and as a result, some of the inputs might not work at all or give unexpected results in future versions of Mathematica.
0.5.3 Earlier Versions of the Notebooks The first printing of the Programming volume and the Graphics volumes of the Mathematica GuideBooks were published in October 2004. The electronic components of these two books contained the corresponding evaluated chapter notebooks as well as unevaluated versions of preversions of the notebooks belonging to the Numerics and Symbolics volumes. Similarly, the electronic components of the Numerics and Symbolics volume contain the corresponding evaluated chapter notebooks and unevaluated copies of the notebooks of the Programming and Graphics volumes. This allows the reader to follow cross-references and look up relevant concepts discussed in the other volumes. The author has tried to keep the notebooks of the GuideBooks as up-to-date as possible. (Meaning with respect to the efficient and appropriate use of the latest version of Mathematica, with respect to maintaining a list of references that contains new publications, and examples, and with respect to incorporating corrections to known problems, errors, and mistakes). As a result, the notebooks of all four volumes that come with later printings of the Programming and Graphics volumes, as well with the Numerics and Symbolics volumes will be different and supersede the earlier notebooks originally distributed with the
Introduction
xxviii
Programming and Graphics volumes. The notebooks that come with the Numerics and Symbolics volumes are genuine Mathematica Version 5.1 notebooks. Because most advances in Mathematica Version 5 and 5.1 compared with Mathematica Version 4 occurred in functions carrying out numerical and symbolical calculations, the notebooks associated with Numerics and Symbolics volumes contain a substantial amount of changes and additions compared with their originally distributed version.
0.6 Style and Design Elements 0.6.1 Text and Code Formatting The GuideBooks are divided into chapters. Each chapter consists of several sections, which frequently are further subdivided into subsections. General remarks about a chapter or a section are presented in the sections and subsections numbered 0. (These remarks usually discuss the structure of the following section and give teasers about the usefulness of the functions to be discussed.) Also, sometimes these sections serve to refresh the discussion of some functions already introduced earlier. Following the style of The Mathematica Book [45], the GuideBooks use the following fonts: For the main text, Times; for Mathematica inputs and built-in Mathematica commands, Courier plain (like Plot); and for user-supplied arguments, Times italic (like userArgument1 ). Built-in Mathematica functions are introduced in the following style: MathematicaFunctionToBeIntroduced[typeIndicatingUserSuppliedArgument(s)] is a description of the built-in command MathematicaFunctionToBeIntroduced upon its first appearance. A definition of the command, along with its parameters is given. Here, typeIndicatingUserSuppliedArgument(s) is one (or more) user-supplied expression(s) and may be written in an abbreviated form or in a different way for emphasis.
The actual Mathematica inputs and outputs appear in the following manner (as mentioned above, virtually all inputs are given in InputForm). (* A comment. It will be/is ignored as Mathematica input: Return only one of the solutions *) Last[Solve[{x^2 - y == 1, x - y^2 == 1}, {x, y}]]
When referring in text to variables of Mathematica inputs and outputs, the following convention is used: Fixed, nonpattern variables (including local variables) are printed in Courier plain (the equations solved above contained the variables x and y). User supplied arguments to built-in or defined functions with pattern variables are printed in Times italic. The next input defines a function generating a pair of polynomial equations in x and y. equationPair[x_, y_] := {x^2 - y == 1, x - y^2 == 1}
x and y are pattern variables (usimng the same letters, but a different font from the actual code fragments x_ and y_) that can stand for any argument. Here we call the function equationPair with the two arguments u + v and w - z. equationPair[u + v, w - z]
Occasionally, explanation about a mathematics or physics topic is given before the corresponding Mathematica implementation is discussed. These sections are marked as follows:
Introduction
xxix
Mathematical Remark: Special Topic in Mathematics or Physics A short summary or review of mathematical or physical ideas necessary for the following example(s). 1
From time to time, Mathematica is used to analyze expressions, algorithms, etc. In some cases, results in the form of English sentences are produced programmatically. To differentiate such automatically generated text from the main text, in most instances such text is prefaced by “ë” (structurally the corresponding cells are of type "PrintText" versus "Text" for author-written cells). Code pieces that either run for quite long, or need a lot of memory, or are tangent to the current discussion are displayed in the following manner. Make Input
mathematicaCodeWhichEitherRunsVeryLongOrThatIsVeryMemoryIntensive OrThatProducesAVeryLargeGraphicOrThatIsASideTrackToTheSubjectUnder Discussion (* with some comments on how the code works *)
To run a code piece like this, click the Make Input button above it. This will generate the corresponding input cell that can be evaluated if the reader’s computer has the necessary resources. The reader is encouraged to add new inputs and annotations to the electronic notebooks. There are two styles for reader-added material: "ReaderInput" (a Mathematica input style and simultaneously the default style for a new cell) and "ReaderAnnotation" (a text-style cell type). They are primarily intended to be used in the Reading environment. These two styles are indented more than the default input and text cells, have a green left bar and a dingbat. To access the "ReaderInput" and "ReaderAnnotation" styles, press the system-dependent modifier key (such as Control or Command) and 9 and 7, respectively.
0.6.2 References Because the GuideBooks are concerned with the solution of mathematical and physical problems using Mathematica and are not mathematics or physics monographs, the author did not attempt to give complete references for each of the applications discussed [38], [20]. The references cited in the text pertain mainly to the applications under discussion. Most of the citations are from the more recent literature; references to older publications can be found in the cited ones. Frequently URLs for downloading relevant or interesting information are given. (The URL addresses worked at the time of printing and, hopefully, will be still active when the reader tries them.) References for Mathematica, for algorithms used in computer algebra, and for applications of computer algebra are collected in the Appendix A. The references are listed at the end of each chapter in alphabetical order. In the notebooks, the references are hyperlinked to all their occurrences in the main text. Multiple references for a subject are not cited in numerical order, but rather in the order of their importance, relevance, and suggested reading order for the implementation given. In a few cases (e.g., pure functions in Chapter 3, some matrix operations in Chapter 6), references to the mathematical background for some built-in commands are given—mainly for commands in which the mathematics required extends beyond the familiarity commonly exhibited by non-mathematicians. The GuideBooks do not discuss the algorithms underlying such complicated functions, but sometimes use Mathematica to “monitor” the algorithms.
xxx
Introduction
References of the form abbreviationOfAScientificField/yearMonthPreprintNumber (such as quant-ph/0012147) refer to the arXiv preprint server [43], [22], [30] at http://arXiv.org. When a paper appeared as a preprint and (later) in a journal, typically only the more accessible preprint reference is given. For the convenience of the reader, at the end of these references, there is a Get Preprint button. Click the button to display a palette notebook with hyperlinks to the corresponding preprint at the main preprint server and its mirror sites. (Some of the older journal articles can be downloaded free of charge from some of the digital mathematics library servers, such as http://gdz.sub.uni-goettingen.de, http://www.emis.de, http://www.numdam.org, and http://dieper.aib.unilinz.ac.at.) As much as available, recent journal articles are hyperlinked through their digital object identifiers (http://www.doi.org).
0.6.3 Variable Scoping, Input Numbering, and Warning Messages Some of the Mathematica inputs intentionally cause error messages, infinite loops, and so on, to illustrate the operation of a Mathematica command. These messages also arise in the user’s practical use of Mathematica. So, instead of presenting polished and perfected code, the author prefers to illustrate the potential problems and limitations associated with the use of Mathematica applied to “real life” problems. The one exception are the spelling warning messages General::spell and General::spell1 that would appear relatively frequently because “similar” names are used eventually. For easier and less defocused reading, these messages are turned off in the initialization cells. (When working with the notebooks, this means that the pop-up window asking the user “Do you want to automatically evaluate all the initialization cells in the notebook?” should be evaluated should always be answered with a “yes”.) For the vast majority of graphics presented, the picture is the focus, not the returned Mathematica expression representing the picture. That is why the Graphics and Graphics3D output is suppressed in most situations. To improve the code’s readability, no attempt has been made to protect all variables that are used in the various examples. This protection could be done with Clear, Remove, Block, Module, With, and others. Not protecting the variables allows the reader to modify, in a somewhat easier manner, the values and definitions of variables, and to see the effects of these changes. On the other hand, there may be some interference between variable names and values used in the notebooks and those that might be introduced when experimenting with the code. When readers examine some of the code on a computer, reevaluate sections, and sometimes perform subsidiary calculations, they may introduce variables that might interfere with ones from the GuideBooks. To partially avoid this problem, and for the reader’s convenience, sometimes Clear[sequenceOfVariables]and Remove[sequenceOfVariables] are sprinkled throughout the notebooks. This makes experimenting with these functions easier. The numbering of the Mathematica inputs and outputs typically does not contain all consecutive integers. Some pieces of Mathematica code consist of multiple inputs per cell; so, therefore, the line numbering is incremented by more than just 1. As mentioned, Mathematica should be restarted at every section, or subsection or solution of an exercise, to make sure that no variables with values get reused. The author also explicitly asks the reader to restart Mathematica at some special positions inside sections. This removes previously introduced variables, eliminates all existing contexts, and returns Mathematica to the typical initial configuration to ensure reproduction of the results and to avoid using too much memory inside one session.
Introduction
xxxi
0.6.4 Graphics In Mathematica 5.1, displayed graphics are side effects, not outputs. The actual output of an input producing a graphic is a single cell with the text Graphics or Graphics3D or GraphicsArray and so on. To save paper, these output cells have been deleted in the printed version of the GuideBooks. Most graphics use an appropriate number of plot points and polygons to show the relevant features and details. Changing the number of plot points and polygons to a higher value to obtain higher resolution graphics can be done by changing the corresponding inputs. The graphics of the printed book and the graphics in the notebooks are largely identical. Some printed book graphics use a different color scheme and different point sizes and line and edge thicknesses to enhance contrast and visibility. In addition, the font size has been reduced for the printed book in tick and axes labels. The graphics shown in the notebooks are PostScript graphics. This means they can be resized and rerendered without loss of quality. To reduce file sizes, the reader can convert them to bitmap graphics using the Cellö Convert ToöBitmap menu. The resulting bitmap graphics can no longer be resized or rerendered in the original resolution. To reduce file sizes of the main content notebooks, the animations of the GuideBooks are not part of the chapter notebooks. They are contained in a separate directory.
0.6.5 Notations and Symbols The symbols used in typeset mathematical formulas are not uniform and unique throughout the GuideBooks. Various mathematical and physical quantities (such as normals, rotation matrices, and field strengths) are used repeatedly in this book. Frequently the same notation is used for them, but depending on the context, also different ones are used, e.g. sometimes bold is used for a vector (such as r) and sometimes an arrow (such as ”r). Matrices appear in bold or as doublestruck letters. Depending on the context and emphasis placed, different notations are used in display equations and in the Mathematica input form. For instance, for a time-dependent scalar quantity of one variable yHt; xL, we might use one of many patterns, such as ψ[t][x] (for emphasizing a parametric t-dependence) or ψ[t, x] (to treat t and x on an equal footing) or ψ[t, {x}] (to emphasize the one-dimensionality of the space variable x). Mathematical formulas use standard notation. To avoid confusion with Mathematica notations, the use of square brackets is minimized throughout. Following the conventions of mathematics notation, square brackets are used for three cases: a) Functionals, such as t @ f HtLD HwL for the Fourier transform of a function f HtL. b) Power series coefficients, @xk D H f HxLL denotes the coefficient of xk of the power series expansion of f HxL around x = 0. c) Closed intervals, like @a, bD (open intervals are denoted by Ha, bL). Grouping is exclusively done using parentheses. Upper-case double-struck letters denote domains of numbers, for integers, for nonnegative integers, for rational numbers, for reals, and for complex numbers. Points in n (or n ) with explicitly given coordinates are indicated using curly braces 8c1 , …, cn <. The symbols fl and fi for And and Or are used in logical formulas. For variable names in formula- and identity-like Mathematica code, the symbol (or small variations of it) traditionally used in mathematics or physics is used. In program-like Mathematica code, the author uses very descriptive, sometimes abbreviated, but sometimes also slightly longish, variable names, such as buildBril louinZone and FibonacciChainMap.
xxxii
Introduction
0.6.6 Units In the examples involving concepts drawn from physics, the author tried to enhance the readability of the code (and execution speed) by not choosing systems of units involving numerical or unit-dependent quantities. (For more on the choice and treatment of units, see [39], [4], [5], [10], [13], [11], [12], [36], [35], [31], [37], [44], [21], [25], [18], [26], [24].) Although Mathematica can carry units along with the symbols representing the physical quantities in a calculation, this requires more programming and frequently diverts from the essence of the problem. Choosing a system of units that allows the equations to be written without (unneeded in computations) units often gives considerable insight into the importance of the various parts of the equations because the magnitudes of the explicitly appearing coefficients are more easily compared.
0.6.7 Cover Graphics The cover graphics of the GuideBooks stem from the Mathematica GuideBooks themselves. The construction ideas and their implementation are discussed in detail in the corresponding GuideBook.
† The cover graphic of the Programming volume shows 42 tori, 12 of which are in the dodecahedron’s face planes and 30 which are in the planes perpendicular to the dodecahedron’s edges. Subsections 1.2.4 of Chapter 1 discusses the implementation. † The cover graphic of the Graphics volume first subdivides the faces of a dodecahedron into small triangles and then rotates randomly selected triangles around the dodecahedron’s edges. The proposed solution of Exercise 1b of Chapter 2 discusses the implementation. † The cover graphic of the Numerics volume visualizes the electric field lines of a symmetric arrangement of positive and negative charges. Subsection 1.11.1 discusses the implementation. † The cover graphic of the Symbolics volume visualizes the derivative of the Weierstrass ƒ£ function over the Riemann sphere. The “threefold blossoms” arise from the poles at the centers of the periodic array of period parallelograms. Exercise 3j of Chapter 2 discusses the implementation. † The four spine graphics show the inverse elliptic nome function q-1 , a function defined in the unit disk with a boundary of analyticity mapped to a triangle, a square, a pentagon, and a hexagon. Exercise 16 of Chapter 2 of the Graphics volume discusses the implementation.
0.7 Production History The original set of notebooks was developed in the 1991–1992 academic year on an Apple Macintosh IIfx with 20 MB RAM using Mathematica Version 2.1. Over the years, the notebooks were updated to Mathematica Version 2.2, then to Version 3, and finally for Version 4 for the first printed edition of the Programming and Graphics volumes of the Mathematica GuideBooks (published autumn 2004). For the Numerics and Symbolics volumes, the GuideBooks notebooks were updated to Mathematica Version 5 in the second half of 2004. Historically, the first step in creating the book was the translation of a set of Macintosh notebooks used for lecturing and written in German into English by Larry Shumaker. This was done primarily by a translation program and afterward by manually polishing the English version. Then the notebooks were transformed into T E X files using the program nb2tex on a NeXT computer. The resulting files were manually edited, equations prepared in the original German notebooks were formatted with T E X , and macros were added corresponding to the design of the book. (The translation to T E X was necessary because Mathematica Version 2.2 did not allow
Introduction
xxxiii
for book-quality printouts.) They were updated and refined for nearly three years, and then Mathematica 3 notebooks were generated from the T E X files using a preliminary version of the program tex2nb. Historically and technically, this was an important step because it transformed all of the material of the GuideBooks into Mathematica expressions and allowed for automated changes and updates in the various editing stages. (Using the Mathematica kernel allowed one to process and modify the notebook files of these books in a uniform and time-efficient manner.) Then, the notebooks were expanded in size and scope and updated to Mathematica 4. In the second half of the year 2003, and first half of the year 2004, the Mathematica programs of the notebooks were revised to be compatible with Mathematica 5. In October 2004, the Programming and the Graphics volumes were published. In the last quarter of 2004, all four volumes of the GuideBooks were updated to be tailored for Mathematica 5.1 A special set of styles was created to generate the actual PostScript as printouts from the notebooks. All inputs were evaluated with this style sheet, and the generated PostScript was directly used for the book production. Using a little Mathematica program, the index was generated from the notebooks (which are Mathematica expressions), containing all index entries as cell tags.
0.8 Four General Suggestions A reader new to Mathematica should take into account these four suggestions.
† There is usually more than one way to solve a given problem using Mathematica. If one approach does not work or returns the wrong answer or gives an error message, make every effort to understand what is happening. Even if the reader has succeeded with an alternative approach, it is important to try to understand why other attempts failed. † Mathematical formulas, algorithms, and so on, should be implemented as directly as possible, even if the resulting construction is somewhat “unusual” compared to that in other programming languages. In particular, the reader should not simply translate C, Pascal, Fortran, or other programs line-by-line into Mathematica, although this is indeed possible. Instead, the reader should instead reformulate the problem in a clear mathematical way. For example, Do, While, and For loops are frequently unnecessary, convergence (for instance, of sums) can be checked by Mathematica, and If tests can often be replaced by a corresponding pattern. The reader should start with an exact mathematical description of the problem [28], [29]. For example, it does not suffice to know which transformation formulas have to be used on certain functions; one also needs to know how to apply them. “The power of mathematics is in its precision. The precision of mathematics must be used precisely.” [17] † If the exercises, examples, and calculation of the GuideBooks or the listing of calculation proposals from Exercise 1 of Chapter 1 of the Programming volume are not challenging enough or do not cover the reader’s interests, consider the following idea, which provides a source for all kinds of interesting and difficult problems: The reader should select a built-in command and try to reconstruct it using other built-in commands and make it behave as close to the original as possible in its operation, speed, and domain of applicability, or even to surpass it. (Replicating the following functions is a serious challenge: N, Factor, FactorInteger, Integrate, NIntegrate, Solve, DSolve, NDSolve, Series, Sum, Limit, Root, Prime, or PrimeQ.) † If the reader tries to solve a smaller or larger problem in Mathematica and does not succeed, keep this problem on a “to do” list and periodically review this list and try again. Whenever the reader has a clear strategy to solve a problem, this strategy can be implemented in Mathematica. The implementation of the algorithm might require some programming skills, and by reading through this book, the reader will become able to code more sophisticated procedures and more efficient implementations. After the reader has acquired a certain amount of Mathemat ica programming familiarity, implementing virtually all “procedures” which the reader can (algorithmically) carry out with paper and pencil will become straightforward.
Introduction
xxxiv
References 1
P. Abbott. The Mathematica Journal 4, 415 (2000).
2
P. Abbott. The Mathematica Journal 9, 31 (2003).
3
H. Abelson, G. Sussman. Structure and Interpretation of Computer Programs, MIT Press, Cambridge, MA, 1985.
4
G. I. Barenblatt. Similarity, Self-Similarity, and Intermediate Asymptotics, Consultants Bureau, New York, 1979.
5
F. A. Bender. An Introduction to Mathematical Modeling, Wiley, New York, 1978.
6
G. Benfatto, G. Gallavotti. Renormalization Group, Princeton University Press, Princeton, 1995.
7
L. Blum, F. Cucker, M. Shub, S. Smale. Complexity and Real Computation, Springer, New York, 1998.
8
P. Bürgisser, M. Clausen, M. A. Shokrollahi. Algebraic Complexity Theory, Springer, Berlin, 1997.
9
L. Cardelli, P. Wegner. Comput. Surveys 17, 471 (1985).
10
J. F. Carinena, M. Santander in P. W. Hawkes (ed.). Advances in Electronics and Electron Physics 72, Academic Press, New York, 1988.
11
E. A. Desloge. Am. J. Phys. 52, 312 (1984).
12
C. L. Dym, E. S. Ivey. Principles of Mathematical Modelling, Academic Press, New York, 1980.
13
A. C. Fowler. Mathematical Models in the Applied Sciences, Cambridge University Press, Cambridge, 1997.
14
T. Gannon. arXiv:math.QA/9906167 (1999).
15 16
R. J. Gaylord, S. N. Kamin, P. R. Wellin. An Introduction to Programming with Mathematica, TELOS/Springer-Verlag, Santa Clara, 1993. J. Glynn, T. Gray. The Beginner’s Guide to Mathematica Version 3, Cambridge University Press, Cambridge, 1997.
17
D. Greenspan in R. E. Mickens (ed.). Mathematics and Science, World Scientific, Singapore, 1990.
18
G. W. Hart. Multidimensional Analysis, Springer-Verlag, New York, 1995.
19
A. K. Hartman, H. Rieger. arXiv:cond-mat/0111531 (2001).
20
M. Hazewinkel. arXiv:cs.IR/0410055 (2004).
21
E. Isaacson, M. Isaacson. Dimensional Methods in Engineering and Physics, Edward Arnold, London, 1975.
22
A. Jackson. Notices Am. Math. Soc. 49, 23 (2002).
23
R. D. Jenks, B. M. Trager in J. von zur Gathen, M. Giesbrecht (eds.). Symbolic and Algebraic Computation, ACM Press, New York, 1994.
24
C. G. Jesudason. arXiv:physics/0403033 (2004).
25
C. Kauffmann in A. van der Burgh (ed.). Topics in Engineering Mathematics, Kluwer, Dordrecht, 1993.
26
R. Khanin in B. Mourrain (ed.). ISSAC 2001, ACM, Baltimore, 2001.
27
P. Kleinert, H. Schlegel. Physica A 218, 507 (1995).
28
D. E. Knuth. Am. Math. Monthly 81, 323 (1974).
29
D. E. Knuth. Am. Math. Monthly 92, 170 (1985).
30
G. Kuperberg. arXiv:math.HO/0210144 (2002).
31
J. D. Logan. Applied Mathematics, Wiley, New York, 1987.
32
K. C. Louden. Programming Languages: Principles and Practice, PWS-Kent, Boston, 1993.
33
R. Maeder. Programming in Mathematica, Addison-Wesley, Reading, 1997.
34
R. Maeder. The Mathematica Programmer, Academic Press, New York, 1993.
Introduction 35
B. S. Massey. Measures in Science and Engineering, Wiley, New York, 1986.
36
G. Messina, S. Santangelo, A. Paoletti, A. Tucciarone. Nuov. Cim. D 17, 523 (1995).
37
J. Molenaar in A. van der Burgh, J. Simonis (eds.). Topics in Engineering Mathematics, Kluwer, Dordrecht, 1992.
38
E. Pascal. Repertorium der höheren Mathematik Theil 1/1 [page V, paragraph 3], Teubner, Leipzig, 1900.
39
S. H. Romer. Am. J. Phys. 67, 13 (1999).
40
R. Sedgewick, P. Flajolet. Analysis of Algorithms, Addison-Wesley, Reading, 1996.
41
R. Sethi. Programming Languages: Concepts and Constructions, Addison-Wesley, New York, 1989.
42
D. B. Wagner. Power Programming with Mathematica: The Kernel, McGraw-Hill, New York, 1996.
43
S. Warner. arXiv:cs.DL/0101027 (2001).
44
H. Whitney. Am. Math. Monthly 75, 115, 227 (1968).
45
S. Wolfram. The Mathematica Book, Wolfram Media, Champaign, 2003.
xxxv
CHAPTER
1
Numerical Computations 1.0 Remarks This chapter deals with numerical methods in Mathematica. The Mathematica Book [1919] does not discuss all options and option settings of the numerical functions in great depth, so as an important additional source about many details of selected numerical functions we strongly recommend the Advanced Documentation located in the help browser. For numerical analysis itself, see the special textbooks, such as [1812], or [481]. (For some general issues on how to do numerical analysis in nonprocedural languages, see [1730].) In this chapter, we discuss numerics within Mathematica. Concerning the use of computer algebra for deriving stable numerical methods, see [659] and [244]. For the application of numerical methods to carry out mathematical proofs, see [797], [588]. We discussed numerical methods stemming from problems in linear algebra in Chapter 6 of the Programming volume [1793], so there is no need to repeat the discussion here extensively. Before proceeding to a discussion of numbers with an arbitrary number of digits, we make a few comments about computing with machine numbers. In the nine chapters of the Programming volume [1793] and Graphics volume [1794], we made repeated use of N[expr]. N[expr] calculates a numerical value of expr by using machine arithmetic. If we add some numbers that differ greatly in size, it is possible to get a very poor result. Here is an example. In[1]:= Out[1]=
-10.0^22 + 2.0 - 1.0 + 10.0^22 0.
The result is 0.0, even though the “expected” result is 1.0. We can get various “incorrect” results if we change the order of the summands. Here all possibilities are investigated. In[2]:= Out[2]=
Apply[Plus, Permutations[{-10.0^22, 10.0^22, 2.0, -1.0}], {1}] 81., 1., −1., 0., 2., 0., 1., 1., −1., 0., 2., 0., −1., 0., −1., 0., 0., 0., 2., 0., 2., 0., 0., 0.<
The summation process loses the “remaining” -1.0 and 2.0 because the number of digits used in machine arithmetic calculations is less than 22. In[3]:= Out[3]=
$MachinePrecision 15.9546
The machine numbers are summed in the order given. Here, one summation is done step by step. In[4]:= Out[4]=
Fold[Plus, 0, #]& /@ Permutations[{-10.0^22, 10.0^22, 2.0, -1.0}] 81., 1., −1., 0., 2., 0., 1., 1., −1., 0., 2., 0., −1., 0., −1., 0., 0., 0., 2., 0., 2., 0., 0., 0.<
Numerical Computations
2
From the last experiment, we have to conclude that for machine numbers (for a better overall efficiency of Mathematica) the Orderless attribute of Plus does not go into effect before the summation is carried out. For exact numbers or high-precision numbers, the order of the summands does, of course, not matter. (In the following, we will use the words “high-precision number” and “bignumber”, as well as “high-precision arithmetic” and “bignum arithmetic” interchangeably.) In[5]:= Out[5]=
Apply[Plus, Permutations[{-10^22, 10^22, 2, -1}], {1}] 81, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1<
With this amount of digits, it is not always possible to correctly keep track of the last digit. The automatic precision control of Mathematica shows that not a single digit is known and estimates the order of magnitude of the result. In[6]:= Out[6]=
Apply[Plus, Permutations[N[{-10^22, 10^22, 2, -1}, $MachinePrecision + 1]], {1}] 80. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 , 0. × 105 <
Using 30 digits allows obtaining the correct result for all permutations. In[7]:= Out[7]=
Apply[Plus, Permutations[N[{-10^22, 10^22, 2, -1}, 30]], {1}] 81.0000000, 1.0000000, 1.0000000, 1.0000000,
1.0000000, 1.0000000, 1.0000000, 1.0000000,
1.0000000, 1.0000000, 1.0000000, 1.0000000,
1.0000000, 1.0000000, 1.0000000, 1.0000000,
1.0000000, 1.0000000, 1.0000000, 1.0000000,
1.0000000, 1.0000000, 1.0000000, 1.0000000<
Note that inserting parentheses does not help because the brackets only influence the parsing process, but not the evaluation process. It is possible to force Mathematica to give the correct result by explicitly enforcing a certain evaluation order. In[8]:= Out[8]=
Identity[-10.0^22 + 10.0^22] + 2.0 - 1.0 1.
The function Total (to be discussed below) cannot give a unique result here either. In[9]:= Out[9]=
Map[Total[#]&, Permutations[{-10.0^22, 10.0^22, 2.0, -1.0}], {1}] 81., 1., −1., 0., 2., 0., 1., 1., −1., 0., 2., 0., −1., 0., −1., 0., 0., 0., 2., 0., 2., 0., 0., 0.<
For more on this and related issues, see [887], [563], [1016], [1540], [945], and [832]. The capability of Mathematica to calculate numerical quantities with an arbitrary number of digits is an important ingredient in “experimental mathematics”. Given a conjectured identity, the possibility to check that it is correct to 10, to 100, to 1000 or even more digits at “randomly” selected points does not constitute a proof, but is often very strong evidence that often opens the possibility for a symbolic proof. The probability to accidentally get hundreds of digits that are identical in two expressions, or to get zero to hundreds of digits in an expression of order 1, is far smaller than is the probability of an error in a human-generated proof [458], [479]. (This is a kind of “experimental mathematics” [257], [115], [256], [254], [116], [278], [117], [1178].) As a first example of the usefulness of being able to carry out calculations with many hundred of digits, let us just “check” an identity numerically. Let us deal with the following statement: The function 1 ∑logHJ £ HtL6 J HtL-4 HJHtL - 1L-3 L wHtL = ÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ 2 ∑t
1.0 Remarks
3
is a solution of the Chazy equation [794], [201], [407] 3 w£ HtL2 - 2 wHtL w££ HtL + wH3L HtL = 0. Here, J HtL is Klein’s modular function, in Mathematica KleinInvariantJ. While there is a closed form for the derivative of Klein’s modular function through elliptic integrals KHzL, EHzL and the modular function lHzL 8 i KHlHtLL2 HlHtL - 2L HlHtL + 1L H2 lHtL - 1L HHlHtL - 1L lHtL + 1L2 ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ , J £ HtL = - ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ 27 HKH1 - lHtLL HEHlHtLL - KHlHtLLL + EH1 - lHtLL KHlHtLLL HlHtL - 1L2 lHtL2 we do not carry the differentiations symbolically because the resulting expressions are very large. This is the conjectured solution of the Chazy equation. In[10]:=
w[τ_] = 1/2 D[Log[J'[τ]^6/(J[τ]^4(J[τ] - 1)^3)], τ]
Out[10]=
7
7
5
4 J @τD 3 J @τD 6 J @τD J @τD H−1 + J@τDL3 J@τD4 I− − + M H−1+J@τDL3 J@τD5 H−1+J@τDL4 J@τD4 H−1+J@τDL3 J@τD4 6 2 J @τD
We substitute the solution in the differential equation, and write the result over a common denominator. In[11]:=
ode = w'''[τ] - 2 w[τ] w''[τ] + 3 w'[τ]^2;
In[12]:=
(odeNum = Numerator[Together[ode]]) // Simplify
Out[12]=
H32 − 80 J@τD + 120 J@τD2 − 80 J@τD3 + 35 J@τD4 L J @τD8 − 108 H−1 + J@τDL4 J@τD4 J @τD4 + 6 H−1 + J@τDL2 J@τD2 H24 − 40 J@τD + 35 J@τD2 L J @τD5 JH3L @τD + 144 H−1 + J@τDL4 J@τD4 J @τD J @τD2 JH3L @τD + H−1 + J@τDL2 J@τD2 J @τD4 H−9 H24 − 40 J@τD + 35 J@τD2 L J @τD2 + 10 J@τD H4 − 11 J@τD + 7 J@τD2 L JH4L @τDL − 12 H−1 + J@τDL3 J@τD3 J @τD2 I−5 H−4 + 7 J@τDL J @τD3 − 2
6 H−1 + J@τDL J@τD JH3L @τD + 10 H−1 + J@τDL J@τD J @τD JH4L @τDM + 12 H−1 + J@τDL3 J@τD3 J @τD3 H−5 H−4 + 7 J@τDL J @τD JH3L @τD + H−1 + J@τDL J@τD JH5L @τDL
Now, we define a function d that calculates an approximation of the derivative of a function f at x0 using symmetric differences. In[13]:=
d[f_, x0_, ∂_] = (f[x0 + ∂] - f[x0 - ∂])/(2∂);
Because the above polynomial odeNum contains up to fifth derivatives, we calculate the derivatives of J HzL using the function d. d implements recursively a simple finite difference approximation. In[14]:=
j[0][x_, ∂_] = KleinInvariantJ[x]; Do[j[i][x_, ∂_] = Together[d[j[i - 1][#, ∂]&, x, ∂]], {i, 1, 5}]
Here are the definitions of j. In[16]:=
??j Global`j
Numerical Computations
4 j@0D@x_, ∂_D = KleinInvariantJ@xD −KleinInvariantJ@x−∂D+KleinInvariantJ@x+∂D j@1D@x_, ∂_D = 2∂
−2 KleinInvariantJ@xD+KleinInvariantJ@x−2 ∂D+KleinInvariantJ@x+2 ∂D j@2D@x_, ∂_D = 4 ∂2
j@3D@x_, ∂_D = −KleinInvariantJ@x−3 ∂D+3 KleinInvariantJ@x−∂D−3 KleinInvariantJ@x+∂D+KleinInvariantJ@x+3 ∂D 8 ∂3 1 j@4D@x_, ∂_D = 16 ∂4
H6 KleinInvariantJ@xD + KleinInvariantJ@x − 4 ∂D − 4 KleinInvariantJ@x − 2 ∂D − 4 KleinInvariantJ@x + 2 ∂D + KleinInvariantJ@x + 4 ∂DL j@5D@x_, ∂_D = 1 H−KleinInvariantJ@x − 5 ∂D + 5 KleinInvariantJ@x − 3 ∂D − 32 ∂5 10 KleinInvariantJ@x − ∂D + 10 KleinInvariantJ@x + ∂D − 5 KleinInvariantJ@x + 3 ∂D + KleinInvariantJ@x + 5 ∂DL
If we now use high-precision values for x0 and small values of ¶s, we will get reliable values for the derivatives. We display the first 50 digits of the derivatives. In[17]:= Out[18]=
In[19]:= Out[19]=
z0 = 1/7 + I/3; Table[N[j[3][N[z0, 400], 10^-k], 50], {k, 10, 50, 10}] 81.9491815278678998385779209611995390716036646976072× 108 + 6.6931642042995765002200844562245560872132319110987× 108 , 1.9491815278678997056851738096547832251773036170940× 108 + 6.6931642042995765636469838413684337616098626718096× 108 , 1.9491815278678997056851738096547832238483761455785× 108 + 6.6931642042995765636469838413684337622441316656611× 108 , 1.9491815278678997056851738096547832238483761455785× 108 + 6.6931642042995765636469838413684337622441316656611× 108 , 1.9491815278678997056851738096547832238483761455785× 108 + 6.6931642042995765636469838413684337622441316656611× 108 < Last[%] - Drop[%, -1] 8−1.328927471515447558477552885520287× 10−8 + 6.34268993851438776750308997545624× 10−9 , −1.3289274715154× 10−28 + 6.342689938514× 10−29 , 0. × 10−42 + 0. × 10−42 , 0. × 10−42 + 0. × 10−42 <
We now convert the sum odeNum into a list and substitute numerical values for the derivatives at the random point x0 = 1 ê 7 + i ê 3. In[20]:=
z0 = 1/7 + I/3; (odeNumTerms = (List @@ odeNum) /. (* use numerical values for derivatives *) Table[Derivative[k][J][τ] -> j[k][N[z0, 400], 10^-40], {k, 0, 5}] // N) // Short[#, 6]&
Out[21]//Short=
8−4.2528 × 1043 − 2.28422 × 1044 , −9.69544 × 1047 + 2.59522 × 1048 , 1.55149 × 1052 − 1.23336 × 1052 , −6.24876 × 1055 + 8.17256 × 1054 , 1.20518 × 1059 + 5.26045 × 1058 , 46, −1.39674 × 1047 + 4.12372 × 1048 , 1.60648 × 1052 − 2.47643 × 1052 , −8.43616 × 1055 + 4.11394 × 1055 , 1.1148 × 1059 + 9.82261 × 1057 < In[22]:= Out[22]=
{Min[#], Max[#]}& @ Abs[odeNumTerms] 82.16278 × 1044 , 2.34245 × 1060 <
1.0 Remarks
5
The magnitudes of the single terms for the last list are between 44 and 60. Adding the terms gives a result of order 44. This means that all higher terms are cancelled to the precision 16. In[23]:= Out[23]=
Plus @@ % 1.11504 × 1044 + 1.25442 × 1043
Using better approximations for the derivatives, the sum can be reduced to about 10-40 . In[24]:=
Out[25]=
z0 = 1/7 + I/3; odeNum /. Table[Derivative[k][J][τ] -> j[k][N[z0, 500], 10^-50], {k, 0, 5}] // N −3.69059 × 10−41 + 6.1683 × 10−42
Using still better approximations for the derivatives, the sum can be further reduced to about 10-240. This means about 300 digits cancelled each other. The probability of this happening “accidentally” is astronomically small. In[26]:=
Out[27]=
z0 = 1/7 + I/3; odeNum /. Table[Derivative[k][J][τ] -> j[k][N[z0, 1200], 10^-150], {k, 0, 5}] // N −3.69059 × 10−241 + 6.1683 × 10−242
As a security check, let us try two other values x0. In[28]:=
Out[28]=
Function[z0, {{Min[Abs[#]], Max[Abs[#]]} // N, (Plus @@ #) // N}&[ (List @@ odeNum) /. Table[Derivative[k][J][τ] -> j[k][N[z0, 1200], 10^-150], {k, 0, 5}]]] /@ {1/5 + I 2/9, 13/11 + I 53/71} 8883.30221 × 1044 , 1.19972 × 1060 <, 2.98747 × 10−241 + 2.94343 × 10−241 <, 883.06251 × 1011 , 7.63516 × 1013 <, −3.34019 × 10−288 − 1.22776 × 10−287 <<
Again, a few hundred digits cancel. This gives strong evidence that the function wHtL from above is a solution of the Chazy equation wH3L HtL + 3 w£ HtL2 - 2 wHtL w££ HtL = 0. This kind of high-precision checking is not a mathematical proof, but the probability that for random numbers one achieves hundreds of coinciding digits is overwhelming. (For some examples where this kind of reasoning can go wrong, see [249].) Using the well-known differential equation for Klein’s modular function J HzL, it is also possible to symbolically prove that wHtL = 1 ê 2 ∑logHJ £ HtL6 JHtL-4 HJ HtL - 1L-3 L ê ∑t is a solution of the Chazy equation wH3L HtL + 3 w£ HtL2 - 2 wHtL w££ HtL = 0. To do this, we show that odeNum lies in the ideal generated by the differential equation for Klein's modular function J HzL and its derivatives. The following code contains the corresponding Mathematica inputs (we discuss the function GroebnerBasis, which is used in the following inputs in Chapter 1 of the Symbolics volume [1795]). In[29]:=
Clear[w]; (* the differential equation for KleinInvariantJ *) odeJ = -32 w'[z]^4 + 41 w[z] w'[z]^4 - 36 w[z]^2 w'[z]^4 + 108 w[z]^2 w''[z]^2 - 216 w[z]^3 w''[z]^2 + 108 w[z]^4 w''[z]^2 - 72 w[z]^2 w'[z] w'''[z] + 144 w[z]^3 w'[z] w'''[z] - 72 w[z]^4 w'[z] w'''[z];
In[32]:=
(* the conjectured solution of the differential equation w'''[z] - 2 w[z]w''[z] + 3w'[z]^2 == 0 *) w[z_] = 1/2 D[Log[J'[z]^6/(J[z]^4(J[z] - 1)^3)], z];
In[34]:=
(* the differential equation *) ode = w'''[z] - 2 w[z] w''[z] + 3 w'[z]^2;
In[36]:=
Clear[w]; (* the numerator of the resulting identity *) odeNum = Numerator[Together[ode]] /. J -> w;
Numerical Computations
6 In[39]:=
(* a basis for the ideal generated by odeJ, D[odeJ, z], and D[odeJ, z, z] *) gb = GroebnerBasis[{odeJ, D[odeJ, z], D[odeJ, z, z]}, (* derivatives are the variables *) Table[Derivative[k][w][z], {k, 0, 5}], MonomialOrder -> DegreeReverseLexicographic];
In[41]:=
(* odeNum lies in the above ideal *) PolynomialReduce[odeNum, gb, Table[Derivative[k][w][z], {k, 0, 5}], MonomialOrder -> DegreeReverseLexicographic][[2]] 0
Out[42]=
The 0 in the second element of the last list returned by PolynomialReduce shows that odeNum follows from odeJ. We give another introductory example for the use of high-precision arithmetic. Here is a trajectory (starting at the origin) of the famous Rössler system, x£ HtL = - yHtL - zHtL y£ HtL = xHtL + a yHtL z£ HtL = b - c zHtL + xHtL zHtL a coupled system of three nonlinear ordinary differential equations. (We use the parameter values a = 1 ê 2, b = 2, and c = 4.) In[43]:=
Module[{a = 1/2, b = 2, c = 4, T = 500}, ndsolRössler = NDSolve[{x'[t] == -y[t] - z[t], y'[t] == x[t] + a y[t], z'[t] == b + x[t] z[t] - c z[t], (* start at the origin *) x[0] == 0, y[0] == 0, z[0] == 0}, {x, y, z}, {t, 0, T}, MaxSteps -> 10^5]; (* show trajectory *) ParametricPlot3D[Evaluate[{x[t], y[t], z[t]} /. ndsolRössler[[1]]], {t, 0, T}, PlotPoints -> 10000, PlotRange -> All]];
-6
-4
-2
0
2 -2.5
0
2.5
5
10 7.5 5 2.5 0
While the built-in function NDSolve can easily solve these differential equations, we will quickly investigate the value of the largest step size h that can be used in a simple forward Euler discretization so that the trajectories do not escape to infinity [1131]: HhL HhL = xn + h H- yHhL xn+1 n - zn L
HhL HhL = yn + h HxHhL yn+1 n + a yn L
HhL HhL HhL zHhL n+1 = zn + h Hb - c zn + xn zn L
1.0 Remarks In[44]:=
7
hpEuler[n_, h_, xyz0_:{0, 0, 0}] := Module[{a = 1/2, b = 2, c = 4, x = xyz0[[1]], y = xyz0[[2]], z = xyz0[[3]]}, Table[{x, y, z} = {x + h (-y - z), y + h (x + a y), z + h (b + x z - c z)}, {n}]]
The next series of five graphics shows the solution obtained from using the Euler discretization for step sizes k 10-2 for k = 1, …, 6. We follow the solution over a time interval of length 100. We use a step size of precision 1000. This guarantees that we have at t = 100 still about 800 valid digits left. In[45]:=
Show[GraphicsArray[ Table[Graphics3D[Line[N[hpEuler[Round[10^4/k], N[k 10^-2, 10^3]]]], PlotRange -> All, PlotLabel -> k], {k, 6}]]] 1
2
3
4
5
6
Starting with an upper bound for the maximal step size (meaning a step size so that the trajectory surely diverges—we use the value 1 here), it is straightforward to implement a simple bisection algorithm findMaxi malStepSize that brackets the maximal step size in an interval of length ¶. To get a sufficiently small interval HhL HhL for the step size, we use high-precision arithmetic with initial 4000 digits to evolve the 8xHhL n , yn , zn <. We consider a trajectory that escapes to infinity if its coordinates becomes larger than 106 . In[46]:=
(* message in case all precision is lost *) findMaximalStepSize::eps = "Excessive precision loss in iterations at step size `1`."; findMaximalStepSize[hp_, ∂_, n_:4000, prec_:4000, max_:10^6, hMax0_:1] := Module[{hMin = 0, hMax = hMax0, hNew, res, escape = 10^6, msg}, (* turn off message *) msg = General::ovfl; Off[General::ovfl]; (* iterate forward at interval midpoint *) Do[hNew = (hMin + hMax)/2; res = hp[n, N[hNew, prec]][[-1]]; (* enough precision left? *) If[Precision[DeleteCases[res, Overflow[], Infinity]] < 20, Message[findMaximalStepSize::eps, hNew]]; (* new interval for the maximal step size *) If[MemberQ[res, Overflow[], Infinity] || Max[Abs[res]] > escape, hMax = hNew, hMin = hNew]; print[{k, N[hMax - hMin]}], {k, Ceiling[Log[1/2, ∂]] + 1}]; (* turn on message *) If[Head[msg] === String, On[General::ovfl]]; {hMin, hMax}]
For a given bracketing interval HhMin, hMaxL for the maximal step size, the function makeTrajectory Graphics visualizes various properties of the corresponding forward iterated solutions. The first row of graphics shows the trajectories for step sizes hMin ê 10 (surely nondiverging), hMin (on the edge of diverging), HhL HhL and hMax (barely diverging). The second row shows the precision of the 8xHhL n , yn , zn < as a function of n HhL HhL (making sure that in all iteration steps we still have digits left), the components xn , yHhL n , and zn as a function of Hhmax L Hhmin L Hhmax L Hhmin L Hhmax L Hhmin L xn §, †yn - yn §, †zn - zn §< as a function of n. n and the last graphic shows 8†xn In[49]:=
makeTrajectoryGraphics[hp_, {hMin_, hMax_}, o_, prec_:4000] := Module[{cOpts, dataMinP, dataMin, dataMaxP, dataMax, dataSmall, max, pos, pr},
Numerical Computations
8
cOpts[x_] := Sequence[PlotRange -> All, BoxRatios -> {1, 1, 1}, Axes -> True, PlotRange -> All, PlotLabel -> Subscript["h", x]]; (* iterate forward propagation *) {dataMinP, dataMaxP, dataSmall} = hp[o, N[Rationalize[#, 0], prec]]& /@ {hMin, hMax, (hMin + hMax)/10}; (* extract nondiverging part of diverging trajectory *) max = 2 Max[Abs[dataMinP]]; pos = Position[dataMaxP, _?(Max[Abs[#]] > max&), {1}, 1][[1, 1]]; dataMax = Take[dataMaxP, pos]; dataMin = Take[dataMinP, pos]; (* make two graphics arrays *) GraphicsArray /@ Block[{$DisplayFunction = Identity, pr = FullOptions[Graphics3D[Line[dataMinP], PlotRange -> All], PlotRange], xTicks = Table[k 1000, {k, 0, Round[Log[10, o + 1]]}], rgb = {RGBColor[1, 0, 0], RGBColor[0, 1, 0], RGBColor[0, 0, 1]}}, Print[xTicks]; (* show trajectories for three step sizes *) {Graphics3D[Line[#1], PlotRange -> 3/2 pr, cOpts[#2]]& @@@ {{dataSmall, "small"}, {dataMinP, "min"}, {dataMax, "max"}}, (* precision of iterates *) {Graphics[{PointSize[0.003], Transpose[{rgb, MapIndexed[Point[{#2[[1]] - 1, Precision[#1]}]&, #]& /@ {dataSmall, dataMinP, dataMax}}]}, PlotRange -> {All, {0, 1.1 prec}}, Frame -> True, PlotLabel -> "Precision"], (* x, y, and z-component of iterates *) Graphics[{PointSize[0.003], Transpose[{rgb, MapIndexed[Point[{#2[[1]] - 1, #1}]&, #]& /@ Transpose[dataMax]}]}, Frame -> True, PlotRange -> {{0, o}, Automatic}, FrameTicks -> {xTicks, Automatic, None, None}, PlotLabel -> (Subscript[#, n]& /@ {x, y, z})], (* difference in trajectories does bracketing step sizes *) Graphics[{PointSize[0.003], Transpose[{rgb, MapIndexed[If[# != 0, Point[{#2[[1]] - 1, Log[10, Abs[#1]]}], {}]&, #]& /@ Transpose[N[dataMax - Take[dataMin, pos]]]}]}, Frame -> True, PlotRange -> {{0, o}, Automatic}, FrameTicks -> {xTicks, Automatic, None, None}, PlotLabel -> (Subscript[#, n]& /@ {δx, δy, δz})]}}]]
Here are the just-described graphics for the Euler iterations for a starting interval of length 10-25 . We obtain General::ovfl messages from the diverging trajectories. The maximal step length is hmax = 0.06832597…. In[50]:= Out[50]= In[51]:=
findMaximalStepSize[hpEuler, 10^-25] 1321616582571470688732441 2643233165142941377464883 9 , = 19342813113834066795298816 38685626227668133590597632 Show /@ makeTrajectoryGraphics[hpEuler, (* findMaximalStepSize[hpEuler, 10^-25] = *) {1321616582571470688732441/19342813113834066795298816, 2643233165142941377464883/38685626227668133590597632}, 4000] General::ovfl : Overflow occurred in computation. More…
1.0 Remarks
9 80, 1000, 2000, 3000, 4000< 0
hsmall
hmin
0
-5
0
-10
-10
-10
30
30
30
20
20
20
10
10
0
10
0
-5
0
-5
0
-5
0
5
0
5 10
5 10
10
8xn , yn , z n <
Precision 4000
8dxn , dyn , dzn < 0
10
-5
3000
5
2000
0
-15
1000
-5
-20
-10
-25
-10 0
1000
2000
hmax
-5
-5
3000
4000
1000
2000
3000
4000
1000
2000
3000
4000
The range of possible step sizes can be increased by using different discretization schemes. Here is a rather sophisticated-looking nonstandard discretization scheme [1131], [1270]. In[52]:=
hpMickens[n_, h_, xyz0_:{0, 0, 0}] := Module[{a = 1/2, b = 2, c = 4, ϕ1, ϕ2, ϕ3, xc, x = xyz0[[1]], y = xyz0[[2]], z = xyz0[[3]]}, ϕ1 = h; ϕ2 = (1 - Exp[-h a])/a; xc = (-c + Sqrt[c^2 - 4 a b])/2; ϕ3 = (1 - Exp[-h xc])/xc; Table[x = x - ϕ1 (y + z); {y, z} = {(1 + a ϕ2) y + ϕ2 x, b ϕ3 + (1 + ϕ3 (x - c)) z}; {x, y, z}, {n}]]
And here are the resulting trajectories at the edge of stability—they look quite different from the ones of the Euler discretization. In[53]:= Out[53]= In[54]:=
findMaximalStepSize[hpMickens, 10^-25] 16830551331383143306135827 4207637832845785826533957 9 , = 38685626227668133590597632 9671406556917033397649408 Show /@ makeTrajectoryGraphics[hpMickens, (* findMaximalStepSize[hpMickens, 10^-25] = *) {16830551331383143306135827/38685626227668133590597632, 4207637832845785826533957/9671406556917033397649408}, 4000] General::ovfl : Overflow occurred in computation. More…
80, 1000, 2000, 3000, 4000<
Numerical Computations
10 h 2.5 small
2.5
hmin
h 2.5 max 0
0
0
-2.5 -5
-2.5 -5
-2.5 -5 -7.5 .5
-7.5 .5
-7.5 .5
0
0
0
-10
-10
-10
-20
-20 -5
-20 -5
-5 0
0 5
0 5
5 8xn , yn , zn <
Precision
8dxn , dyn , dzn <
10 7.5 5 2.5 0 -2.5 -5 -7.5
4000 3000 2000 1000 0
1000
2000
3000
4000
0 -5 -10 -15 -20 -25 1000
2000
3000
4000
1000
2000
3000
4000
One could now go on and investigate the dependence of the maximal step size on the initial conditions. The following input calculates the maximal step size for a dense grid over the xH0L,yH0L-plane (we keep zH0L = 0) and will run a few hours. A structure mildly resembling the projected attractor arises. (The corresponding graphic for the nonstandard discretization hpMickens looks qualitatively similar.) Make Input
(* calculate maximal step sizes *) With[{L = 10, pp = 120}, dataEuler = Table[{x0, y0, Plus @@ findMaximalStepSize[ hpEuler[#1, #2, {x0, y0, 0}]&, 10^-4, 1000, 600]/2}, {x0, -L, L, 2L/pp}, {y0, -L, L, 2L/pp}]]; (* visualize maximal step sizes *) Show[GraphicsArray[ Block[{$DisplayFunction = Identity}, {ListDensityPlot[Transpose[Map[Last, dataEuler, {2}]], Mesh -> False, PlotRange -> All, FrameTicks -> None], ListPlot3D[Transpose[Map[Last, dataEuler, {2}]], Mesh -> False, PlotRange -> All, Axes -> False]}]]]
So far, we have discussed the usefulness of high-precision arithmetic for numerical calculations. High-precision arithmetic allows one to obtain reliable results with many digits that sometimes are impossible to obtain using machine arithmetic. On the other hand, for many calculations, machine precision is sufficient and speed is
1.0 Remarks
11
essential. Mathematica frequently compiles numerical calculations invisible to the user to accelerate calculations and also allows for explicit compilation. Because of the importance of compilation, let us give one typical example in this introductory section. We will encounter many more invocations of the Compile function throughout this chapter. We consider a simple 1D molecular dynamics simulation [661], [1897], [1548], [1505], [766]. Between two walls, located at z = 0 and z = L we have n particles with potentially different masses. If two (or more) particles meet, and have a center of mass energy less than V, they undergo an elastic collision. If they have a center of mass energy greater than V , they just move through each other (this is a very crude model of a realistic collision process). The two walls are held at temperatures T0 and TL and if particles collide with the walls they thermalize, meaning, after the wall collision, their velocities are Maxwellian with temperature T0 or TL . In the following, we will assume the data for each particle in the form {particleNumber, mass, startTime, startPoint, velocity} and the data for all particles is a list of such 5-element lists. The function collisionStep evolves a system of particles until the next collision and returns the particle data with the new startTimes, startPoints, and velocities. We account for the possibility that more than one collision could happen at one time. In[55]:=
collisionStep[particleData_?(MatrixQ[#, NumberQ]&), L_, V_, Ts_?(VectorQ[#, NumberQ]&)] := Module[{, , n, tOld, cts, δxδvs, leftt, rightt, ts, tC, cList, c, m1, v1, m2, v2, Tc, V1, V2, , T0 = Ts[[1]], TL = Ts[[2]]}, = particleData; = ; = $MaxMachineNumber/10; n = Length[]; tOld = [[1, 3]]; (* collision of all adjacent pairs *) δxδvs = {#[[2, 4]] - #[[1, 4]], #[[1, 5]] - #[[2, 5]]}& /@ Partition[, 2, 1]; cts = If[#[[2]] == 0., , #[[1]]/#[[2]]]& /@ δxδvs; {leftt, rightt} = {0 - [[1, 4]]/[[1, 5]], (L - [[n, 4]])/[[n, 5]]}; ts = Join[{leftt}, cts, {rightt}]; (* time of next collision *) tC = Max[ts]; Do[If[0. < ts[[j]] < tC, tC = ts[[j]]], {j, Length[ts]}]; (* list of colliding pairs; multiple collision can (depending on time granularity) happen at the same time *) (* which particles collide? *) cList = {-1}; Do[If[ts[[j]] == tC, AppendTo[cList, j - 1]], {j, n + 1}]; (* new collision times and new positions *) Do[[[j, 3]] = tOld + tC; [[j, 4]] = [[j, 4]] + tC [[j, 5]], {j, n}]; (* carry out the collision(s) *) Do[c = cList[[j]]; Which[(* left wall collision *) c == 0, [[1, 4]] = 0.; (* thermalize on left wall *) [[1, 5]] = Sqrt[2 T0/[[1, 2]]] Abs[InverseErf[2 Random[] - 1]], (* two particle collision *) 0 < c < n, {m1, v1} = [[c, {2, 5}]]; {m2, v2} = [[c + 1, {2, 5}]]; If[(* high-energy collision *) Tc = 1/2 m1 m2/(m1 + m2) (v1 - v2)^2; Tc > V, {[[c]], [[c + 1]]} = {[[c + 1]], [[c]]}; [[c + 1, 4]] = [[c, 4]], (* else -- a real collision *) (* velocities after collision *) {V1, V2} = {v1, v2} + 2 {m2, -m1}(v2 - v1)/(m1 + m2); {[[c, 5]], [[c + 1, 5]]} = {V1, V2}; [[c + 1, 4]] = [[c, 4]]],
Numerical Computations
12
(* right wall collision *) c == n, [[n, 4]] = L; (* thermalize on right wall *) [[n, 5]] = -Sqrt[2 TL/[[n, 2]]] Abs[InverseErf[2 Random[] - 1]]], {j, 2, Length[cList]}]; (* do nearby in time collisions *) Do[If[[[j, 4]] == [[j + 1, 4]] && [[j, 5]] > [[j + 1, 5]], [[j + 1, 4]] = [[j, 4]]; {[[j]], [[j + 1]]} = {[[j + 1]], [[j]]}], {j, n - 1}]; If[[[1, 4]] == 0. && [[1, 5]] < 0., [[1, 5]] = -[[1, 5]]]; If[[[n, 4]] == L && [[n, 5]] > 0., [[n, 5]] = -[[n, 5]]]; (* return new state *) ]
To visualize the trajectories of the particles, we implement a function collisionHistoryGraphics that shows the space-time curves of the particles with time running upwards. We color the particles according to their mass. In[56]:=
collisionHistoryGraphics[collisions_, L_, opts___] := Module[{segments = Transpose /@ Partition[Sort /@ collisions, 2, 1]}, Graphics[{(* the two walls *) {GrayLevel[0.5], Thickness[0.02], Line[{{0, #1}, {0, #2}}], Line[{{L, #1}, {L, #2}}]}& @@ ({Min[#], Max[#]}&[#[[1, 3]]& /@ collisions]), (* the particle trajectories *) {Map[{Hue[#[[1, 2]]], Line[{{#[[1, 4]], #[[1, 3]]}, {#[[2, 4]], #[[2, 3]]}}]}&, segments, {2}]}}, opts, FrameTicks -> None, Frame -> True, PlotRange -> {{-L/10, 1.1 L}, All}, AspectRatio -> 1]]
Here is an example. We start with 30 particles with random masses, starting points, and starting velocities in a well of length 100. The leftmost graphic uses V = ¶, meaning all collisions are elastic. As a result, only after the first particle hits a wall (with temperature 10; the Boltzmann constant is set to 1), we see an interesting structure of the trajectories evolving, propagating from the right to the left through successive collisions. The middle and right graphics use V = 2 and V = 1. Because now some collisions do not happen, the disturbance can propagate more quickly to the left through a particle not colliding with each particle on the way. In[57]:=
Module[{n = 30, Ts = {10, 10}, L = 100, cs = 1000, pd, data, particleData}, SeedRandom[33]; (* initial particle positions and velocities *) particleData = Sort[Table[{k, 0.8 Random[], 0, L Random[], 1/100 (2 Random[Integer] - 1)}, {k, n}], (* sort left to right *) #1[[4]] < #2[[4]]&]; (* show the three graphics *) Show[GraphicsArray[ Function[V, (* initialize random number generation *) SeedRandom[111]; (* carry out cs collisions *) data = NestList[collisionStep[#, L, V, Ts]&, particleData, cs]; collisionHistoryGraphics[data, L]] /@ {Infinity, 2, 1}]]]
1.0 Remarks
13
Next, we will use two kinds of particles (meaning two masses) and walls of different temperatures. We expect to see the Ludwig–Soret effect [761] in the resulting spatial distribution of the particles, meaning the lighter particles should accumulate near the cold wall. We now carry out 1000 collisions for 2 μ 50 particles with masses m = 1 and m = 0.12, wall temperatures T0 = 0 and T200 = 10. particleData is the random initial configuration of the particles. In[58]:=
particleData = With[{n = 50, Ta = 5.5, L = 200, μ1 = 1., μ2 = 0.12}, Sort[Join[(* initial conditions for heavy particles *) Table[{k, μ1, 0., L Random[], Sqrt[2 Ta/μ1] InverseErf[2 Random[] - 1]}, {k, n}], (* initial conditions for light particles *) Table[{n + k, μ2, 0., L Random[], Sqrt[2 Ta/μ2] InverseErf[2 Random[] - 1]}, {k, n}]], (* sort left to right *) #1[[4]] < #2[[4]]&]];
We carry out 100 collisions using the initial configuration particleData and measure how long this takes. In[59]:=
Out[60]=
L = 200; n = 50; SeedRandom[111]; (dataLS1 = NestList[collisionStep[#, L, 2, {1, 10}]&, particleData, 100]); // Timing 80.45 Second, Null<
Invoking now the Mathematica compiler explicitly, we can considerably speed-up this calculation. To compile Mathematica code explicitly, we invoke the function Compile. We want to compile the definition of the function collisionStep and to access the body of the function collisionStep; we use its downvalues. For compilation, the type of the input arguments must be known. The above pattern for the particle data particleData_?(MatrixQ[#, NumberQ]&) translates (for our purposes here) to {particleData, _Real, 2} and the pattern Ts_?(VectorQ[#, NumberQ]&) for the wall temperatures translates to {Ts, _Real, 1}. The inverse error function needed to generate the Maxwellian velocity distribution is not compilable; so, we tell the Compile function that the inverse error function returns a single real number here. In[61]:=
collisionStepC = Function[body, Compile @@ Join[Hold[{{particleData, _Real, 2}, L, V, {Ts, _Real, 1}}], body, (* evaluate outside the compiler *) Hold[{{InverseErf[_], _Real}}]], (* extracted and held original definition *) {HoldAll}] @ MapAt[Hold, DownValues[collisionStep], {1, 2}][[1, 2]];
Now, we run the compiled function collisionStepC and measure again how long it takes. In[62]:=
SeedRandom[111]; (dataLS1C = NestList[collisionStepC[#, L, 2, {1, 10}]&, particleData, 100]); // Timing
Numerical Computations
14 Out[63]=
80.07 Second, Null<
The result of the compiled run agrees with the result of the uncompiled run (we initialized the random number generator identical in the two runs). In[64]:= Out[64]=
dataLS1 == dataLS1C True
Using the faster function collisionStepC, we now carry out 100000 collision steps. (Carrying out all collisions within a compiled function and keeping a list of next collision times would eliminate some of the repeated calculation of the next collision times and would further speed-up the calculation.) Instead of graphing the individual trajectories, we display only the centers of the light and heavy particles. The function centersOf Gravity calculates the center of masses for the 2 n particles. In[65]:=
centersOfGravity[pd_, n_] := Plus @@@ Map[#[[4]]&, {Take[#, {1, n}], Take[#, {n + 1, 2n}]}&[(* heavy particles first *) Sort[pd]], {2}]/n
We start again with the initial particle configuration particleData. We clearly see how the light particles accumulate near the cold wall. (The center of gravity of the heavy particles does not change much because the light particles accumulate at a relatively small distance from the cold wall.) In[66]:=
SeedRandom[111]; (* list of center of gravity data *) cogDataLS2 = Block[{pd = particleData, cs = 100000}, Table[pd = collisionStepC[pd, L, 2, {1, 10}]; {{pd[[1, 3]], #[[1]]}, {pd[[1, 3]], #[[2]]}}&[ centersOfGravity[pd, n]], {cs}]];
In[69]:=
Show[ListPlot[#1 /@ cogDataLS2, PlotStyle -> {#2}, DisplayFunction -> Identity]& @@@ (* light particles in red; heavy particles in blue *) {{First, RGBColor[0, 0, 1]}, {Last, RGBColor[1, 0, 0]}} , DisplayFunction -> $DisplayFunction, AspectRatio -> 1/4, PlotRange -> All, Axes -> False, Frame -> True] 140 120 100 80 60 40 20 0
200
400
600
800
1000
Many related simulations could be carried out, such as modeling the gas temperature for finite reservoirs [1115].
1.1 Approximate Numbers
15
1.1 Approximate Numbers 1.1.0 Remarks This subsection will cover the basics of the usage and underlying principles of machine and high-precision numbers. While calculations with machine numbers have been used already extensively in the chapters of the Programming [1793] and Graphics [1794] volumes, high-precision arithmetic was used only rarely. So in the next subsection we will discuss the notations of precision and accuracy of a real and a complex number, the principles of automatic precision control and its consequences, the phenomena of precision gain and loss in calculations, and the possibilities to raise or lower the precision or accuracy of a number. Later we will investigate the internal structure of high-precision numbers and how high-precision numbers are treated special in functions like Union and which system options are of relevance here. In discussing all these subjects, we will provide a large number of examples to demonstrate all statements. The second subsection will deal with intervals. Intervals provide a method to carry out validated numerical calculations. The third subsection will discuss continued fractions and related expansions in detail. Continued fractions are relevant for converting approximate numbers to nearby rational numbers. The last subsection of this section deals with a more advanced topic for machine number calculations—packed arrays. While packed arrays are in many instances automatically generated by Mathematica without the users explicit influences, for more complicated calculations it is sometimes necessary for the user to pack and unpack lists of machine numbers under his guidance to achieve optimal performance.
1.1.1 Numbers with an Arbitrary Number of Digits For most numerical calculations, around 14 to 19 digits of (hardware-dependent) machine precision is adequate. Machine arithmetic is used when evaluating expressions of the form N[expr]. However, sometimes we would like to work with more digits to ensure that a certain number of interesting digits is correct. (For several examples, it is necessary to use more than around 20 digits in applications; see [1904], [1667], [1349], [453], [503], and [413].) In such situations, Mathematica’s high-precision numbers, together with their automatic precision control, come in very handy. We will use Mathematica’s high-precision capabilities many times in this and the following chapters. We have already introduced N[expression, digits] in Chapter 2 of the Programming volume [1793]. It computes the expression expression to a precision of digits digits. Mathematica’s high-precision arithmetic is based on significance arithmetic (see [1266] and [1267]). This means that the result of a high-precision calculation is a number (composed of digits) including the knowledge of which of the digits are correct. In OutputForm and StandardForm only the certified digits are printed. InputForm and FullForm will display all digits together with the information of how many of them are correct. In a nutshell, here is how Mathematica can be used to carry out high-precision arithmetic. In principle, there are three possibilities: † Floating-point arithmetic with a fixed number of digits. This can be forced by choosing $MinPrecision = $MaxPrecision = number (which is better used inside a Block by using localized versions of $MinPreci
Numerical Computations
16 sion and $MaxPrecision).
† Floating-point arithmetic with a variable number of digits. This is the method implemented in the current Mathematica kernel. Roughly speaking, the main idea is to simulate interval arithmetic by constantly maintaining a “few more digits” than needed (hereafter called guard digits), and to use them to analyze the error. † Interval arithmetic. This stricter method is implemented in Mathematica for real numbers (see the subsection below). For more detailed information on Mathematica’s implementation of bignum arithmetic and interval arithmetic (see below), as well as a discussion of the general problem of calculating with approximate numbers and intervals, see [967], [830], [964], [887], [697], [1667], [1293], [1294], [36], [959], [1850], [968], and [969]. In the last section, we used high-precision arithmetic to “check” an identity numerically. Because we obtained zero to hundreds of digits we did not have to worry about the correctness of the digits. (The probability that the identity would be wrong and that accumulated errors in the carried out arithmetic make a wrong identity correct is vanishing small.) In the next two introductory examples, we are not primarily dealing with many digits, but rather how much we can trust the digits calculated. Here is a first simple example using high-precision arithmetic. This is an iterated modified logistic map [735], [539]. In[1]:=
modifiedLogisticMap[x_, λ_, ∂_, n_] = (λ + (-1)^n ∂) x (1 - x); modifiedLogisticMapData[x0_, λ_, ∂_, l_, l0_] := Drop[FoldList[modifiedLogisticMap[#, λ, ∂, #2]&, x0, Range[l]], l0]
Making a plot using machine arithmetic yields a “typical” looking picture for a logistic equation [949]. In[3]:=
Show[Graphics[{PointSize[0.003], Table[Point[{λ, #}]& /@ modifiedLogisticMapData[1/210, λ, 0.2, 300, 100], {λ, 2.6, 3.8, 0.01}]}], PlotRange -> All, Frame -> True] 0.9 0.8 0.7 0.6 0.5 0.4 0.3
2.6
2.8
3
3.2
3.4
3.6
3.8
Using bignum arithmetic yields a qualitatively similar looking picture. In[4]:=
Show[Graphics[{PointSize[0.003], Table[Point[{λ, #}]& /@ modifiedLogisticMapData[N[1/210, 200], λ, 2/10, 300, 100], {λ, 26/10, 38/10, 1/100}]}], PlotRange -> All, Frame -> True]
1.1 Approximate Numbers
17
0.9 0.8 0.7 0.6 0.5 0.4 0.3
2.6
2.8
3
3.2
3.4
3.6
3.8
But the actual orbit for a fixed l shows that after around 50 iterations, the machine result is wrong. In[5]:=
ListPlot[ (* difference between machine-precision and high-precision version *) modifiedLogisticMapData[1/210, 3.49, 0.2, 300, 100] modifiedLogisticMapData[N[1/210, 200], 349/100, 2/10, 300, 100], PlotJoined -> True, PlotRange -> All, Frame -> True, Axes -> False] 0.4
0.2
0
-0.2
-0.4 0
50
100
150
200
As long as the calculated high-precision numbers have explicitly displayed digits, we can trust them to be correct. If we would carry out the last iteration a bit longer, no correct digit remains. The numbers of the form 0 μ 10n indicate that the result is somewhere in the interval @-10n , +10n D. (Of course, by starting with a higher initial precision we can get verified correct results for any number of iterations.) In[6]:=
Take[modifiedLogisticMapData[N[1/210, 200], 359/100, 2/10, 400, 100], {-45, - 30}] General::unfl : Underflow occurred in computation. More…
Out[6]=
80.4906, 0.8472, 0.491, 0.847, 0.49, 0.85, 0.5, 0.8, 0. × 10−1 , 0., 0., 0. × 101 , 0. × 104 , 0. × 108 , 0. × 1017 , 0. × 1036 <
For an interesting two-dimensional (2D) version of a logistic map, see [349]; for the logistic map in general, see [788]. Here is another small example. It shows a too pessimistic result. The following input implements the iterative calculation of numerical values of the Weierstrass function ƒHz; g2 , g3 L [431] based on argument reduction and series expansion.
Numerical Computations
18
ƒHz; g2 , g3 L = lim HnL Hz; g2 , g3 L nض HnL Hz; g2 , g3 L = è HHn-1L Hz; g2 , g3 L; g2 , g3 L 2
g2 H6 z2 - ÅÅÅÅ ÅÅ L 2 ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅ - 2 z è Hz; g2 , g3 L = ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ 4 H4 z3 - g2 z - g3 L
2n 2 g2 z 2 g3 z 4 H0L Hz; g2 , g3 L = J ÅÅÅÅÅÅÅÅÅ N + ÅÅÅÅÅÅÅÅÅÅ J ÅÅÅÅÅnÅÅÅÅ N + ÅÅÅÅÅÅÅÅÅÅ J ÅÅÅÅÅnÅÅÅÅ N z 20 2 28 2 The function weierstrass℘List returns a list of approximations. In[7]:=
weierstrass℘List[z_, {g2_, g3_}, o_] := First /@ NestList[Function[{w, n}, {Nest[-2# + (6#^2 - g2/2)^2/(4 (4#^3 - g2 # - g3))&, 1/#^2 + g2/20 #^2 + g3/28 #^4&[z/2^n], n], n + 1}] @@ #&, {z, 1}, o]
Using the function with machine numbers gives a converging (looking) result. In[8]:= Out[8]=
weierstrass℘List[N[2], {2, 3}, 40] 82., 6.82755, 6.44608, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555, 6.44555<
The result actually agrees with the value calculated by Mathematica. In[9]:= Out[9]=
WeierstrassP[2., {2, 3}] 6.44555 + 0.
Using high-precision arithmetic and its automatic precision control in the iterations shows that 1) massive calculations occurred in the process of calculations, and that 2) we cannot trust the above result completely blindly and a more detailed analysis of the stability of the iterations is needed. (In the last inputs, all occurrences of Slot[1] were treated independently, although they all represent the same number; we will come back to this example later in this subsection.) In[10]:=
weierstrass℘List[N[2, 20], {2, 3}, 20] 1 Power::infy : Infinite expression encountered. More… 0. × 103 ∞::indet : Indeterminate expression 0. × 104 ComplexInfinity encountered. More… 1 Power::infy : Infinite expression encountered. More… 0. × 105 ∞::indet : Indeterminate expression 0. × 106 ComplexInfinity encountered. More… 1 Power::infy : Infinite expression encountered. More… 0. × 106 General::stop : Further output of Power::infy will be suppressed during this calculation. More… ∞::indet : Indeterminate expression 0. × 109 ComplexInfinity encountered. More… General::stop : Further output of ∞::indet will be suppressed during this calculation. More…
Out[10]=
82.0000000000000000000, 6.82755307730362983, 6.446077715813308, 6.4455510992952, 6.44554936048, 6.4455493540, 6.44554935, 6.445549, 6.4455, 6.45, 6., 0., Indeterminate, Indeterminate, Indeterminate, Indeterminate, Indeterminate, Indeterminate, Indeterminate, Indeterminate, Indeterminate<
1.1 Approximate Numbers
19
Mathematica has a high-precision arithmetic for calculating with numbers with an arbitrary number of digits in the interval $MaxNumber < †x§ < $MaxNumber (provided the final answers and all intermediate results stay in this range). The current value of $MaxNumber is the following. In[11]:=
$MaxNumber 1.920224672692357× 10646456887
Out[11]=
The user can establish an upper bound on the number of digits (the precision) carried by setting the value of $MaxPrecision. $MaxPrecision gives the maximum number of digits digits used in numerical calculations with N[expression, digits].
By default, the value of $MaxPrecision is infinity. (Practically this means that as much precision, as is possible within the memory and operating system constraints as possible will be used. In some rare cases, one might want to use a finite upper limit to avoid long-running calculations.) In[12]:=
defaultMaxPrecisionValue = $MaxPrecision ∞
Out[12]=
If we attempt to evaluate an expression with more digits, such as N[2, 2 $MaxPrecision], we get an error message. In[13]:=
Block[{$MaxPrecision = 100}, N[2, 2 $MaxPrecision]] N::preclg : Requested precision 200 is larger than $MaxPrecision. Using current $MaxPrecision of 100.` instead. $MaxPrecision = Infinity specifies that any precision should be allowed. More…
Out[13]=
2.0000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000
In such cases, the computation continues with $MaxPrecision digits. The analogous lower bound is $Min Precision. $MinPrecision gives the minimum number of digits digits used in numerical calculations with N[expression, digits].
Here is its current (user-changeable) value. In[14]:=
$MinPrecision −∞
Out[14]=
This is in comparison to the $MachinePrecision value. In[15]:= Out[15]=
$MachinePrecision 15.9546
To uniquely differentiate between a high-precision calculation that uses $MachinePrecision precision, the built-in command MachinePrecision exists. MachinePrecision represents a symbol to indicate machine precision.
Numerical Computations
20
While MachinePrecision does not evaluate to a number directly, it evaluates to a number after applying N. In[16]:=
{MachinePrecision, N[MachinePrecision]} 8MachinePrecision, 15.9546<
Out[16]=
Sometimes, we would like to carry out parts of a calculation with more than $MachinePrecision. To avoid using N[…, digits] for each intermediate expression, we can use a construction of the form Block[{$MinPrecision = digits}, Module[{ allOtherVariables}, theCalculation]]
Sometimes, one also wants to do calculations with more digits than provided by the hardware, but with a fixed precision instead of using the automatic precision control of Mathematica (for instance to avoid excessive loss of precision). This can be achieved via the following construction. Block[{$MinPrecision = digits, $MaxPrecision = digits}, Module[{ allOtherVariables}, theCalculation]]
But the reader should be careful when using such constructions. In such situations, the reader has to do all error analysis alone because such constructions disable the automatic precision control. Occasionally one does not have to know any digits of the numerical value of an expression, but knowledge that the value is less than a certain given size is needed. This can be realized with N too. N[expression, {precision, accuracy}] tries to numericalize expression to precision precision or accuracy accuracy.
Here is a small quantity (of approximate size 4.3 10-1656521). In[17]:=
smallQuantity = Exp[-Exp[Exp[E]]];
The next input establishes that smallQuantity has an absolute value less than 10-1000000. In[18]:= Out[18]=
N[smallQuantity, {Infinity, 10^6}] 0. × 10−1000001
Because the absolute value is larger than 10-10000000, for the next input four digits are calculated. In[19]:= Out[19]=
N[smallQuantity, {4, 10^7}] 4.289 × 10−1656521
There are relatively few instances where the use of fixed precision arithmetic is preferable over the use of the default significance arithmetic. Basically, significance arithmetic can never be wrong (as long as one uses enough guaranteed digits, say at least 10). But because the method assumes that all errors are completely independent, it might overestimate the precision loss in calculations. Fixed precision arithmetic on the other side becomes error-free in the limit of totally correlated errors in the limit of many operations. Here is an example of the use of fixed precision arithmetic. We carry out the recursion zn = ≤ zn-1 ≤ zn-2 [1854]. The coefficients ≤1 of zn-1 are chosen randomly in each recursion step. In[20]:=
randomFibonacciFP[n_, prec_] := Block[(* fix precision inside Block *) {$MinPrecision = prec, $MaxPrecision = prec, (* the random ±1 *) := 2 Random[Integer] - 1}, Last /@ NestList[{#[[2]], #[[1]] + #[[2]]}&, N[{1, 1}, prec], n]]
Here is the result of 1000 steps of the recursion. (We use SeedRandom to generate reproducible results.)
1.1 Approximate Numbers In[21]:= Out[22]=
21
SeedRandom[11111]; randomFibonacciFP[1000, $MachinePrecision + 1][[-1]] −2.5055139869821551× 1055
Using significance arithmetic and starting with 155 digits gives only a single guaranteed digit after carrying out 1000 iterations. But all of the guard digits (see below) are correct anyway. In[23]:=
randomFibonacciSA[n_] := Block[{ := 2 Random[Integer] - 1}, Last /@ NestList[{#[[2]], #[[1]] + #[[2]]}&, N[{1, 1}, 155], n]]
In[24]:=
SeedRandom[11111]; {#, InputForm[#]}& @ randomFibonacciSA[1000][[-1]] 8−3. × 1055 , −2.505513986982155`1.3427663032154002*^55<
Out[25]=
Here is a more extreme example. Using pseudorandom numbers, we carry out one million recursions for this sequence. The result obtained in exact integer arithmetic is identical to the result using only $MachinePreci sion + 1 digits. In addition, we carry out the calculation using machine precision. Because we add numbers of similar magnitude at each step of the recursion, even the machine arithmetic result is correct. In[26]:=
Out[26]=
With[{prec = $MachinePrecision + 1}, Block[{n = 10^6, $MinPrecision = prec, $MaxPrecision = prec, r1, r2}, Nest[((* two pseudorandom numbers *) r1 = 2 Abs[Round[Sin[#[[4]]]]] - 1; r2 = 2 Abs[Round[Cos[#[[4]]]]] - 1; (* machine value, fixed precision value, exact value, and counter *) {{#[[1, 2]], r1 #[[1, 1]] + r2 #[[1, 2]]}, {#[[2, 2]], r1 #[[2, 1]] + r2 #[[2, 2]]}, {#[[3, 2]], r1 #[[3, 1]] + r2 #[[3, 2]]}, #[[4]] + 1})&, {N[{1, 1}], N[{1, 1}, prec], {1, 1}, 0}, n]]] // N 88−1.916001878177566× 1036474, 5.052944278059832× 1036473<, 8−1.916001878177571× 1036474, 5.052944278059843× 1036473<, 8−1.916001878177571× 1036474, 5.052944278059843× 1036473<, 1. × 106 <
This random Fibonacci recursion has the interesting feature that the quantity †zn §1ên approaches, for almost all realizations of the random ≤1 choices, a constant limit. The next graphic shows three examples of carrying out 10000 recursion steps. In[27]:=
SeedRandom[11111]; Show[Table[ListPlot[MapIndexed[Abs[#1]^(1/#2[[1]])&, randomFibonacciFP[10^5, 20]], (* set options properly *) Frame -> True, PlotStyle -> {PointSize[0.003], Hue[k/3]}, DisplayFunction -> Identity, PlotRange -> {1.10, 1.20}], {k, 0, 2}], DisplayFunction -> $DisplayFunction] 1.2 1.18 1.16
1.14 1.12
0
20000
40000
60000
80000
100000
Numerical Computations
22
(For related random recurrences, see [1052], [176].) In regard to machine precision, note the following observation: N[expression, digits] with machine precision numbers present in expression typically results in a machine precision result. In[29]:=
machineNumber = 1.2345678987654321;
Out[30]=
{N[machineNumber, 10], N[machineNumber, 20]} 81.23457, 1.23457<
N[expression, digits] with digits < machinePrecision is still using high-precision in all calculations. But with only a few digits in use, the calculations become less reliable. In[31]:=
exactNumber = 12345678987654321/10^12;
Out[32]=
{N[exactNumber, 2], N[exactNumber, 4], N[exactNumber, 6]} 81.2 × 104 , 1.235 × 104 , 12345.7<
Typically, for machine-precision numbers, only about six digits are displayed, so the numericalized version of the following fractions displays in the output as 1. In[33]:= Out[33]= In[34]:= Out[34]=
N[111111111111/111111111112, (* not needed *) MachinePrecision] 1. 1 - % 9.00002 × 10−12
InputForm and FullForm show all digits. In[35]:=
FullForm[%%]
Out[35]//FullForm=
0.999999999991` In[36]:=
InputForm[%%%]
Out[36]//InputForm=
0.999999999991
We make one more remark concerning expressions of the form N[something]. After calculating something as far as possible using standard evaluation, N is invoked (N does not have any Hold-like attribute). For some choices of something (e.g., Solve, Sum, Integrate), not only are the corresponding variables converted to numbers, but N also activates a corresponding numerical command (e.g., NSolve, NSum, NIntegrate). Here is an example of an integral that cannot be computed symbolically in closed form (currently). In[37]:= Out[37]=
Integrate[Sin[x^3]/(x^3 + x^2 + 1), {x, 0, Pi}] π Sin@x3 D x ‡ 1 + x2 + x3 0
N[%] does not produce the essentially worthless result Integrate[Sin[x^3]/(x^3 + x^2 + 1.), {x, 0, 3.14159}] but instead uses NIntegrate (see below) to numerically compute the following. In[38]:= Out[38]=
N[%] 0.179654
1.1 Approximate Numbers
23
But, for such expressions, N[symbolicNumericalExpression, prec] typically does not give a result with precision prec. Typically, the expression will be evaluated with working precision prec and this typically results in a precision less than prec. Here is an example for such a situation. In[39]:=
N[Sum[k^-k, {k, Infinity}], 100] N::meprec : Internal precision limit ∞
$MaxExtraPrecision = 50.` reached while evaluating ‚ k−k . More… k=1
Out[39]= In[40]:= Out[40]=
1.2912859970626635404072825905956005414986193684 Precision[%] 46.1588
As we will see later, the second argument of N influenced the WorkingPrecision option setting, not the setting of the PrecisionGoal option. In[41]:= Out[41]= In[42]:= Out[42]=
NSum[k^-k, {k, Infinity}, WorkingPrecision -> 100] 1.2912859970626635404072825905956005414986193684 Precision[%] 46.1588
Now, let us discuss what exactly characterizes a high-precision number. With high-precision arithmetic, it is important to carefully distinguish between accuracy and precision. In principle, this distinction is also important in calculations using machine precision, but unfortunately in this case, we do not have full control over these values. The accuracy of a number is (roughly) defined to be the number of correct digits to the right of the decimal point. The precision of a number is (roughly) defined to be the total number of correct significant digits. (The difference p - a is sometimes called scale or order of magnitude of a number.) Here are the most important cases. For brevity, let p stand for precision and a for accuracy. † 0 < a < p. These numbers have the form: d1 d2 … ds .ds+1 ds+2 … d p . Here, a + s = p. † a ¥ p > 0. These numbers have the form: 0.000000 … 0000000 d1 d2 … d p . † a < 0 and p > 0. These numbers have the form: d1 d2 … d p 000000 … 000000.0. † p § 0. These numbers have no guaranteed digits at all. (For this case, a can be positive or negative.) But nevertheless, a indicates the order of magnitude of the number. Numbers with a negative precision are automatically converted to numbers with the corresponding (negative or positive) accuracy. In Mathematica, the concepts accuracy and precision are realized by the two functions Precision and Accuracy.
Numerical Computations
24
Precision[number] gives the precision of the number number (the total approximative number of significant digits in number). Accuracy[number] gives the accuracy of the number number (the approximative number of significant digits to the right of the decimal point in number).
In general, the accuracy and precision of a number are not integers; so the above rough definitions apply after appropriate rounding. (There are two reasons for this: 1) a number is internally represented through bits and one bit is a fraction of a decimal digit and 2) when doing an arithmetic operation the error estimation might involve a fraction of a bit.) The following definitions of precision and accuracy are mathematically stricter. Suppose the representation of a number x has an error of size e. Then, the accuracy of x ≤ e ê 2 is defined to be -log10 †e§, and its precision is defined by -log10 †e ê x§ (be aware that for x = 0, this definition has a singularity). Reverting these definitions, we can say that a number z with accuracy a and precision p will lie with certainty in the interval Hx - 10-a ê 2, x + 10-a ê 2L = Hx - x 10-p ê 2, x + x 10-p ê 2L (a distinction between open and closed intervals is not useful for approximative numbers). The next examples involve Precision and Accuracy. We make use of a function that produces a two-element list containing the precision and the accuracy for each input number. In[43]:=
[x_] := {Precision[x], Accuracy[x]}
Here is a number with machine precision, that corresponds to the second case above, and so has an accuracy greater than its precision. In[44]:= Out[44]=
[0.000123] 8MachinePrecision, 19.8647<
Here is a longer number (it is not a high-precision number, despite the many zeros that are input to the right of the decimal point). In[45]:= Out[45]=
With[{x = 0.000000000000000000000000000000000000000000123}, {[x], MachineNumberQ[x]}] 88MachinePrecision, 58.8647<, True<
Here are a total of approximately $MachinePrecision digits after the first nonzero digit is recognized. So the second of the next numbers is a high-precision number. In[46]:= Out[46]=
{[0.00000123], [0.00000123000000000000000000000000000000000]} 88MachinePrecision, 21.8647<, 835.0899, 41.<<
Explicit zeros at the end of a number (after the decimal point) are kept. Here is a machine number. In[47]:= Out[47]=
[123.45] 8MachinePrecision, 13.8631<
Here is a comparison with a high-precision number. In[48]:= Out[48]=
[123.45600000000000000000000000000000000000000000000] 849.0915, 47.<
In the next example, we get a negative accuracy because 45 is larger than is $MachinePrecision and shows that we have uncertain digits to the left of the decimal point.
1.1 Approximate Numbers In[49]:= Out[49]=
25
[3.56 10^45] 8MachinePrecision, −29.5969<
According to the above definitions, the following relation holds between the precision and the accuracy of a real number x: precisionHxL = accuracyHxL + log10 H†x§L (the quantity log10 H†x§L is sometimes called the scale of the number x; available as the experimental function Experimental`$NumberScale[ x]). Here are some examples demonstrating this identity. In[50]:=
Out[50]=
Function[x, {Precision[x], Accuracy[x] + Log[10, Abs[x]]}] /@ (* numbers of different accuracy *) {N[1, 100], N[10^100, 30], N[2^-300, 40], N[10^20, 3000]} 88100., 100.<, 830., 30.<, 840., 40.<, 83000., 3000.<<
Here are two examples for numbers with zero precision. The accuracy of such numbers can be positive or negative. In[51]:=
Out[51]= In[52]:=
Out[52]=
With[{x = (* a high precision zero with positive accuracy arising from subtraction *) N[10^10, 50] - N[10^10, 50]}, {x, [x]}] 80. × 10−40 , 80., 39.699<< With[{x = (* a high precision zero with negative accuracy arising from subtraction *) N[10^100, 50] - N[10^100, 50]}, {x, [x]}] 80. × 1050 , 80., −50.301<<
The precision and accuracy can also be defined for complex numbers. Here is a complex number that is a machine number. In[53]:= Out[53]=
[2.1 + 7.8 I] 8MachinePrecision, 15.0473<
A complex number is considered to be of high precision if the real and imaginary parts are of high precision. In[54]:= Out[54]= In[55]:= Out[55]= In[56]:= Out[56]=
[2.1 + 7.80000000000000000000000022222222222222222 I] 8MachinePrecision, 15.6324< [2.1 + 7.8`4 I] 8MachinePrecision, 3.10791< [2.10000000000000000000000000004444444444444444444 + 7.80000000000000000000000022222222222222222222222 I] 847.7568, 46.8495<
For complex numbers, the values of Precision and Accuracy are determined in the following way: If z = Hx ≤ ex ê 2L + iHy ≤ e y ê 2L, the uncertainty of z is determined by the size of the real and imaginary parts as well as by their precision. Here are some typical situations for Accuracy and Precision of some complex numbers composed from real and imaginary numbers of different values for accuracy and precision. (We give a detailed form in a moment.) In the next input, a dominates the precision and the accuracy of the sum. In[57]:= Out[57]=
/@ {a = N[3 10^35, 50], b = N[I 3 10^-35, 20], a + b} 8850., 14.5229<, 820., 54.5229<, 850., 14.5229<<
In the next input, a again determines the precision and the accuracy of the result.
Numerical Computations
26 In[58]:= Out[58]=
/@ {a = N[3 10^35, 20], b = N[I 3 10^-35, 50], a + b} 8820., −15.4771<, 850., 84.5229<, 820., −15.4771<<
Now, both numbers a and b influence the precision of the result. In[59]:= Out[59]=
/@ {a = N[3 10^35, 120], b = N[I 3 10^-35, 30], a + b} 88120., 84.5229<, 830., 64.5229<, 8100., 64.5229<<
The following two pictures show the precision and the accuracy of a + i b as a function of the precision of a and b. We use again the values a = 3 μ 1035 and b = 3 μ 10-35 . In[60]:=
With[{pMax = 120}, Show[GraphicsArray[ ListPlot3D[(* the real and imaginary parts of different sizes and precisions *) Table[#[N[3 10^35, precA] + I N[3 10^-35, precB]], {precB, 1, pMax}, {precA, 1, pMax}], PlotRange -> All, MeshRange -> {{1, pMax}, {1, pMax}}, Mesh -> False, DisplayFunction -> Identity, AxesLabel -> {"Precision[a]", "Precision[b]", None}, PlotLabel -> StyleForm[ToString[#] <> "[a + I b]", FontFamily -> "Courier"]]& /@ (* accuracy and precision *) {Precision, Accuracy}]]] Precision@a + I bD
Precision@aD
In the next two pictures, we fix the precision of the summands and then plot the precision and the accuracy of the sum as a function of the size of the real and imaginary parts. We vary the size of the summands on an exponential scale. Obviously, the precision is constant in this case and the accuracy is symmetric in the two summands. In[61]:=
With[{ = 100, pp = 61}, Show[GraphicsArray[ ListPlot3D[(* the real and imaginary parts of different sizes and precisions *) Table[#[N[10^k, 30] + I N[10^l, 30]], {l, -, , 2/pp}, {k, -, , 2/pp}], DisplayFunction -> Identity, PlotRange -> All, AxesLabel -> {"10^k", "10^l", None}, MeshRange -> {{-100, 100}, {-100, 100}}, Mesh -> False, DisplayFunction -> Identity, AxesLabel -> {"10^k", "10^l", None}, Mesh -> False, PlotLabel -> StyleForm[ToString[#] <> "[a + I b]", FontFamily -> "Courier"]]& /@ (* accuracy and precision *) {Precision, Accuracy}]]]
1.1 Approximate Numbers
27 Accuracy@a + I bD
Precision@a + I bD
30 100
30 50
30 -100 0
0 10^l
-50
-100 0
-50
0 10^k
100 50 0 -50
100 50 0 10^l
-50 0 10^k
50 100
-50
-100
50 100
-100
The real and the imaginary parts of a complex number of a given precision might have different precisions than does the number itself. This happens when the magnitudes of the real and the imaginary parts are (greatly) different. In[62]:= Out[62]=
With[{ = Precision}, {[#], [Re[#]], [Im[#]]}&[ (* the complex number *) N[1, 20] + N[10^10, 40] I]] 830., 20., 40.<
Here is the “same” number with the real and imaginary parts having the same precision. In[63]:= Out[63]=
With[{p = Precision}, {p[#], p[Re[#]], p[Im[#]]}&[ N[1, 40] + N[10^10, 40] I]] 840., 40., 40.<
The two last numbers are indeed considered as identical by Mathematica. In[64]:= Out[64]=
N[1, 20] + N[10^10, 40] I === N[1, 40] + N[10^10, 40] I True
The following plot shows the precision of the real and imaginary parts for different ratios of the real and imaginary parts. (We will discuss the function SetPrecision in a moment.) In[65]:=
With[{p = Precision}, Show[Graphics[Line /@ Transpose[ Table[({i, #}& /@ {p[Re[#]], p[Im[#]]}&)[ (* make an approximate number with precision 6 *) SetPrecision[SetPrecision[1 + I 10^i, 6], 6]], {i, -10, 10, 1/20}]]], Frame -> True]] 6 5 4 3 2 1 0 -10
-5
0
5
10
The accuracy of a complex number z = x + i y, x and y being real, is defined in the following way: accuracyHzL = -1 ê 2 log10 H10-2 accuracyHxL + 10-2 accuracyHyL L. Here is a randomly chosen example demonstrating this formula.
Numerical Computations
28 In[66]:=
Out[66]=
With[{x = N[Pi^300, 120], y = N[E^-234, 400]}, {Accuracy[x + I y], N[-Log[10, 10^(-2 Accuracy[x]) + 10^(-2 Accuracy[y])]/2]}] 8−29.145, −29.145<
The precision of a complex number z = x + i y, x and y being real, is defined in the following way: precisionHzL = accuracyHzL+log10 H†z§L. This is the exact equivalent of the corresponding formula for real numbers. Here is a randomly chosen example demonstrating this formula. In[67]:=
Out[67]=
With[{x = N[(E/Pi)^333, 300], y = N[(Pi/E)^555, 600]}, {Precision[x + I y], Accuracy[x + I y] + N[Log[10, Abs[x + I y]]]}] 8355.816, 355.816<
Now, we continue with infinite precision and accuracy values. For exact numbers and mathematical constants, the result of either Precision or Accuracy is Infinity. Here are some examples. In[68]:= Out[68]=
{[3], [3/7], [3 + 4/5 I], [E]} 88∞, ∞<, 8∞, ∞<, 8∞, ∞<, 8∞, ∞<<
For any exact quantity with the head Symbol (or String), we get the following accuracy and precision. In[69]:= Out[69]=
{[Sin], [veryInexactNumber], ["aStringHasNoPrecision"]} 88∞, ∞<, 8∞, ∞<, 8∞, ∞<<
Also, Infinity and Indeterminate are exact quantities. In[70]:= Out[70]=
{[Infinity], [Indeterminate]} 88∞, ∞<, 8∞, ∞<<
Looking at the Precision and Accuracy of 0.0 and related quantities shows the following values. (We mentioned the singularity in the definition.) In[71]:= Out[71]=
{[0], [0.0], [0``50]} 88∞, ∞<, 8MachinePrecision, 307.653<, 80., 50.<<
Note that N[0] gives 0.0, but that N[0, precisionOtherThanMachinePrecisionToken] stays as an exact 0. The numericalization with N happens to allow packing of lists of numbers (see below) and the nonnumericalization with N[…, prec] happens because it is not possible to give any nontrivial digit for 0. In[72]:= Out[72]=
{N[0, MachinePrecision], N[0, $MachinePrecision]} 80., 0<
In carrying out a numerical calculation, we may lose precision. Here is an example in which we iterate f HxL = Hx2 - 1L ê Hx - 1L - 1 ( = x up to numerical precision loss) [1953]. In[73]:=
Block[{i = -1}, Nest[(* print out current precision of the result *) i = i + 1; (CellPrint[Cell["Î Iteration: " <> ToString[i] <> " Precision: " <> ToString[Precision[#]] <> " Accuracy: " <> ToString[Accuracy[#]], "PrintText"]]; (#^2 - 1)/(# - 1) - 1)&, N[2, 200], 12]]
1.1 Approximate Numbers
29
ë Iteration: 0 Precision: 200. Accuracy: 199.699 ë Iteration: 0 Precision: 199.155 Accuracy: 198.854 ë Iteration: 0 Precision: 198.31 Accuracy: 198.009 ë Iteration: 0 Precision: 197.465 Accuracy: 197.164 ë Iteration: 0 Precision: 196.62 Accuracy: 196.319 ë Iteration: 0 Precision: 195.775 Accuracy: 195.473 ë Iteration: 0 Precision: 194.929 Accuracy: 194.628 ë Iteration: 0 Precision: 194.084 Accuracy: 193.783 ë Iteration: 0 Precision: 193.239 Accuracy: 192.938 ë Iteration: 0 Precision: 192.394 Accuracy: 192.093 ë Iteration: 0 Precision: 191.549 Accuracy: 191.248 ë Iteration: 0 Precision: 190.704 Accuracy: 190.403 Out[73]=
In[74]:= Out[74]=
2.0000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000 [%] 8189.859, 189.558<
If we carry this calculation still further, we end up with less than one correct digit. In[75]:= Out[75]= In[76]:= Out[76]=
Nest[(#^2 - 1)/(# - 1) - 1&, N[2, 200], 236] 2. [%] 80.556863, 0.255833<
Performing still one more iteration yields 0. In[77]:= Out[77]= In[78]:= Out[78]=
Nest[(#^2 - 1)/(# - 1) - 1&, N[2, 200], 237] 0. [%] 80., −0.589265<
The appearance of 0 as the precision and accuracy here indicates a potential problem for the reliability of the calculation carried out. And performing one more iteration step results in the value Indeterminate. This result arises from the uncertainty of the last zero that is occurs in a denominator. In[79]:=
Nest[(#^2 - 1)/(# - 1) - 1&, N[2, 200], 238] 1 Power::infy : Infinite expression encountered. More… 0. ∞::indet : Indeterminate expression 0. × 101 ComplexInfinity encountered. More…
Out[79]=
Indeterminate
Of course, the opposite can also happen, and one can gain precision in a calculation. To gain precision the function value should not change much when the argument changes. Here is a simple example. In[80]:=
var1 = N[999999/100000, 20]; Precision[var1]
Numerical Computations
30 Out[81]= In[82]:= Out[82]=
20. 1 - Exp[-var1^2] // Precision 61.1283
Here is a still simpler example, involving just the addition of an exact number. In[83]:= Out[83]=
{Precision[N[10^-30, 100]], Precision[1 + N[10^-30, 100]]} 8100., 130.<
Now let us deal with calculating out mathematical functions of high-precision numbers. When carrying out an arithmetic operation, Mathematica keeps track of the precision of the result. So, how exactly does this happen? Let f be the (smooth) univariate function under consideration and dx be the (nonnegative) uncertainty of the dx ÅÅ . argument x. This means the argument is known as x ≤ dx. Then, the precision of the input is given as -log10 ÅÅÅÅ x ∑ f HxL Å ÅÅÅ Å dx•. From this follows that the precision The uncertainty of the output df can then be approximated as d f º ° ÅÅÅÅÅÅÅÅ ∑x ∑ f HxL x dx of the output -log10 ÅÅÅÅfdÅHxL ÅÅÅf ÅÅ can be approximated as -log10 ÅÅÅÅfdÅHxL ÅÅÅf ÅÅ º -log10 I° ÅÅÅÅÅÅÅÅ ÅÅÅÅÅ ÅÅÅÅf ÅHxL ÅÅÅÅÅ • ÅÅÅÅ ÅÅ M. The condition number ∑x x ∑ f HxL x ÅÅÅÅÅ ÅÅÅÅf ÅHxL ÅÅÅÅÅ • is what determines the precision of the result. This quantity can be less or greater than unity, and as a ° ÅÅÅÅÅÅÅÅ ∑x result, the precision of the output can be smaller or larger than the precision of the input x. Here is a plot of the precision of sinHxL as a function of x and the precision of x. The right graphic visualizes the ∑ f HxL x dx above precision formula f HxL º -log10 I° ÅÅÅÅÅÅÅÅ ÅÅÅÅÅ ÅÅÅÅf ÅHxL ÅÅÅÅÅ • ÅÅÅÅ ÅÅ M, and we obtain the same picture. ∑x x In[84]:=
Show[GraphicsArray[ Block[{$DisplayFunction = Identity}, {(* left: measure precision of the expression *) Plot3D[Precision[Sin[SetPrecision[x, p]]], {x, 1/2, 3}, {p, 1, 20}, Compiled -> False, PlotPoints -> 41], (* right: model precision of the expression *) Plot3D[-Log[10, Abs[Cos[x] x/Sin[x] 10^-p]], {x, 1/2, 3}, {p, 1, 20}, Compiled -> False, PlotPoints -> 41]}]]]
20 15 10 5 0 0.5 5
20 15 10 1
20 15 10 5 0 0.5 5
20 15 10 1
1.5
5
2 2.5
1.5
5
2 2.5
3
3
The small difference between the precision of Precision[Sin[SetPrecision[ x, p]]] and -Log[10, Abs[Cos[x] x/Sin[x] 10^-p]] is practically zero. It is caused by the internal numerical methods used to calculate the sine function. Here we show the difference between the actual precision and the calculated precision for a fixed p and varying x. The agreement is excellent. In[85]:=
With[{p = 2 $MachinePrecision}, Plot[Precision[Sin[SetPrecision[x, p]]] + Log[10, Abs[Cos[x] x/Sin[x] 10^-p]], {x, -10, 10}, Compiled -> False, PlotPoints -> 41, PlotRange -> All, Frame -> True, Axes -> False]]
1.1 Approximate Numbers
31
1 μ 10-14 5 μ 10-15 0 -5 μ 10-15 -1 μ 10-14 -10
-5
0
5
10
∑ f HxL Observe the small peak around x = p ê 2 in the above 3D graphic. Here, ÅÅÅÅÅÅÅÅ ÅÅÅÅÅ vanishes for f HxL = sinHxL, and the ∑x precision has a singularity. The next picture shows in detail the region x º p ê 2 for a fixed precision. In[86]:=
With[{δ = 10^-1, prec = 50}, ListPlot[Table[{x, Precision[Sin[SetPrecision[Pi/2 + x, prec]]]}, {x, -δ, δ, 2δ/1001}], PlotRange -> All, PlotJoined -> True, Frame -> True, Axes -> False]] 53.5 53 52.5 52 51.5 51 50.5 -0.1
-0.05
0
0.05
0.1
The formula for determining the precision of the output is based on the first-order Taylor series of f HxL. This dx ÅÅ ` 1. For arguments of f HxL with a small precision, means that this precision model will be accurate for ÅÅÅÅ x Mathematica’s high-precision arithmetic might give either too pessimistic or even wrong results. In the following picture, the two blue lines bound sinHoneL, where one is 1 with precision p, one = 1 ≤ 10-p as a function of the precision p. The red lines represent the exact bound for sinHoneL. They are obtained using interval arithmetic; see below. For precision greater than two, there is virtually no difference between the two curves anymore. In[87]:=
Show[GraphicsArray[ {#, (* magnification for larger values of p *) MapAt[(PlotRange -> {{2, 3}, {0.836, 0.846}})&, #, {-1}]}&[ With[{(* value and function *) one = 1, = Sin}, Graphics[{{PointSize[0.008], Hue[0], (* exact interval arithmetic in red *) Table[Point[{p, #}]& /@ [Interval[one + 1/2 10^-p {-1, 1}]][[1]], {p, 10^-2, 3, 10^-2}]}, {PointSize[0.004], Hue[0.7], (* high-precision arithmetic in blue *) Table[{Point[{p, [one] - #}], Point[{p, [one] + #}]}&[ (* estimated uncertainty of [one] *) [one] 1/2 10^-Precision[[SetPrecision[one, p]]]], {p, 10^-2, 3, 10^-2}]}} // N, Frame -> True, PlotRange -> All]]]]]
Numerical Computations
32 1.1
0.846
1
0.844
0.9 0.842 0.8 0.84
0.7
0.838
0.6 0.5 0
0.5
1
1.5
2
2.5
2.2
3
2.4
2.6
2.8
3
Now, let us come back to the Weierstrass ƒ calculation from above. The iterated expressions contain many occurrences of z. Here is the result after the first iteration. In[88]:=
weierstrass℘List[z, {2, 3}, 1][[-1]] 2
Out[88]=
4
2 2
4 z 3z J−1 + 6 I + + M N 4 z2 3 z4 z2 40 448 −2 J 2 + + N + 2 4 z 40 448 4 z 3z 4 z2 3 z4 3 4 I−3 − 2 I + + M + 4 I + + M M z2 40 448 z2 40 448
Substituting an approximative value for z and evaluating the above iteration effectively treats every z as a different number. As a result, we see the loss of nine digits in the following input. In[89]:= Out[89]=
{#, Precision[#]}& @ weierstrass℘List[N[2, 30], {2, 3}, 5][[-1]] 86.44554935396550836559, 20.9498<
A more careful analysis that identifies all of the z occurring in the expression yields a much smaller loss of precision (the symbolic expression D[, z] in the following input in quite large, its size is about 5 MB). In[90]:= Out[91]=
= Last[weierstrass℘List[z, {2, 3}, 5]]; D[, z] z/ δz/z /. {z -> 2, δz -> 10^-30} // N[#, 22]& 5.039811020046271555398× 10−30
The precision model described, generalizes in an obvious way to multivariate functions. Let, for instance, f be a bivariate function of x and y and dx, dy be the uncertainties of the arguments. The uncertainty of the output df ∑ f Hx,yL ∑ f Hx,yL can then be approximated as df º ° ÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅ • dx +° ÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅ • dy. From this follows that the precision of the output ∑x ∑y df df ∑ f Hx,yL ∑ f Hx,yL y dy x dx Å ÅÅÅ Å Å can be approximated as -log ÅÅÅÅÅÅÅÅ Å ÅÅÅ Å ÅÅ º -log10 I° ÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅ • ÅÅÅÅ ÅÅ +° ÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅ • ÅÅÅÅ ÅÅ M. Here are two -log10 ÅÅÅÅÅÅÅÅ 10 f Hx,yL f Hx,yL ∑x f Hx,yL ∑y f Hx,yL x y examples of a built-in bivariate function—arctan and log. In[92]:=
modelPrecision[f_, {x_, y_}, {x0_, y0_}] := (* form symbolic derivatives *) (Abs[D[f[x, y], x] x/f[x, y] 10^-px] + Abs[D[f[x, y], y] y/f[x, y] 10^-py]) /. (* substitute actual values *) {x -> x0, px -> Precision[x0], y -> y0, py -> Precision[y0]} // N // -Log[10, #]&
In[93]:=
Function[{x0, y0}, {Precision[ArcTan[x0, y0]], modelPrecision[ArcTan, {x, y}, {x0, y0}]}][ SetPrecision[10^-50, 20], SetPrecision[2, 30]] 870.4971, 70.4971<
Out[93]=
Expressing arctan through logarithm and square root gives a lower precision because of the repeated (independent) appearance of x and y. (We use the function TrigToExp, to be discussed in Chapter 1 of the Symbolics volume [1795], to rewrite ArcTan in logarithms.) In[94]:=
f = Function[{x, y}, Evaluate[TrigToExp[ArcTan[x, y]]]]
1.1 Approximate Numbers Out[94]= In[95]:=
Out[95]=
33
x+y FunctionA8x, y<, − LogA EE è!!!!!!!!!!!!!!!! x2 + y2 Function[{x0, y0}, {Precision[f[x0, y0]], modelPrecision[f, {x, y}, {x0, y0}]}][ SetPrecision[10^-50, 20], SetPrecision[2, 30]] 829.8951, 70.4971<
Here is the corresponding univariate version, which for x ∫ 0, allows expressing the bivariate arctan. In[96]:=
Out[96]=
With[{f = Function[{x, y}, ArcTan[y/x]]}, Function[{x0, y0}, {Precision[f[x0, y0]], modelPrecision[f, {x, y}, {x0, y0}]}][ SetPrecision[10^-50, 20], SetPrecision[2, 30]]] 870.4971, 70.4971<
As the second bivariate function, we will choose the two-argument form of Log. Again, the modeled and the actual precision agree. In[97]:=
Out[97]=
Function[{x0, y0}, {Precision[Log[x0, y0]], modelPrecision[Log, {x, y}, {x0, y0}]}][ SetPrecision[10^50, 10], SetPrecision[2 10^60, 12]] 812.0576, 12.0576<
The precision of multivariate functions with more than two arguments arise as obvious generalizations of the described model. The precision of (nested) expression is the minimal precision of all of its subparts. Here are two examples. In the second example, the two high-precision zeros result in precision 0.. In[98]:= Out[98]=
{Precision[[1.`20, 1.`30, 1.`40]], Precision[[0``50, 0``20, 1]]} 820., 0.<
The accuracy of an expression is the minimal accuracy of all of its subparts. In[99]:= Out[99]=
{Accuracy[ @@ Table[N[10^-k, l], {k, 20, 50, 10}, {l, 20, 50, 10}]], Accuracy[N[10^-20, 20]]} 840., 40.<
Let us make the following definition for f. We give f the NumericFunction attribute. In[100]:=
Remove[f] SetAttributes[f, NumericFunction] f[x_?InexactNumberQ] := Abs[x]
Adding a real number to f[I] results in one real number for the whole expression (be aware that f has never been explicitly called with an inexact argument; but internally it is called with an approximative argument). In[103]:= Out[103]=
2. + f[I] 3.
The numericalization happens because f[I] is considered as a numeric expression. In[104]:= Out[104]=
f[I] // NumericQ True
In the next input, the precision of the inexact number N[2, 22] is used for the numericalization of f[I].
Numerical Computations
34 In[105]:= Out[105]=
N[2, 22] + f[I] 3.000000000000000000000
Now, using the functions Precision and Accuracy we can quantify a previous remark concerning precision loss in calculations. Because a loss of precision can happen in numerical calculations (starting with an inexact number), Mathematica varies the Precision of the resulting numbers depending on the numerical operation being performed. Precisions produced in longer calculations by Mathematica are usually too pessimistic (which is better than overly optimistic ones or no estimates at all). In most calculations, one typically loses precision. Here, this is shown for nesting the tangent function. In[106]:=
ListPlot[Precision /@ NestList[Tan, N[10 + 1I, 20], 100]] 20 18 16 14 12 10
20
40
60
80
100
In the following repeated application of the cosine function and the square root function, one gains precision. (The behavior depends on the starting values.) In[107]:=
Show[GraphicsArray[ Block[{$DisplayFunction = Identity}, {(* iterated cosinus function *) ListPlot[Precision /@ NestList[Cos, N[Pi/2 +10^-5, 20], 100]], (* iterated square root function *) ListPlot[Precision /@ NestList[Power[#, 1/2]&, N[10 + 1 I, 20], 100]]}]]] 40
20
35
19
30 18 25 17 20
20
40
60
80
100
20
40
60
80
100
We now give some additional examples in which precision and accuracy are varied (and not just reduced) during a numerical calculation. (We use the following values a = 10-10 with precision 10, b = 1010 with precision 10, one = 1 with precision 10, mOne = 1 as a machine number, and zero = 0 to accuracy 10.) The second element of the returned list is again the list with the precision and accuracy of the result. (We encourage the reader to have a careful look at each row of the following table.) In[108]:=
Block[{zero, one, mOne, a, b}, (* the values for zero, one, mOne, a, b *)
1.1 Approximate Numbers
35
zero = SetAccuracy[0, 10]; one = SetPrecision[1, 10]; mOne = 1.0; a = SetPrecision[10^-10, 10]; b = SetPrecision[10^+10, 10]; {#[[1]], #[[2]], {Precision[#[[2]]], Accuracy[#[[2]]]}}& /@ Transpose[{#, ToExpression[#]}&[ (* all expressions to be investigated *) {"a + b", "a - b", "a/b", "b/a", "a^b", "b^a", "1 + a", "1 + b", "1/a", "1/b", "1^a", "1^b", "mOne + a", "mOne + b", "mOne/a", "mOne/b", "mOne^a", "mOne^b", "one + a", "one + b", "one/a", "one/b", "one^a", "one^b", "zero + a", "zero + b", "zero/a", "zero/b", "zero^a", "zero^b", "Sin[a]", "Sin[b]"}]]] // (* make a table *) TableForm[#, TableDepth -> 1]& General::unfl : Underflow occurred in computation. More… Out[108]//TableForm=
8a + b, 1.0000000000× 1010 , 810., 0.<< 8a − b, −1.0000000000× 1010 , 810., 0.<< 8aêb, 1.000000000× 10−20 , 89.69897, 29.699<< 8bêa, 1.000000000× 1020 , 89.69897, −10.301<< 8a^b, Underflow@D, 80., ∞<< 8b^a, 1.000000002302585096, 818.6193, 18.6193<< 81 + a, 1.00000000010000000000, 820., 20.<< 81 + b, 1.000000000× 1010 , 810., 0.<< 81êa, 1.0000000000× 1010 , 810., 0.<< 81êb, 1.0000000000× 10−10 , 810., 20.<< 81^a, 1, 8∞, ∞<< 81^b, 1, 8∞, ∞<< 8mOne + a, 1., 8MachinePrecision, 15.9546<< 8mOne + b, 1. × 1010 , 8MachinePrecision, 5.95459<< 8mOneêa, 1. × 1010 , 8MachinePrecision, 5.95459<< 8mOneêb, 1. × 10−10 , 8MachinePrecision, 25.9546<< 8mOne^a, 1., 8MachinePrecision, 15.9546<< 8mOne^b, 1., 8MachinePrecision, 15.9546<< 8one + a, 1.000000000, 810., 10.<< 8one + b, 1.000000000× 1010 , 810., −4.34292 × 10−11 << 8oneêa, 1.000000000× 1010 , 89.69897, −0.30103<< 8oneêb, 1.000000000× 10−10 , 89.69897, 19.699<< 8one^a, 1.00000000000000000000, 820., 20.<< 8one^b, 0., 84.27789 × 10−15 , −0.434294<< 8zero + a, 0. × 10−10 , 80., 10.<< 8zero + b, 1.000000000 × 1010 , 810., −4.34292 × 10−11 << 8zeroêa, 0., 80., 0.<< 8zeroêb, 0. × 10−20 , 80., 20.<< 8zero^a, 0. × 10−1 , 80., 1. × 10−9 << 8zero^b, 0. × 10−20201778, 80., 2.02018 × 107 << 8Sin@aD, 1.0000000000× 10−10 , 810., 20.<< 8Sin@bD, 0., 80., 0.<<
It is a general rule that the high-precision arithmetic is used whenever 1) the numbers that appear in the expression to be calculated are already high-precision numbers, 2) if the use of high-precision numbers is explicitly
Numerical Computations
36
forced via N[expr, prec] or 3) via the WorkingPrecision option of the N-functions such as FindRoot, NSolve, …. So, for instance, in N[Sin[2.0], 200], the 2.0 is only a machine number for which digits beyond $MachinePrecision are not known, and so it is impossible to generate a meaningful 200-digit result for N[Sin[2.0], 200]. Consequently, every time we want a high-precision result, we must take care that the starting expression has sufficient precision (typically, we will only convert exact symbolic expressions, such as integers, algebraic numbers, or transcendental constants into high-precision numbers). One exception to this rule is the use of high-precision arithmetic in expressions whose magnitude is becoming bigger than $MaxMachineNumber. In this case, Mathematica automatically switches to high-precision numbers. Here, i this is visualized. We display the precision of 1010 as a function of i. In[109]:=
ListPlot[Function[i, {i, Precision[10.^(10^i)]}] /@ Table[i, {i, 0, 8.5, 0.01}], Frame -> True, PlotStyle -> {PointSize[0.005]}, PlotRange -> All, Axes -> False, FrameLabel -> (StyleForm[#, FontFamily -> "Courier"]& /@ {"Log[10, Log[10, x]]", "Precision[x]"}), (* vertical line *) GridLines -> {{Log[10, Log[10, $MaxMachineNumber]] // N}, None}] 16
Precision@xD
14
12
10 8 0
2
4 6 Log@10, Log@10, xDD
8
We clearly see the decrease in Precision[x] around Log[10, Log[10, $MaxMachineNumber]]. In[110]:= Out[110]=
Log[10, Log[10, $MaxMachineNumber]] 2.48891
Frequently, we would like to change the accuracy and/or the precision of a number (or a larger expression) in the course of a calculation. This can be accomplished as follows. (We used the function SetPrecision already repeatedly.) SetAccuracy[expression, accuracy] replaces all numbers and constants appearing in expression by their numerical values with accuracy accuracy. If this requires an increase in accuracy, zeros will be added in base 2 after using any digits in the internal representation, if present. The resulting number is a high-precision number, even if the number of digits requested is smaller than the machine precision. If accuracy is Infinity, all approximate numbers in expression will be converted to exact numbers. SetPrecision[expression, precision] replaces all numbers and constants in expression by their numerical values with precision precision. If the precision is increased, zeros will be introduced in base 2 after using any digits in the internal representation, if present. The resulting number is a high-precision number, even if the required number of digits is smaller than the machine precision. If precision is Infinity, all approximate numbers in expression will be converted to exact numbers.
Because the functions SetPrecision and SetAccuracy operate on the numbers inside an expression and the resulting approximate numbers might cause further evaluations, the result returned might have a lower
1.1 Approximate Numbers
37
precision/accuracy than specified in the second argument of SetPrecision/SetAccuracy. The difference between N[expr, prec] and SetPrecision[expr, prec] is best demonstrated in case expr is numerically zero. N[expr, prec] will give a message because no reliable digit can be calculated. SetPrecision[expr, prec] just replaces the numbers inside with approximate ones, and then the expression evaluates. In[111]:=
zero = Sin[Pi/16] - Sqrt[2 - Sqrt[2 + Sqrt[2]]]/2; N[zero, 2 MachinePrecision] N::meprec : Internal precision limit $MaxExtraPrecision = 50.` 1 "############################################################### π è!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # reached while evaluating − 2 − 2 + Power@2D + SinA E. More… 2 16
0. × 10−82
Out[112]= In[113]:= Out[113]=
{SetPrecision[zero, 2 MachinePrecision], SetAccuracy[ zero, 2 MachinePrecision]} 80. × 10−32 , 0. × 10−32 <
By avoiding the evaluation, we see the numericalized numbers and constants. In[114]:= Out[114]=
Hold[Evaluate[zero]] // SetPrecision[#, Round[MachinePrecision/2]]& HoldA−0.50000000
"########################################################################################################################## è!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 2.0000000 − 1.0000000 2.0000000 + 1.41421356 +
[email protected] 3.1415927DE
It follows from the definitions of precision and accuracy that the accuracy of a sum and the precision of a product depend only on the accuracy of the summands or the precision of the factors, but not from the precision of the summands or the accuracy of the factors. Here this is shown. (We use SetAccuracy and SetPreci sion to generate the numbers that are added and multiplied.) In[115]:=
Out[115]= In[116]:=
Out[117]= In[118]:=
Out[118]= In[119]:=
Out[120]=
With[{x = SetAccuracy[12478923 10^100, 12], y = SetAccuracy[ 848964 10^-30, 63]}, {Accuracy[x + y], Precision[x + y], Accuracy[x y], Precision[x y]}] 812., 119.096, −44.0962, 38.9289< (* same accuracy as last input; accuracy of sum is the same *) With[{x = SetAccuracy[12478923 10^140, 12], y = SetAccuracy[ 848964 10^-60, 63]}, {Accuracy[x + y], Precision[x + y], Accuracy[x y], Precision[x y]}] 812., 159.096, −84.0962, 8.92889< With[{x = SetPrecision[12478923 10^100, 12], y = SetPrecision[ 848964 10^-30, 63]}, {Accuracy[x + y], Precision[x + y], Accuracy[x y], Precision[x y]}] 8−95.0962, 12., −71.0251, 12.< (* same precision as last input; precision of product is the same *) With[{x = SetPrecision[12478923 10^150, 12], y = SetPrecision[ 848964 10^-80, 63]}, {Accuracy[x + y], Precision[x + y], Accuracy[x y], Precision[x y]}] 8−145.096, 12., −71.0251, 12.<
To check if a number is a machine number, we make use of MachineNumberQ. (There is no built-in function HighPrecisionNumberQ, but it is straightforward to implement such a function.) MachineNumberQ[number] is True if number is a number with machine precision, and is otherwise False.
Numerical Computations
38
We draw a picture, similar to the above one, to show switching to high-precision numbers by testing if a number is a machine number. Again, we see the switch at Log[10, Log[10, $MaxMachineNumber]]≈2.49. In[121]:=
ListPlot[Function[i, {i, If[MachineNumberQ[10.^(10^i)], 1, -1]}] /@ Table[i, {i, 0, 8.5, 0.01}], Frame -> True, PlotStyle -> {PointSize[0.01]}, PlotRange -> All, Axes -> False, FrameLabel -> {"Log[10, Log[10, x]]", None}] 1
0.5
0
-0.5
-1 0
2
4 6 Log@10, Log@10, xDD
8
We now start with an integer and manipulate its accuracy and precision. The function testNumber gives various information about the number number. In[122]:=
(* show the input form of the number, its precision, accuracy and if it is a machine number *) testNumber[number_] := {FullForm[number], Precision[number], Accuracy[number], MachineNumberQ[number]}
In[124]:=
testNumber[SetPrecision[5, 3]]
Out[124]= In[125]:= Out[125]= In[126]:= Out[126]= In[127]:= Out[127]=
85.`2.9999999999999973, 3., 2.30103, False< testNumber[SetPrecision[5, 30]] 85.`30., 30., 29.301, False< testNumber[SetAccuracy[5, 3]] 85.`3.6989700043360183, 3.69897, 3., False< testNumber[SetAccuracy[5, 30]] 85.`30.698970004336022, 30.699, 30., False<
We repeat the last four tests with real numbers. In[128]:= Out[128]= In[129]:= Out[129]= In[130]:= Out[130]= In[131]:= Out[131]=
testNumber[SetPrecision[5.0, 3]] 85.`2.9999999999999973, 3., 2.30103, False< testNumber[SetPrecision[5.0, 30]] 85.`30., 30., 29.301, False< testNumber[SetAccuracy[5.0, 3]] 85.`3.6989700043360183, 3.69897, 3., False< testNumber[SetAccuracy[5.0, 30]] 85.`30.698970004336022, 30.699, 30., False<
Here is what happens when we begin with a high-precision number. In[132]:= Out[132]= In[133]:=
testNumber[SetPrecision[5, 100]] 85.`100., 100., 99.301, False< testNumber[SetAccuracy[5, 100]]
1.1 Approximate Numbers Out[133]= In[134]:= Out[134]= In[135]:= Out[135]=
39
85.`100.69897000433602, 100.699, 100., False< testNumber[SetPrecision[N[5 10^34, 100], 10]] 85.`10.*^34, 10., −24.699, False< testNumber[SetAccuracy[N[5 10^34, 100], 10]] 85.`44.698970004336026*^34, 44.699, 10., False<
We can also make a high-precision number with $MachinePrecision. In[136]:= Out[136]=
MachineNumberQ[SetPrecision[1, $MachinePrecision]] False
But this does give a machine number. In[137]:= Out[137]=
MachineNumberQ[SetPrecision[1, MachinePrecision]] True
Using SetPrecision, we can avoid the difficulty in the above iteration (but this does not guarantee that the result is correct). In[138]:= Out[139]=
(* now "regain" lost precision in each step *) Nest[SetPrecision[(#^2 - 1)/(# - 1) - 1, 200]&, N[2, 200], 237] 2.0000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000
In[140]:=
[%]
Out[140]=
8200., 199.699<
Note that when doing a calculation with higher precision, it is better (for readability and speed) to use a construction like the following. (Now SetPrecision is automatically invoked when needed.) In[141]:= Out[141]=
In[142]:= Out[142]=
Block[{$MinPrecision = 200}, Nest[(#^2 - 1)/(# - 1) - 1&, N[2, 200], 237]] 2.0000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000 Precision[%] 200.
We reiterate that in such cases, one has to be aware that this causes the loss of any control over the precision of the result. Depending on its accuracy, a real 0 (head Real) will be output differently. In[143]:= Out[144]=
$MinPrecision = -100; Table[SetAccuracy[0, -i], {i, 10, -10, -1}] 80. × 1010 , 0. × 109 , 0. × 108 , 0. × 107 , 0. × 106 , 0. × 105 , 0. × 104 , 0. × 103 , 0. × 102 , 0. × 101 , 0., 0. × 10−1 , 0. × 10−2 , 0. × 10−3 , 0. × 10−4 , 0. × 10−5 , 0. × 10−6 , 0. × 10−7 , 0. × 10−8 , 0. × 10−9 , 0. × 10−10 <
Further, these zeros also explain the above iteration problem with the value 0 for the Precision. And this converts a real 0.0 to an exact one. In[145]:= Out[145]=
SetAccuracy[0.0, Infinity] 0
A nonzero number will always be represented in essentially the same way, assuming that it has some meaningful digits. For negative accuracy, we obtain an approximate zero with zero precision. Next, we use the numbers 10-10 , 1, and 1010 and numericalize these numbers to various accuracies.
Numerical Computations
40 In[146]:= Out[146]=
In[147]:= Out[147]=
In[148]:= Out[148]=
In[149]:= Out[149]=
Table[SetAccuracy[10^-10, -i], {i, 10, -10, -1}] 80. × 1010 , 0. × 109 , 0. × 108 , 0. × 107 , 0. × 106 , 0. × 105 , 0. × 104 , 0. × 103 , 0. × 102 , 0. × 101 , 0., 0. × 10−1 , 0. × 10−2 , 0. × 10−3 , 0. × 10−4 , 0. × 10−5 , 0. × 10−6 , 0. × 10−7 , 0. × 10−8 , 0. × 10−9 , 0. × 10−10 < Table[SetAccuracy[1, -i], {i, 10, -10, -1}] 80. × 1010 , 0. × 109 , 0. × 108 , 0. × 107 , 0. × 106 , 0. × 105 , 0. × 104 , 0. × 103 , 0. × 102 , 0. × 101 , 0., 1., 1.0, 1.00, 1.000, 1.0000, 1.00000, 1.000000, 1.0000000, 1.00000000, 1.0000000000< Table[SetAccuracy[10^+10, -i], {i, 10, -10, -1}] 80. × 1010 , 1. × 1010 , 1.0 × 1010 , 1.00 × 1010 , 1.000 × 1010 , 1.0000 × 1010 , 1.00000 × 1010 , 1.000000 × 1010 , 1.0000000 × 1010 , 1.00000000× 1010 , 1.0000000000× 1010 , 1.00000000000× 1010 , 1.00000000000× 1010 , 1.000000000000× 1010 , 1.0000000000000× 1010 , 1.000000000000000× 1010 , 1.0000000000000000× 1010 , 1.0000000000000000× 1010 , 1.000000000000000000× 1010 , 1.0000000000000000000× 1010 , 1.00000000000000000000× 1010 < {Precision /@ %, Accuracy /@ %} 880., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20.<, 8−10., −9., −8., −7., −6., −5., −4., −3., −2., −1., 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.<<
In the next input, we try to generate numbers with negative precision. Such numbers are converted to zero precision numbers with an accuracy corresponding to the intended negative precision. In[150]:= Out[150]= In[151]:= Out[151]= In[152]:= Out[152]=
SetPrecision[#, -3]& /@ {10^-10, Pi, Pi, 10^10 Pi} 80. × 10−7 , 0., 0., 0. × 1013 < Precision /@ % 80., 0., 0., 0.< Accuracy /@ %% 87., −0.49715, −0.49715, −13.4971<
The corresponding accuracy results from the above-mentioned identity precision - accuracy = scale. In[153]:=
Experimental`NumberScale /@ %%%
Out[153]=
8−7., 0.49715, 0.49715, 13.4971<
êêêêêêêê Here is an example involving the above-mentioned added binary zeros. In binary, 0.2 is equal to 0. 0011. (The command BaseForm[number, base] represents the number number in the number system with base base.) In[154]:=
BaseForm[0.200000000000000000000, 2]
Out[154]//BaseForm=
0.001100110011001100110011001100110011001100110011001100110011001100110 2
Thus, we have the following string of digits. (The nonzero digits start at $MachinePrecision + 1). In[155]:= Out[155]=
SetPrecision[0.2, 50] 0.20000000000000001110223024625156540423631668090820
The number produced by Mathematica is not 0.200000000000000000000000000000000000000000000000…. In base 2, this is the difference. We use RealDigits and BaseForm to display the bits. In[156]:=
{BaseForm[1/5 - %, 2], RealDigits[1/5 - %, 2]}
1.1 Approximate Numbers Out[156]=
41
8−1.10011001100110011001100110011001100110011001100110011001100110011001100110 01100110011001100110011001100110011012 × 2−57 , 881, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1<, −56<<
Here, we make use of SetPrecision with an exact number as its argument. In[157]:=
SetPrecision[1/5, 50] // FullForm
Out[157]//FullForm=
0.2`50.
The added binary zeros become visible only if all other bits of the number are already displayed. Here, this is visualized (for i ¥ 6, the result does not change anymore). (Here, we use the lower precision 6 to avoid lengthy outputs.) With increasing precision, we see the validated digits, the guard digits, and the base 2 zeros. In[158]:=
Table[SetPrecision[N[1/3, 6], 10 i], {i, 1, 7}] // TableForm
Out[158]//TableForm=
0.3333333333 0.33333333333333333332 0.333333333333333333315263297125 0.3333333333333333333152632971252415927665 0.33333333333333333331526329712524159276654245331883 0.333333333333333333315263297125241592766542453318834304809570 0.3333333333333333333152632971252415927665424533188343048095703125000000
The internal representation of N[1/3, 20] has slightly more than 20 correct digits. In[159]:= Out[159]=
N[SetPrecision[N[1/3, 20], Infinity] - 1/3, 20] 5.6468863150286689271× 10−22
We now use SetPrecision to elaborate on a remark from above concerning the precision of functions of high-precision numbers. If we mix high-precision numbers with different precisions in a calculation (assuming no machine numbers are involved and no function that raises the precision is present), the number with the smallest precision will typically dominate the precision of the result. In[160]:= Out[160]= In[161]:= Out[161]=
(SetPrecision[1, 20] + SetPrecision[2, 40])^3 // Precision 20. (SetPrecision[1, 2] + SetPrecision[2, 4])^3 // Precision 1.9914
When one of the numbers is a machine number (that is smaller than $MaxMachineNumber), the result is also a machine number, independently of whether the precision of the high-precision number is greater or less than $MachinePrecision. In[162]:= Out[162]= In[163]:= Out[163]=
(1.0 + SetPrecision[2, 40])^3 // Precision MachinePrecision (1.0 + SetPrecision[2, 04])^3 // Precision MachinePrecision
If we use Infinity for the second variable of SetAccuracy or SetPrecision, the first argument will be converted to an exact expression. In view of the way in which approximate numbers are input, this does not always yield the “expected” result, because of the internal representation in base 2. (We search here for one of these numbers.)
Numerical Computations
42 In[164]:=
Out[166]=
(* find rational number such that numericalization and making and exact number are not idempotent *) For[i = 1, 1/i - SetPrecision[N[1/i], Infinity] === 0, i = i + 1, Null]; {1/i, SetPrecision[N[1/i], Infinity]} 1 6004799503160661 9 , = 3 18014398509481984
The two fractions are, however, almost equal. In[167]:= Out[167]= In[168]:= Out[168]=
N[Subtract @@ %, 50] 1.8503717077085942340393861134847005208333333333333× 10−17 RealDigits[%, 2] 881, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1<, −55<
Next, we repeat the last search for high-precision numbers. We use twice the precision of machine numbers. In[169]:=
Out[170]= In[171]:= Out[171]= In[172]:= Out[172]=
For[i = 1, 1/i - SetPrecision[N[1/i, 2 $MachinePrecision], Infinity] === 0, i = i + 1, Null]; {1/i, SetPrecision[N[1/i, 2$MachinePrecision], Infinity]} 1 108172851219475575594385340192085 9 , = 3 324518553658426726783156020576256 N[Subtract @@ %, 50] 1.0271626370065257882965215693786279032203208790482× 10−33 RealDigits[%, 2] 881, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1<, −109<
We now examine graphically the errors of several fractions generated by SetPrecision[N[i/j], Infin ity]. In[173]:=
ListPlot[Transpose[{#, (* make numerical and after that again exact *) 10^MachinePrecision N[SetPrecision[N[#], Infinity]& /@ # - #]}], (* setting all options for the plot *) PlotStyle -> {PointSize[0.01]}, AxesLabel -> {"n", None}, PlotLabel -> StyleForm[ "10^MachinePrecision N[SetPrecision[N[n], Infinity]", FontFamily ->"Courier", FontWeight -> "Bold", FontSize -> 6], GridLines -> {None, {0}}, PlotRange -> All, AxesOrigin -> {0, -1/2}, AspectRatio -> 1/4, (* a lot of fractions as ticks *) Ticks -> {{#, StyleForm[#, FontSize -> 5]}& /@ #, Automatic}]&[ (* the fractions to be investigated *) Union[Flatten[Table[j/i, {i, 16}, {j, i}]]]]
1.1 Approximate Numbers
43 10^MachinePrecision N@SetPrecision@N@nD, InfinityD 0.4 0.2 0 -0.2 -0.4 n 11111 1 1 1 1 2 1 2 1 23 1 3 2 3 1 43 2 3 45 1 ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅ 16 15 14 13 121110 9 8157 13 6 11 16 5 14913 4 15 11 7 1013 16 3
54 3 5 2 5 3 7 4 5 67 ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ 14 11 813 5 12 7169 1113 15
1 ÅÅÅÅ 2
87 6 5 9 4 7 3 8 5 79 ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ 15 13119167 12 5 138 11 14
2 119 7 5 811 3 10711 4 139 5 11 6137 8 91011 12 13 14 15 ÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ 3 16 1310 7 11 15 4 13914 5 16 11 6 13 7158 9 101112 13 14 15 16
1
The precision of variables (head Symbol) and of 0 (head Integer) cannot be altered with SetPrecision. In[174]:=
{#, Precision[#]}& @ SetPrecision[0, 12] 80, ∞<
Out[174]= In[175]:=
{#, Accuracy[#]}& @ SetAccuracy[abcxyz, 4] 8abcxyz, ∞<
Out[175]=
We now present an illustration of the idea of “a few more digits than required”. In this example, there are more than thirty 3’s in SetPrecision[SetPrecision[a, 30], 50]. In[176]:=
a = 1/3; a30 = SetPrecision[a, 30]; {N[a, 50], SetPrecision[a30, 50]} 80.33333333333333333333333333333333333333333333333333, 0.33333333333333333333333333333333333333333333333311<
Out[178]=
In[179]:=
Position[RealDigits[%[[2]]][[1]], _?(# =!= 3&)][[2]] 849<
Out[179]=
Analogously, if we reduce the precision, we do not lose so many digits. Indeed, a10 still has more than ten 3’s. In[180]:=
{N[a, 50], a10 = SetPrecision[a30, 10]; SetPrecision[a10, 50]} 80.33333333333333333333333333333333333333333333333333, 0.33333333333333333333333333332912607517215460370447<
Out[180]=
We can get knowledge of all (base 2) digits (the explicitly shown ones and the hidden ones) of a number in the form of their binary representation by using the undocumented function $NumberBits. $NumberBits[number] gives a list of the form {sign, listOfCorrectBits, listOfGuardBits, base2Exponent} of the approximate real number approximateRealNumber.
We put the context that contains the symbol $NumberBits on the context path. In[181]:=
??*`*NumberBits NumericalMath`$NumberBits Attributes@NumericalMath`$NumberBitsD = 8Listable, Protected<
In[182]:=
Out[183]=
(* in case the function "hides" in some context other than System` *) If[FreeQ[$ContextPath, #], AppendTo[$ContextPath, #]]& /@ Context /@ Union[Join[Names["*`*Number*Bits*"], Names["*`*Bits*Number*"]]] 88Global`, System`, NumericalMath`<<
Numerical Computations
44
Let us look at some examples. For a machine number, no precision analysis is done, and so the second list containing the correct digits is an empty list. In[184]:= Out[184]=
$NumberBits[-12.34] 8−1, 8<, 81, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0<, 4<
Here are two high-precision numbers. Now, the first list is nonempty, and the third list shows the guard digits in binary form, which are hidden in the ordinary output form. In[185]:= Out[185]=
In[186]:= Out[186]=
$NumberBits[SetPrecision[1/3, 4]] 81, 81, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1<, 80, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1<, −1< $NumberBits[SetPrecision[1/5, 40]] 81, 81, 0, 1, 0, 0, 1, 0, 0, 1, 81, 0, 0, 0,
1, 1, 1, 0, 1, 0, 1,
0, 0, 1, 1, 0, 1, 1,
0, 0, 0, 1, 0, 1, 0,
1, 1, 0, 0, 1, 0, 0,
1, 1, 1, 0, 1, 0, 1,
0, 0, 1, 1, 0, 1, 1,
0, 0, 0, 1, 0, 1, 0,
1, 1, 0, 0, 1, 0, 0,
1, 1, 1, 0, 1, 0, 1,
0, 0, 1, 1, 0, 1, 1,
0, 0, 0, 1, 0, 1, 0,
1, 1, 0, 0, 1, 0, 0,
1, 1, 1, 0, 1, 0, 1,
0, 0, 1, 1, 0, 1, 1,
0, 0, 0, 1, 0, 1, 0,
1, 1, 0, 0, 1, 0, 0,
1, 1, 1, 0, 1, 0, 1,
0, 0, 1, 1, 0, 1, 1,
0, 0, 0, 1, 0, 1, 0,
1, 1, 0, 0, 1, 0, 0,
1, 1, 1, 0, 1, 0, 1,
0, 0, 1, 1, 0, 1, 1,
0, 0, 0, 1, 0, 1, 0,
1, 1, 0, 1, 1, 0, 1, 0, 1<, 0, 0, 1, 1, 0, 1, 1<, −2<
The next input shows that for 1 with precision 10 one has to add about 2.3 μ 10-10 to change the last significant bit of this number. In[187]:=
Out[187]=
Block[{one = N[1, 10], k = 0}, (* add small fraction until last significant bit changes *) While[$NumberBits[one][[2]] === $NumberBits[one + k 10^-14][[2]], k++]; k] 23284
Let us now look graphically at the number of correct and guard digits stored in the number generated by SetPrecision[1/3, i]. In[188]:=
Show[GraphicsArray[ MapThread[ListPlot[#2, PlotLabel -> #1, AxesLabel -> {"i", None}, DisplayFunction -> Identity, PlotRange -> All, AxesOrigin -> {0, 0}, PlotRange -> All]&, (* labels *) {{"number of correct bits", "number of guard bits", "number of all bits"}, (* the data to visualize *) {Length /@ #[[2]], Length /@ #[[3]], Length /@ #[[2]] + Length /@ #[[3]]}&[ Transpose[Table[$NumberBits[SetPrecision[1/3, i]], {i, 100}]]]}]]] number of correct bits
number of guard bits 60 55 50 45 40 35
300 250 200 150 100 50 i 20
40
60
80
100
number of all bits 350 300 250 200 150 100
i 20 40 60 80 100
20
40
60
80
100
i
1.1 Approximate Numbers
45
Because the result of $NumberBits contains all of the binary information on the number, we can easily reconstruct the corresponding number in the decimal system. Here, this is implemented (the result of toExact NumberGuaranteed and toExactNumberAll are typically fractions and sometimes integers). In[189]:=
toExactNumberGuaranteed[l_List] := (* use only guaranteed bits *) First[l] 2^Last[l] l[[2]].Table[2^-i, {i, Length[l[[2]]]}]
In[190]:=
toExactNumberAll[l_List] := (* use all bits *) First[l] 2^Last[l] Join[l[[2]], l[[3]]]. Table[2^-i, {i, Length[l[[2]]] + Length[l[[3]]]}]
In[191]:=
(* for convenience, accept also a real numbers as argument *) toExactNumberGuaranteed[z_Real?(Not[MachineNumberQ[#]]&)] := toExactNumberGuaranteed[$NumberBits[z]] toExactNumberAll[z_Real?(Not[MachineNumberQ[#]]&)] := toExactNumberAll[$NumberBits[z]]
In the following example, we use only the correct bits of SetPrecision[1/3, i]. The resulting number has i 3’s. In[194]:= Out[194]=
In[195]:= Out[195]=
Table[toExactNumberGuaranteed[$NumberBits[SetPrecision[1/3, i]]], {i, 2, 16}] 85 341 5461 87381 349525 5592405 89478485 9 , , , , , , , 256 1024 16384 262144 1048576 16777216 268435456 357913941 5726623061 91625968981 366503875925 , , , , 1073741824 17179869184 274877906944 1099511627776 5864062014805 93824992236885 375299968947541 6004799503160661 , , , = 17592186044416 281474976710656 1125899906842624 18014398509481984 N[%, 60] 80.332031250000000000000000000000000000000000000000000000000000, 0.333007812500000000000000000000000000000000000000000000000000, 0.333312988281250000000000000000000000000000000000000000000000, 0.333332061767578125000000000000000000000000000000000000000000, 0.333333015441894531250000000000000000000000000000000000000000, 0.333333313465118408203125000000000000000000000000000000000000, 0.333333332091569900512695312500000000000000000000000000000000, 0.333333333022892475128173828125000000000000000000000000000000, 0.333333333313930779695510864257812500000000000000000000000000, 0.333333333332120673730969429016113281250000000000000000000000, 0.333333333333030168432742357254028320312500000000000000000000, 0.333333333333314385527046397328376770019531250000000000000000, 0.333333333333332149095440399833023548126220703125000000000000, 0.333333333333333037273860099958255887031555175781250000000000, 0.333333333333333314829616256247390992939472198486328125000000<
If we use all stored bits, we see that, in fact, groups of numbers are equal because the guard digits get added in blocks (this feature was already visible from the above pictures for the i used here). In[196]:= Out[196]=
Table[toExactNumberAll[$NumberBits[SetPrecision[1/3, i]]], {i, 2, 16}] 6148914691236517205 6148914691236517205 6148914691236517205 9 , , , 18446744073709551616 18446744073709551616 18446744073709551616 6148914691236517205 6148914691236517205 6148914691236517205 , , , 18446744073709551616 18446744073709551616 18446744073709551616 6148914691236517205 6148914691236517205 26409387504754779197847983445 , , , 18446744073709551616 18446744073709551616 79228162514264337593543950336
Numerical Computations
46 26409387504754779197847983445 , 79228162514264337593543950336 26409387504754779197847983445 , 79228162514264337593543950336 26409387504754779197847983445 , 79228162514264337593543950336 In[197]:= Out[197]=
26409387504754779197847983445 , 79228162514264337593543950336 26409387504754779197847983445 , 79228162514264337593543950336 26409387504754779197847983445 = 79228162514264337593543950336
N[Union[%], 60] 80.333333333333333333315263297125241592766542453318834304809570, 0.333333333333333333333333333329126075172154603704470780985158<
Because most rational numbers are not exactly representable as a base 2 floating point number, we do not get 1 ê 7 back in the following example [784]. In[198]:=
Out[198]=
{toExactNumberGuaranteed[#], toExactNumberAll[#]}&[ $NumberBits[N[1/7, 20]]] 10540996613548315209 48611766702991209066196372490252601637 9 , = 73786976294838206464 340282366920938463463374607431768211456
Let us come again back to the iteration NestList[(#^2 - 1)/(# - 1) - 1&, N[2, 200], 200] used above. Here are pictures of the number of correct and the number of guard digits. In[199]:=
manyTwos = NestList[(#^2 - 1)/(# - 1) - 1&, N[2, 200], 200];
In[200]:=
With[{tr = Transpose[$NumberBits /@ manyTwos]}, Show[GraphicsArray[{ (* number of significant bits *) ListPlot[Length /@ tr[[2]], DisplayFunction -> Identity, PlotLabel -> "number of significant bits"], (* number of guard bits *) ListPlot[Length /@ tr[[3]], PlotJoined -> True, PlotRange -> All, AxesOrigin -> {0, 0}, PlotLabel -> "number of guard bits", DisplayFunction -> Identity]}]]] number of significant bits
number of guard bits 65 60 55 50 45 40 35
600 500 400 300 200 100 50
100
150
200
50
100
150
200
Although the number of correct digits continuously decreases, the number itself (generated using toExactNum berAll) is actually the same every time (but Mathematica cannot guarantee that the guard digits are correct). In[201]:= Out[201]=
toExactNumberAll[$NumberBits[#]]& /@ manyTwos 82, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2<
2, 2, 2, 2, 2,
Let us use toExactNumberAll to investigate what happens when one raises the precision of a number. We start with the machine number 1./3.
1.1 Approximate Numbers
47
In[202]:=
ξ = 1./3;
In[203]:=
FullForm[ξ]
Out[203]//FullForm=
0.3333333333333333` In[204]:= Out[204]=
$NumberBits[ξ] 81, 8<, 81, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1<, −1<
Raising the precision of the last number shows added zeros in base 2. In[205]:= Out[205]= In[206]:= Out[206]=
ξP = SetPrecision[ξ, 70] 0.3333333333333333148296162562473909929394721984863281250000000000000000 $NumberBits[ξP] 81, 81, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0<, 0, 0, 0, 0, 0, 0<, −1<
Here are the added zeros counted. In[207]:= Out[207]=
{Union[#], Length[#]}& @ Drop[$NumberBits[ξP][[2]], Length[$NumberBits[ξ][[3]]]] 880<, 180<
The additional digits displayed in ξP arise from the precision raising of the value of ξ. In[208]:= Out[208]=
toExactNumberAll[$NumberBits[ξ]] // N[#, 70]& 0.3333333333333333148296162562473909929394721984863281250000000000000000
Be aware that $NumberBits returns the full internal binary representation of a real floating-point number. RealDigits returns the base 2 representation of the correct digits of a number. This means that $Number Bits[number][[2]] and RealDigits[number, 2][[1]] might be slightly different (remember that the precision of a number is an general not in integer in base 10 and/or base 2). Here is such a case. In[209]:= Out[209]=
With[{pi = N[Pi, $MachinePrecision + 1]}, {$NumberBits[pi][[2]], RealDigits[pi, 2][[1]]}] 881, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 81, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0,
1, 0, 1, 1,
0, 0, 0, 1,
1, 0, 1, 0,
0, 0, 0, 0,
1, 1, 1, 0,
0, 1, 0, 0, 0, 0<, 0, 1, 0, 0, 1, 0<<
The following graphic shows the number of certified bits of SetPrecision[x, prec] as a function of prec. We use four different exact x: the integer 2, and the three irrational numbers 21ê2 , p, and cosH1L. The red curve is the nearest integer to prec ê log10 2. In[210]:=
Show[GraphicsArray[ Plot[(* estimated rounded precision in red *)
Numerical Computations
48
{Round[prec/Log[10, 2]], Length[$NumberBits[SetPrecision[#, prec]][[2]]]}, {prec, $MachinePrecision - 2, $MachinePrecision + 2}, Compiled -> False, Frame -> True, Axes -> False, PlotLabel -> StyleForm[#, FontFamily -> "Courier"], PlotPoints -> 200, DisplayFunction -> Identity, PlotStyle -> {{Hue[0], Thickness[0.015]}, {GrayLevel[0]}}]& /@ (* four numbers *) {2, Sqrt[2], Pi, Cos[1]}]] è!!!! 2
2 60 58 56 54 52 50 48 46 14
15
16
17
18
14
15
16
π
60 58 56 54 52 50 48 46
60 58 56 54 52 50 48 46 17
18
14
15
16
cosH1L 60 58 56 54 52 50 48 46 17
18
14
15
16
17
18
We now look at a small application of high-precision arithmetic in the calculation of p. It is well known that ¶ H-1Lk ê H2 k + 1L = p ê 4, but the convergence is very slow. To check the speed of convergence, we find the ⁄k=0 sum of the first 10 terms in the series, maintaining 1000-digit precision, and compare the result with the one above to see how many digits are correct. The resulting list gives roughly the number of correct digits of the ith partial sum. In[211]:=
arcTanSeriesTerms[x_, o_] := Table[(-1)^k/(2k + 1) x^(2k + 1), {k, 0, o}]
In[212]:=
series1 = Table[N[4 (-1)^k/(2k + 1), 1000], {k, 0, 10}];
In[213]:=
(* for comparison purposes *) pi = N[Pi, 1000];
The function correctPartialSumDigits gives a list of the number of correct digits of the partial sums. In[215]:=
correctPartialSumDigits[series_, exactValue_:Pi] := Abs[Log[10, (* difference to Pi/4 *) Abs[N[exactValue - #, 4]]]]& /@ (* form partial sums *) Drop[FoldList[Plus, 0, series], 1]
In[216]:=
correctPartialSumDigits[series1]
Out[216]=
80.0663, 0.3234, 0.4880, 0.6084, 0.7031, 0.7811, 0.8473, 0.9048, 0.9556, 1.0011, 1.0423<
¶ The series expansion of arctan itself is ⁄k=0 H-1Lk ê H2 k + 1L x2 k+1 = arctanHxL. Using the alternative series o
1 k+1 k !2 4k 1 arctanHxL = ÅÅÅÅÅÅ „ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ J1 + ÅÅÅÅÅ2ÅÅÅ N x H2 k + 1L ! x k=0
we obtain a much faster converging sequence. In[217]:=
arcTanSeriesTermsFast[x_, o_] := Table[(k!)^2 4^k/(2k + 1)!(1/x^2 + 1)^(k + 1), {k, 0, o}]/x
In[218]:=
series1Fast = arcTanSeriesTermsFast[N[1, 100], 10]; correctPartialSumDigits[series1Fast] 80.0575, 0.1833, 0.7628, 1.1175, 1.4168, 1.6968, 1.9703, 2.2427, 2.5162, 2.7916, 3.0690<
Out[219]=
Most of the classic series representations of p are based on series expansions of arctan around 0. For example, we have:
1.1 Approximate Numbers
49
16 arctanH1 ê 5L - 4 arctanH1 ê 239L = p 4 arctanH1 ê 2L + 4 arctanH1 ê 5L + 4 arctanH1 ê 8L = p. (For many more of this type of formulae, see [69], [1122], [1521], [1475], [354], [355], and [1770]. For the construction of such type identities, see [1641].) The next series converges considerably faster than does the above one. 1 1 1 1 p = 88 arctanJ ÅÅÅÅÅÅÅÅÅ N + 8 arctanJ ÅÅÅÅÅÅÅÅÅÅÅÅÅ N - 20 arctanJ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅ Å N - 40 arctanJ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ N 28 443 1393 11018 In[220]:=
Out[222]=
series2 = N[88 8 20 40
arcTanSeriesTerms[1/28, 10] + arcTanSeriesTerms[1/443, 10] arcTanSeriesTerms[1/1393, 10] arcTanSeriesTerms[1/11018, 10], 1000];
correctPartialSumDigits[series2] 82.8744, 5.9907, 9.0312, 12.0346, 15.0161, 17.9830, 20.9395, 23.8881, 26.8308, 29.7686, 32.7024<
Using the alternative expansion for arctan for the last linear combination gives us about 94 correct digits for the first 10 terms. In[223]:=
Out[225]=
series2Fast = N[88 8 20 40
arcTanSeriesTermsFast[1/28, 10] + arcTanSeriesTermsFast[1/443, 10] arcTanSeriesTermsFast[1/1393, 10] arcTanSeriesTermsFast[1/11018, 10], 1000];
correctPartialSumDigits[series2Fast] 813.7288, 21.6365, 29.6238, 37.6410, 45.6741, 53.7169, 61.7663, 69.8206, 77.8785, 85.9392, 94.0022<
In 1914, Ramanujan (see [246], [367] and [250]) gave the following series, which converges very fast. ¶
23ê2 1103 + 26390 k 1 ÅÅÅÅÅÅÅÅÅÅÅÅÅÅ Å „ H4 nL! ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅ = ÅÅÅÅÅ 4 4k 4 k 9801 p 4 k ! 99 k=0
In[226]:=
Out[228]=
In[229]:= Out[229]=
series3 = N[2 Sqrt[2]/9801 * Table[(4k)!/4^(4k)/k!^4 * (1103 + 26390k)/99^(4k), {k, 0, 10}], 1000]; correctPartialSumDigits[1/series3] 87.1168, 8.1111, 16.1884, 24.2398, 32.2751, 40.3002, 48.3184, 56.3316, 64.3411, 72.3476, 80.3517< correctPartialSumDigits[series2] 82.8744, 5.9907, 9.0312, 12.0346, 15.0161, 17.9830, 20.9395, 23.8881, 26.8308, 29.7686, 32.7024<
This gives us approximately eight additional correct digits for each term include! A still better series was found by D. V. Chudnovsky and G. V. Chudnovsky [247]: ¶
13591409 + 545140134 k 1 12 „ H-1Lk H6 kL ! ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ = ÅÅÅÅÅÅ p k !3 H3 kL! H262537412640768000Lk+1ê2 k=0
Numerical Computations
50
Here, each term increases the number of correct digits by approximately 14. In[230]:=
Out[232]=
series4 = 12 Table[N[(-1)^k (6k)! (13591409 + k 545140134)/(k!^3 (3k)! * (640320^3)^(k + 1/2)), 1000], {k, 0, 10}]; correctPartialSumDigits[1/series4] 813.2289, 14.2232, 28.5060, 42.7586, 56.9942, 71.2191, 85.4369, 99.6496, 113.8584, 128.0641, 142.2675<
This last formula is also used inside Mathematica to calculate p to arbitrary digits. For more on efficient series expansions of p, see [403], [247], [248], [182], [1831], [245], [1500], and http://www.cecm.sfu.ca/pi/. For an interesting method to compute the digits of p digit by digit, see [1866]. For a billiard-based method to calculate p, see [654]. For the relativistic invariance of p (when defined by the circumference and radius of a disk, see [1348]). If one needs a large number of digits of p, N[Pi, 10^6] calculates in about 12 seconds on a 2 GHz computer. In[233]:= Out[233]=
N[Pi, 10^6] // Precision // Timing 89.46 Second, 1. × 106 <
Now, we have finished discussing high-precision numbers themselves. We continue to discuss some functions that handle high-precision numbers in a special way. We illustrate the use of the option SameTest for Equal, Union, and Sort, which can be used to compare two numbers that differ from each other by less than their respective accuracies. We start by demonstrating the “obvious” behavior. The following two numbers are neither equal (in the Mathematica sense of Equal) nor identical (in the Mathematica sense of SameQ). In[234]:= Out[234]=
In[235]:= Out[235]=
Union[N[{2, 2 + 10^-35}, 40], SameTest -> SameQ] 82.000000000000000000000000000000000000000, 2.000000000000000000000000000000000010000< Union[N[{2, 2 + 10^-35}, 40], SameTest -> Equal] 82.000000000000000000000000000000000000000, 2.000000000000000000000000000000000010000<
The length two output no longer happens when we use our own (less restrictive) test in the following example. In[236]:= Out[236]=
Union[N[{2, 2 + 10^-35}, 40], SameTest -> (Abs[#1 - #2] < 10^-34&)] 82.000000000000000000000000000000000000000<
In the next input, the two numbers are identified by Union with the option setting Equal. In[237]:= Out[237]=
Union[N[{2, 2 + (* smaller *) 10^-38}, 40], SameTest -> Equal] 82.000000000000000000000000000000000000000<
Here we compare some more numbers. In[238]:= Out[238]= In[239]:= Out[239]= In[240]:= Out[240]=
Union[Table[#[3, SetPrecision[3, i]], {i, 1000}]]& /@ {Equal, SameQ} 88True<, 8False<< Union[Table[#[3, SetPrecision[3., i]], {i, 1000}]]& /@ {Equal, SameQ} 88True<, 8False<< Union[Table[#[3., SetPrecision[3., i]], {i, 1000}]]& /@ {Equal, SameQ} 88True<, 8True<<
As we see, SameQ takes into account the type of a number. An integer is never the same as an approximative number.
1.1 Approximate Numbers
51
The a’s and b’s in the following table are equal (in the sense of Equal), because they differ from each other by less than the precision of a. In[241]:=
Block[{(* make it local here *) $MinPrecision, a, b}, Table[{$MinPrecision = i - 1, a = SetPrecision[2, i]; b = SetPrecision[2 + 10^(-i - 6), i]; a == b, a - b}, {i, 5, 15}] // TableForm[#, TableSpacing -> {0, 3}, TableHeadings -> {{}, StyleForm[#, FontWeight -> "Bold"]& /@ {"prec", "a == b","a - b"}}]&]
Out[241]//TableForm=
prec 4 5 6 7 8 9 10 11 12 13 14
a == b True True True True True True True True True True True
a − b 0. × 10−5 0. × 10−6 0. × 10−7 0. × 10−8 0. × 10−9 0. × 10−10 0. × 10−11 0. × 10−12 0. × 10−13 0. × 10−14 0. × 10−15
Comparison of high-precision numbers is set up in such a way that (despite the potential uncertainty contained in high-precision numbers) two high-precision numbers are never equal and not equal (in the sense of Less and Greater) at the same time. Here is an example demonstrating this property. In[242]:= Out[243]=
ξ = SetPrecision[10, 1]; η = SetPrecision[11, 1]; {ξ === η, ξ == η, ξ <= η, ξ < η, ξ >= η, ξ > η, ξ != η, ξ =!= η} 8True, True, True, False, True, False, False, False<
The behavior of Equal and SameQ with respect to floating point numbers can be influenced by the two functions Experimental`$EqualTolerance and Experimental`$SameQTolerance . They represent the number of digits such that two numbers are considered to be equal or the same. The current values are as follows. In[244]:= Out[244]=
{Experimental`$EqualTolerance, Experimental`$SameQTolerance} 82.10721, 0.30103<
In binary, this means that, by default, Equal ignores the last seven bits and SameQ ignores the last bit. In[245]:= Out[245]=
Log[2, 10^%] 87., 1.<
The values of Experimental`$EqualTolerance and Experimental`$SameQTolerance can be changed. In the following setting, all binary digits are ignored. As a result, the number 1. becomes equal to the number 2.. In[246]:= Out[246]=
Block[{Experimental`$EqualTolerance = Length[$NumberBits[1.][[3]]]}, 1. == 2.] True
Setting Experimental`$EqualTolerance to zero yields strict comparisons. In[247]:= Out[247]=
Block[{Experimental`$EqualTolerance = 0}, 1`30 == 1`30 + 10^-29] False
Using a negative value for Experimental`$EqualTolerance allows even to take guard digits into account.
Numerical Computations
52 In[248]:= Out[248]=
Table[{k, Block[{Experimental`$EqualTolerance = -3}, 1`30 == 1`30 + 10^-k]}, {k, 25, 35}] 8825, False<, 826, False<, 827, False<, 828, False<, 829, False<, 830, True<, 831, True<, 832, True<, 833, True<, 834, True<, 835, True<<
Frequently, it is useful to have a way to substitute 0 for numbers whose absolute values are smaller than some given bound. This can be accomplished using Chop. Chop[expression, bound] substitutes 0 for all numbers occurring in expression whose absolute values are smaller than bound. If the second variable is not present, the default value is 10-10 .
Here are some examples. In[249]:=
Out[249]=
Chop[{10.0^-3, 10.0^-21, 10.0^-22, 10.0^-23, 0.000000000000000000000999999999999999999999, 0.0000000000000000000001, 0.0000000000000000000002, 10^-28, N[1 + 10^-55, 100], -10.0^-34, 1.001 10^-22}, 10^-22] 90.001, 1. × 10−21 , 1. × 10−22 , 0, 9.99999999999999999999× 10−22 , 1 , 1. × 10−22 , 2. × 10−22 , 10000000000000000000000000000 1.000000000000000000000000000000000000000000000000000000100000000000000000000 0000000000000000000000000, 0, 1.001 × 10−22 =
For complex numbers, Chop works with the real and imaginary parts, rather than with the absolute value of the number. In[250]:=
Out[250]=
Chop[{10^-12, 10^-12 I, 10^-12 + 10^-12 I, 10^-12 - 10^-12 I, 10^-12 + 10^-11 I, 10^-11 - 10^-12 I, 1.001 10^-11 - 10^-12 I} // N, 1.2 10^-12] 80, 0, 0, 0, 1. × 10−11 , 1. × 10−11 , 1.001 × 10−11 <
When carrying out approximative calculations using machine arithmetic, it might happen that a number smaller than $MinMachineNumber, or a number larger than $MaxMachineNumber, will be encountered. In such situations, Mathematica will automatically switch to its high-precision arithmetic. Here is such a case. In[251]:= Out[251]= In[252]:= Out[252]= In[253]:= Out[253]= In[254]:= Out[254]=
smallMachineInteger = $MinMachineNumber 2.22507 × 10−308 MachineNumberQ[%] True $MinMachineNumber^2 4.95095367581213× 10−616 {MachineNumberQ[%], Precision[%]} 8False, 15.6536<
Taking the square root of the above $MinMachineNumber^2 does not give the original $MinMachineNum ber. $MinMachineNumber^2 is a high-precision number and so will be its square root. In[255]:= Out[255]=
Sqrt[%%] 2.225073858507201× 10−308
Numbers might be so big, that they cannot even be represented as high-precision numbers. All numbers larger than $MaxNumber yield Overflow[].
1.1 Approximate Numbers In[256]:=
53
$MaxNumber^2 General::ovfl : Overflow occurred in computation. More…
Out[256]=
Overflow@D
All numbers smaller than $MinNumber are represented as Underflow[]. In[257]:= Out[257]=
1/% Underflow@D
Here is an exact input that results in Overflow[]. In[258]:=
10^10^10 General::ovfl : Overflow occurred in computation. More…
Out[258]=
Overflow@D
Because no (complex) sign information is contained in Overflow[], the following gives the result Indeterminate. In[259]:= Out[259]=
Exp[-%] Indeterminate
The automatic switch to high-precision numbers in case of the occurrence of numbers that are too small or too large to be represented as machine numbers is in many situations very useful. But from time to time, this is not what one wants (because either using the high-precision arithmetic is just too expensive or because for all practical purposes, such a small number can be considered as 0 anyway). If one does not want the switch to happen, one can use the system option "CatchMachineUnderflow" to tell Mathematica that any too small quantity that develops from a machine number calculation is to be set to 0.0. Here is a simple example. In the following iteration, in the first few steps, the machine arithmetic is used. Then, the high-precision arithmetic is used, and finally, the numbers become smaller than $MinNumber and Underflow[] is produced. In[260]:=
Developer`SetSystemOptions["CatchMachineUnderflow" -> True]; FixedPointList[#^8&, 0.1, 50] General::unfl : Underflow occurred in computation. More…
Out[261]=
80.1, 1. × 10−8 , 1. × 10−64 , 1.000000000000055× 10−512 , 1.00000000000044× 10−4096, 1.0000000000035× 10−32768, 1.000000000028× 10−262144, 1.000000000225× 10−2097152, 1.00000000180× 10−16777216, 1.0000000144× 10−134217728, Underflow@D, Underflow@D<
Setting the system option "CatchMachineUnderflow" to False results in a direct switch from the machine numbers to 0.0. In[262]:= Out[263]=
Developer`SetSystemOptions["CatchMachineUnderflow" -> False]; FixedPointList[#^4&, 0.1, 50] 80.1, 0.0001, 1. × 10−16 , 1. × 10−64 , 1. × 10−256 , 0., 0.<
High-precision arithmetic is not influenced by the setting the system option "CatchMachineUnderflow". In[264]:=
FixedPointList[#^4&, SetPrecision[1/10, $MachinePrecision]] General::unfl : Underflow occurred in computation. More…
Out[264]=
80.1000000000000000, 0.0001000000000000000, 1.00000000000000× 10−16 , 1.00000000000000× 10−64 , 1.0000000000000× 10−256 , 1.000000000000× 10−1024, 1.000000000000× 10−4096, 1.00000000000× 10−16384, 1.00000000000× 10−65536, 1.0000000000× 10−262144, 1.000000000× 10−1048576, 1.000000000× 10−4194304, 1.00000000× 10−16777216, 1.00000000× 10−67108864, 1.0000000 × 10−268435456, Underflow@D, Underflow@D<
Numerical Computations
54
1.1.2 Interval Arithmetic Mathematica includes an implementation of interval arithmetic for simple arithmetic operations with real numbers. This is useful when manipulating data subject that are to measurement errors, requiring as good an estimate as possible of the final error (see, e.g., [412], [1059], [1807], [721], [268], and the references mentioned in the last subsection). The basic object in interval arithmetic is Interval. For details on interval arithmetic, see [1222], [1656], [1468], [1438], [900], [965], and http://cs.utep.edu/interval-comp/main.html. Interval arithmetic can be considered as a made rigorous version of the significance arithmetic discussed in the last subsection. As such, it comes with a price: It is slower and, especially for lower precision, its results are more “pessimistic” than the significance arithmetic results. Also, interval arithmetic is currently available only for elementary functions and for real arguments. But is powerful enough to provide the rigor of a mathematical proof [1800], [1598], [862], [472], [756]. Interval[{xl1 , xu1 }, {xl2 , xu2 }, …] represents subintervals (xli , xui ) of the real line. Here, xli and xui can be either approximate or exact real numbers. Interval[x] is equivalent to Interval[{x - e, x + e}], where e is the uncertainty in x.
Here are two examples. In[1]:=
Interval[{1, 2}]
Out[1]=
Interval@81, 2
In[2]:=
Interval[{Pi // N, 3.15}]
Out[2]=
[email protected], 3.15
For Interval-objects with inexact limits, the notion of open or closed intervals does not apply. Interval-objects with exact limits are often considered to be closed intervals. The order of the variables in the argument of Interval is arbitrary. They will be reordered so that the smaller interval endpoint is to the left. In[3]:=
Interval[{Pi, E}]
Out[3]=
Interval@8 , π
Intervals can extend to infinity. In[4]:= Out[4]=
Interval[{0, Infinity}] Interval@80, ∞
Here, Mathematica cannot numerically determine which of the two interval endpoints is larger. Mathematica is not being able to order the two endpoints of the interval in increasing order. We will come back to such problems in detail in the last subsection of this section. In[5]:=
Interval[{1, 1 + (* a hidden zero *) Sin[Pi/16] - 1/2 Sqrt[2 - Sqrt[2 + Sqrt[2]]]}] N::meprec : Internal precision limit $MaxExtraPrecision = 50.` 1 "############################################################### π è!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # reached while evaluating 2 − 2 + Power@2D − SinA E. More… 2 16
Out[5]=
1 π "################# è!!!! IntervalA91, 1 − $%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 − 2 + 2 % + SinA E=E 2 16
Intervals can be added and multiplied. In[6]:=
Interval[{1, 2}] + Interval[{5, 6}]
1.1 Approximate Numbers Out[6]=
55
Interval@86, 8
In[7]:=
Interval[{1, 2}] - Interval[{5, 6}] Interval@8−5, −3
Out[7]= In[8]:= Out[8]=
Interval[{1, 2}] Interval[{5, 6}] Interval@85, 12
In[9]:= Out[9]=
Interval[{1, 2}]/Interval[{5, 6}] 1 2 IntervalA9 , =E 6 5
The next input subtracts two Interval-objects, each containing two intervals. The resulting Interval-object contains three disjoint intervals. In[10]:=
Interval[{-2, -1}, {1, 2}] - Interval[{-2, -1}, {1, 2}] Interval@8−4, −2<, 8−1, 1<, 82, 4
Out[10]=
Here is a symbolic proof of the last result. It uses the function Reduce (to be discussed in the Symbolic volume [1795]). In[11]:=
Out[11]=
Exists[{1, 2}, Element[{1, 2, }, Reals] && (-2 < 1 < -1 || 1 < 1 < 2) && (-2 < 2 < -1 || 1 < 2 < 2), == 1 - 2] // Reduce −4 < < −2 »» −1 < < 1 »» 2 < < 4
Intervals do not obey the same rules as “ordinary” numbers. For instance, the difference between two identical intervals is not the zero interval. In[12]:= Out[12]=
Interval[{1, 3}] - Interval[{1, 3}] Interval@8−2, 2
Also, the distributive law does not hold anymore. In[13]:= Out[13]=
Function[{a, b, c}, {a (b + c), a b + a c}][ Interval[{1, 2}], Interval[{-3, 8}], Interval[{5, 6}]] 8Interval@82, 28
When one divides by an Interval-object, the result contains two disjoint intervals if the original interval contained 0. In[14]:=
1/Interval[{-1, 1}] Interval@8−∞, −1<, 81, ∞
Out[14]=
The following input does not make sense in the present implementation because intervals must be subintervals of the real axis. In[15]:=
Interval[{-I, I}]
Out[15]=
Interval@8−,
We can take unions and intersections of intervals. IntervalUnion[interval1 , interval2 , …, intervaln ] represents the (set theoretic) union of the intervals intervali . IntervalIntersection[int1 , int2 , …, intn ] represents the (set theoretic) intersection of the intervals inti .
Numerical Computations
56
And we can test, whether a number or an interval is contained in a given interval. IntervalMemberQ[interval, numberOrInterval] tests if numberOrInterval is contained in interval.
We now give a few simple examples of these operations on interval objects. The result of the following union of two intervals is one interval with a single argument. In[16]:= Out[16]=
IntervalUnion[Interval[{1, 3}], Interval[{2, 4}]] Interval@81, 4
The following two intervals have a finite overlap. In[17]:= Out[17]=
IntervalIntersection[Interval[{1, 3}], Interval[{2, 4}]] Interval@82, 3
Mathematica thinks that the following intervals (with exact limits) do not overlap. In[18]:= Out[18]=
IntervalIntersection[Interval[{1, 2}], Interval[{2, 4}]] Interval@D
But if we work with approximate numbers, 2.0 is a common point. In[19]:= Out[19]=
IntervalIntersection[Interval[{1, 2.0}], Interval[{2.0, 4}]] Interval@82., 2.
Several intervals can be contained into one Interval-object, in particular, if they do not intersect. In[20]:= Out[20]=
IntervalUnion[Interval[{1, 2.0}], Interval[{3.0, 4}]] Interval@81, 2.<, 83., 4
E is inside the interval Interval[2, 3]. In[21]:= Out[21]=
IntervalMemberQ[Interval[{2, 3}], E] True
However, 1 + $MachineEpsilon is not in Interval[0, 1]. In[22]:= Out[22]=
IntervalMemberQ[Interval[{0, 1}], 1 + $MachineEpsilon] False
But 1 + $MachineEpsilon is contained in Interval[0, 1.]. In[23]:= Out[23]=
IntervalMemberQ[Interval[{0, 1.}], 1 + $MachineEpsilon] True
Next, we generate the number 10 with precision 3. According to the last subsection it represents a number in the interval H10 H1 - 10-3 ê 2L, 10 H1 + 10-3 ê 2LL. In[24]:=
prec = 3; Ξ = 10; ξ = SetPrecision[Ξ, prec]; (* the interval that represents x *) int = Interval[Ξ {1 - 10^-prec/2, 1 + 10^-prec/2}];
Because of the above-discussed behavior of comparison functions for high-precision numbers, IntervalMem berQ will return True for (inexact, low-precision numbers) that are outside of the actual interval.
1.1 Approximate Numbers In[28]:=
57
Plot[{If[IntervalMemberQ[int, SetPrecision[ζ, prec]], 1, 0]}, {ζ, 9.95, 10.05}, PlotStyle -> {GrayLevel[0]}, Epilog -> {Hue[0], Thickness[0.02], Line[{{int[[1, 1]], 0}, {int[[1, 2]], 0}}]}] 1
0.8
0.6
0.4
0.2
9.96
9.98
10.02
10.04
We can also apply elementary numerical operations to intervals. In[29]:= Out[29]= In[30]:= Out[30]=
2 Interval[{1, 2}] Interval@82, 4
In[31]:=
Sin[Interval[{1, 2}]]
Out[31]=
Interval@8Sin@1D, 1
In calculating with interval arithmetic, “every approximate symbol/number is always uncertain”. So, the difference of an Interval-object x with itself is not zero. When the subtraction is actually carried out, the x already evaluated (see Chapter 4 of the Programming volume [1793]) to an Interval-object and Mathematica no longer knows that these two Interval-objects originated from the same variable. (In affine arithmetic [465], and in generalized interval arithmetic [785], [1804] it is possible to keep track of correlations in the errors of quantities.) In[32]:=
x = Interval[{-1, 1}];
Out[33]=
x - x Interval@8−2, 2
But as a Mathematica expression (meaning structurally), x is of course equal to itself. In[34]:= Out[34]=
{x == x, x === x} 8True, True<
But with symbolic arguments in Interval, we get the behavior discussed in Chapter 2 of the Programming volume [1793] and not a result with head Interval. In[35]:= Out[35]=
Interval[{i, j}] - Interval[{i, j}] 0
Here is a somewhat more complicated example. Let f HxL = sin2 H2 x + 1 ê Hx + 1LL ê Hx2 + 1L. The variable x enters f HxL in three different places. In[36]:=
Clear[x]; f[x_] := Sin[2 x + 1/(x + 1)]^2/(x^2 + 1)
Numerical Computations
58
In the following plot, the corresponding values of the intervals are shown in red; the horizontal width corresponds to the associated x-interval. In areas where the function is monotonically increasing or decreasing, the function often passes through the red rectangle at the corner points. In[38]:=
ePlot[function_, curveInterval_, rectangleInterval_, intervalLength_, opts___] := Module[{ξ}, Show[Graphics[ {Function[x, (* make rectangles from intervals *) {Hue[0], Rectangle @@ Transpose[{x[[1, 1]], x[[2, 1]]}]}] /@ (* the x-intervals and the y-intervals *) ({#, function[#]}& /@ (* the x-intervals *) (Table @@ {Interval[{ξ, ξ + intervalLength}], Prepend[rectangleInterval, ξ]}) // N), {Thickness[0.01], (* the original curve *) Line[{#, function[#]}& /@ Range @@ curveInterval]}} // N], opts, Axes -> True, AxesOrigin -> {0.4, 0}, PlotRange -> All]]
In[39]:=
ePlot[f, {0.1, Pi, Pi/400}, {0.2, Pi, 0.2}, 0.16] 0.8
0.6
0.4
0.2
0.5
1
1.5
2
2.5
3
Here, we should note that the curve does not always pass through the diagonal of the red rectangles. This behavior of Interval[…] is caused by the fact that in this example x makes several (for the interval arithmetic independent) appearances in sin H2 x + 1 ê Hx + 1LL2 ê Hx2 + 1L [358]. Using more intervals, we can make sure that the given curve is “bracketed”. For comparison, we now plot the maximal deviations of f HxL for the case of “the same” x (dashed black line) [1336] and “independent” x (blue line), based on a naive maximum error estimate calculated via the corresponding total differential [1495]. (Because of Mathematica’s evaluation strategy—as discussed in Chapter 4 of the Programming volume [1793]—it is impossible for Mathematica to recognize that the x is always the same and to nevertheless get smaller intervals as the result of the calculation.) In[40]:=
Show[{ePlot[f, {0, Pi, Pi/40}, {0, Pi, 0.01}, 0.1, DisplayFunction -> Identity], Plot[Evaluate[{f[x] + #, f[x] - #}&[0.1* (* total differential approximation for x == y == z independent *) (Abs[D[#, x]] + Abs[D[#, y]] + Abs[D[#, z]])&[ Sin[2x + 1/(y + 1)]^2/(z^2 + 1)] //. {y -> x, z -> x}]], {x, 0, Pi}, DisplayFunction -> Identity, PlotStyle -> {{RGBColor[0, 0, 1], Thickness[0.005]}}], Plot[Evaluate[{f[x] + #, f[x] - #}&[0.1* (* total differential approximation for one x only *) Abs[D[Sin[2x + 1/(x + 1)]^2/(x^2 + 1), x]]]], {x, 0, Pi}, DisplayFunction -> Identity, PlotStyle -> {{GrayLevel[0], Thickness[0.002], Dashing[{0.01, 0.01}]}}]}, DisplayFunction -> $DisplayFunction]
1.1 Approximate Numbers
59 1 0.8 0.6 0.4 0.2
0
0.5
1
1.5
2
2.5
3
We clearly see the differing sensitivities of the function values to errors, depending on the values of the arguments. Interval is also useful in solving some problems in linear algebra. We calculate the determinant of a 2 μ 2 Hilbert matrix whose matrix elements have “small” errors. In[41]:= Out[41]=
In[42]:= Out[42]=
Table[Interval[1/(i + j + 1) + {-1/1000, 1/1000}], {i, 2}, {j, 2}] 1003 251 997 249 99IntervalA9 3000 =E, IntervalA9 1000 =E=, , , 3000 1000 251 201 249 199 9IntervalA9 1000 =E, IntervalA9 1000 =E== , , 1000 1000 {Det[%], N[Det[%]]} 47 13 9IntervalA9 15000 =E,
[email protected], 0.0052
Here is a comparison with the numericalized version of the exact determinant. (But be aware that using intervals in other Mathematica functions just carries out calculations with intervals, it does not carry out calculations that minimize the intervals by reordering the operations occurring in calculating the determinant.) In[43]:= Out[43]=
N[Det[Array[1/(#1 + #2 + 1)&, {2, 2}]]] 0.00416667
In the next example, no comparison is possible because the power of the interval remains partially uncomputed, leaving, for example, expressions of the following form. In[44]:=
Interval[{3.0, 1.0}]^(1/Pi) < 100 Interval@81., 3.
1
Out[44]=
<
100
For more complicated functions such as the special functions of mathematical physics, interval arithmetic is also not yet available. In[45]:=
Gamma[Interval[{0.3, 1.3}]]
Out[45]=
Gamma@
[email protected], 1.3
Intervals formed from numbers with higher accuracy have an interesting feature in the last few digits in order to correctly represent the interval. Here is such an example. In[46]:= Out[46]=
In[47]:= Out[47]=
Interval[{N[2, 50], N[3, 50]}]
[email protected], 3.0000000000000000000000000000000000000000000000000
For machine numbers, we do not see the interval ends directly in output form.
Numerical Computations
60 In[48]:= Out[48]=
Interval[{N[2], N[3]}] Interval@82., 3.
But when we subtract the endpoints, we see the outward rounding. In[49]:= Out[49]=
First[%] - {2, 3} 8−4.44089 × 10−16 , 4.44089 × 10−16 <
Here is the interval around a machine zero. In[50]:= Out[50]=
Interval[{0., 0.}] Interval@8−2.22507 × 10−308 , 2.22507 × 10−308
We now use Mathematica’s interval arithmetic to follow the inaccuracies in the last subsection Nest[(#^2 1)/(# - 1) - 1& … example. We begin with the interval (2 - 10-200 , 2 + 10-200 ), and watch how it grows (on a logarithmic scale) as the calculation proceeds with 500 digits. In[51]:=
ListPlot[(* use log in base 10 *) Log[10, -N[Subtract @@ #[[1]]]]& /@ NestList[(* the function to be iterated *) (#^2 - 1)/(# - 1) - 1&, (* starting interval with enough digits: N[{2 - 10^-200, 2 + 10^-200}, 500] *) Interval[N[{2 - 10^-200, 2 + 10^-200}, 500]], 237], FrameLabel -> {"iteration", "log(interval width)"}, Frame -> True, RotateLabel -> True, PlotStyle -> {PointSize[0.003]}]
logHinterval widthL
0
-50
-100
-150
-200 0
50
100 150 iteration
200
Using Intervals, we can also directly compare the growth of the fuzziness of the above iteration for high-precision numbers. In[52]:= Out[53]=
In[54]:=
Out[55]=
(* to watch for all details in the precision *) Precision /@ NestList[(#^2 - 1)/(# - 1) - 1&, N[2, 200], 20] 8200., 199.155, 198.31, 197.465, 196.62, 195.775, 194.929, 194.084, 193.239, 192.394, 191.549, 190.704, 189.859, 189.014, 188.169, 187.324, 186.478, 185.633, 184.788, 183.943, 183.098< (* according to definition of precision *) N[-Log[10, Abs[Subtract @@ #[[1]]]]]& /@ NestList[(#^2 - 1)/(# - 1) - 1&, (* starting interval equivalent to N[2, 200] *) Interval[{SetPrecision[2 - 5 10^-201, 500], SetPrecision[2 + 5 10^-201, 500]}], 20] 8200., 199.155, 198.31, 197.465, 196.62, 195.775, 194.929, 194.084, 193.239, 192.394, 191.549, 190.704, 189.859, 189.014, 188.169, 187.324, 186.478, 185.633, 184.788, 183.943, 183.098<
For not too small precisions, the high-precision arithmetic is an excellent approximation to interval arithmetic. We generate a random rational function to demonstrate this.
1.1 Approximate Numbers In[56]:=
61
(* make a "random" rational function *) randomProduct[n_, c_, x_] := Product[x - SetPrecision[Random[Real, {-c, c}], Infinity], {n}] SeedRandom[111]; [x_] = Expand[randomProduct[20, 5, x]]/Expand[randomProduct[20, 5, x]];
The function is quite large when written out and contains numbers with nearly 300 digits. In[61]:= Out[61]=
{ByteCount[[x]], N @ Max[Flatten[{Numerator[#], Denominator[#]}& /@ Cases[[x], _Rational, Infinity]]]} 812144, 2.09534 × 10299 <
The following graphic shows the precision of [N[x, 30]] as a red line and the corresponding quantity calculated using exact interval arithmetic as a black line. The points indicate the corresponding values for the quality of the high-precision values. As visible, the two lines agree perfectly and the points are all above the two lines—as it should be for significance arithmetic with sufficiently many digits. In[62]:=
(* precision from high-precision arithmetic *) SA[f_, x_, prec_] := Precision[f[N[SetPrecision[x, Infinity], prec]]]
In[64]:=
(* precision from interval arithmetic *) I[f_, x_, prec_] := With[{ = f[Interval[N[SetPrecision[x, Infinity]* {1 - 1/2 10^-prec, 1 + 1/2 10^-prec}, 3 prec]]]}, (* -Log[10, intervalLength/intervalCenter] *) -Log[10, Abs[Subtract @@ [[1]]]/Abs[Plus @@ [[1]]/2]]]
In[66]:=
With[{prec = 30}, Plot[{SA[, x, prec], I[, x, prec]}, {x, -6, 6}, (* -Log[10, relativeError] *) Epilog -> {PointSize[0.003], RGBColor[0, 0, 1], Table[Point[{x, -Log[10, Abs[([x] SetPrecision[[SetPrecision[x, prec]], Infinity])/ [x]]]}], {x, -6, 6, 12/101}] // N}, PlotRange -> All, PlotPoints -> 100, Frame -> True, Axes -> False, PlotStyle -> {{Thickness[0.02], Hue[0]}, {Thickness[0.002], GrayLevel[0]}}]] 30 27.5 25 22.5 20 17.5 -6
-4
-2
0
2
4
6
The next input shows the precision of cosHkL where the integers k have precision 20 versus the corresponding lengths of intervals representing these fuzzy integers. The rightmost plot shows the differences between the two data sets.
Numerical Computations
62 In[67]:=
Show[GraphicsArray[ Block[{$DisplayFunction = Identity, o = 2^12, p = 20, f = Cos, t1, t2, opts = Sequence[PlotRange -> {All, {14, 22}}, Frame -> True, Axes -> False, PlotStyle -> PointSize[0.002]]}, {(* using high-precision arithmetic *) ListPlot[t1 = Table[Precision[f[SetPrecision[k, p]]], {k, o}], opts], (* using interval arithmetic *) ListPlot[t2 = Table[-Log[10, Abs[Subtract @@ f[Interval[N[k {1 - 10^-p/2, 1 + 10^-p/2}, 5p]]][[1]]/f[k]]], {k, o}], opts], (* difference between the two results *) ListPlot[Reverse @ Sort[t1 - t2], PlotRange -> All, opts]}]]] 22 21 20 19 18 17 16 15
22 21 20 19 18 17 16 15 0
1000
2000
3000
4000
6 μ 10-15 4 μ 10-15
2 μ 10-15 0 -2 μ 10-15 0 0
1000
2000
3000
1000
2000
3000
4000
4000
We end this subsection with an application of interval arithmetic.
Computing Relative Global Attractors of Rationals Maps We consider a rational function f Hx1 , x2 , …, xd L from d to d and a compact region V of d . The global attractor f relative to V is the intersection of all f HV L, f H f HVLL, f H f H f HV LLL, … [1067], [474], [1113]. The global attractor f relative to V can be calculated in the following way: We start with a subdivision 9SkH0L = (k = 1, 2, …, n) of V. The union of regions S1H0L , S2H0L , …, SnH0L covers V. We then form the images TkH0L = f -1 ISkH0L M H0L and select the SkH0L having a nonempty intersection with any of the T H0L j . Then we subdivide these selected Sk to H1L H1L H1L -1 H1L obtain a subdivision 9Sk =. We continue and form the Tk = f ISk M and select the Sk having a nonempty HnL intersection with any of the T H1L j . Continuing this way yields a better and better approximation 9Sk = of the global attractor f relative to V . For a more detailed mathematical description, see [475], [477], and [476]. In the following, we will consider the case d = 2. For the SkHnL , we will use rectangles in 2 . The most difficult step in the described algorithm is the calculation of the TkHnL . We will use intervals to describe the SkHnL and HnL interval arithmetic with machine-real number intervals to calculate the T HnL j . To select the relevant Sk at each HnL HnL step we cover the Tk with the Sk . 1
Here is an example map f . In[68]:=
Clear[x, y, ξ, η]; f1[{x_, y_}] = {-29/39 + 1637/857 x^6 + 59/238 y, x};
Direct numerical forward iteration yields the following graphic of the attractor. In[70]:=
iterationGraphics[f_, {n_, k_}, opts___] := Graphics[{PointSize[0.003], Table[Point /@ Drop[NestList[f, (* k random starting points *) Table[Random[Real, {-1, 1}], {2}], n], Floor[n/100]], {k}]}, opts, Frame -> True]
In[71]:=
SeedRandom[111]; Show @ iterationGraphics[f1, {10^4, 5}]
1.1 Approximate Numbers
63
0.75 0.5 0.25 0 -0.25 -0.5 -0.75 -1
-1
-0.75
-0.5
-0.25
0
0.25
0.5
0.75
This is the inverse map f -1 . (The function f has been selected in such a way that f -1 does not contain radicals that are currently nonexhaustively-handled Interval-objects.) In[73]:= Out[73]=
f1Inv[{x_, y_}] = {ξ, η} /. Solve[{x, y} == f1[{ξ, η}], {ξ, η}][[1]] 238 H24853 + 33423 x − 63843 y6 L = 9y, 1971957
The next inputs implement the above-described procedure for calculating 9SkHnL =. For the initial region, we use the square H-L, LL μ H-L, LL. We subdivide each square at each step into four new squares of identical size and edge length d. To find the intersections without testing each SkHnL with each TkHnL , we discretize the TkHnL through the SkHnL and define a function active that marks the current SkHnL inside the H-L, LL μ H-L, LL square. The three functions intervalToSquareBounds, intervalPairToCoveringSquares , and inter valPairToCoveringSquares convert covering intervals to pairs of integers representing the position of a subdivision square in the H-L, LL μ H-L, LL square. In[74]:=
(* bounds for squares *) intervalToSquareBounds[Interval[{a_, b_}], {x1_, x2_}, δ_] := With[{n = Round[(x2 - x1)/δ], L = x2 - x1}, {Max[Ceiling[(a - x1)/L n], 1], Min[Ceiling[(b - x1)/L n], n]}]
In[76]:=
(* all squares corresponding to a pair rectangular 2D interval *) intervalPairToCoveringSquares[{i1:Interval[{_, _}], i2:Interval[{_, _}]}, {x1_, x2_}, δ_] := Outer[List, Range @@ intervalToSquareBounds[i1, {x1, x2}, δ], Range @@ intervalToSquareBounds[i2, {x1, x2}, δ]] // Flatten[#, 1]&
In[78]:=
(* reduce multiintervals to individual intervals *) intervalPairToCoveringSquares[{Interval[l1__], Interval[l2__]}, {x1_, x2_}, δ_] := Join @@ (intervalPairToCoveringSquares[#, {x1, x2}, δ]& /@ Flatten[Outer[List, Interval /@ {l1}, Interval /@ {l2}], 1])
subDivideSquareInterval subdivides a square into four new ones. In[80]:=
(* subdivide rectangular 2D interval into four new ones *) subDivideSquareInterval[{Interval[{a_, b_}], Interval[{c_, d_}]}] := Module[{xN = (a + b)/2, yN = (c + d)/2, = Interval}, {{[{a, xN}], [{c, yN}]}, {[{xN, b}], [{c, yN}]}, {[{a, xN}], [{yN, d}]}, {[{xN, b}], [{yN, d}]}}]
In[82]:=
(* square bounds corresponding to a pair rectangular 2D interval *) intervalPairToSquareRanges[{Interval[l1__], Interval[l2__]}, {x1_, x2_}, δ_] := Map[intervalToSquareBounds[#, {x1, x2}, δ]&, Flatten[Outer[List, Interval /@ {l1}, Interval /@ {l2}], 1], {2}]
Numerical Computations
64
The function isOverlappingQ determines if the interval pair {i1, i2} overlaps any of the currently marked as active squares. To do this, it converts the square intervals into square numbers and compares these square numbers against the active ones. In[84]:=
(* does 2D interval overlap with any active 2D interval? *) isOverlappingQ[{{i1_Interval, i2_Interval}, {x1_, x2_}, δ_}, active_] := Module[{squareRanges = intervalPairToSquareRanges[{i1, i2}, {x1, x2}, δ], l1, flag = False, xl, xu, yl, yu, xc, yc, c1 = 1}, l1 = Length[squareRanges]; While[flag === False && c1 <= l1, (* initial square counting *) {{xl, xu}, {yl, yu}} = squareRanges[[c1]]; {xc, yc} = {xl, yl}; (* loop over subsquares until active one is found *) While[yc <= yu && Not[flag], xc = xl; While[xc <= xu && (If[TrueQ[active[{xc, yc}]], flag = True]; Not[flag]), xc++]; yc++]; c1++]; flag]
The function refine carries out the main work: it subdivides the squares, maps them using the function fInv, and selects the new global relative attractor approximations. In[86]:=
(* refine subdivision, map squares and select relevant ones *) refine[{activeSquares_, {x1_, x2_}, δ_}, fInv_] := Module[{currentSquares = Flatten[subDivideSquareInterval /@ activeSquares, 1], currentSquareNumbers, active, numberedMappedSquares}, currentSquareNumbers = Union[Flatten[intervalPairToCoveringSquares[#, {x1, x2}, δ/2]& /@ currentSquares, 1]]; (* mark current squares as active *) (active[#] = True)& /@ currentSquareNumbers; numberedMappedSquares = MapIndexed[{#2[[1]], fInv[#1]}&, currentSquares]; (* select squares with intersections *) currentSquares[[First /@ Select[numberedMappedSquares, isOverlappingQ[{#[[2]], {x1, x2}, δ/2}, active]&]]]]
globalRelativeAttractorList finally returns a list of the first o approximations of the global relative attractor. In[88]:=
globalRelativeAttractorList[fInv_, L_, o_] := Module[{activeSquares, δ = 2}, activeSquares[0] = {{Interval[{-L, L}], Interval[{-L, L}]}} // N; Table[activeSquares[k] = refine[{activeSquares[k - 1], {-L, L}, δ/2^(k - 1)}, fInv], {k, o}]]
We calculate the first 12 approximations to the global relative attractor shown above. In[89]:= Out[90]=
gral1 = globalRelativeAttractorList[f1Inv, 2., 12]; Length /@ gral1 84, 12, 25, 55, 134, 316, 721, 1651, 3746, 8540, 19523, 43434<
The function globalRelativeAttractorListGraphic visualizes the various approximations given by the list of interval pairs returned by globalRelativeAttractorList. In[91]:=
intervalPairToPolygon[{Interval[{a_, b_}], Interval[{c_, d_}]}] := Polygon[{{a, c}, {b, c}, {b, d}, {a, d}}]
In[92]:=
globalRelativeAttractorListGraphic[l_, L_] := With[{n = Length[l]}, Graphics[MapIndexed[{Hue[0.8 (#2[[1]] - 1)/n],
1.1 Approximate Numbers
65
intervalPairToPolygon /@ #1}&, l], PlotRange -> L{{-1, 1}, {-1, 1}}, AspectRatio -> Automatic]]
The so-calculated attractor covers the points from above, that were obtained by forward iteration of single points. Typically, the global relative attractor is larger than the one obtained from forward iteration of points. In[93]:=
Show[globalRelativeAttractorListGraphic[gral1, 2]]
Here are two further polynomial maps f2 and f3. Their inverses are quite complicated rational functions. In[94]:=
Out[96]= In[97]:=
Out[99]=
f2[{x_, y_}] = {178/339 - 1299/889x^4 + (16/45 + 349/443 x^2)y, x}; (* calculate inverse map *) f2Inv[{x_, y_}] = {ξ, η} /. Solve[{x, y} == f2[{ξ, η}], {ξ, η}][[1]] 6645 H−158242 + 301371 x + 440361 y4 L = 9y, 100457 H7088 + 15705 y2 L f3[{x_, y_}] = {-152/193 - 337/475 x + 20/113 x^2 + 10/101 x^3 - 198/257 y + 233/273 x y - 1/161 x^2 y - 71/435 x^3 y, x}; (* calculate inverse map *) f3Inv[{x_, y_}] = {ξ, η} /. Solve[{x, y} == f3[{ξ, η}], {ξ, η}][[1]] 46797387 H−824018600 − 1046286775 x − 742312933 y + 185183500 y2 + 103592750 y3 L 9y, = 209257355 H180270090 − 199703135 y + 1453335 y2 + 38190971 y3 L
And here are the corresponding graphics for the global attractors. In[100]:=
SeedRandom[111]; Show[GraphicsArray[iterationGraphics[#, {10^4, 5}]& /@ {f2, f3}]]
In[102]:=
gral2 = globalRelativeAttractorList[f2Inv, 2., 9]; gral3 = globalRelativeAttractorList[f3Inv, 3., 9]; {Length /@ gral2, Length /@ gral3} 884, 16, 54, 155, 406, 1111, 3167, 8555, 22725<, 84, 16, 61, 227, 792, 2733, 9307, 30184, 93193<<
Out[104]=
In[105]:=
Show[GraphicsArray[globalRelativeAttractorListGraphic @@@ {{gral2, 2}, {gral3, 3}}]]
Numerical Computations
66
1.1.3 Converting Approximate Numbers to Exact Numbers Sometimes, we need the “inverse” of N. Rationalize[number, maxError] converts the floating-point number number to an exact rational number, such that the difference between the two numbers is smaller than a function of the exact rational number and maxError. If only the first variable is present, the conversion will take place only if a “sufficiently close” fraction exists. The command Rationalize[number, 0] always results in a conversion.
The following example shows a number that converts nicely. In[1]:= Out[1]=
Rationalize[4.68] 117 25
This is not the case for the machine-precision approximation of p. In[2]:= Out[2]=
Rationalize[N[Pi]] 3.14159
However, we can force a conversion. In[3]:= Out[3]=
Rationalize[N[Pi], 0] 245850922 78256779
We now look at a series of approximations of p. In[4]:=
pi = N[Pi, 150]; (piTab = Table[Rationalize[pi, 10^-i], {i, 50}]) // Short[#, 4]& 22 22 201 333 355 355 237108599624676709032116 9 , , , , , , 39, , 7 7 64 106 113 113 75474011359728834791267 436681609736090076010871 2857198258041217165097342 , , 139000073493649328482341 909474452321624805685313 17579871157983393066594923 26151465932107044561886949 , = 5595846787423398162594219 8324270144388272579650158
Out[5]//Short=
The following plot shows the error in these approximations of p by fractions. In[6]:=
ListPlot[(Log[10, Abs[N[#, 300] - pi]])& /@ piTab, PlotRange -> All, AxesLabel -> {"i", "digits"}]
1.1 Approximate Numbers
67
digits
-10
i 10
20
30
40
50
-20 -30 -40 -50
Rationalize also works with complex arguments. Here it recovers the given numericalized fraction. In[7]:= Out[7]=
Rationalize[N[637/534 + I 991/451]] 637 991 + 534 451
There is one other obvious method for changing approximate numbers to exact ones—using SetPreci sion[…, Infinity]. But this method typically results in quite large denominators. In[8]:=
Out[9]=
x = 0.75458; {Rationalize[x], SetPrecision[x, Infinity]} 37729 3398326206821229 9 , = 50000 4503599627370496
The two rationals are equal within machine precision. In[10]:= Out[10]=
N[Subtract @@ %] −2.84928 × 10−17
xPrec is obtained by the direct transformation from the binary representation of x (all guard bits are taken into account). In[11]:=
BaseForm[x, 2]
Out[11]//BaseForm=
0.11000001001011000012
Rationalize will typically not convert a randomly chosen, machine-real number to a rational number. If Rationalize succeeds, the resulting denominators will typically have only five or six digits. In[12]:=
Out[12]=
Module[{c}, SeedRandom[111]; Table[c = 0; (* how many trials? *) While[c++; Head[Rationalize[r = Random[]]] == Real, Null]; {c, Rationalize[r]}, {3}]] 54713 24452 75799 999849, =, 95185, =, 95731, == 84946 210421 84439
Very intimately related (and being the algorithmic underpinning of the function Rationalize) to the conversion of floating point numbers or exact irrational numbers to rational numbers are continued fractions [1050]. Regular continued fractions are of the form 1 a0 + ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ . 1 a1 + ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ 1 a2 + ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ 1 a3 + ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ a4 + ∫ In Mathematica, they can be obtained using the function ContinuedFraction.
Numerical Computations
68
ContinuedFraction[realNumber, order] gives order terms ai of the continued fraction approximation of realNumber.
Given the first n of the ak , the function FromContinuedFractions generates the corresponding rational number. FromContinuedFraction[listOfIntegers] calculates the convergent corresponding to the continued fraction listOfIntegers.
The GoldenRatio has a particularly simple continued fraction. In[13]:= Out[13]=
ContinuedFraction[GoldenRatio, 50] 81, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1<
The continued fraction of e shows also some regularities. In[14]:= Out[14]=
ContinuedFraction[E, 50] 82, 1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8, 1, 1, 10, 1, 1, 12, 1, 1, 14, 1, 1, 16, 1, 1, 18, 1, 1, 20, 1, 1, 22, 1, 1, 24, 1, 1, 26, 1, 1, 28, 1, 1, 30, 1, 1, 32, 1, 1<
Certain rational functions of e also have continued fraction representations with simple patterns. In[15]:=
ContinuedFraction[Tanh[1/2], 50] ContinuedFraction::terms : Warning: ContinuedFraction only obtained 38 of 50 requested terms using precision 134. Try increasing $MaxExtraPrecision. More…
Out[15]=
80, 2, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126, 130, 134, 138, 142, 146<
The continued fraction of p does not show any obvious (known) pattern. In[16]:= Out[16]=
ContinuedFraction[Pi, 50] 83, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 2, 1, 1, 2, 2, 2, 2, 1, 84, 2, 1, 1, 15, 3, 13, 1, 4, 2, 6, 6, 99, 1, 2, 2, 6, 3, 5, 1, 1, 6, 8, 1, 7, 1, 2, 3, 7<
Here is another continued fraction with an interesting pattern [606], [1032]. In[17]:= Out[17]=
ContinuedFraction[BesselJ[0, 2]/BesselJ[1, 2], 60] 80, 2, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 8, 1, 9, 1, 10, 1, 11, 1, 12, 1, 13, 1, 14, 1, 15, 1, 16, 1, 17, 1, 18, 1, 19, 1, 20, 1, 21, 1, 22, 1, 23, 1, 24, 1, 25, 1, 26, 1, 27, 1, 28, 1, 29, 1<
Here is an interesting continued fraction of a rational number [420]. It is the order-8 approximation to the ¶ 10-k! [315]. For brevity we use 9k to represents the integer 9…9 consisting of k 9’s. Liouville constant ⁄k=0 In[18]:= Out[18]=
ContinuedFraction[Sum[10^-k!, {k, 0, 8}]] /. i_Integer :> (If[Union[#] === {9}, Subscript[9, Length[#]], i]&[IntegerDigits[i]]) 80, 4, 1, 3, 5, 92 , 1, 4, 3, 1, 4, 912 , 1, 3, 1, 3, 4, 1, 92 , 5, 3, 1, 4, 972 , 1, 3, 1, 3, 5, 92 , 1, 4, 3, 1, 3, 1, 912 , 4, 1, 3, 4, 1, 92 , 5, 3, 1, 4, 9480, 1, 3, 1, 3, 5, 92 , 1, 4, 3, 1, 4, 912 , 1, 3, 1, 3, 4, 1, 92 , 5, 3, 1, 3, 1, 972 , 4, 1, 3, 5, 92 , 1, 4, 3, 1, 3, 1, 912 , 4, 1, 3, 4, 1, 92 , 5, 3, 1, 4, 93600 , 1, 3, 1, 3, 5, 92 , 1, 4, 3, 1, 4, 912 , 1, 3, 1, 3, 4, 1, 92 , 5, 3, 1, 4, 972 , 1, 3, 1, 3, 5, 92 , 1, 4, 3, 1, 3, 1, 912 , 4, 1, 3, 4, 1, 92 , 5, 3, 1, 3, 1, 9480 , 4, 1, 3, 5, 92 , 1, 4, 3, 1,
1.1 Approximate Numbers
69
4, 912 , 1, 3, 1, 3, 4, 1, 92 , 5, 3, 1, 3, 1, 972 , 4, 1, 3, 5, 92 , 1, 4, 3, 1, 3, 1, 912 , 4, 1, 3, 4, 1, 92 , 5, 3, 1, 4, 930240, 1, 3, 1, 3, 5, 92 , 1, 4, 3, 1, 4, 912 , 1, 3, 1, 3, 4, 1, 92 , 5, 3, 1, 4, 972 , 1, 3, 1, 3, 5, 92 , 1, 4, 3, 1, 3, 1, 912 , 4, 1, 3, 4, 1, 92 , 5, 3, 1, 4, 9480 , 1, 3, 1, 3, 5, 92 , 1, 4, 3, 1, 4, 912 , 1, 3, 1, 3, 4, 1, 92 , 5, 3, 1, 3, 1, 972 , 4, 1, 3, 5, 92 , 1, 4, 3, 1, 3, 1, 912 , 4, 1, 3, 4, 1, 92 , 5, 3, 1, 3, 1, 93600 , 4, 1, 3, 5, 92 , 1, 4, 3, 1, 4, 912 , 1, 3, 1, 3, 4, 1, 92 , 5, 3, 1, 4, 972 , 1, 3, 1, 3, 5, 92 , 1, 4, 3, 1, 3, 1, 912 , 4, 1, 3, 4, 1, 92 , 5, 3, 1, 3, 1, 9480 , 4, 1, 3, 5, 92 , 1, 4, 3, 1, 4, 912 , 1, 3, 1, 3, 4, 1, 92 , 5, 3, 1, 3, 1, 972 , 4, 1, 3, 5, 92 , 1, 4, 3, 1, 3, 1, 912 , 4, 1, 3, 4, 1, 92 , 5, 3, 1, 4<
For a nice collection of closed-form continued fractions, see [1870] and [1173]. Rational numbers have finite continued fraction representations. Suppressing the second argument in ContinuedFraction returns, for an exact rational first argument, all terms of the continued fraction expansion. In[19]:= Out[19]= In[20]:= Out[20]=
ContinuedFraction[181916/140563315] 80, 772, 1, 2, 6, 1, 2, 23, 1, 10, 1, 9< FromContinuedFraction[%] 181916 140563315
Here is a slightly larger calculation involving the continued fraction of rational numbers. The length of the continued fraction expansion of H p ê qLk (where gcdH p, qL = 1) is conjectured to approach 12 ê p2 lnH2L lnHqL [629]. Here we display the length of H p ê 11Lk for p = 1, … , 10 and k § 500 together with the conjectured asymptotic value. In[21]:=
Show[Graphics[ With[{q = 11, n = 500}, Table[{Hue[p/q], Line[{{n/2, 12/Pi^2 Log[2] Log[p]}, {n, 12/Pi^2 Log[2] Log[p]}}], PointSize[0.003], Table[Point @ {k, 1/k Length[ContinuedFraction[(p/q)^k]]}, {k, n}]}, {p, q - 1}] // N]], Frame -> True, PlotRange -> {0, 2.5}] // Timing 2.5
2
1.5
1
0.5
0
100
200
300
400
500
For floating point numbers, ContinuedFraction sometimes returns fewer terms than expected or even no terms at all. In[22]:=
ContinuedFraction /@ {2., N[2, 22], 2} ContinuedFraction::start : Warning: ContinuedFraction was unable to obtain at least one term using precision 15.954589770191005`. More… ContinuedFraction::start : Warning: ContinuedFraction was unable to obtain at least one term using precision 22.`. More…
Out[22]=
[email protected],
[email protected], 82<<
Numerical Computations
70
The reason is that small changes in the input number may give different integers of the continued fraction expansion. In[23]:= Out[23]=
{ContinuedFraction[2 + 10^-22], ContinuedFraction[2 - 10^-22]} 882, 10000000000000000000000<, 81, 1, 9999999999999999999999<<
Finite continued fractions are basically rational functions in the a j . For a given set of numbers a j , and nonnegative integers k § l § m § n, the following identities hold for continued fractions [1299]: n l-1 jij zy ji zy jj‰ Ha j + 1 ê Ha j+1 + 1 ê H∫ + 1 ê an LLLzzz jjj ‰ Ha j + 1 ê Ha j-1 + 1 ê H∫ + 1 ê al LLLzzz = j zj z { k j=k { k j=m+1
y y i l-1 ij n jj ‰ Ha + 1 ê Ha + 1 ê H∫ + 1 ê a LLLzzz jjj‰ Ha + 1 ê Ha + 1 ê H∫ + 1 ê a LLLzzz. jj j-1 j+1 j k z j m z z z jj { k j=k k j=m+1 { Here this identity is implemented. In[24]:=
cfIdentity[aList_, k_, l_, m_, n_] := Module[{}, (* define the continued fractions from the list aList *) [j0_, j1_, δj_:1] := FromContinuedFraction[Table[aList[[j]], {j, j0, j1, δj}]]; (* the identity *) Product[[j, n], {j, k, l - 1}] Product[[j, l, -1], {j, m + 1, n}] Product[[j, k, -1], {j, m + 1, n}] Product[[j, m], {j, k, l - 1}]]
Here is a quick check for all possible 330 realizations of the above identity for eight a j . In[25]:=
Out[25]=
With[{o = 8}, Union[Factor //@ (cfIdentity[Table[a[j], {j, o}], ##]& @@@ Flatten[Table[{k, l, m, n}, {k, o}, {l, k, o}, {m, l, o}, {n, m, o}], 3])]] 80<
How does the length of a continued fraction of a rational number behave as a function of this rational number? Here we investigate all rational numbers with denominators less than or equal to 256. For some theoretical results, see [628]. In[26]:=
With[{n = 256}, ListPlot[{#, Length[ContinuedFraction[#]]}& /@ Union[Flatten[Table[i/j, {j, n}, {i, 0, j}]]], PlotRange -> All, PlotJoined -> True, Frame -> True, PlotStyle -> {Thickness[0.002]}, Axes -> False]] 12 10 8 6 4
2 0
0.2
0.4
0.6
0.8
1
1.1 Approximate Numbers
71
Square roots of integers have continued fraction representations that are ultimately periodic [1286], [1285]. (For closed forms of nonsimple continued fractions for higher roots of rationals numbers, see [1106].) In[27]:= Out[27]=
ContinuedFraction[2 + 3 Sqrt[79], 80] 828, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1, 52, 1, 1, 1<
In this case, the second argument can be suppressed. ContinuedFraction then returns the periodic part enclosed in an additional list. In[28]:= Out[28]= In[29]:= Out[29]=
ContinuedFraction[2 + 3 Sqrt[79]] 828, 81, 1, 1, 52<< FromContinuedFraction[%] è!!!!!! 2 + 3 79!
è!!!! The length of the periodic part of the continued fraction expansion of k varies greatly as a function of k [1907], [1189]. The right graphic shows the length of the periodic parts for all rational numbers with denominators less than or equal to 128. In[30]:=
Show[GraphicsArray[ Block[{$DisplayFunction = Identity, opts = Sequence[Axes -> False, Frame -> True, PlotRange -> All, PlotStyle -> {PointSize[0.003]}]}, {(* length of period for square root of integers *) ListPlot[Table[{k, If[IntegerQ[Sqrt[k]], 0, (* length of periodic part *) Length[Last[ContinuedFraction[Sqrt[k]]]]]}, {k, 10000}], opts], (* length of period for square root of fractions *) ListPlot[{#, Length[ContinuedFraction[Sqrt[#]][[-1]]]}& /@ Union[Flatten[Table[i/j, {j, 128}, {i, 0, j}]]], opts]}]]] 150
200
125 150 100 100
75 50
50 25 0
0 0
2000
4000
6000
8000
10000
0
0.2
0.4
0.6
0.8
1
Continued fraction expansions of rational numbers and square roots can quite frequently explain various properties of number theoretical functions and related objects. For instance, the only slightly different continued è!!!! è!!!! fraction expansions of 2 and 3 can be used to explain the grossly different behaviors of the Cesaro means of the cumulative sums of the average fractional parts (m = 2, m = 3) [150] n
l
1 1 è!!!!! n HmL = ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ‚ ‚ JfracIk m M - ÅÅÅÅÅÅ N. n+1 2 l=1 k=1
Here are the periodic continued fractions of In[31]:=
è!!!! è!!!! 2 and 3 .
ContinuedFraction /@ {Sqrt[2], Sqrt[3]}
Numerical Computations
72 Out[31]=
881, 82<<, 81, 81, 2<<<
This implements the function n HmL. In[32]:=
[m_, n_] := MapIndexed[#1/(#2[[1]] + 1)&, Rest[FoldList[Plus, 0, Rest[FoldList[Plus, 0, Table[FractionalPart[k m] - 1/2, {k, n}]]]]]]
And the following graphic shows n HmL as a function of n for m = In[33]:=
è!!!! è!!!! 2 and m = 3 .
Show[GraphicsArray[ ListPlot[[#, 10^5], Frame -> True, Axes -> False, PlotRange -> All, DisplayFunction -> Identity]& /@ Sqrt[{2., 3.}]]] 0.8
0.06
0.7 0.04 0.6 0.02
0.5
0
0.4 0.3
-0.02
0.2 -0.04
0.1 0
20000
40000
60000
80000
100000
0
20000
40000
60000
80000
100000
Only solutions of quadratic polynomials have periodic continued fraction expansions. So the following expansion of the real root of x3 - 8 x - 10 = 0 fails. (For some discussion of this special continued fraction, see [1705] and [404].) In[34]:=
Ξ = (135 - 3 Sqrt[489])^(1/3)/3 + (45 + Sqrt[489])^(1/3)/3^(2/3); ContinuedFraction[Ξ] ContinuedFraction::noterms : è!!!!!!!!!!! 1ê3 1 è!!!!!!!!!!! 1ê3 H45 + 489 L I135 − 3 489 M + does not have a terminating 3 32ê3 or periodic continued fraction expansion; specify an explicit number of terms to generate. More…
Out[35]=
è!!!!!!!!!! 1ê3 1 è!!!!!!!!!! 1ê3 H45 + 489 L ContinuedFractionA H135 − 3 489 L + E 3 32ê3
Specifying an explicit number of terms works, of course. In[36]:= Out[36]=
ContinuedFraction[Ξ, 150] 83, 5, 1, 1, 1, 7,
3, 7, 4, 2, 30, 1, 8, 3, 1, 1, 1, 9, 2, 2, 1, 3, 22986, 2, 1, 32, 8, 2, 1, 8, 55, 1, 2, 28, 1, 5, 1, 1501790, 1, 2, 1, 7, 6, 1, 1, 5, 2, 1, 6, 2, 2, 1, 2, 1, 1, 3, 3, 1, 2, 4, 3, 1, 35657, 1, 17, 2, 15, 1, 1, 2, 1, 1, 5, 3, 2, 1, 1, 7, 2, 1, 7, 3, 25, 49405, 1, 1, 3, 1, 1, 4, 1, 2, 15, 1, 2, 83, 1, 162, 2, 1, 1, 1, 2, 2, 53460, 1, 6, 4, 3, 4, 13, 5, 15, 6, 1, 4, 1, 4, 1, 1, 2, 1, 16467250, 1, 3, 1, 2, 6, 1, 95, 20, 1, 2, 1, 6, 1, 1, 8, 1, 48120, 1, 2, 17, 2, 1, 2, 1, 4, 2, 3<
There is an intimate relation between the function Rationalize and continued fractions: Let t be a positive real number. Every “best” approximation to t is a convergent of the regular continued fraction approximation of t. Here, the first 200 convergents of p are calculated. In[37]:=
convergents = With[{cf = ContinuedFraction[Pi, 200]}, Table[FromContinuedFraction[Take[cf, i]], {i, 200}]];
1.1 Approximate Numbers
73
The absolute value of the difference between the convergent An and p is a monotonic function of n. In[38]:= Out[38]=
N[Pi - Take[convergents, 20], 22] // N 80.141593, −0.00126449, 0.0000832196, −2.66764 × 10−7 , 5.77891 × 10−10 , −3.31628 × 10−10 , 1.22357 × 10−10 , −2.91434 × 10−11 , 8.71547 × 10−12 , −1.61074 × 10−12 , 4.04067 × 10−13 , −2.21448 × 10−14 , 5.79087 × 10−16 , −1.64084 × 10−16 , 7.81794 × 10−17 , −1.93638 × 10−17 , 3.07008 × 10−18 , −5.51366 × 10−19 , 7.62659 × 10−20 , −3.12317 × 10−20 <
In[39]:=
$MaxExtraPrecision = 1000;
In[40]:=
ListPlot[Log[10, Abs[N[(Pi - #)& /@ convergents, 22] // N]]] 50
100
150
200
-50
-100
-150
-200
If Ak = pk ê qk is a convergent of the continued fraction approximation of t, then † pk ê qk - t§ < 1 ê q2k [1543], [259] (the opposite does not hold). Here, for the above convergents this is visualized. In[41]:=
ListPlot[N[Denominator[#]^2 (Pi - #)& /@ convergents, 22] // N, PlotRange -> All] 0.75 0.5 0.25
50
100
150
200
-0.25 -0.5 -0.75
The following graphic shows the difference between p and its nearest rational approximation for denominators less than 105 in steps of 10 [800]. In[42]:=
ListPlot[Table[{q, With[{p = IntegerPart[q Pi]}, (* select nearest rational number for a given denominator *) Log[10, N[Min[Abs[{Pi - p/q, Pi - (p + 1)/q}]], 22]]]}, {q, 1, 10^5, 10}], Frame -> True, Axes -> False, PlotRange -> All, PlotStyle -> {PointSize[0.003]}]
Numerical Computations
74
-2
-4
-6
-8
0
20000
40000
60000
80000
100000
The clearly visible minima in the above picture occur at the denominators of the consecutive continued fraction approximations. In[43]:=
Out[43]=
Table[FromContinuedFraction[Take[ ContinuedFraction[Pi, 20], k]], {k, 8}] 22 333 355 103993 104348 208341 312689 93, , , , , , , = 7 106 113 33102 33215 66317 99532
The fact that the continued fraction convergents are the good approximations to a given number is used inside Rationalize. Here, the difference p - e is rationalized. In[44]:= Out[44]=
Table[Rationalize[N[Pi - E, 30], 10^-i], {i, 20}] 1 3 11 58 69 908 1253 6334 10093 9 , , , , , , , , , 2 7 26 137 163 2145 2960 14963 23843 79491 119863 469359 589222 13432243 22270573 , , , , , , 187784 283156 1108781 139