In order to make this data more accessible and easily computable, we created an internal version of the MGP data using the Wolfram Language’s entity framework. Using this dataset within the Wolfram Language allows one to easily make computations and visualizations that provide interesting and sometimes unexpected insights into mathematicians and their works. Note that for the time being, these entities are defined only in our private dataset and so are not (yet) available for general use.

The search interface to the MGP is illustrated in the following image. It conveniently allows searches based on a number of common fields, such as parts of a mathematician’s name, degree year, Mathematics Subject Classification (MSC) code and so on:

For a quick look at the available data from the MGP, consider a search for the prolific mathematician Paul Erdős made by specifying his first and last names in the search interface. It gives this result:

Clicking the link in the search result returns a list of available data:

Note that related mathematicians (i.e. advisors and advisees) present in the returned database results are hyperlinked. In contrast, other fields (such as school, degree years and so on), are not. Clearly, the MGP catalogs a wealth of information of interest to anyone wishing to study the history of mathematicians and mathematical research. Unfortunately, only relatively simple analyses of the underlying data are possible using a web-based search interface.

For those readers not familiar with the Wolfram Language entity framework, we begin by giving a number of simple examples of its use to obtain information about the `"MGPPerson"` entities we created. As a first simple computation, we use the `EntityValue` function to obtain a count of the number of people in the `"MGPPerson"` domain:

✕
EntityValue["MGPPerson","EntityCount"] |

Note that this number is smaller than the 230,000+ present in the database due to subsequent additions to the MGP. Similarly, we can return a random person:

✕
person=RandomEntity["MGPPerson"] |

Mousing over an “entity blob” such as in the previous example gives a tooltip showing the underlying Wolfram Language representation.

We can also explicitly look at the internal structure of the entity:

✕
InputForm[person] |

Copying, pasting and evaluating that expression to obtain the formatted version again:

✕
Entity["MGPPerson","94172"] |

We now extract the domain, canonical name and common name of the entity programmatically:

✕
Through[{EntityTypeName,CanonicalName,CommonName}[person]]//InputForm |

We can simultaneously obtain a set of random people from the `"MGPPerson"` domain:

✕
RandomEntity["MGPPerson",10] |

To obtain a list of properties available in the `"MGPPerson"` domain, we again use `EntityValue`:

✕
properties=EntityValue["MGPPerson","Properties"] |

As we did for entities, we can view the internal structure of the first property:

✕
InputForm[First[properties]] |

We can also view the string of canonical names of all the properties:

✕
CanonicalName[properties] |

The URL to the relevant MGP page is available directly as its own property, which can be done concisely as:

✕
EntityValue[person,"MathematicsGenealogyProjectURL"] |

… with an explicit `EntityProperty` wrapper:

✕
EntityValue[person,EntityProperty["MGPPerson","MathematicsGenealogyProjectURL"]] |

… or using a curried syntax:

✕
person["MathematicsGenealogyProjectURL"] |

We can also return multiple properties:

✕
person[{"AdvisedBy","Degrees","DegreeDates","DegreeSchoolEntities"}] |

Another powerful feature of the Wolfram Language entity framework is the ability to create an implicitly defined `Entity` class:

✕
EntityClass["MGPPerson","Surname"->"Nelson"] |

Expanding this class, we obtain a list of people with the given surname:

✕
SortBy[EntityList[EntityClass["MGPPerson","Surname"->"Nelson"]],CommonName] |

To obtain an overview of data for a given person, we can copy and paste from that list and query for the `"Dataset"` property using a curried property syntax:

✕
Entity["MGPPerson", "174871"]["Dataset"] |

As a first simple computation, we use the Wolfram Language function `NestGraph` to produce a ten-generation-deep mathematical advisor tree for mathematician Joanna “Jo” Nelson:

✕
NestGraph[#["AdvisedBy"]&,Entity["MGPPerson", "174871"],10,VertexLabels->Placed["Name",After,Rotate[#,30 Degree,{-3.2,0}]&]] |

Using an implicitly defined `EntityClass`, let’s now look up people with the last name “Hardy”:

✕
EntityList[EntityClass["MGPPerson","Surname"->"Hardy"]] |

Having found the Hardy we had in mind, it is now easy to make a mathematical family tree for the descendants of G. H. Hardy, highlighting the root scholar:

✕
With[{scholar=Entity["MGPPerson", "17806"]}, HighlightGraph[ NestGraph[#["Advised"]&,scholar,2,VertexLabels->Placed["Name",After,Rotate[#,30 Degree,{-3.2,0}]&],ImageSize->Large,GraphLayout->"RadialDrawing"], scholar] ] |

A fun example of the sort of computation that can easily be performed using the Wolfram Language is visualizing the distribution of mathematicians based on first and last initials:

✕
Histogram3D[Select[Flatten[ToCharacterCode[#]]&/@Map[RemoveDiacritics@StringTake[#,1]&,DeleteMissing[EntityValue["MGPPerson",{"GivenName","Surname"}],1,2],{2}],(65<=#[[1]]<=90&&65<=#[[2]]<=90)&],AxesLabel->{"given name","surname"},Ticks->({#,#,Automatic}&[Table[{j,FromCharacterCode[j]},{j,65,90}]])] |

As one might expect, mathematician initials (as well as those of all people in general) are not uniformly distributed with respect to the alphabet.

The Wolfram Language contains a powerful set of functionality involving geographic computation and visualization. We shall make heavy use of such functionality in the following computations.

It is interesting to explore the movement of mathematicians from the institutions where they received their degrees to the institutions at which they did their subsequent advising. To do so, first select mathematicians who received a degree in the 1980s:

✕
p1980=Select[DeleteMissing[EntityValue["MGPPerson",{"Entity",EntityProperty["MGPPerson","DegreeDates"]}],1,2],1980 |

Find where their students received their degrees:

✕
unitransition[person_]:=Module[{ds="DegreeSchoolEntities",advisoruni,adviseeunis},advisoruni=person[ds]; adviseeunis=#[ds]&/@DeleteMissing[Flatten[{person["Advised"]}]]; {advisoruni,adviseeunis}] |

Assume the advisors were local to the advisees:

✕
moves=Union[Flatten[DeleteMissing[Flatten[Outer[DirectedEdge,##]&@@@(unitransition/@Take[p1980,All]),2],2,1]]]; |

Now show the paths of the advisors:

✕
GeoGraphics[{Thickness[0.001],Opacity[0.1],Red,Arrowheads[0.01],Arrow@GeoPath[List@@#]&/@moves},GeoRange->"World",GeoBackground->"StreetMapNoLabels"]//Quiet |

We can also perform a number of computations involving mathematical degrees. As with the `"MGPPerson"` domain, we first briefly explore the contents of the `"MGPDegree"` domain and show how to access them.

To begin, show a count of the number of theses in the `"MGPDegree"` domain:

✕
EntityValue["MGPDegree","EntityCount"] |

List five random theses from the `"MGPDegree"` domain:

✕
RandomEntity["MGPDegree",5] |

Show available `"MGPDegree"` properties:

✕
EntityValue["MGPDegree","Properties"] |

Return a dataset of an `"MGPDegree"` entity:

✕
Entity["MGPDegree", "120366"]["Dataset"] |

Moving on, we now visualize the historical numbers of PhDs awarded worldwide:

✕
DateListLogPlot[phddata={#[[1,1]],Length[#]}&/@GatherBy[Cases[EntityValue["MGPDegree",{"Date","DegreeType"}],{_DateObject,"Ph.D."}],First], PlotRange->{DateObject[{#}]&/@{1800,2010},All}, GridLines->Automatic] |

We can now make a fit to the number of new PhD mathematicians over the period 1875–1975:

✕
fit=Fit[Select[{#1["Year"],1. Log[2,#2]}&@@@phddata,1875<#[[1]]<1975&],{1,y},y] |

This gives a doubling time of about 1.5 decades:

✕
Quantity[1/Coefficient[fit,y],"Years"] |

Let’s write a utility function to visualize the number of degrees conferred by a specified university over time:

✕
DegreeCountHistogram[school_,bin_,opts___]:=DateHistogram[DeleteMissing[EntityValue[EntityList[EntityClass["MGPDegree","SchoolEntity"->school]],"Date"]], bin,opts] |

Look up the University of Chicago entity of the `"University"` type in the Wolfram Knowledgebase:

✕
Interpreter["University"]["university of chicago"] |

Show the number of degrees awarded by the University of Chicago, binned by decade:

✕
DegreeCountHistogram[Entity["University", "UniversityOfChicago::726rv"],"Decades"] |

... and by year:

✕
DegreeCountHistogram[Entity["University", "UniversityOfChicago::726rv"],"Years",DateTicksFormat->"Year"] |

Now look at the national distribution of degrees awarded. Begin by again examining the structure of the data. In particular, there exist PhD theses with no institution specified in `"SchoolEntity"` but a country specified in `"SchoolLocation"`:

✕
TextGrid[Take[Cases[phds=EntityValue["MGPDegree",{"Entity","DegreeType","SchoolEntity","SchoolLocation"}],{_,"Ph.D.",_Missing,_List}],5],Dividers->All] |

There also exist theses with more than a single country specified in `"SchoolLocation"`:

✕
TextGrid[Cases[phds,{_,"Ph.D.",_Missing,_List?(Length[#]!=1&)}],Dividers->All] |

Tally the countries (excluding the pair of multiples):

✕
TextGrid[Take[countrytallies=Reverse@SortBy[Tally[Cases[phds,{_,"Ph.D.",_,{c_Entity}}:>c]],Last],UpTo[10]],Alignment->{{Left,Decimal}},Dividers->All] |

A total of 117 countries are represented:

✕
Length[countrytallies] |

Download flag images for these countries from the Wolfram Knowledgebase:

✕
Take[flagdata=Transpose[{EntityValue[countrytallies[[All,1]],"Flag"],countrytallies[[All,2]]}],5] |

Create an image collage of flags, with the flags sized according to the number of math PhDs:

✕
ImageCollage[Take[flagdata,40],ImagePadding->3] |

As another example, we can explore degrees awarded by a specific university. For example, extract mathematics degrees that have been awarded at the University of Miami since 2010:

✕
Length[umiamidegrees=EntityList[ EntityClass["MGPDegree",{ "SchoolEntity"->Entity["University", "UniversityOfMiami::9c2k9"], "Date"-> GreaterEqualThan[DateObject[{2010}]]} ]]] |

Create a timeline visualization:

✕
TimelinePlot[Association/@Rule@@@EntityValue[umiamidegrees,{"Advisee","Date"}],ImageSize->Large] |

Now consider recent US mathematics degrees. Select the theses written at US institutions since 2000:

✕
Length[USPhDs=Cases[Transpose[{ EntityList["MGPDegree"], EntityValue["MGPDegree","SchoolLocation"], EntityValue["MGPDegree","Date"] }], { th_, loc_?(ContainsExactly[{Entity["Country", "UnitedStates"]}]),DateObject[{y_?(GreaterEqualThan[2000])},___] }:>th ]] |

Make a table showing the top US schools by PhDs conferred:

✕
TextGrid[Take[schools=Reverse[SortBy[Tally[Flatten[EntityValue[USPhDs,"SchoolEntity"]]],Last]],12],Alignment->{{Left,Decimal}},Dividers->All] |

Map schools to their geographic positions:

✕
geopositions=Rule@@@DeleteMissing[Transpose[{EntityValue[schools[[All,1]],"Position"],schools[[All,2]]}],1,2]; |

Visualize the geographic distribution of US PhDs :

✕
GeoBubbleChart[geopositions,GeoRange->Entity["Country", "UnitedStates"]] |

Show mathematician thesis production as a smooth kernel histogram over the US:

✕
GeoSmoothHistogram[Flatten[Table[#1,{#2}]&@@@geopositions],"Oversmooth",GeoRange->GeoVariant[Entity["Country", "UnitedStates"],Automatic]] |

We now make some explorations of the titles of mathematical theses.

To begin, extract theses authored by people with the surname “Smith”:

✕
Length[smiths=EntityList[EntityClass["MGPPerson","Surname"->"Smith"]]] |

Create a `WordCloud` of words in the titles:

✕
WordCloud[DeleteStopwords[StringRiffle[EntityValue[DeleteMissing[Flatten[EntityValue[smiths,"Degrees"]]],"ThesisTitle"]]]] |

Now explore the titles of all theses (not just those written by Smiths) by extracting thesis titles and dates:

✕
tt=DeleteMissing[EntityValue["MGPDegree",{"Date","ThesisTitle"}],1,2]; |

The average string length of a thesis is remarkably constant over time:

✕
DateListPlot[{#[[1,1]],Round[Mean[StringLength[#[[All,-1]]]]]}&/@SplitBy[Sort[tt],First], PlotRange->{DateObject[{#}]&/@{1850,2010},All}] |

The longest thesis title on record is this giant:

✕
SortBy[tt,StringLength[#[[2]]]&]//Last |

Motivated by this, extract explicit fragments appearing in titles:

✕
tex=Cases[ImportString[#,"TeX"]&/@Flatten[DeleteCases[StringCases[#2,Shortest["$"~~___~~"$"]]&@@@tt,{}]],Cell[_,"InlineFormula",___],∞]//Quiet; |

... and display them in a word cloud:

✕
WordCloud[DisplayForm/@tex] |

Extract types of topological spaces mentioned in thesis titles and display them in a ranked table:

✕
TextGrid[{StringTrim[#1],#2}&@@@Take[Select[Reverse[SortBy[Tally[Flatten[DeleteCases[StringCases[#2,Shortest[" ",((LetterCharacter|"_")..)~~(" space"|"Space ")]]&@@@tt,{}]]],Last]], Not[StringMatchQ[#[[1]],(" of " | " in " |" and "|" the " | " on ")~~__]]&],12],Dividers->All,Alignment->{{Left,Decimal}}] |

Get all available Mathematics Subject Classification (MSC) category descriptions for mathematics degrees conferred by the University of Oxford and construct a word cloud from them:

✕
WordCloud[DeleteMissing[EntityValue[EntityList[EntityClass["MGPDegree","SchoolEntity"->Entity["University", "UniversityOfOxford::646mq"]]],"MSCDescription"]],ImageSize->Large] |

Explore the MSC distribution of recent theses. To begin, `Iconize` a list to use that holds MSC category names that will be used in subsequent examples:

✕
mscnames=List; |

Extract degrees awarded since 2010:

✕
Length[degrees2010andlater=Cases[Transpose[{EntityList["MGPDegree"],EntityValue["MGPDegree","Date" ]}],{th_,DateObject[{y_?(GreaterEqualThan[2010])},___]}:>th]] |

Extract the corresponding MSC numbers:

✕
degreeMSCs=DeleteMissing[EntityValue[degrees2010andlater,"MSCNumber"]]; |

Make a pie chart showing the distribution of MSC category names and numbers:

✕
With[{counts=Sort[Counts[degreeMSCs],Greater][[;;20]]},PieChart[Values[counts],ChartLegends->(Row[{#1,": ",#2," (",#3,")"}]&@@@(Flatten/@Partition[Riffle[Keys@counts,Partition[Riffle[(Keys@counts/.mscnames),ToString/@Values@counts],2]],2])),ChartLabels->Placed[Keys@counts,"RadialCallout"],ChartStyle->24,ImageSize->Large]] |

Extract the MSC numbers for theses since 1990 and tally the combinations of {year, MSC}:

✕
msctallies=Tally[Sort[Cases[DeleteMissing[EntityValue["MGPDegree",{"Date","MSCNumber"}],1,2], {DateObject[{y_?(GreaterEqualThan[1990])},___],msc_}:>{y,msc}]]] |

Plot the distribution of MSC numbers (mouse over the graph in the attached notebook to see MSC descriptions):

✕
Graphics3D[With[{y=#[[1]],msc=ToExpression[#[[2]]],off=1/3},Tooltip[Cuboid[{msc-off,y-off,0},{msc+off,y+off,#2}], #[[2]]/.mscnames]]&@@@msctallies,BoxRatios->{1,1,0.5},Axes->True, AxesLabel->{"MSC","year","thesis count"},Ticks->{None,Automatic,Automatic}] |

Most students do research in the same area as their advisors. Investigate systematic transitions from MSC classifications of advisors’ works to those of their students. First, write a utility function to create a list of MSC numbers for an advisor’s degrees and those of each advisee:

✕
msctransition[person_]:=Module[{msc="MSCNumber",d="Degrees",advisormsc,adviseemscs,dm=DeleteMissing}, advisormsc=#[msc]&/@person[d]; adviseemscs=#[msc]&/@Flatten[#[d]&/@dm[Flatten[{person["Advised"]}]]]; dm[{advisormsc,{#}}&/@DeleteCases[adviseemscs,Alternatives@@advisormsc],1,2]] |

For example, for Maurice Fréchet:

✕
TextGrid[msctransition[Entity["MGPPerson", "17947"]]/.mscnames,Dividers->All] |

Find MSC transitions for degree dates after 1988:

✕
transitiondata=msctransition/@Select[DeleteMissing[ EntityValue["MGPPerson",{"Entity","DegreeDates"}],1,2],Min[#["Year"]&/@#[[2]]]>1988&][[All,1]]; |

✕
transitiondataaccumulated=Tally[Flatten[Apply[Function[{a,b},Outer[DirectedEdge,a,b]], Flatten[Take[transitiondata,All],1],{1}],2]]/.mscnames; |

✕
toptransitions=Select[transitiondataaccumulated,Last[#]>10&]/.mscnames; |

✕
Grid[Reverse[Take[SortBy[transitiondataaccumulated,Last],-10]],Dividers->Center,Alignment->Left] |

✕
msctransitiongraph=Graph[First/@toptransitions,EdgeLabels->Placed["Name",Tooltip],VertexLabels->Placed["Name",Tooltip],GraphLayout->"HighDimensionalEmbedding"]; |

✕
With[{max=Max[Last/@toptransitions]}, HighlightGraph[msctransitiongraph,Style[#1,Directive[Arrowheads[0.05(#2/max)^.5],ColorData["DarkRainbow"][(#2/max)^6.],Opacity[(#2/max)^.5],Thickness[0.005(#2/max)^.5]]]&@@@transitiondataaccumulated]] |

Construct a list of directed edges from advisors to their students:

✕
Length[advisorPairs=Flatten[Function[{a,as},DirectedEdge[a,#]&/@as]@@@DeleteMissing[EntityValue["MGPPerson",{"Entity","Advised"}],1,2]]] |

Some edges are duplicated because the same student-advisor relationship exists for more than one degree:

✕
SelectFirst[Split[Sort[advisorPairs]],Length[#]>1&] |

For example:

✕
(EntityValue[Entity["MGPPerson", "110698"],{"AdvisedBy","Degrees"}]/.e:Entity["MGPDegree",_]:>{e,e["DegreeType"]}) |

So build an explicit advisor graph by uniting the {advisor, advisee} pairs:

✕
advisorGraph=Graph[Union[advisorPairs],GraphLayout->None] |

The advisor graph contains more than 3,500 weakly connected components:

✕
Length[graphComponents=WeaklyConnectedGraphComponents[advisorGraph]] |

Visualize component sizes on a log-log plot:

✕
ListLogLogPlot[VertexCount/@graphComponents,Joined->True,Mesh->All,PlotRange->All] |

Find the size of the giant component (about 190,000 people):

✕
VertexCount[graphComponents[[1]]] |

Find the graph center of the second-largest component:

✕
GraphCenter[UndirectedGraph[graphComponents[[2]]]] |

Visualize the entire second-largest component:

✕
Graph[graphComponents[[2]],VertexLabels->"Name",ImageSize->Large] |

Identify the component in which David Hilbert resides:

✕
FirstPosition[VertexList/@graphComponents,Entity["MGPPerson", "7298"]][[1]] |

Show Hilbert’s students:

✕
With[{center=Entity["MGPPerson", "7298"]},HighlightGraph[Graph[Thread[center->AdjacencyList[graphComponents[[1]],center]],VertexLabels->"Name",ImageSize->Large],center]] |

As it turns out, the mathematician Gaston Darboux plays an even more central role in the advisor graph. Here is some detailed information about Darboux, whose 1886 thesis was titled “Sur les surfaces orthogonales”:

✕
Entity["MGPPerson", "34254"] ["PropertyAssociation"] |

And here is a picture of Darboux:

✕
Show[WikipediaData["Gaston Darboux","ImageList"]//Last,ImageSize->Small] |

Many mathematical constructs are named after Darboux:

✕
Select[EntityValue["MathWorld","Entities"],StringMatchQ[#[[2]],"*Darboux*"]&] |

... and his name can even be used in adjectival form:

✕
StringCases[Normal[WebSearch["Darbouxian *",Method -> "Google"][All,"Snippet"]], "Darbouxian"~~" " ~~(LetterCharacter ..)~~" " ~~(LetterCharacter ..)]//Flatten//DeleteDuplicates // Column |

Many well-known mathematicians are in the subtree starting at Darboux. In particular, in the directed advisor graph we find a number of recent Fields Medal winners. Along the way, we also see many well-known mathematicians such as Laurent Schwartz, Alexander Grothendieck and Antoni Zygmund:

✕
{path1,path2,path3,path4}=(DirectedEdge@@@Partition[FindShortestPath[graphComponents[[1]],Entity["MGPPerson", "34254"],#],2,1])&/@ {Entity["MGPPerson", "13140"],Entity["MGPPerson", "22738"],Entity["MGPPerson", "43967"],Entity["MGPPerson", "56307"]} |

Using the data from the `EntityStore`, we build the complete subgraph starting at Darboux:

✕
adviseeedges[pList_]:=Flatten[Function[p,DirectedEdge[Last[p],#]&/@ DeleteMissing[Flatten[{Last[p][advised]}]]]/@pList] |

✕
advgenerations=Rest[NestList[adviseeedges,{Null->Entity["MGPPerson", "34254"]},7]]; |

✕
alladv=Flatten[advgenerations]; |

It contains more than 14,500 mathematicians:

✕
Length[Union[Cases[alladv,_Entity,∞]]]-1 |

Because it is a complicated graph, we display it in 3D to avoid overcrowded zones. Darboux sits approximately in the center:

✕
gr3d=Graph3D[alladv,GraphLayout->"SpringElectricalEmbedding"] |

We now look at the degree centrality of the nodes of this graph in a log-log plot:

✕
ListLogLogPlot[Tally[DegreeCentrality[gr3d]]] |

Let’s now highlight the path to that plot for Fields Medal winners:

✕
style[path_,color_]:=Style[#,color,Thickness[0.004]]&/@path |

✕
HighlightGraph[gr3d, Join[{Style[Entity["MGPPerson", "34254"],Orange,PointSize[Large]]}, style[path1,Darker[Red]],style[path2,Darker[Yellow]],style[path3,Purple], style[path4,Darker[Green]]]] |

Geographically, Darboux’s descendents are distributed around the whole world:

✕
makeGeoPath[e1_e2_] := With[{s1=e1["DegreeSchoolEntities"],s2=e2["DegreeSchoolEntities"],d1=e1["DegreeDates"],d2=e2["DegreeDates"],color=ColorData["DarkRainbow"][(Mean[{#1[[1,1,1]],#2[[1,1,1]]}]-1870)/150]&}, If[MemberQ[{s1,s2,d1,d2},_Missing,∞]||s1===s2,{},{Thickness[0.001],color[d1,d2],Arrowheads[0.012],Tooltip[Arrow[GeoPath[{s1[[1]],s2[[1]]}]], Grid[{{"","advisor","advisee"},{"name",e1,e2},Column/@{{"school"},s1,s2}, Column/@{{"degree date"},d1,d2}},Dividers->Center]]}]] |

Here are the paths from the advisors’ schools to the advisees’ schools after four and six generations:

✕
GeoGraphics[makeGeoPath/@Flatten[Take[advgenerations,4]],GeoBackground->"StreetMapNoLabels",GeoRange->"World"]//Quiet |

✕
GeoGraphics[makeGeoPath /@ Flatten[Take[advgenerations, 6]], GeoBackground -> "StreetMapNoLabels", GeoRange -> "World"] // Quiet |

Extract a list of advisors and the dates at which their advisees received their PhDs:

✕
Take[AdvisorsAndStudentPhDDates=SplitBy[Sort[Flatten[Thread/@Cases[EntityValue["MGPDegree",{"Advisors","DegreeType","Date"}],{l_List,"Ph.D.",DateObject[{y_},___]}:>{l,y}],1]],First],5] |

This list includes multiple student PhD dates for each advisor, so select the dates of the first students’ PhDs only:

✕
Take[AdvisorsAndFirstStudentPhDDates=DeleteCases[{#[[1,1]],Min[DeleteMissing[#[[All,2]]]]}&/@AdvisorsAndStudentPhDDates,{_,Infinity}],10] |

Now extract a list of PhD awardees and the dates of their PhDs:

✕
Take[PhDAndDates=DeleteCases[Sort[Cases[EntityValue["MGPDegree",{"Advisee","DegreeType","Date"}],{p_,"Ph.D.",DateObject[{y_},___]}:>{p,y}]],{_Missing,_}],10] |

Note that some advisors have more than one PhD:

✕
Select[SplitBy[PhDAndDates,First],Length[#]>1&]//Take[#,5]&//Column |

For example:

✕
Entity["MGPPerson", "100896"]["Degrees"] |

... who has these two PhDs:

✕
EntityValue[%,{"Date","DegreeType","SchoolName"}] |

While having two PhDs is not unheard of, having three is unique:

✕
Tally[Length/@SplitBy[PhDAndDates,First]] |

In particular:

✕
Select[SplitBy[PhDAndDates,First],Length[#]===3&] |

Select the first PhDs of advisees and make a set of replacement rules to their first PhD dates:

✕
Take[FirstPhDDateRules=Association[Thread[Rule@@@SplitBy[PhDAndDates,First][[All,1]]]],5] |

Now replace advisors by their first PhD years and subtract from the year of their first students’ PhDs:

✕
Take[times=-Subtract@@@(AdvisorsAndFirstStudentPhDDates/.FirstPhDDateRules),10] |

The data contains a small number of discrepancies where students allegedly received their PhDs prior to their advisors:

✕
SortBy[Select[Transpose[{AdvisorsAndFirstStudentPhDDates[[All,1]],AdvisorsAndFirstStudentPhDDates/.FirstPhDDateRules}],GreaterEqual@@#[[2]]&],-Subtract@@#[[2]]&]//Take[#,10]& |

Removing these problematic points and plotting a histogram reveals the distribution of years between advisors’ and first advisees’ PhDs:

✕
Histogram[Cases[times,_?Positive]] |

We hope you have found this computational exploration of mathematical genealogy of interest. We thank Mitch Keller and the Mathematics Genealogy Project for their work compiling and maintaining this fascinating and important dataset, as well as for allowing us the opportunity to explore it using the Wolfram Language. We hope to be able to freely expose a Wolfram Data Repository version of the MGP dataset in the near future so that others may do the same.

]]>One of the many beautiful aspects of mathematics is that often, things that look radically different are in fact the same—or at least share a common core. On their faces, algorithm analysis, function approximation and number theory seem radically different. After all, the first is about computer programs, the second is about smooth functions and the third is about whole numbers. However, they share a common toolset: asymptotic relations and the important concept of asymptotic scale.

By comparing the “important parts” of two functions—a common trick in mathematics—asymptotic analysis classifies functions based on the relative size of their absolute values near a particular point. Depending on the application, this comparison provides quantitative answers to questions such as “Which of these algorithms is fastest?” or “Is function a good approximation to function *g*?”. Version 11.3 of the Wolfram Language introduces six of these relations, summarized in the following table.

The oldest (and probably the most familiar of the six relations) is `AsymptoticLessEqual`, which is commonly called big O or big Omicron. It was popularized by Paul Bachmann in the 1890s in his study of analytic number theory (though the concept had appeared earlier in the work of Paul du Bois-Reymond). At a point , is asymptotically less than or equal to , written , if for some constant for all near . This captures the notion that cannot become arbitrarily larger in magnitude than . Bachmann used this in his study of sums and the growth rate of number theoretic functions to show that he could split complicated sums into two parts: a leading part with an explicit form, and a subleading part without a concrete expression. The subleading part could, however, be shown to be asymptotically less than or equal to some other function that is, for some reason, unimportant compared with the leading part, and therefore only the leading part needed to be kept. Donald Knuth would popularize the notion of big O in computer science, using it to sort algorithms from fastest to slowest by whether the run time of one is asymptotically less than or equal to the next at infinity.

`AsymptoticLess`, also called little O or little Omicron, came next. Introduced by Edmund Landau approximately 15 years after Bachmann’s work (leading to the name “Bachmann–Landau symbols” for asymptotic relations in certain disciplines), it quantified the notion of the unimportance of the subleading part. In particular, is asymptotically less than , written , if for all constants and all near . The condition that the inequality holds for all positive , not just a single , means that can be made arbitrarily smaller in magnitude compared to *g*. Thus, the essentially equals . `AsymptoticLess` is also important in the analysis of algorithms, as it allows strengthening statements from “algorithm a is no slower than algorithm b” to “algorithm a is faster than algorithm b.”

After this point, the history becomes rather complicated, so for the time being we’ll skip to the 1970s, when Knuth popularized `AsymptoticEqual` (commonly called big Theta). This captures the notion that neither function is ignorable compared with the other near the point of interest. More formally, is asymptotically equal to , written , if for some constant and , for all near . After exploring these first three relations with examples, both the history and the other relations will be easily explained and understood.

Consider three simple polynomials: , and . The two linear polynomials are both asymptotically less than and asymptotically less than or equal to the quadratic one at infinity:

✕
AsymptoticLessEqual[x,x^2,x->∞] |

Even though for many values of , because eventually will become bigger and continue increasing in size:

✕
AsymptoticLess[10^5 x,x^2,x->∞] |

On the other hand, is not asymptotically less than . Even though is always smaller, the ratio is a constant instead of going to zero:

✕
AsymptoticLess[x,10^5 x,x->∞] |

Indeed, the two linear polynomials are asymptotically equal because their ratio stays in a fixed range away from zero:

✕
AsymptoticEqual[10^5 x,x,x->∞] |

The linear polynomials are not asymptotically equal to the quadratic one, however:

✕
AsymptoticEqual[10^6 x,x^2,x->∞] |

The following log-log plot illustrates the relationships among the three functions. The constant offset in the log-log scale between the two linear functions shows that they are asymptotically equal, while their smaller slopes with respect to show that the former are asymptotically less than the latter.

✕
LogLogPlot[{Abs[x],Abs[10^5 x],Abs[x^2]},{x,10,10^9},PlotLegends->"Expressions"] |

A typical example for the application of these examples concerns analyzing the running time of an algorithm. A classic example is the merge sort. This sort works by recursively splitting a list in two, sorting each half and then combining them in sorted order. The following diagram illustrates these steps:

The time to sort elements will be the sum of some constant time to compute the middle, to sort each half and some multiple of the number of elements to combine the two halves (where and are determined by the particular computer on which the algorithm is run):

✕
reqn=T[n]==2T[n/2]+ a n +b |

In this particular case, solving the recurrence equation to find the time to sort elements is straightforward:

✕
t=RSolveValue[reqn, T[n],n]//Expand |

Irrespective of the particular values of and and the constant of summation , , and thus the algorithm is said to have run time:

✕
AsymptoticEqual[t,n Log[n],n->∞,Assumptions->a>0] |

Any other algorithm that has run time takes roughly the same amount of time for large inputs. On the other hand, any algorithm with run time , such as radix sort, will be faster for large enough inputs, because :

✕
AsymptoticLess[n,n Log[n],n->∞] |

Conversely, any algorithm with run time , such as bubble sort, will be slower for large inputs, as :

✕
AsymptoticLess[n Log[n],n^2,n->∞] |

Another set of applications for `AsymptoticEqual` comes from convergence testing. Two functions that are asymptotically equal to each other will have the same summation or integration convergence—for example, at infinity:

✕
AsymptoticEqual[1/n,ArcCot[n], n->∞] |

It is well known that the sum of , known as the harmonic series, diverges:

✕
DiscreteLimit[Sum[1/n,{n,1,k}],k->∞] |

Thus, must also diverge:

✕
SumConvergence[ArcCot[n], n] |

Be careful: although the name `AsymptoticLessEqual` suggests a similarity to the familiar operator, the former is a partial order, and not all properties carry over. For example, it is the case that for any two real numbers and , either or , but it is not true that for any two functions either or :

✕
{AsymptoticLessEqual[Sin[1/x],x,x->0],AsymptoticLessEqual[x,Sin[1/x],x->0]} |

Similarly, if , then it is true that either or . But it is possible for to be true while both and are false:

✕
{AsymptoticLessEqual[Sin[x],1,x->∞],AsymptoticLess[Sin[x],1,x->∞],AsymptoticEqual[1,Sin[x],x->∞]} |

Because `AsymptoticLessEqual` is a partial order, there are two possibilities for what `AsymptoticGreaterEqual` (also called big Omega) could mean. One option is the logical negation of `AsymptoticLessEqual`, i.e. iff . In the previous example, then, 1 and are each asymptotically greater than or equal to each other. This captures the notion that is never less than some fixed multiple of even if the relative sizes of the two functions change infinitely many times close to . Another sensible definition for `AsymptoticGreaterEqual` would be simply the notational reverse of `AsymptoticLessEqual`, i.e. iff . This captures the notion that is eventually no greater than some fixed multiple of in magnitude. Similar considerations apply to `AsymptoticGreater`, also called little Omega.

Historically, Godfrey Harold Hardy and John Edensor Littlewood first used and popularized `AsymptoticGreaterEqual` in their seminal work on series of elliptic functions in 1910s, using the first definition. This definition is still presently used in analytic number theory. In the 1970s, Knuth proposed that the first definition is not widely used, and that the second definition would be more useful. This has become the standard in the analysis of algorithms and related fields. The Wolfram Language follows the second definition as well. Knuth also proposed using a similar definition for `AsymptoticGreater`, i.e. iff , which is used in computer science.

The last of the newly introduced relations, `AsymptoticEquivalent`, also comes from Hardy’s work in the early part of the 20th century. Roughly speaking, if their ratio approaches 1 at the limit point. More formally, is asymptotically equivalent to if for all constants and all near . Put another way, iff . Hence, asymptotic equivalence captures the notion of approximation with small relative error, also called asymptotic approximation. A well-known example of such an approximation is Stirling’s approximation for the factorial function:

✕
s[n_]:=Sqrt[2π n] (n/E)^n |

This function is asymptotically equivalent to the factorial function:

✕
AsymptoticEquivalent[n!,s[n],n->∞] |

This means the relative error, the size of the difference relative to the size of the factorial function, goes to zero at infinity:

✕
Underscript[, n->∞](n!-s[n])/n! |

Note that this is only a statement about relative error. The actual difference between and blows up at infinity:

✕
Underscript[, n->∞](n!-s[n]) |

Because asymptotic approximation only demands a small relative error, it can be used to approximate many more classes of functions than more familiar approximations, such as Taylor polynomials. However, by Taylor’s theorem, every differentiable function is asymptotically equivalent to each of its Taylor polynomials. For example, the following computation shows that is equivalent to each of its first three Maclaurin polynomials:

✕
{AsymptoticEquivalent[1,E^x,x->0],AsymptoticEquivalent[1+x,E^x,x->0],AsymptoticEquivalent[1+x+x^2/2,E^x,x->0]} |

Yet is also asymptotically equivalent to many other polynomials:

✕
AsymptoticEquivalent[1+2x,E^x,x->0] |

Plotting the relative errors for each of the four polynomials shows that it does go to zero for all of them:

✕
Plot[{Abs[(1-E^x)/E^x],Abs[(1+x-E^x)/E^x],Abs[(1+x+x^2/2-E^x)/E^x],Abs[(1+2x-E^x)/E^x]},{x,-.1,.1},PlotLegends->"Expressions",ImageSize->Medium] |

What, then, makes the first-order and second-order polynomials better than the zeroth? In the previous plot, they seem to be going to zero faster than the linear polynomials, but this needs to be made quantitative. For this, it is necessary to introduce an asymptotic scale, which is a family of functions for which . For Maclaurin series, that family is . Each monomial is, in fact, asymptotically greater than the one before:

✕
AsymptoticGreater[x^m,x^n,x->0,Assumptions->m |

Once an asymptotic scale has been defined, the error in the -order approximation can be compared not with the original function but with the member of the asymptotic scale. If that error is small, then the approximation is valid to order . Each of the three Maclaurin polynomials for has this property, again by Taylor’s theorem:

✕
{AsymptoticLess[1-Exp[x],1,x->0],AsymptoticLess[1+x-Exp[x],x,x->0],AsymptoticLess[1+x+x^2/2-Exp[x],x^2,x->0]} |

On the other hand, while is a valid zeroth-order approximation to at 0, it is not a valid first-order approximation:

✕
{AsymptoticLess[1+2x-Exp[x],1,x->0],AsymptoticLess[1+2x-Exp[x],x,x->0]} |

Indeed, is the only linear polynomial that is a first-order approximation to at 0 using the asymptotic scale . Visualizing the ratio of to and it is clear that the error is small with respect to but not with respect to . The ratio of the error to goes to 1, though any positive number would indicate is false:

✕
Plot[{Abs[1+2x-E^x],Abs[(1+2x-E^x)/x]},{x,-.1,.1},PlotLegends->"Expressions",ImageSize->Medium] |

The scale , often called the Taylor or power scale, is the simplest and most familiar of a huge number of different useful scales. For example, the Laurent scale is used to expand functions in the complex plane. In “Getting to the Point: Asymptotic Expansions in the Wolfram Language,” my colleague Devendra Kapadia showed how different scales arise when finding approximate solutions using the new functions `AsymptoticDSolveValue` and `AsymptoticIntegrate`. For example, the asymptotic scale (a type of Puiseux scale) comes up when solving Bessel’s equation, the asymptotic scale is needed to approximate the integraland Airy’s equation leads to the scale . We can verify that each of these indeed forms a scale by creating a small wrapper around `AsymptoticGreater`:

✕
AsymptoticScaleQ[list_,x_->x0_]:=And@@BlockMap[AsymptoticGreater[#1[[1]],#1[[2]],x->x0]&,list,2,1] |

The first few examples are asymptotic scales at 0:

✕
AsymptoticScaleQ[{1/x^2,1/x,1,x,x^2},x->0] |

✕
AsymptoticScaleQ[{x^(1/2),x^(5/2),x^(9/2),x^(13/2)},x->0] |

The last two, however, are asymptotic scales at ∞:

✕
AsymptoticScaleQ[{E^ω/ω^(1/2),E^ω/ω^(3/2),E^ω/ω^(5/2)},ω->∞] |

✕
AsymptoticScaleQ[{E^(-((2 x^(3/2))/3)) x^(-1/4),E^(-((2 x^(3/2))/3)) x^(-7/4),E^(-((2 x^(3/2))/3)) x^(-13/4)},x->∞] |

In computer science, algorithms are rated by whether they are linear, quadratic, exponential, etc. (in other words, whether their run times are asymptotically less than or equal to particular monomials, the exponential function, etc.). However, the preferred exponential scale is different— rather than . Thus, in addition to , they also consider . These are both asymptotic scales:

✕
AsymptoticScaleQ[{n^3,n^2,n,1},n->∞] |

✕
AsymptoticScaleQ[{2^n^4,2^n^3 ,2^n^2,2^n},n->∞] |

Problems are then classified by the run time scale of the fastest algorithm for solving them. Those that can be solved in polynomial time are said to be in , while problems that require an exponential time algorithm are in . The famous problem asks whether the class of problems that can be verified in polynomial time can also be solved in polynomial time. If then it is theoretically possible that , i.e. all problems solvable in exponential time are verifiable in polynomial time.

The power of asymptotic relations comes from the fact that they provide the means to define asymptotic scales, but the particular choice of scale and how it is used is determined by the application. In function approximation, the scales define asymptotic expansions—families of better and better asymptotic approximations using a given a scale. Depending on the function, different scales are possible. The examples in this blog illustrate power and exponential scales, but there are also logarithmic, polynomial and many other scales. In computer science, the scales are used for both theoretical and practical purposes to analyze and classify problems and programs. In number theory, scales are chosen to analyze the distribution of primes or other special numbers. But no matter what the application, the Wolfram Language gives you the tools to study them. Make sure you download your free trial of Wolfram|One in order to give Version 11.3 of the Wolfram Language a try!

Asymptotic expansions have played a key role in the development of fields such as aerodynamics, quantum physics and mathematical analysis, as they allow us to bridge the gap between intricate theories and practical calculations. Indeed, the leading term in such an expansion often gives more insight into the solution of a problem than a long and complicated exact solution. Version 11.3 of the Wolfram Language introduces two new functions, `AsymptoticDSolveValue` and `AsymptoticIntegrate`, which compute asymptotic expansions for differential equations and integrals, respectively. Here, I would like to give you an introduction to asymptotic expansions using these new functions.

The history of asymptotic expansions can be traced back to the seventeenth century, when Isaac Newton, Gottfried Leibniz and others used infinite series for computing derivatives and integrals in calculus. Infinite series continued to be used during the eighteenth century for computing tables of logarithms, power series representations of functions and the values of constants such as π. The mathematicians of this era were aware that many series that they encountered were divergent. However, they were dazzled by the power of divergent series for computing numerical approximations, as illustrated by the Stirling series for `Gamma`, and hence they adopted a pragmatic view on the issue of divergence. It was only in the nineteenth century that Augustin-Louis Cauchy and others gave a rigorous theory of convergence. Some of these rigorists regarded divergent series as the devil’s invention and sought to ban their use in mathematics forever! Fortunately, eighteenth-century pragmatism ultimately prevailed when Henri Poincaré introduced the notion of an asymptotic expansion in 1886.

Asymptotic expansions refer to formal series with the property that a truncation of such a series after a certain number of terms provides a good approximation for a function near a point. They include convergent power series as well as a wide variety of divergent series, some of which will appear in the discussion of `AsymptoticDSolveValue` and `AsymptoticIntegrate` that follows.

As a first example for `AsymptoticDSolveValue`, consider the linear differential equation for `Cos`:

✕
deqn={(y^′′)[x]+y[x]==0,y[0]==1,(y^′)[0]==0}; |

The following input returns a Taylor series expansion up to order 8 around 0 for the cosine function:

✕
sol = AsymptoticDSolveValue[deqn, y[x], {x, 0, 8}] |

Here is a plot that compares the approximate solution with the exact solution :

✕
Plot[Evaluate[{sol, Cos[x]}], {x, 0, 3 π}, PlotRange -> {-2, 5},PlotLegends->"Expressions"] |

Notice that the Taylor expansion agrees with the exact solution for a limited range of near 0 (as required by the definition of an asymptotic expansion), but then starts to grow rapidly due to the polynomial nature of the approximation. In this case, one can get progressively better approximations simply by increasing the number of terms in the series. The approximate solution then wraps itself over larger portions of the graph for the exact solution:

✕
nsol[n_]:=Callout[AsymptoticDSolveValue[{y''[x]+y[x]==0,y[0]==1,y'[0]==0},y[x],{x,0,n}],n] |

✕
Plot[{nsol[4],nsol[8],nsol[12],nsol[16],nsol[20],Cos[x]}//Evaluate,{x,0,3Pi},PlotRange->{-2,5}] |

Next, consider Bessel’s equation of order , which is given by:

✕
besseleqn= x^2 (y^′′)[x]+x (y^′)[x]+(x^2-1/4) y[x]==0; |

This linear equation has a singularity at in the sense that when , the order of the differential equation decreases because the term in becomes 0. However, this singularity is regarded as a mild problem because dividing each term in the equation by results in a pole of order 1 in the term for and a pole of order 2 for . We say that is a regular singular point for the differential equation and, in such cases, there is a Frobenius series solution that is computed here:

✕
sol=AsymptoticDSolveValue[besseleqn,y[x],{x,0,24}] |

Notice that there are fractional powers in the solution, and that only the second component has a singularity at . The following plot shows the regular and singular components of the solution:

✕
Plot[{sol /. {C[1] -> 1, C[2] -> 0}, sol /. {C[1] -> 0, C[2] ->1}}//Evaluate, {x, 0,3π}, PlotRange -> {-2, 2}, WorkingPrecision -> 20,PlotLegends->{"regular solution", "singular solution"}] |

These solutions are implemented as `BesselJ` and `BesselY`, respectively, in the Wolfram Language, with a particular choice of constant multiplying factor :

✕
Series[{BesselJ[1/2,x],-BesselY[1/2,x]},{x,0,8}]//Normal |

As a final example of a linear differential equation, let us consider the Airy equation, which is given by:

✕
airyode=(y^′′)[x]-x y[x]==0; |

This equation has an irregular singular point at , which may be seen by setting , and then letting approach 0, so that approaches . At such a point, one needs to go beyond the Frobenius scale, and the solution consists of asymptotic series with exponential factors:

✕
AsymptoticDSolveValue[airyode, y[x], {x, ∞, 3}] |

The components of this solution correspond to the asymptotic expansions for `AiryAi` and `AiryBi` at `Infinity`:

✕
s1 = Normal[Series[AiryAi[x], {x, ∞, 4}]] |

✕
s2 = Normal[Series[AiryBi[x], {x, ∞, 4}]] |

The following plot shows that the approximation is very good for large values of :

✕
Plot[Evaluate[{AiryAi[x], AiryBi[x], s1, s2}], {x, -3, 3}, PlotLegends -> {AiryAi[x], AiryBi[x], "s1", "s2"}, PlotStyle -> Thickness[0.008]] |

The asymptotic analysis of nonlinear differential equations is a very difficult problem in general. Perhaps the most useful result in this area is the Cauchy–Kovalevskaya theorem, which guarantees the existence of Taylor series solutions for initial value problems related to analytic differential equations. `AsymptoticDSolveValue` computes such a solution for the following first-order nonlinear differential with an initial condition. `Quiet` is used to suppress the message that there are really two branches of the solution in this case:

✕
eqn={3 (y^′)[x]^2+4 x (y^′)[x]-y[x]+x^2==0,y[0]==1}; |

✕
sol=AsymptoticDSolveValue[eqn, y[x],{x,0,37}]//Quiet |

Notice that only three terms are returned in the solution shown, although 37 terms were requested in the input. This seems surprising at first, but the confusion is cleared when the solution is substituted in the equation, as in the following:

✕
eqn /. {y -> Function[{x}, Evaluate[sol]]} // Simplify |

Thus, the asymptotic expansion is actually an exact solution! This example shows that, occasionally, asymptotic methods can provide efficient means of finding solutions belonging to particular classes of functions. In that example, the asymptotic method gives an exact polynomial solution.

The examples that we have considered so far have involved expansions with respect to the independent variable . However, many problems in applied mathematics also involve a small or large parameter *ϵ*, and in this case, it is natural to consider asymptotic expansions with respect to the parameter. These problems are called perturbation problems and the parameter is called the perturbation parameter, since a change in its value may have a dramatic effect on the system.

Modern perturbation theory received a major impetus after the German engineer Ludwig Prandtl introduced the notion of a boundary layer for fluid flow around a surface to simplify the Navier–Stokes equations of fluid dynamics. Prandtl’s idea was to divide the flow field into two regions: one inside the boundary layer, dominated by viscosity and creating the majority of the drag; and one outside the boundary layer, where viscosity can be neglected without significant effects on the solution. The following animation shows the boundary layer in the case of smooth, laminar flow of a fluid around an aerofoil.

Prandtl’s work revolutionized the field of aerodynamics, and during the decades that followed, simple examples of perturbation problems were created to gain insight into the difficult mathematics underlying boundary layer theory. An important class of such examples are the so-called singular perturbation problems for ordinary differential equations, in which the order of the equation decreases when the perturbation parameter is set to 0. For instance, consider the following second-order boundary value problem:

✕
eqn={ϵ (y^′′)[x]+2 (y^′)[x]+y[x]==0,y[0]==0,y[1]==1/2}; |

When *ϵ* is 0, the order of the differential equation decreases from 2 to 1, and hence this is a singular perturbation problem. Next, for a fixed small value of the parameter, the nature of the solution depends on the relative scales for and , and the solution can be regarded as being composed of a boundary layer near the left endpoint 0, where *ϵ* is much larger than , and an outer region near the right endpoint 1, where is much larger than . For this example, `AsymptoticDSolveValue` returns a perturbation solution with respect to :

✕
psol = AsymptoticDSolveValue[eqn, y[x], x, {ϵ, 0, 1}] |

For this example, an exact solution can be computed using `DSolveValue` as follows:

✕
dsol = DSolveValue[eqn, y[x], x] |

The exact solution is clearly more complicated than the leading term approximation from the perturbation expansion, and yet the two solutions agree in a very remarkable manner, as seen from the plots shown here (the exact solution has been shifted vertically by 0.011 to distinguish it from the approximation!):

✕
Plot[Evaluate[{psol,dsol+0.011}/. {ϵ->1/30}],{x,0,1},PlotStyle->{Red,Blue}] |

In fact, the approximate solution approaches the exact solution asymptotically as *ϵ* approaches 0. More formally, these solutions are asymptotically equivalent:

✕
AsymptoticEquivalent[dsol, psol,ϵ->0,Direction->-1,Assumptions->0 |

Asymptotic expansions also provide a powerful method for approximating integrals involving a parameter. For example, consider the following elliptic integral, which depends on the parameter

:

✕
Integrate[1/Sqrt[1-m Sin[θ]^2],{θ,0,π/2},Assumptions->0 |

The result is an analytic function of for small values of this parameter, and hence one can obtain the first five terms, say, of the Taylor series expansion using `Series`:

✕
Normal[Series[%, {m, 0, 5}]] |

The same result can be obtained using `AsymptoticIntegrate` by specifying the parameter in the third argument as follows:

✕
AsymptoticIntegrate[1/Sqrt[1-m Sin[θ]^2],{θ,0,π/2},{m,0,5}] |

This technique of series expansions is quite robust and applies to a wide class of integrals. However, it does not exploit any specific properties of the integrand such as its maximum value, and hence the approximation may only be valid for a small range of parameter values.

In 1812, the French mathematician Pierre-Simon Laplace gave a powerful method for computing the leading term in the asymptotic expansion of an exponential integral depending on a parameter, whose integrand has a sharp peak on the interval of integration. Laplace argued that such an approximation could be obtained by performing a series expansion of the integrand around the maximum, where most of the area under the curve is likely to be concentrated. The following example illustrates Laplace’s method for an exponential function with a sharp peak at :

✕
f[x_]:=E^(-ω (x^2-2 x)) (1+x)^(5/2) |

✕
Plot[f[x] /. {ω -> 30}, {x, 0, 10}, PlotRange -> All, Filling -> Axis, FillingStyle -> Yellow] |

Laplace’s method gives the following simple result for the leading term in the integral of from 0 to `Infinity`, for large values of the parameter :

✕
AsymptoticIntegrate[f[x], {x, 0, ∞}, {ω, ∞, 1}] |

The following inputs compare the value of the approximation for with the numerical result given by `NIntegrate`:

✕
% /. {ω -> 30.} |

✕
NIntegrate[Exp[-30 (x^2-2 x)] (1+x)^(5/2),{x,0,∞}] |

The leading term approximation is reasonably accurate, but one can obtain a better approximation by computing an extra term:

✕
AsymptoticIntegrate[f[x], {x, 0, ∞}, {ω, ∞, 2}] |

The approximate answer now agrees very closely with the result from `NIntegrate`:

✕
% /. {ω -> 30.} |

The British mathematicians Sir George Gabriel Stokes and Lord Kelvin modified Laplace’s method so that it applies to oscillatory integrals in which the phase (exponent of the oscillatory factor) depends on a parameter. The essential idea of their method is to exploit the cancellation of sinusoids for large values of the parameter everywhere except in a neighborhood of stationary points for the phase. Hence this technique is called the method of stationary phase. As an illustration of this approach, consider the oscillatory function defined by:

✕
f[x_]:=E^(I ω Sin[t]) |

The following plot of the real part of this function for a large value of shows the cancellations except in the neighborhood of , where has a maximum:

✕
Plot[Re[f[x]/. {ω->50}],{t,0,π},Filling->Axis,FillingStyle->Yellow] |

The method of stationary phase gives a first-order approximation for this integral:

✕
int =AsymptoticIntegrate[f[t],{t,0,π},{ω,∞,1}] |

This rather simple approximation compares quite well with the result from numerical integration for a large value of :

✕
int/. ω->5000. |

✕
NIntegrate[Exp[I 5000 Sin[t]],{t,0,π},MinRecursion->20,MaxRecursion->20] |

As noted in the introduction, a divergent asymptotic expansion can still provide a useful approximation for a problem. We will illustrate this idea by using the following example, which computes eight terms in the expansion for an integral with respect to the parameter :

✕
aint=AsymptoticIntegrate[E^(-t)/(1+x t),{t,0,Infinity},{x,0,8}] |

The term in the asymptotic expansion is given by:

✕
a[n_]:=(-1)^n n! x^n |

✕
Table[a[n],{n,0,8}] |

`SumConvergence` informs us that this series is divergent for all nonzero values of :

✕
SumConvergence[a[n],n] |

However, for any fixed value of sufficiently near 0 (say, ), the truncated series gives a very good approximation:

✕
aint/.x-> 0.05 |

✕
NIntegrate[E^(-t)/(1 + 0.05 t),{t,0,Infinity}] |

On the other hand, the approximation gives very poor results for the same value of when we take a large number of terms, as in the case of 150 terms:

✕
AsymptoticIntegrate[E^(-t)/(1 + x t), {t, 0, Infinity}, {x, 0, 150}]/.{x-> 0.05`20} |

Thus, a divergent asymptotic expansion will provide excellent approximations if we make a judicious choice for the number of terms. Contrary to the case of convergent series, the approximation typically does not improve with the number of terms, i.e. more is not always better!

Finally, we note that the exact result for this integral can be obtained either by using `Integrate` or Borel regularization:

✕
Integrate[E^(-t)/(1+x t),{t,0,Infinity},Assumptions-> x>0] |

✕
Sum[a[n],{n,0,Infinity},Regularization->"Borel"] |

Both these results give essentially the same numerical value as the asymptotic expansion with eight terms:

✕
{%,%%}/.x-> 0.05 |

In connection with the previous example, it is worth mentioning that Dutch mathematician Thomas Jan Stieltjes studied divergent series related to various integrals in his PhD thesis from 1886, and is regarded as one of the founders of asymptotic expansions along with Henri Poincaré.

As a concluding example for asymptotic approximations of integrals, consider the following definite integral involving `GoldenRatio`, which cannot be done in the sense that an answer cannot presently be found using `Integrate`:

✕
Integrate[1/(Sqrt[1+x^4](1+x^GoldenRatio)),{x,0,∞}] |

This example was sent to me by an advanced user, John Snyder, shortly after the release of Version 11.3. John, who is always interested in trying new features after each release, decided to try the example using `AsymptoticIntegrate` after replacing `GoldenRatio` with a parameter *α*, as shown here:

✕
sol=AsymptoticIntegrate[1/(Sqrt[1+x^4](1+x^α)),{x,0,∞},{α,0,4}] |

He noticed that the result is independent of *α*, and soon realized that the `GoldenRatio` in the original integrand is just a red herring. He confirmed this by verifying that the value of the approximation up to 80 decimal places agrees with the result from numerical integration:

✕
N[sol, 80] |

✕
NIntegrate[1/(Sqrt[1+x^4](1+x^GoldenRatio)),{x,0,∞},WorkingPrecision->80] |

Finally, as noted by John, the published solution for the integral is exactly equal to the asymptotic result. So `AsymptoticIntegrate` has allowed us to compute an exact solution with essentially no effort!

Surprising results such as this one suggest that asymptotic expansions are an excellent tool for experimentation and discovery using the Wolfram Language, and we at Wolfram look forward to developing functions for asymptotic expansions of sums, difference equations and algebraic equations in Version 12.

I hope that you have enjoyed this brief introduction to asymptotic expansions and encourage you to download a trial version of Version 11.3 to try out the examples in the post. An upcoming post will discuss asymptotic relations, which are used extensively in computer science and elsewhere.

Here are 10 terms in a sequence:

And here’s what their numerical values are:

But what is the *limit* of the sequence? What would one get if one continued the sequence forever?

In Mathematica and the Wolfram Language, there’s a function to compute that:

Limits are a central concept in many areas, including number theory, geometry and computational complexity. They’re also at the heart of calculus, not least since they’re used to define the very notions of derivatives and integrals.

Mathematica and the Wolfram Language have always had capabilities for computing limits; in Version 11.2, they’ve been dramatically expanded. We’ve leveraged many areas of the Wolfram Language to achieve this, and we’ve invented some completely new algorithms too. And to make sure we’ve covered what people want, we’ve sampled over a million limits from Wolfram|Alpha.

Let’s talk about a limit that Hardy and Ramanujan worked out in 1918. But let’s build up to that. First, consider the sequence *a*(*n*) that is defined as follows:

Here is a table of the first ten values for the sequence.

The following plot indicates that the sequence converges to 0 as *n* approaches `Infinity`.

The `DiscreteLimit` function, which was introduced in Version 11.2, confirms that the limit of this sequence is indeed 0.

Many sequences that arise in practice (for example, in signal communication) are periodic in the sense that their values repeat themselves at regular intervals. The length of any such interval is called the period of the sequence. As an example, consider the following sequence that is defined using `Mod`.

A plot of the sequence shows that the sequence is periodic with period 6.

In contrast to our first example, this sequence does not converge, since it oscillates between 0 and 5. Hence, `DiscreteLimit` returns `Indeterminate` in this case.

The new Version 11.2 functions `DiscreteMinLimit` and `DiscreteMaxLimit` can be used to compute the lower and upper limits of oscillation, respectively, in such cases. Thus, we have:

`DiscreteMinLimit` and `DiscreteMaxLimit` are often referred to as “lim inf” and “lim sup,” respectively, in the mathematical literature. The traditional underbar and overbar notations for these limits are available, as shown here.

Our next example is an oscillatory sequence that is built from the trigonometric functions `Sin` and `Cos`, and is defined as follows.

Although `Sin` and `Cos` are periodic when viewed as functions over the real numbers, this integer sequence behaves in a bizarre manner and is very far from being a periodic sequence, as confirmed by the following plot.

Hence, the limit of this sequence does not exist.

However, it turns out that for such “densely aperiodic sequences,” the extreme values can be computed by regarding them as real functions. `DiscreteMinLimit` uses this method to return the answer 0 for the example, as expected.

Using the same method, `DiscreteMaxLimit` returns a rather messy-looking result in terms of `Root` objects for this example.

The numerical value of this result is close to 0.8, as one might have guessed from the graph.

Discrete limits also occur in a natural way when we try to compute the value of infinitely nested radicals. For example, consider the problem of evaluating the following nested radical.

The successive terms in the expansion of the radical can be generated by using `RSolveValue`, since the sequence satisfies a nonlinear recurrence. For example, the third term in the expansion is obtained as follows.

The value of the infinitely nested radical appears to be 2, as seen from the following plot that is generated using `RecurrenceTable`.

Using Version 11.2, we can confirm that the limiting value is indeed 2 by requesting the value *r*(∞) in `RSolveValue`, as shown here.

The study of limits belongs to the branch of mathematics called asymptotic analysis. Asymptotic analysis provides methods for obtaining approximate solutions of problems near a specific value such as 0 or `Infinity`. It turns out that, in practice, the efficiency of asymptotic approximations often increases precisely in the regime where the corresponding exact computation becomes difficult! A striking example of this phenomenon is seen in the study of integer partitions, which are known to grow extremely fast as the size of the number increases. For example, the number 6 can be partitioned in 11 distinct ways using `IntegerPartitions`, as shown here.

The number of distinct partitions can be found directly using `PartitionsP` as follows.

As noted earlier, the number of partitions grows rapidly with the size of the integer. For example, there are nearly 4 trillion partitions of the number 200.

In 1918, Hardy and Ramanujan provided an asymptotic approximation for this number, which is given by the following formula.

The answer given by this estimate for the number 200 is remarkably close to 4 trillion.

With a much larger integer, we get an even better approximation for the number of partitions almost instantaneously, as seen in the following example.

Finally, we can confirm that the asymptotic estimate approaches the number of partitions as *n* tends to `Infinity` using `DiscreteLimit`, which is aware of the Hardy–Ramanujan formula discussed above.

Formally, we say that exact and approximate formulas for the number of partitions are asymptotically equivalent as n approaches `Infinity`.

Asymptotic notions also play an important rule in the study of function limits. For instance, the small-angle approximation in trigonometry asserts that “*sin*(*x*) is nearly equal to *x* for small values of *x*.” This may be rephrased as “*sin*(*x*) is asymptotically equivalent to *x* as *x* approaches 0.” A formal statement of this result can be given using `Limit`, which computes function limits, as follows.

This plot provides visual confirmation that the limit is indeed 1.

The above limit can also be calculated using L’Hôspital’s rule by computing the derivatives, cos(*x*) and 1, of the numerator and denominator respectively, as shown here.

L’Hôspital’s rule gives a powerful method for evaluating many limits that occur in practice. However, it may require a large number of steps before arriving at the answer. For example, consider the following limit.

That limit requires six repeated applications of L’Hôspital’s rule to arrive at the answer 0, since all the intermediate computations give indeterminate results.

Thus, we see that L’Hôspital’s rule has limited utility as a practical algorithm for finding function limits, since it is impossible to decide when the algorithm should stop! Hence, the built-in `Limit` function uses a combination of series expansions and modern algorithms that works well on inputs involving exponentials and logarithms, the so-called “exp-log” class. In fact, `Limit` has received a substantial update in Version 11.2 and now handles a wide variety of difficult examples, such as the following, in a rather comprehensive manner (the last two examples work only in the latest release).

As in the cases of sequences, the limits of periodic and oscillatory functions will often not exist. One can then use `MaxLimit` and `MinLimit`, which, like their discrete counterparts, give tight bounds for the oscillation of the function near a given value, as in this classic example.

The graph indicates the function oscillates rapidly between –1 and 1 near 0. These bounds are confirmed by `MaxLimit` and `MinLimit`, while `Limit` itself returns `Indeterminate`.

In the previous example, the limit fails to exist because the function oscillates wildly around the origin. Discontinuous functions provide other types of examples where the limit at a point may fail to exist. We will now consider an example of such a function with a jump discontinuity at the origin and other values. The function is defined in terms of `SquareWave` and `FresnelS`, as follows.

This plot shows the jump discontinuities, which are caused by the presence of `SquareWave` in the definition of the function.

We see that the limiting values of the function at 0, for instance, depend on the direction from which we approach the origin. The limiting value from the right (“from above”) can be calculated using the `Direction` option.

Similarly, the limit from the left can be calculated as follows.

The limit, if it exists, is the “two-sided” limit for the function that, in this case, does not exist.

By default, `Limit` computes two-sided limits in Version 11.2. This is a change from earlier versions, where it computed the limit from above by default. Hence, we get an `Indeterminate` result from `Limit`, with no setting for the `Direction` option.

Directional limits acquire even more significance in the multivariate case, since there are many possible directions for approaching a given point in higher dimensions. For example, consider the bivariate function *f*(*x*,*y*) that is defined as follows.

The limiting value of this function at the origin is 0 if we approach it along the *x* axis, which is given by *y*=0, since the function has the constant value 0 along this line.

Similarly, the limiting value of the function at the origin is 0 if we approach it along the *y* axis, which is given by *x*=0.

However, the limit is 1/2 if we approach the origin along the line *y*=*x*, as seen here.

More generally, the limiting value changes as we approach the origin along different lines *y*=*m x*.

The directional dependence of the limiting value implies that the true multivariate limit does not exist. In Version 11.2, `Limit` handles multivariate examples with ease, and quickly returns the expected answer `Indeterminate` for the limit of this function at the origin.

A plot of the surface *z*=*f*(*x*,*y*) confirms the behavior of the function near the origin.

This example indicates that, in general, multivariate limits do not exist. In other cases, such as the following, the limit exists but the computation is subtle.

This plot indicates that the limit of this function at {0,0} exists and is 0, since the function values appear to approach 0 from all directions.

The answer can be confirmed by applying `Limit` to the function directly.

A rich source of multivariate limit examples is provided by the steady stream of inputs that is received by Wolfram|Alpha each day. We acquired around 100,000 anonymized queries to Wolfram|Alpha from earlier years, which were then evaluated using Version 11.2 . Here is a fairly complicated example from this vast collection that `Limit` handles with ease in the latest version.

It is a sheer joy to browse through the examples from Wolfram|Alpha, so we decided to share 1,000 nontrivial examples from the collection with you. Sample images of the examples are shown below. The five notebooks with the examples can be downloaded here.

Version 11.2 evaluates 90% of the entire collection in the benchmark, which is remarkable since the functionality for multivariate limits is new in this release.

Version 11.2 also evaluates a higher fraction (96%) of an even larger collection of 1,000,000 univariate limits from Wolfram|Alpha when compared with Version 11.1 (94%). The small percentage difference between the two versions can be explained by noting that most Wolfram|Alpha queries for univariate limits relate to a first or second course in college calculus and are easily computed by `Limit` in either version.

`Limit` has been one of the most dependable functions in the Wolfram Language ever since it was first introduced in Version 1 (1988). The improvements for this function, along with `DiscreteLimit` and other new functions in Version 11.2, have facilitated our journey through the world of limits. I hope that you have enjoyed this brief tour, and welcome any comments or suggestions about the new features.

“We’ve just got to decide: is a chemical like a city or like a number?” I spent my day yesterday—as I have for much of the past 30 years—designing new features of the Wolfram Language. And yesterday afternoon one of my meetings was a fast-paced discussion about how to extend the chemistry capabilities of the language.

At some level the problem we were discussing was quintessentially practical. But as so often turns out to be the case for things we do, it ultimately involves some deep intellectual issues. And to actually get the right answer—and to successfully design language features that will stand the test of time—we needed to plumb those depths, and talk about things that usually wouldn’t be considered outside of some kind of philosophy seminar.

Part of the issue, of course, is that we’re dealing with things that haven’t really ever come up before. Traditional computer languages don’t try to talk directly about things like chemicals; they just deal with abstract data. But in the Wolfram Language we’re trying to build in as much knowledge about everything as possible, and that means we have to deal with actual things in the world, like chemicals.

We’ve built a whole system in the Wolfram Language for handling what we call *entities*. An entity could be a city (like New York City), or a movie, or a planet—or a zillion other things. An entity has some kind of name (“New York City”). And it has definite properties (like population, land area, founding date, …).

We’ve long had a notion of chemical entities—like water, or ethanol, or tungsten carbide. Each of these chemical entities has properties, like molecular mass, or structure graph, or boiling point.

And we’ve got many hundreds of thousands of chemicals where we know lots of properties. But all of these are in a sense *concrete chemicals*: specific compounds that we could put in a test tube and do things with.

But what we were trying to figure out yesterday is how to handle abstract chemicals—chemicals that we just abstractly construct, say by giving an abstract graph representing their chemical structures. Should these be represented by entities, like water or New York City? Or should they be considered more abstract, like lists of numbers, or, for that matter, mathematical graphs?

Well, of course, among the abstract chemicals we can construct are chemicals that we already represent by entities, like sucrose or aspirin or whatever. But here there’s an immediate distinction to make. Are we talking about individual molecules of sucrose or aspirin? Or about these things as bulk materials?

At some level it’s a confusing distinction. Because, we might think, once we know the molecular structure, we know everything—it’s just a matter of calculating it out. And some properties—like molar mass—are basically trivial to calculate from the molecular structure. But others—like melting point—are very far from trivial.

OK, but is this just a temporary problem that one shouldn’t base a long-term language design on? Or is it something more fundamental that will never change? Well, conveniently enough, I happen to have done a bunch of basic science that essentially answers this: and, yes, it’s something fundamental. It’s connected to what I call computational irreducibility. And for example, the precise value of, say, the melting point for an infinite amount of some material may actually be fundamentally uncomputable. (It’s related to the undecidability of the tiling problem; fitting in tiles is like seeing how molecules will arrange to make a solid.)

So by knowing this piece of (rather leading-edge) basic science, we know that we can meaningfully make a distinction between bulk versions of chemicals and individual molecules. Clearly there’s a close relation between, say, water molecules, and bulk water. But there’s still something fundamentally and irreducibly different about them, and about the properties we can compute for them.

Alright, so let’s talk about individual molecules. Obviously they’re made of atoms. And it seems like at least when we talk about atoms, we’re on fairly solid ground. It might be reasonable to say that any given molecule always has some definite collection of atoms in it—though maybe we’ll want to consider “parametrized molecules” when we talk about polymers and the like.

But at least it seems safe to consider types of atoms as entities. After all, each type of atom corresponds to a chemical element, and there are only a limited number of those on the periodic table. Now of course in principle one can imagine additional “chemical elements”; one could even think of a neutron star as being like a giant atomic nucleus. But again, there’s a reasonable distinction to be made: almost certainly there are only a limited number of fundamentally stable types of atoms—and most of the others have ridiculously short lifetimes.

There’s an immediate footnote, however. A “chemical element” isn’t quite as definite a thing as one might imagine. Because it’s always a mixture of different isotopes. And, say, from one tungsten mine to another, that mixture might change, giving a different effective atomic mass.

And actually this is a good reason to represent types of atoms by entities. Because then one just has to have a single entity representing tungsten that one can use in talking about molecules. And only if one wants to get properties of that type of atom that depend on qualifiers like which mine it’s from does one have to deal with such things.

In a few cases (think heavy water, for example), one will need to explicitly talk about isotopes in what is essentially a chemical context. But most of the time, it’s going to be enough just to specify a chemical element.

To specify a chemical element you just have to give its atomic number *Z*. And then textbooks will tell you that to specify a particular isotope you just have to say how many neutrons it contains. But that ignores the unexpected case of tantalum. Because, you see, one of the naturally occurring forms of tantalum (^{180m}Ta) is actually an excited state of the tantalum nucleus, which happens to be very stable. And to properly specify this, you have to give its excitation level as well as its neutron count.

In a sense, though, quantum mechanics saves one here. Because while there are an infinite number of possible excited states of a nucleus, quantum mechanics says that all of them can be characterized just by two discrete values: spin and parity.

Every isotope—and every excited state—is different, and has its own particular properties. But the world of possible isotopes is much more orderly than, say, the world of possible animals. Because quantum mechanics says that everything in the world of isotopes can be characterized just by a limited set of discrete quantum numbers.

We’ve gone from molecules to atoms to nuclei, so why not talk about particles too? Well, it’s a bigger can of worms. Yes, there are the well-known particles like electrons and protons that are pretty easy to talk about—and are readily represented by entities in the Wolfram Language. But then there’s a zoo of other particles. Some of them—just like nuclei—are pretty easy to characterize. You can basically say things like: “it’s a particular excited state of a charm-quark-anti-charm-quark system” or some such. But in particle physics one’s dealing with quantum field theory, not just quantum mechanics. And one can’t just “count elementary particles”; one also has to deal with the possibility of virtual particles and so on. And in the end the question of what kinds of particles can exist is a very complicated one—rife with computational irreducibility. (For example, what stable states there can be of the gluon field is a much more elaborate version of something like the tiling problem I mentioned in connection with melting points.)

Maybe one day we’ll have a complete theory of fundamental physics. And maybe it’ll even be simple. But exciting as that will be, it’s not going to help much here. Because computational irreducibility means that there’s essentially an irreducible distance between what’s underneath, and what phenomena emerge.

And in creating a language to describe the world, we need to talk in terms of things that can actually be observed and computed about. We need to pay attention to the basic physics—not least so we can avoid setups that will lead to confusion later. But we also need to pay attention to the actual history of science, and actual things that have been measured. Yes, there are, for example, an infinite number of possible isotopes. But for an awful lot of purposes it’s perfectly useful just to set up entities for ones that are known.

But is it the same in chemistry? In nuclear physics, we think we know all the reasonably stable isotopes that exist—so any additional and exotic ones will be very short-lived, and therefore probably not important in practical nuclear processes. But it’s a different story in chemistry. There are tens of millions of chemicals that people have studied (and, for example, put into papers or patents). And there’s really no limit on the molecules that one might want to consider, and that might be useful.

But, OK, so how can we refer to all these potential molecules? Well, in a first approximation we can specify their chemical structures, by giving graphs in which every node is an atom, and every edge is a bond.

What really is a “bond”? While it’s incredibly useful in practical chemistry, it’s at some level a mushy concept—some kind of semiclassical approximation to a full quantum mechanical story. There are some standard extra bits: double bonds, ionization states, etc. But in practice chemistry is very successfully done just by characterizing molecular structures by appropriately labeled graphs of atoms and bonds.

OK, but should chemicals be represented by entities, or by abstract graphs? Well, if it’s a chemical one’s already heard of, like carbon dioxide, an entity seems convenient. But what if it’s a new chemical that’s never been discussed before? Well, one could think about inventing a new entity to represent it.

Any self-respecting entity, though, better have a name. So what would the name be? Well, in the Wolfram Language, it could just be the graph that represents the structure. But maybe one wants something that seems more like an ordinary textual name—a string. Well, there’s always the IUPAC way of naming chemicals with names like 1,1′-{[3-(dimethylamino)propyl]imino}bis-2-propanol. Or there’s the more computer-friendly SMILES version: CC(CN(CCCN(C)C)CC(C)O)O. And whatever underlying graph one has, one can always generate one of these strings to represent it.

There’s an immediate problem, though: the string isn’t unique. In fact, however one chooses to write down the graph, it can’t always be unique. A particular chemical structure corresponds to a particular graph. But there can be many ways to draw the graph—and many different representations for it. And in fact even the (“graph isomorphism”) problem of determining whether two representations correspond to the same graph can be difficult to solve.

OK, so let’s imagine we represent a chemical structure by a graph. At first, it’s an abstract thing. There are atoms as nodes in the graph, but we don’t know how they’d be arranged in an actual molecule (and e.g. how many angstroms apart they’d be). Of course, the answer isn’t completely well defined. Are we talking about the lowest-energy configuration of the molecule? (What if there are multiple configurations of the same energy?) Is the molecule supposed to be on its own, or in water, or whatever? How was the molecule supposed to have been made? (Maybe it’s a protein that folded a particular way when it came off the ribosome.)

Well, if we just had an entity representing, say, “naturally occurring hemoglobin”, maybe we’d be better off. Because in a sense that entity could encapsulate all these details.

But if we want to talk about chemicals that have never actually been synthesized it’s a bit of a different story. And it feels as if we’d be better off just with an abstract representation of any possible chemical.

But let’s talk about some other cases, and analogies. Maybe we should just treat everything as an entity. Like every integer could be an entity. Yes, there are an infinite number of them. But at least it’s clear what names they should be given. With real numbers, things are already messier. For example, there’s no longer the same kind of uniqueness as with integers: 0.99999… is really the same as 1.00000…, but it’s written differently.

What about sequences of integers, or, for that matter, mathematical formulas? Well, every possible sequence or every possible formula could conceivably be a different entity. But this wouldn’t be particularly useful, because much of what one wants to do with sequences or formulas is to go inside them, and transform their structure. But what’s convenient about entities is that they’re each just “single things” that one doesn’t have to “go inside”.

So what’s the story with “abstract chemicals”? It’s going to be a mixture. But certainly one’s going to want to “go inside” and transform the structure. Which argues for representing the chemical by a graph.

But then there’s potentially a nasty discontinuity. We’ve got the entity of carbon dioxide, which we already know lots of properties about. And then we’ve got this graph that abstractly represents the carbon dioxide molecule.

We might worry that this would be confusing both to humans and programs. But the first thing to realize is that we can distinguish what these two things are representing. The entity represents the bulk naturally occurring version of the chemical—whose properties have potentially been measured. The graph represents an abstract theoretical chemical, whose properties would have to be computed.

But obviously there’s got to be a bridge. Given a concrete chemical entity, one of the properties will be the graph that represents the structure of the molecule. And given a graph, one will need some kind of `ChemicalIdentify` function, that—a bit like `GeoIdentify` or maybe `ImageIdentify`—tries to identify from the graph what chemical entity (if any) has a molecular structure that corresponds to that graph.

As I write out some of the issues, I realize how complicated all this may seem. And, yes, it is complicated. But in our meeting yesterday, it all went very quickly. Of course it helps that everyone there had seen similar issues before: this is the kind of thing that’s all over the foundations of what we do. But each case is different.

And somehow this case got a bit deeper and more philosophical than usual. “Let’s talk about naming stars”, someone said. Obviously there are nearby stars that we have explicit names for. And some other stars may have been identified in large-scale sky surveys, and given identifiers of some kind. But there are lots of stars in distant galaxies that will never have been named. So how should we represent them?

That led to talking about cities. Yes, there are definite, chartered cities that have officially been assigned names–and we probably have essentially all of these right now in the Wolfram Language, updated regularly. But what about some village that’s created for a single season by some nomadic people? How should we represent it? Well, it has a certain location, at least for a while. But is it even a definite single thing, or might it, say, devolve into two villages, or not a village at all?

One can argue almost endlessly about identity—and even existence—for many of these things. But ultimately it’s not the philosophy of such things that we’re interested in: we’re trying to build software that people will find useful. And so what matters in the end is what’s going to be useful.

Now of course that’s not a precise thing to know. But it’s like for language design in general: think of everything people might want to do, then see how to set up primitives that will let people do those things. Does one want some chemicals represented by entities? Yes, that’s useful. Does one want a way to represent arbitrary chemical structures by graphs? Yes, that’s useful.

But to see what to actually do, one has to understand quite deeply what’s really being represented in each case, and how everything is related. And that’s where the philosophy has to meet the chemistry, and the math, and the physics, and so on.

I’m happy to say that by the end of our hour-long meeting yesterday (informed by about 40 years of relevant experience I’ve had, and collectively 100+ years from people in the meeting), I think we’d come up with the essence of a really nice way to handle chemicals and chemical structures. It’s going to be a while before it’s all fully worked out and implemented in the Wolfram Language. But the ideas are going to help inform the way we compute and reason about chemistry for many years to come. And for me, figuring out things like this is an extremely satisfying way to spend my time. And I’m just glad that in my long-running effort to advance the Wolfram Language I get to do so much of it.

*To comment, please visit the copy of this post at the Stephen Wolfram Blog »*

It’s been a busy year here at the Wolfram Blog. We’ve written about ways to avoid the UK’s most unhygienic foods, exciting new developments in mathematics and even how you can become a better Pokémon GO player. Here are some of our most popular stories from the year.

In August, we launched Version 11 of Mathematica and the Wolfram Language. The result of two years of development, Version 11 includes exciting new functionality like the expanded map generation enabled by satellite images. Here’s what Wolfram CEO Stephen Wolfram had to say about the new release in his blog post:

OK, so what’s the big new thing in Version 11? Well, it’s not one big thing; it’s many big things. To give a sense of scale, there are 555 completely new functions that we’re adding in Version 11—representing a huge amount of new functionality (by comparison, Version 1 had a total of 551 functions altogether). And actually that function count is even an underrepresentation—because it doesn’t include the vast deepening of many existing functions.

Using the Wolfram Language, John McLoone analyzes government data about food safety inspections to create visualizations of the most unhygienic food in the UK. The post is a treasure trove of maps and charts of food establishments that should be avoided at all costs, and includes McLoone’s greatest tip for food safety: “If you really care about food hygiene, then the best advice is probably just to never be rude to the waiter until after you have gotten your food!”

Bernat Espigulé-Pons creates visualizations of Pokémon across multiple generations of the game and then uses `WikipediaData`, `GeoDistance` and `FindShortestTour` to create a map to local Pokémon GO gyms. If you’re a 90s kid or an avid gamer, Espigulé-Pons’s Pokémon genealogy is perfect gamer geek joy. If you’re not, this post might just help to explain what all those crowds were doing in your neighborhood park earlier this year.

Connor Flood writes about creating “the world’s first online syntax-free proof generator using induction,” which he designed using Wolfram|Alpha. With a detailed explanation of the origin of the concept and its creation from development to prototyping, this post provides a glimpse into the ways that computational thinking applications are created.

Wolfram|Alpha Chief Scientist Michael Trott returns with a post about the history of the discovery of the exact value of the Planck constant, covering everything from the base elements of superheroes to the redefinition of the kilogram.

In January of 2016, we launched the Wolfram Open Cloud to—as Stephen Wolfram says in his blog post about the launch—“let anyone in the world use the Wolfram Language—and do sophisticated knowledge-based programming—free on the web.” You can read more about this integrated cloud-based computing platform in his January post.

In February, the Laser Interferometer Gravitational-Wave Observatory (LIGO) announced that it had confirmed the first detection of a gravitational wave. Wolfram software engineer Jason Grigsby explains what gravitational waves are and why the detection of them by LIGO is such an exciting landmark in experimental physics.

Silvia Hao uses Mathematica to recreate the renaissance engraving technique of stippling: a kind of drawing style using only points to mimic lines, edges and grayscale. Her post is filled with intriguing illustrations and is a wonderful example of the intersection of math and illustration/drawing.

In April, we reported on new books that use Wolfram technology to explore a variety of STEM topics, from data analysis to engineering. With resources for teachers, researchers and industry professionals and books written in English, Japanese and Spanish, there’s a lot of Wolfram reading to catch up on!

The year 2016 also saw the launch of Wolfram Programming Lab, an interactive online platform for learning to program in the Wolfram Language. Programming Lab includes a digital version of Stephen Wolfram’s 2016 book, *An Elementary Introduction to the Wolfram Language*, as well as Explorations for programmers already familiar with other languages and numerous examples for those who learn best by experimentation.

- “We had a nearly 4-billion-time speedup on this code example.”
- “We’ve worked together for over 9 years, and now we’re finally meeting!”
- “Coding in the Wolfram Language is like collaborating with 200 or 300 experts.”
- “You can turn financial data into rap music. Instead, how about we turn rap music into financial data?”

As a first-timer from the Wolfram Blog Team attending the Technology Conference, I wanted to share with you some of the highlights for me—making new friends, watching Wolfram Language experts code and seeing what the Wolfram family has been up to around the world this past year.

I was only able to attend one talk at a time, and with over a hundred talks going on over three days, there was no way I could see everything—but what I saw, I loved. Tuesday evening, Stephen Wolfram kicked off the event with his fantastic keynote presentation, giving an overview of the present and future of Wolfram Research, demoing live the new features of the Wolfram Language and setting the stage for the rest of the conference.

The nice thing about the Technology Conference is that if you’ve had a burning question about how something in the Wolfram Language works, you won’t get a better opportunity to ask the developers face to face. When someone in the audience asked about storing chemical data, the panel asked, “Is Michael Trott in the room?” And sure enough, Michael Trott was sitting a few seats down from me, and he stood up and addressed the question. Now that’s convenient.

Probably my favorite speaker was Igor Bakshee, a senior research associate here at Wolfram. He described our new publish-subscribe service, the Channel Framework, which allows asynchronous communication between Wolfram systems without dealing with the details of specific senders and receivers. I especially appreciated Igor’s humor and patience as messages came in from someone in the audience: he raised his hands and insisted it was indeed someone else sending them.

This talk was the one I was most looking forward to, and it was exactly what I wanted. Jakub Kabala talked about how he used Mathematica to compare 12th-century Latin texts in his search to determine if the monk of Lido and Gallus Anonymus were actually the same author. Jakub’s talk will also be in our upcoming virtual conference, so be sure to check that out!

It would be downright silly of me to not mention the extremely memorable duo Thomas Carpenter and Daniel “Scantron” Reynolds. The team used Wolfram Language code and JLink to infuse traditional disc jockey and video jockey art with abstract mathematics and visualizations. The experience was made complete when Daniel passed special glasses throughout the audience.

We had the best Wolfram Language programmers all in one place, so of course there had to be competitions! This included both our annual One-Liner Competition and our first after-hours live coding competition on Wednesday night. Phil Maymin won both competitions. Incidentally, in between winning competitions, Phil also gave an energetic presentation, “Sports and eSports Analytics with the Wolfram Language.” Thanks to everyone who participated. Be sure to check out our upcoming blog post on the One-Liner Competition.

Thursday night at Stephen’s Keynote Dinner, six Wolfram Innovator Awards were given out. The Wolfram Innovator Award is our opportunity to recognize people and organizations that have helped bring Wolfram technologies into use around the world. Congratulations again to this year’s recipients, Bryan Minor, Richard Scott, Brian Kanze, Samer Adeeb, Maik Meusel and Ruth Dover!

Like many Wolfram employees around the world, I usually work remote, so a big reason I was eager to go to the Wolfram Technology Conference was to meet people! I got to meet coworkers that I normally only email or talk on the phone with, and I got to speak with people who actually use our technologies and hear what they’ve been up to. After almost every talk, I’d see people shaking hands, trading business cards and exchanging ideas. It was easy to be social at the Technology Conference—everyone there shared an interest in and passion for Wolfram technologies, and the fun was figuring out what that passion was. And Wolfram gave everyone plenty of opportunities for networking and socializing, with lunches, dinners and meet-ups throughout the conference.

Attending the Wolfram Technology Conference has been the highlight of my year. The speakers were great across the board, and a special thanks goes to the technical support team that dealt with network and display issues in stride. I strongly encourage everyone interested in Wolfram technologies to register for next year’s conference, and if you bump into me, please feel free to say hi!

]]>

*Getting Started with Wolfram Language and Mathematica for Raspberry Pi*, Kindle Edition

If you’re interested in the Raspberry Pi and how the Wolfram Language can empower the device, then you ought to check out this ebook by Agus Kurniawan. The author takes you through the essentials of coding with the Wolfram Language in the Raspberry Pi environment. Pretty soon you’ll be ready to try out computational mathematics, GPIO programming and serial communication with Kurniawan’s step-by-step approach.

*Essentials of Programming in Mathematica*

Whether you are already familiar with programming or completely new to it, *Essentials of Programming in Mathematica* provides an excellent example-driven introduction for both self-study and a course in programming. Paul Wellin, an established authority on Mathematica and the Wolfram Language, covers the language from first principles to applications in natural language processing, bioinformatics, graphs and networks, signal analysis, geometry, computer science and much more. With tips and insight from a Wolfram Language veteran and more than 350 exercises, this volume is invaluable for both the novice and advanced Wolfram Language user.

*Geospatial Algebraic Computations, Theory and Applications, Third Edition*

Advances in geospatial instrumentation and technology such as laser scanning have resulted in tons of data—and this huge amount of data requires robust mathematical solutions. Joseph Awange and Béla Paláncz have written this enhanced third edition to respond to these new advancements by including robust parameter estimation, multi-objective optimization, symbolic regression and nonlinear homotopy. The authors cover these disciplines with both theoretical explorations and numerous applications. The included electronic supplement contains these theoretical and practical topics with corresponding Mathematica code to support the computations.

*Boundary Integral Equation Methods and Numerical Solutions: Thin Plates on an Elastic Foundation*

For graduate students and researchers, authors Christian Constanda, Dale Doty and William Hamill present a general, efficient and elegant method for solving the Dirichlet, Neumann and Robin boundary value problems for the extensional deformation of a thin plate on an elastic foundation. Utilizing Mathematica’s computational and graphics capabilities, the authors discuss both analytical and highly accurate numerical solutions for these sort of problems, and both describe the methodology and derive properties with full mathematical rigor.

*Micromechanics with Mathematica*

Seiichi Nomura demonstrates the simplicity and effectiveness of Mathematica as the solution to practical problems in composite materials, requiring no prior programming background. Using Mathematica’s computer algebra system to facilitate mathematical analysis, Nomura makes it practical to learn micromechanical approaches to the behavior of bodies with voids, inclusions and defects. With lots of exercises and their solutions on the companion website, students will be taken from the essentials, such as kinematics and stress, to applications involving Eshelby’s method, infinite and finite matrix media, thermal stresses and much more.

*Tendências Tecnológicas em Computação e Informática* (Portuguese)

For Portuguese students and researchers interested in technological trends in computation and informatics, this book is a real treat. The authors—Leandro Augusto Da Silva, Valéria Farinazzo Martins and João Soares De Oliviera Neto—gathered studies from both research and the commercial sector to examine the topics that mark current technological development. Read about how challenges in contemporary society encourage new theories and their applications in software like Mathematica. Topics include the semantic web, biometry, neural networks, satellite networks in logistics, parallel computing, geoprocessing and computation in forensics.

*The End of Error: Unum Computing*

Written with Mathematica by John L. Gustafson, one of the foremost experts in high-performance computing and the inventor of Gustafson’s law, *The End of Error: Unum Computing* explains a new approach to computer arithmetic: the universal number (unum). The book discusses this new number type, which encompasses all IEEE floating-point formats, obtains more accurate answers, uses fewer bits and solves problems that have vexed engineers and scientists for decades. With rich illustrations and friendly explanations, it takes no more than high-school math to learn about Gustafson’s novel and groundbreaking unum.

Want to find even more Wolfram technologies books? Visit Wolfram Books to discover books ranging across both topics and languages.

]]>The Wolfram Cloud is coming out of beta soon (yay!), and right now I’m spending much of my time working to make it as good as possible (and, by the way, it’s getting to be really great!). Mostly I concentrate on defining high-level function and strategy. But I like to understand things at every level, and as a CEO, one’s ultimately responsible for everything. And at the beginning of March I found myself diving deep into something I never expected…

Here’s the story. As a serious production system that lots of people will use to do things like run businesses, the Wolfram Cloud should be as fast as possible. Our metrics were saying that typical speeds were good, but subjectively when I used it something felt wrong. Sometimes it was plenty fast, but sometimes it seemed way too slow.

We’ve got excellent software engineers, but months were going by, and things didn’t seem to be changing. Meanwhile, we’d just released the Wolfram Data Drop. So I thought, why don’t I just run some tests myself, maybe collecting data in our nice new Wolfram Data Drop?

A great thing about the Wolfram Language is how friendly it is for busy people: even if you only have time to dash off a few lines of code, you can get real things done. And in this case, I only had to run three lines of code to find a problem.

First, I deployed a web API for a trivial Wolfram Language program to the Wolfram Cloud:

Then I called the API 50 times, measuring how long each call took (% here stands for the previous result):

Then I plotted the sequence of times for the calls:

And immediately there seemed to be something crazy going on. Sometimes the time for each call was 220 ms or so, but often it was 900 ms, or even twice that long. And the craziest thing was that the times seemed to be quantized!

I made a histogram:

And sure enough, there were a few fast calls on the left, then a second peak of slow calls, and a third “outcropping” of very slow calls. It was weird!

I wondered whether the times were always like this. So I set up a periodic scheduled task to do a burst of API calls every few minutes, and put their times in the Wolfram Data Drop. I left this running overnight… and when I came back the next morning, this is what I saw:

Even weirder! Why the large-scale structure? I could imagine that, for example, a particular node in the cluster might gradually slow down (not that it should), but why would it then slowly recover?

My first thought was that perhaps I was seeing network issues, given that I was calling the API on a test cloud server more than 1000 miles away. So I looked at ping times. But apart from a couple of weird spikes (hey, it’s the internet!), the times were very stable.

OK, so it must be something on the servers themselves. There’s a lot of new technology in the Wolfram Cloud, but most of it is pure Wolfram Language code, which is easy to test. But there’s also generic modern server infrastructure below the Wolfram Language layer. Much of this is fundamentally the same as what Wolfram|Alpha has successfully used for half a dozen years to serve billions of results, and what web*Mathematica* started using even nearly a decade earlier. But being a more demanding computational system, the Wolfram Cloud is set up slightly differently.

And my first suspicion was that this different setup might be causing something to go wrong inside the webserver layer. Eventually I hope we’ll have pure Wolfram Language infrastructure all the way down, but for now we’re using a webserver system called Tomcat that’s based on Java. And at first I thought that perhaps the slowdowns might be Java garbage collection. Profiling showed that there were indeed some “stop the world” garbage-collection events triggered by Tomcat, but they were rare, and were taking only milliseconds, not hundreds of milliseconds. So they weren’t the explanation.

By now, though, I was hooked on finding out what the problem was. I hadn’t been this deep in the trenches of system debugging for a very long time. It felt a lot like doing experimental science. And as in experimental science, it’s always important to simplify what one’s studying. So I cut out most of the network by operating “cloud to cloud”: calling the API from within the same cluster. Then I cut out the load balancer, that dispatches requests to particular nodes in a cluster, by locking my requests to a single node (which, by the way, external users can’t do unless they have a Private Cloud). But the slowdowns stayed.

So then I started collecting more-detailed data. My first step was to make the API return the absolute times when it started and finished executing Wolfram Language code, and compare those to absolute times in the wrapper code that called the API. Here’s what I saw:

The blue line shows times before the Wolfram Language code is run; the gold line after. I collected this data in a period when the system as a whole was behaving pretty badly. And what I saw was lots of dramatic slowdowns in the “before” times—and just a few quantized slowdowns in the “after” times.

Once again, this was pretty weird. It didn’t seem like the slowdowns were specifically associated with either “before” or “after”. Instead, it looked more as if something was randomly hitting the system from the outside.

One confusing feature was that each node of the cluster contained (in this case) 8 cores, with each core running a different instance of the Wolfram Engine. The Wolfram Engine is nice and stable, so each of these instances was running for hours to days between restarts. But I wondered if perhaps some instances might be developing problems along the way. So I instrumented the API to look at process IDs and process times, and then for example plotted total process time against components of the API call time:

And indeed there seemed to be some tendency for “younger” processes to run API calls faster, but (particularly noting the suppressed zero on the *x* axis) the effect wasn’t dramatic.

I started to wonder about other Wolfram Cloud services running on the same machine. It didn’t seem to make sense that these would lead to the kind of quantized slowdowns we were seeing, but in the interest of simplifying the system I wanted to get rid of them. At first we isolated a node on the production cluster. And then I got my very own Wolfram Private Cloud set up. Still the slowdowns were there. Though, confusingly, at different times and on different machines, their characteristics seemed to be somewhat different.

On the Private Cloud I could just log in to the raw Linux system and start looking around. The first thing I did was to read the results from the “top” and “ps axl” Unix utilities into the Wolfram Language so I could analyze them. And one thing that was immediately obvious was that lots of “system” time was being used: the Linux kernel was keeping very busy with something. And in fact, it seemed like the slowdowns might not be coming from user code at all; they might be coming from something happening in the kernel of the operating system.

So that made me want to trace system calls. I hadn’t done anything like this for nearly 25 years, and my experience in the past had been that one could get lots of data, but it was hard to interpret. Now, though, I had the Wolfram Language.

Running the Linux “strace” utility while doing a few seconds of API calls gave 28,221,878 lines of output. But it took just a couple of lines of Wolfram Language code to knit together start and end times of particular system calls, and to start generating histograms of system-call durations. Doing this for just a few system calls gave me this:

Interestingly, this showed evidence of discrete peaks. And when I looked at the system calls in these peaks they all seemed to be “futex” calls—part of the Linux thread synchronization system. So then I picked out only futex calls, and, sure enough, saw sharp timing peaks—at 250 ms, 500 ms and 1s:

But were these really a problem? Futex calls are essentially just “sleeps”; they don’t burn processor time. And actually it’s pretty normal to see calls like this that are waiting for I/O to complete and so on. So to me the most interesting observation was actually that there weren’t other system calls that were taking hundreds of milliseconds.

So… what was going on? I started looking at what was happening on different cores of each node. Now, Tomcat and other parts of our infrastructure stack are all nicely multithreaded. Yet it seemed that whatever was causing the slowdown was freezing all the cores, even though they were running different threads. And the only thing that could do that is the operating system kernel.

But what would make a Linux kernel freeze like that? I wondered about the scheduler. I couldn’t really see why our situation would lead to craziness in a scheduler. But we looked at the scheduler anyway, and tried changing a bunch of settings. No effect.

Then I had a more bizarre thought. The instances of the Wolfram Cloud I was using were running in virtual machines. What if the slowdown came from “outside The Matrix”? I asked for a version of the Wolfram Cloud running on bare metal, with no VM. But before that was configured, I found a utility to measure the “steal time” taken by the VM itself—and it was negligible.

By this point, I’d been spending an hour or two each day for several days on all of this. And it was time for me to leave for an intense trip to SXSW. Still, people in our cloud-software engineering team were revved up, and I left the problem in their capable hands.

By the time my flight arrived there was already another interesting piece of data. We’d divided each API call into 15 substeps. Then one of our physics-PhD engineers had compared the probability for a slowdown in a particular substep (on the left) to the median time spent in that substep (on the right):

With one exception (which had a known cause), there was a good correlation. It really looked as if the Linux kernel (and everything running under it) was being hit by something at completely random times, causing a “slowdown event” if it happened to coincide with the running of some part of an API call.

So then the hunt was on for what could be doing this. The next suspicious thing noticed was a large amount of I/O activity. In the configuration we were testing, the Wolfram Cloud was using the NFS network file system to access files. We tried tuning NFS, changing parameters, going to asynchronous mode, using UDP instead of TCP, changing the NFS server I/O scheduler, etc. Nothing made a difference. We tried using a completely different distributed file system called Ceph. Same problem. Then we tried using local disk storage. Finally this seemed to have an effect—removing most, but not all, of the slowdown.

We took this as a clue, and started investigating more about I/O. One experiment involved editing a huge notebook on a node, while running lots of API calls to the same node:

The result was interesting. During the period when the notebook was being edited (and continually saved), the API times suddenly jumped from around 100 ms to 500 ms. But why would simple file operations have such an effect on all 8 cores of the node?

We started investigating more, and soon discovered that what seemed like “simple file operations” weren’t—and we quickly figured out why. You see, perhaps five years before, early in the development of the Wolfram Cloud, we wanted to experiment with file versioning. And as a proof of concept, someone had inserted a simple versioning system named RCS.

Plenty of software systems out there in the world still use RCS, even though it hasn’t been substantially updated in nearly 30 years and by now there are much better approaches (like the ones we use for infinite undo in notebooks). But somehow the RCS “proof of concept” had never been replaced in our Wolfram Cloud codebase—and it was still running on every file!

One feature of RCS is that when a file is modified even a tiny bit, lots of data (even several times the size of the file itself) ends up getting written to disk. We hadn’t been sure how much I/O activity to expect in general. But it was clear that RCS was making it needlessly more intense.

Could I/O activity really hang up the whole Linux kernel? Maybe there’s some mysterious global lock. Maybe the disk subsystem freezes because it doesn’t flush filled buffers quickly enough. Maybe the kernel is busy remapping pages to try to make bigger chunks of memory available. But whatever might be going on, the obvious thing was just to try taking out RCS, and seeing what happened.

And so we did that, and lo and behold, the horrible slowdowns immediately went away!

So, after a week of intense debugging, we had a solution to our problem. And repeating my original experiment, everything now ran cleanly, with API times completely dominated by network transmission to the test cluster:

What did I learn from all this? First, it reinforced my impression that the cloud is the most difficult—even hostile—development and debugging environment that I’ve seen in all my years in software. But second, it made me realize how valuable the Wolfram Language is as a kind of metasystem, for analyzing, visualizing and organizing what’s going on inside complex infrastructure like the cloud.

When it comes to debugging, I myself have been rather spoiled for years—because I do essentially all my programming in the Wolfram Language, where debugging is particularly easy, and it’s rare for a bug to take me more than a few minutes to find. Why is debugging so easy in the Wolfram Language? I think, first and foremost, it’s because the code tends to be short and readable. One also typically writes it in notebooks, where one can test out, and document, each piece of a program as one builds it up. Also critical is that the Wolfram Language is symbolic, so one can always pull out any piece of a program, and it will run on its own.

Debugging at lower levels of the software stack is a very different experience. It’s much more like medical diagnosis, where one’s also dealing with a complex multicomponent system, and trying to figure out what’s going on from a few measurements or experiments. (I guess our versioning problem might be the analog of some horrible defect in DNA replication.)

My whole adventure in the cloud also very much emphasizes the value we’re adding with the Wolfram Cloud. Because part of what the Wolfram Cloud is all about is insulating people from the messy issues of cloud infrastructure, and letting them instead implement and deploy whatever they want directly in the Wolfram Language.

Of course, to make that possible, we ourselves have needed to build all the automated infrastructure. And now, thanks to this little adventure in “scientific debugging”, we’re one step closer to finishing that. And indeed, as of today, the Wolfram Cloud has its APIs consistently running without any mysterious quantized slowdowns—and is rapidly approaching the point when it can move out of beta and into full production.

*To comment, please visit the copy of this post at the Stephen Wolfram Blog »*

Computational Explorations in Magnetron Sputtering

Magnetron sputtering is a widely used industrial process for depositing thin films. E. J. McInerney walks you through the physics of magnetron sputtering in a step-by-step fashion using *Mathematica* syntax and functions. The reader is encouraged to explore this fascinating topic by actively following along with a series of simple computer models. By working through these models, readers can build their intuition of PVD processes and develop a deeper appreciation of the underlying physics.

Continuum Mechanics using *Mathematica*: Fundamentals, Methods, and Applications, second edition

In this second edition of *Continuum Mechanics using Mathematica*, Antonio Romano and Addolorata Marasco take a methodological approach that familiarizes readers with the mathematical tools required to correctly define and solve problems in continuum mechanics. It provides a solid basis for a deeper study of more challenging and specialized problems related to nonlinear elasticity, polar continua, mixtures, piezoelectricity, ferroelectricity, magneto-fluid mechanics, and state changes.

*Mathematica* Data Visualization: Create and Prototype Interactive Data Visualizations Using *Mathematica*

Nazmus Saquib begins by introducing you to the *Mathematica* environment and the basics of dataset loading and cleaning. You will then learn about different kinds of widely used datasets, time series, and scientific, statistical, information, and map visualizations. Each topic is demonstrated by walking you through an example project. Along the way, the dynamic interactivity and graphics packages are also introduced. This book teaches you how to build visualizations from scratch, quickly and efficiently.

Interactive Computational Geometry, A Taxonomic Approach

This ebook by Jim Arlow is an interactive introduction to some of the fundamental algorithms of computational geometry. The code base, which is in the Wolfram Language, is integrated into the text and is fully executable. This book is delivered as an interactive CDF (Computable Document Format) file that is viewable in the free CDF Player available from Wolfram, or can be opened in *Mathematica*. Readers are encouraged to have a copy of *Mathematica* in order to get the most out of this text.

学生が学ぶ*Mathematica*入門 (Introduction to *Mathematica* for Students)

Souji Otabe provides a quick introduction to *Mathematica* for students. Engineers and scientists need to know about symbolic computation, but they also need to know numeric computation to analyze experimental data. In this ebook, *Mathematica* is treated as a general problem-solving tool for science and engineering students. Readers will study basic operations of *Mathematica* first, and then they will learn how *Mathematica* can be applied to engineering and science.

Mathematik fur Informatiker (Mathematics for Computer Scientists) Band 2: Analysis und Statistik 3. Auflage

The second edition of Gerald and Susanne Teschl’s textbook introduces mathematical foundations that are concise, yet vivid and easy to follow. They are illustrated using numerous worked-out examples mixed with applications to computer science, historic background, and connections to related areas. The end of every chapter offers a self-test and warmup problems (with detailed solutions), as well as more advanced problems. Complementary sections introduce *Mathematica* to visualize the subjects taught, thereby fostering comprehension of the material.

Neoclassical Physics

In this introductory text by Mark A. Cunningham, physics concepts are introduced as a means of understanding experimental observations, not as a sequential list of facts to be memorized. Numerous exercises are provided that utilize *Mathematica* software to help students explore how the language of mathematics is used to describe physical phenomena. Students will obtain much more detailed information about covered topics and will also gain proficiency with *Mathematica*, a powerful tool with many potential uses in subsequent courses.

Pathways Through Applied and Computational Physics

This book is the collaborative effort of Nicolo Barbero, Matteo Delfino, Carlo Palmisano, and Gianfranco Zosi to illustrate the role that different branches of physics and mathematics play in the execution of actual experiments. The unique feature of the book is that all the subjects addressed are strictly interconnected within the context of the execution of a single experiment with very high accuracy, namely the redetermination of the Avogadro constant NA, one of the fundamental physical constants. Another essential feature is the focus on the role of *Mathematica*, an invaluable, fully integrated software environment for handling diverse scientific and technical computations.

Probability: An Introduction with Statistical Applications, second edition

Thoroughly updated, John J. Kinney’s second edition of this title features a comprehensive exploration of statistical data analysis as an application of probability. The new edition provides an introduction to statistics with accessible coverage of reliability, acceptance sampling, confidence intervals, hypothesis testing, and simple linear regression. Encouraging readers to develop a deeper intuitive understanding of probability, the author presents illustrative geometrical presentations and arguments without the need for rigorous mathematical proofs. This book features an appendix dedicated to the use of *Mathematica* and a companion website containing the referenced data sets.