Wolfram Blog., 16 .
News, views, and ideas from the front lines at Wolfram Research.

1. Former Astronaut Creates Virtual Copilot with Wolfram Neural Nets and a Raspberry Pi., 16 .[−]
For the past two years, FOALE AEROSPACE has been on an exhilarating journey to create an innovative machine learningbased system designed to help prevent airplane crashes, using what might be the most understated machine for the taskthe Raspberry Pi. The system is marketed as a DIY kit for aircraft hobbyists, but the ideas its based upon can be applied to larger aircraft (and even spacecraft!). FOALE AEROSPACE is the brainchild of astronaut Dr. Mike Foale and his daughter Jenna Foale. Mike is a man of many talents (pilot, astrophysicist, entrepreneur) and has spent an amazing 374 days in space! Together with Jenna (who is currently finishing her PhD in computational fluid dynamics), he was able to build a complex machine learning system at minimal cost. All their development work was done in-house, mainly using the Wolfram Language running on the desktop and a Raspberry Pi. FOALE AEROSPACEs system, which it calls the Solar Pilot Guard (SPG), is a solar-charged probe that identifies and helps prevent loss-of-control (LOC) events during airplane flight. Using sensors to detect changes in the acceleration and air pressure, the system calculates the probability of each data point (an instance in time) to be in-family (normal flight) or out-of-family (non-normal flight/possible LOC event), and issues the pilot voice commands over a Bluetooth speaker. The system uses classical functions to interpolate the dynamic pressure changes around the airplane axes; then, through several layers of Wolframs automatic machine learning framework, it assesses when LOC is imminent and instructs the user on the proper countermeasures they should take. You can see the system work its magic in this short video on the FOALE AEROSPACE YouTube channel. As of the writing of this blog, a few versions of the SPG system have been designed and built: the 2017 version (talked about extensively in a Wolfram Community post by Brett Haines) won the bronze medal at the Experimental Aircraft Associations Founders Innovation Prize. In the year since, Mike has been working intensely to upgrade the system from both a hardware and software perspective. As you can see in the following image, the 2018 SPG has a new streamlined look, and is powered by solar cells (which puts the S in SPG). It also connects to an off-the-shelf Bluetooth speaker that sits in the cockpit and gives instructions to the pilot. Building the System: Hardware and Data While the probe required some custom hardware and intense design to be so easily packaged, the FOALE AEROSPACE team used off-the-shelf Wolfram Language functions to create a powerful machine learningbased tool for the system s software. The core of the 2017 system was a neural networkbased classifier (built using Wolfram s Classify function), which enabled the classification of flight parameters into in-family and out-of-family flight (possible LOC) events. In the 2018 system, the team used a more complex algorithm involving layering different machine learning functions together to create a semi-automatic pipeline. The combined several layers of supervised and unsupervised learning result in a semi-automated pipeline for dataset creation and classification. The final deployment is again a classifier that classifies in-family and out-of-family (LOC) flights, but this time in a more automatic and robust way. To build any type of machine learning application, the first thing we need is the right kind of data. In the case at hand, what was needed was actual flight databoth from normal flight patterns and from non-normal flight patterns (the latter leading to LOC events). To do this, one would need to set up the SPG system, start recording with it and take it on a flight. During this flight, it would need to sample both normal flight data and non-normal/LOC events, which means Mike needed to intentionally make his aircraft lose control, over and over again. If this sounds dangerous, its because it is, so dont try this at home. During such a flight, the SPG records acceleration and air pressure data across the longitudinal, vertical and lateral axes (x, y, z). From these inputs, the SPG can calculate the acceleration along the axes, the sideslip angle (?how much it is moving sideways), the angle of attack (?the angle between the direction of the noise and the horizontal reference plane) and the relative velocity (of the airplane to the air around it)respectively, Ax, Ay, Az, ?, ? and Vrel in the following plot: A plot of the flight used as the training set. Note that the vertical axis is inverted so a lower value corresponds to an increase in quantity. Connecting the entire system straight to a Raspberry Pi running the Wolfram Language made gathering all this data and computing with it ridiculously easy. Looking again at the plot, we already notice that there is a phase of almost-steady values (up to 2,000 on the horizontal axis) and a phase of fluctuating values (2,000 onward). Our subject matter expert, Mike Foale, says that these correspond to runway and flight time, respectively. Now that we have some raw data, we need to process and clean it up in order to learn from it. Taking the same dataset, we first remove any data that isnt interesting (for example, anything before the 2,000th data point). Now we can re-plot the data: In the 2017 system, the FOALE AEROSPACE team had to manually curate the right flight segments that correspond to LOC events to create a dataset. This was a labor-intensive process that became semi-automated in the 2018 system. We now take the (lightly) processed data and start applying the needed machine learning algorithms to it. First, we will cluster the training data to create in-family and out-of-family clusters. To assess which clusters are in-family and which are out-of-family, we will need a human subject matter expert. We will then train the first classifier using those clusters as classes. Now we take a new dataset and, using the first classifier we made, filter out any in-family events (normal flight). Finally, we will cluster the filtered data (with some subject matter expert help) and use the resulting clusters as classes in our final classifier. This final classifier will be used to indicate LOC events while in flight. A simplified plot of the process is given here: We start by taking the processed data and clustering it (an unsupervised learning approach). Following is a 3D plot of the clusters resulting from the use of FindClusters (specifying we want to find seven clusters). As you can see, the automatic color scheme is very helpful in visualizing the results. Mike, using his subject matter expertise, assesses groups 1, 2, 3, 6 and 7, which represent normal flight data. Group 5 (pink) is the LOC group, and group 4 (red) is the high-velocity normal flight: To distinguish the LOC cluster from the others, Mike needed to choose more than two cluster groups. After progressively increasing the number of clusters with FindClusters, seven clusters were chosen to reduce the overlap of LOC group 5 from the neighboring groups 1 and 7, which are normal. A classifier trained with clearly distinguishable data will perform better and produce fewer false positives. Using this clustered data, we can now train a classifier that will classify in-family flight data and out-of-family flight data (Low/High ?groups 4, 5). This in-family/out-of-family flight classifier will become a powerful machine learning tool in processing the next flights data. Using the Classify function and some clever preprocessing, we arrive at the following three class classifiers. The three classes are normal flight (Normal), high ? flight (High) and low ? flight (Low). We now take data from a later flight and process it as we did earlier. Here is the resulting plot of that data: Using our first classifier, we now classify the data as representing an in-family flight or an out-of-family flight. If it is in-family (normal flight), we exclude it from the dataset, as we are only looking for out-of-family instances (representing LOC events). With only non-normal data remaining, lets plot the probability of that data being normal: It is interesting to note that more than half of the remaining data points have less than a 0.05 probability of being normal. Taking this new, refined dataset we apply another layer of clustering, which results in the following plot: We now see two main groups: group 3, which Mike explains as corresponding with thermaling; and group 1, which is the high-speed flight group. Thermaling is the act of using rising air columns to gain altitude. This involves flying in circles inside the air column (at speeds so slow it s close to a stall), so it s not surprising that has a wide distribution during this phase. Groups 1 and 6 are also considered to be normal flight. Group 7 corresponds to LOC (a straight stall without sideslip). Groups 4 and 5 are imminent stalls with sideslip, leading to a left or right incipient spin and are considered to be LOC. Group 2 is hidden under group 1 and is a very high-speed flight close to the structural limits of the aircraft, so it s also LOC. Using this data, we can construct a new, second-generation classifier with three classes, low ? (U), high ? (D) and normal flight (N). These letters refer to the action required by the pilotU means pull up, D means push down and N means do nothing. It is interesting to note that while the older classifier required days of training, this new filtered classifier only needed hours (and also greatly improved the speed and accuracy of the predictions, and reduced the occurrences of false positives). As a final trial, Mike went on another flight and maintained a normal flight pattern throughout the entire flight. He later took the recorded data and plotted the probability of it being entirely normal using the second-generation classifier. As we can see here, there were no false positives during this flight: Mike now wanted to test if the classifier would correctly predict possible LOC events. He went on another flight and, again, went into LOC events. Taking the processed data from that flight and plotting the probability of it being normal using the second-generation classifier results in the following final plot: It is easy to see that some events were not classified as normal, although most of them were. Mike has confirmed these events correspond to actual LOC events. Mikes development work is a great demonstration as to how machine learningbased applications are going to affect everything that we do, increasing safety and survivability. This is also a great case study to showcase where and why it is so important to keep human subject matter experts in the loop. Perhaps one of the most striking components of the SPG system is the use of the Wolfram Language on a Raspberry Pi Zero to connect to sensors, record in-flight data and run a machine learning application to compute when LOC is imminentall on a computer that costs $5. Additional details on Mike s journey can be found on his customer story page. Just a few years ago, it would have been unimaginable for any one person to create such complex algorithms and deploy them rapidly in a real-world environment. The recent boom of the Internet of Things and machine learning has been driving great developmental work in these fields, and even after its 30th anniversary, the Wolfram Language has continued to be at the cutting edge of programming. Through its high-level abstractions and deep automation, the Wolfram Language has enabled a wide range of people to use the power of computation everywhere. There are many great products and projects left to be built using the Wolfram Language. Perhaps today is the day to start yours with a free trial of Wolfram|One! (0)

2. Citizen Data Science with Civic Hacking: The Safe Drinking Water Data Challenge., 09 .[−]

Code for Americas National Day of Civic Hacking is coming up on August 11, 2018, which presents a nice opportunity for individuals and teams of all skill levels to participate in the Safe Drinking Water Data Challengea program Wolfram is supporting through free access to Wolfram|One and by hosting relevant structured datasets in the Wolfram Data Repository.

According to the state of California, some 200,000 residents of the state have unsafe drinking water coming out of their taps. While the Safe Drinking Water Data Challenge focuses on California, data science solutions could have impacts and applications for providing greater access to potable water in other areas with similar problems.

The goal of this post is to show how Wolfram technologies make it easy to grab data and ask questions of it, so well be taking a multiparadigm approach and allowing our analysis to be driven by those questions in an exploratory analysis, a way to quickly get familiar with the data.

Details on instructional resources, documentation and training are at the bottom of this post.

Water Challenge Data

To get started, lets walk through one of the datasets that has been added to the Wolfram Data Repository, how to access it and how to visually examine it using the Wolfram Language.

Well first define and grab data on urban water supply and production using ResourceData:


uwsdata = ResourceData["California Urban Water Supplier Monitoring Reports"]

What we get back is a nice structured data frame with several variables and measurements that we can begin to explore. (If youre new to working with data in the Wolfram Language, theres a fantastic and useful primer on Association and Dataset written by one of our power users, which you can check out here.)

Lets first check the dimensions of the data:



We can see that we have close to 19,000 rows of data with 33 columns. Lets pull the first column and row to get a sense of what we might want to explore:



(We can also grab the data dictionary from the California Open Data Portal using Import.)



Reported water production seems like an interesting starting point, so lets dig in using some convenient functions— TakeLargestBy and Select—to examine the top ten water production levels by supplier for the last reporting period:



top10 output

Unsurprisingly, we see very populous regions of the state of California having the highest levels of reported water production. Since we have already defined our top-ten dataset, we can now look at other variables in this subset of the data. Lets visualize which suppliers have the highest percentages of residential water use with BarChart. We will use the top10 definition we just created and use All to examine every row of the data by the column "PercentResidentialUse":


BarChart[top10[All, "PercentResidentialUse"], ColorFunction -> "SolarColors", ChartLabels -> Normal[top10[All, "SupplierName"]], BarOrigin -> Left]

Youll notice that I used ColorFunction to indicate higher values as brighter colors. (There are many pallettes to choose from.) Just as a brief exploration, lets look at these supplier districts by population served:



The Eastern Municipal Water District is among the smallest of these in population, but were looking at percentages of residential water use, which might indicate there is less industrial or agricultural use of water in that district.

Penalty and Enforcement Data

Since were looking at safe drinking water data, lets explore penalties against water suppliers for regulatory violations. Well use the same functions as before, but this time well take the top five and then see what we can find out about a particular district with built-in data:


top5= TakeLargestBy[Select[uwsdata,#ReportingMonth==DateObject[{2018,4,15}]&],#PenaltiesRate &,5]

So we see the City of San Bernardino supplier has the highest penalty rate out of our top five. Lets start looking at penalty rates for the City of San Bernardino district. We have other variables that are related, such as complaints, warnings and follow-ups. Since were dealing with temporal data, i.e. penalties over time, we might want to use TimeSeries functionality, so well go ahead and start defining a few things, including our date range (which is uniform across our data) and the variables we just mentioned. Well also use Select to pull production data for the City of San Bernardino only:


dates=With[{sbdata=Select[uwsdata,#SupplierName=="City of San Bernardino" &]},sbdata[All,"ReportingMonth"]//Normal];

A few things to notice here. First, we used the function With to combine some definitions into more compact code. We then used Normal to transform the dates to a list so theyre easier to manipulate for time series.

Basically, what we said here is, With data from the supplier named City of San Bernardino, define the variable dates as the reporting month from that data and turn it into a list. Once you can start to see the narrative of your code, the better you can start programming at the rate of your thought, kind of like regular typing, something the Wolfram Language is very well suited for.

Lets go ahead and define our penalty-related variables:



So we first put our variables in order in curly brackets and used # (called slot, though it’s tempting to call it hashtag!) as a placeholder for a later argument. So, if we were to read this line of code, it would be something like, For these four variables, use all rows of the San Bernardino data, make them into a list and define each of those variables with the penalty rate, warnings, follow-ups and complaints columns, in that order, as a list. In other words, extract those columns of data as individual variables.

Since well probably be using TimeSeries a good bit with this particular data, we can also go ahead and define a function to save us time down the road:



All weve said here is, Whenever we type ts[], whatever comes in between the brackets will be plugged into the right side of the function where v is. So we have our TimeSeries function, and we went ahead and put dates in there so we dont have to continually associate a range of values with each of our date values every time we want to make a time series. We can also go ahead and define some style options to save us time with visualizations:


style = {PlotRange -> All, Filling -> Axis, Joined -> False,
   Frame -> False};

Now with some setup out of the way (this can be tedious, but its important to stay organized and efficient!), we can generate some graphics:



So we again used With to make our code a bit more compact and used our ts[] time series function and went a level deeper by using # again to apply that time series function to each of those four variables. Again, in plain words, With this variable, take our time series function and apply it to these four variables that come after &. Then, make a plot of those time series values and apply the style we defined to it.

We can see some of the values are flat along the x axis. Lets take a look at the range of values in our variables and see if we can improve upon this:



We can see that the penalty rate has a massively higher maximum value than our other variables. So what should we do? Well, we can log the values and visualize them all in one go with DateListLogPlot:



So it appears that the enforcement program didnt really get into full force until sometime after 2015, and following preliminary actions, penalties started being issued on a massive scale. Penalty-related actions appear to also increase during summer months, perhaps when production is higher, something well examine and confirm a little later. Lets look at warnings, follow-ups and complaints on their own:



We used similar code to the previous graphic, but this time we left out our defined style and used PlotLegends to help us see which variables apply to which values. We can visualize this a little differently using StackedDateListPlot:



We see a strong pattern here of complaints, warnings and follow-ups occurring in tandem, something not all too surprising but that might indicate the effectiveness of reporting systems.

Agriculture and Weather Data

So far, weve looked at one city and just a few variables in exploratory analysis. Lets shift gears and take a look at agriculture. We can grab another dataset in the Wolfram Data Repository to very quicky visualize agricultural land use with a small chunk of code:


GeoRegionValuePlot[ResourceData["California Crop Mapping"][GroupBy["County"],Total,"Acres"]]

We can also visualize agricultural land use a different way using GeoSmoothHistogram with a GeoBackground option:


GeoSmoothHistogram[ResourceData["California Crop Mapping"][GroupBy["County"],Total,"Acres"],GeoBackground->"Satellite",PlotLegends->Placed[Automatic,Below]]

Between these two visualizations, we can clearly see Californias central valley has the highest levels of agricultural land use.

Now lets use our TakeLargestBy function again to grab the top five districts by agricultural water use from our dataset:


TakeLargestBy[Select[uwsdata,#ReportingMonth==DateObject[{2018,4,15}]&],#AgricultureReported &,5]



So for the last reporting month, we see the Rancho California Water District has the highest amount of agricultural water use. Lets see if we can find out where in California that is by using WebSearch:


WebSearch["rancho california water district map"]



Looking at the first link, we can see that the water district serves the city of Temecula, portions of the city of Murrieta and Vail Lake.

One of the most convenient features of the Wolfram Language is the knowledge thats built directly into the language. (Theres a nice Wolfram U training course about the Wolfram Data Framework you can check out here.)

Lets grab a map and a satellite image to see what sort of terrain were dealing with:


GeoGraphics[Entity["Lake", "VailLake::6737y"],ImageSize->600]
GeoImage[Entity["Lake", "VailLake::6737y"],ImageSize->600]

This looks fairly rural and congruent with our data showing higher levels of agricultural water use, but this is interestingly enough not in the central valley where agricultural land use is highest, something to perhaps note for future exploration and examination.

Lets now use WeatherData to get rainfall data for the city of Temecula, since it is likely coming from the same weather station as Vail Lake and Murrieta:


temecula=WeatherData[Entity["City", {"Temecula", "California", "UnitedStates"}],"TotalPrecipitation",{{2014,6,15},{2018,4,15},"Month"}];

We can also grab water production and agricultural use for the district and see if we have any correlations going on with weather and water usea fairly obvious guess, but its always nice to show something with data. Lets go ahead and define a legend variable first:


legend=PlotLegends->{"Water Production","Agricultural Usage","Temecula Rainfall"};


ranchoprod=With[{ranchodata=Select[uwsdata,#SupplierName=="Rancho California Water District" &]},ranchodata[All,"ProductionReported"]//Normal];





Weve logged some values here, but we could also manually rescale to get a better sense of the comparisons:



And we can indeed see some dips in water production and agricultural use when rainfall increases, indicating that both usage and production are inversely correlated with rainfall and, by definition, usage and production are correlated with one another.

Machine Learning for Classification

One variable that might be useful to examine in the dataset is whether or not a district is under mandatory restrictions on outdoor irrigation. Lets use Classify and its associated functions to measure how we can best predict bans on outdoor irrigation to perhaps inform what features water districts could focus on for water conservation. Well begin by using RandomSample to split our data into training and test sets:







Well now build a classifier with the outcome variable defined as mandatory restrictions:



We have a classifier function returned, and the Wolfram Language automatically chose GradientBoostedTrees to best fit the data. If we were sure we wanted to use something like logistic regression, we could easily specify which algorithm wed like to use out of several choices.

But lets take a closer look at what our automated model selection came up with using ClassifierInformation:





We get back a general description of the algorithm chosen and can see the learning curves for each algorithm, indicating why gradient boosted trees was the best fit. Lets now use ClassifierMeasurements with our test data to look at how well our classifier is behaving:





Ninety-three percent is acceptable for our purposes in exploring this dataset. We can now generate a plot to see what the rejection threshold is for achieving a higher accuracy in case we want to think about improving upon that:



And lets pull up the classifiers confusion matrix to see what we can glean from it:



It looks like the classifier could be improved for predicting False. Lets get the F-score to be sure:



Again, not too terrible with predicting that at a certain point in time a given location will be under mandatory restrictions for outdoor irrigation based on the features in our dataset. As an additional line of inquiry, we could use FeatureExtraction as a preprocessing step to see if we can improve our accuracy. But for this exploration, we see that we could indeed examine conditions under which a given district might be required to restrict outdoor irrigation and give us information on what water suppliers or policymakers might want to pay the most attention to in water conservation.

So far, weve looked at some of the top water-producing districts, areas with high penalty rates and how other enforcement measures compare, the impact of rainfall on agricultural water use with some built-in data and how we might predict what areas will fall under mandatory restrictions on outdoor irrigationa nice starting point for further explorations.

Try It for Yourself

Think youre up for the Safe Drinking Water Data Challenge? Try it out for yourself! You can send an email to partner-program@wolfram.com and mention the Safe Drinking Water Data Challenge in the subject line to get a license to Wolfram|One. You can also access an abundance of free training resources for data science and statistics at Wolfram U. In case you get stuck, you can check out the following resources, or go over to Wolfram Community and make sure to post your analysis there as well.

Additional resources:

We look forward to seeing what problems you can solve with some creativity and data science with the Wolfram Language.

Download this post as a Wolfram Notebook.


3. Computational Exploration of the Mathematics Genealogy Project in the Wolfram Language., 02 .[−]
The Mathematics Genealogy Project (MGP) is a project dedicated to the compilation of information about all mathematicians of the world, storing this information in a database and exposing it via a web-based search interface. The MGP database contains more than 230,000 mathematicians as of July 2018, and has continued to grow roughly linearly in size since its inception in 1997. In order to make this data more accessible and easily computable, we created an internal version of the MGP data using the Wolfram Languages 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 mathematicians 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. Explore Mathematicians 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: #10005 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: #10005 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: #10005 InputForm[person] Copying, pasting and evaluating that expression to obtain the formatted version again: #10005 Entity["MGPPerson","94172"] We now extract the domain, canonical name and common name of the entity programmatically: #10005 Through[{EntityTypeName,CanonicalName,CommonName}[person]]//InputForm We can simultaneously obtain a set of random people from the "MGPPerson" domain: #10005 RandomEntity["MGPPerson",10] To obtain a list of properties available in the "MGPPerson" domain, we again use EntityValue: #10005 properties=EntityValue["MGPPerson","Properties"] As we did for entities, we can view the internal structure of the first property: #10005 InputForm[First[properties]] We can also view the string of canonical names of all the properties: #10005 CanonicalName[properties] The URL to the relevant MGP page is available directly as its own property, which can be done concisely as: #10005 EntityValue[person,"MathematicsGenealogyProjectURL"] with an explicit EntityProperty wrapper: #10005 EntityValue[person,EntityProperty["MGPPerson","MathematicsGenealogyProjectURL"]] or using a curried syntax: #10005 person["MathematicsGenealogyProjectURL"] We can also return multiple properties: #10005 person[{"AdvisedBy","Degrees","DegreeDates","DegreeSchoolEntities"}] Another powerful feature of the Wolfram Language entity framework is the ability to create an implicitly defined Entity class: #10005 EntityClass["MGPPerson","Surname"->"Nelson"] Expanding this class, we obtain a list of people with the given surname: #10005 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: #10005 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: #10005 NestGraph[#["AdvisedBy"] ,Entity["MGPPerson", "174871"],10,VertexLabels->Placed["Name",After,Rotate[#,30 Degree,{-3.2,0}] ]] Using an implicitly defined EntityClass, lets now look up people with the last name Hardy: #10005 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: #10005 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: #10005 Histogram3D[Select[Flatten[ToCharacterCode[#]] /@Map[RemoveDiacritics@StringTake[#,1] ,DeleteMissing[EntityValue["MGPPerson",{"GivenName","Surname"}],1,2],{2}],(65Automatic] We can now make a fit to the number of new PhD mathematicians over the period 18751975: #10005 fit=Fit[Select[{#1["Year"],1. Log[2,#2]} @@@phddata,1875"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": #10005 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": #10005 TextGrid[Cases[phds,{_,"Ph.D.",_Missing,_List?(Length[#]!=1 )}],Dividers->All] Tally the countries (excluding the pair of multiples): #10005 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: #10005 Length[countrytallies] Download flag images for these countries from the Wolfram Knowledgebase: #10005 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: #10005 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: #10005 Length[umiamidegrees=EntityList[ EntityClass["MGPDegree",{ "SchoolEntity"->Entity["University", "UniversityOfMiami::9c2k9"], "Date"-> GreaterEqualThan[DateObject[{2010}]]} ]]] Create a timeline visualization: #10005 TimelinePlot[Association/@Rule@@@EntityValue[umiamidegrees,{"Advisee","Date"}],ImageSize->Large] Now consider recent US mathematics degrees. Select the theses written at US institutions since 2000: #10005 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: #10005 TextGrid[Take[schools=Reverse[SortBy[Tally[Flatten[EntityValue[USPhDs,"SchoolEntity"]]],Last]],12],Alignment->{{Left,Decimal}},Dividers->All] Map schools to their geographic positions: #10005 geopositions=Rule@@@DeleteMissing[Transpose[{EntityValue[schools[[All,1]],"Position"],schools[[All,2]]}],1,2]; Visualize the geographic distribution of US PhDs : #10005 GeoBubbleChart[geopositions,GeoRange->Entity["Country", "UnitedStates"]] Show mathematician thesis production as a smooth kernel histogram over the US: #10005 GeoSmoothHistogram[Flatten[Table[#1,{#2}] @@@geopositions],"Oversmooth",GeoRange->GeoVariant[Entity["Country", "UnitedStates"],Automatic]] Explore Thesis Titles We now make some explorations of the titles of mathematical theses. To begin, extract theses authored by people with the surname Smith: #10005 Length[smiths=EntityList[EntityClass["MGPPerson","Surname"->"Smith"]]] Create a WordCloud of words in the titles: #10005 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: #10005 tt=DeleteMissing[EntityValue["MGPDegree",{"Date","ThesisTitle"}],1,2]; The average string length of a thesis is remarkably constant over time: #10005 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: #10005 SortBy[tt,StringLength[#[[2]]] ]//Last Motivated by this, extract explicit fragments appearing in titles: #10005 tex=Cases[ImportString[#,"TeX"] /@Flatten[DeleteCases[StringCases[#2,Shortest["$"~~___~~"$"]] @@@tt,{}]],Cell[_,"InlineFormula",___],?]//Quiet; ... and display them in a word cloud: #10005 WordCloud[DisplayForm/@tex] Extract types of topological spaces mentioned in thesis titles and display them in a ranked table: #10005 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}}] Explore Mathematical Subjects 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: #10005 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: #10005 mscnames=List; Extract degrees awarded since 2010: #10005 Length[degrees2010andlater=Cases[Transpose[{EntityList["MGPDegree"],EntityValue["MGPDegree","Date" ]}],{th_,DateObject[{y_?(GreaterEqualThan[2010])},___]}:>th]] Extract the corresponding MSC numbers: #10005 degreeMSCs=DeleteMissing[EntityValue[degrees2010andlater,"MSCNumber"]]; Make a pie chart showing the distribution of MSC category names and numbers: #10005 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}: #10005 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): #10005 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 advisors degrees and those of each advisee: #10005 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: #10005 TextGrid[msctransition[Entity["MGPPerson", "17947"]]/.mscnames,Dividers->All] Find MSC transitions for degree dates after 1988: #10005 transitiondata=msctransition/@Select[DeleteMissing[ EntityValue["MGPPerson",{"Entity","DegreeDates"}],1,2],Min[#["Year"] /@#[[2]]] 1988 ][[All,1]]; #10005 transitiondataaccumulated=Tally[Flatten[Apply[Function[{a,b},Outer[DirectedEdge,a,b]], Flatten[Take[transitiondata,All],1],{1}],2]]/.mscnames; #10005 toptransitions=Select[transitiondataaccumulated,Last[#]>10 ]/.mscnames; #10005 Grid[Reverse[Take[SortBy[transitiondataaccumulated,Last],-10]],Dividers->Center,Alignment->Left] #10005 msctransitiongraph=Graph[First/@toptransitions,EdgeLabels->Placed["Name",Tooltip],VertexLabels->Placed["Name",Tooltip],GraphLayout->"HighDimensionalEmbedding"]; #10005 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]] Explore Advisors Construct a list of directed edges from advisors to their students: #10005 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: #10005 SelectFirst[Split[Sort[advisorPairs]],Length[#]>1 ] For example: #10005 (EntityValue[Entity["MGPPerson", "110698"],{"AdvisedBy","Degrees"}]/.e:Entity["MGPDegree",_]:>{e,e["DegreeType"]}) So build an explicit advisor graph by uniting the {advisor, advisee} pairs: #10005 advisorGraph=Graph[Union[advisorPairs],GraphLayout->None] The advisor graph contains more than 3,500 weakly connected components: #10005 Length[graphComponents=WeaklyConnectedGraphComponents[advisorGraph]] Visualize component sizes on a log-log plot: #10005 ListLogLogPlot[VertexCount/@graphComponents,Joined->True,Mesh->All,PlotRange->All] Find the size of the giant component (about 190,000 people): #10005 VertexCount[graphComponents[[1]]] Find the graph center of the second-largest component: #10005 GraphCenter[UndirectedGraph[graphComponents[[2]]]] Visualize the entire second-largest component: #10005 Graph[graphComponents[[2]],VertexLabels->"Name",ImageSize->Large] Identify the component in which David Hilbert resides: #10005 FirstPosition[VertexList/@graphComponents,Entity["MGPPerson", "7298"]][[1]] Show Hilberts students: #10005 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: #10005 Entity["MGPPerson", "34254"] ["PropertyAssociation"] And here is a picture of Darboux: #10005 Show[WikipediaData["Gaston Darboux","ImageList"]//Last,ImageSize->Small] Many mathematical constructs are named after Darboux: #10005 Select[EntityValue["MathWorld","Entities"],StringMatchQ[#[[2]],"*Darboux*"] ] ... and his name can even be used in adjectival form: #10005 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: #10005 {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: #10005 adviseeedges[pList_]:=Flatten[Function[p,DirectedEdge[Last[p],#] /@ DeleteMissing[Flatten[{Last[p][advised]}]]]/@pList] #10005 advgenerations=Rest[NestList[adviseeedges,{Null->Entity["MGPPerson", "34254"]},7]]; #10005 alladv=Flatten[advgenerations]; It contains more than 14,500 mathematicians: #10005 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: #10005 gr3d=Graph3D[alladv,GraphLayout->"SpringElectricalEmbedding"] We now look at the degree centrality of the nodes of this graph in a log-log plot: #10005 ListLogLogPlot[Tally[DegreeCentrality[gr3d]]] Lets now highlight the path to that plot for Fields Medal winners: #10005 style[path_,color_]:=Style[#,color,Thickness[0.004]] /@path #10005 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, Darbouxs descendents are distributed around the whole world: #10005 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: #10005 GeoGraphics[makeGeoPath/@Flatten[Take[advgenerations,4]],GeoBackground->"StreetMapNoLabels",GeoRange->"World"]//Quiet #10005 GeoGraphics[makeGeoPath /@ Flatten[Take[advgenerations, 6]], GeoBackground -> "StreetMapNoLabels", GeoRange -> "World"] // Quiet Distribution of Intervals between the Date at Which an Advisor Received a PhD and the Date at Which That Advisor's First Student's PhD Was Awarded Extract a list of advisors and the dates at which their advisees received their PhDs: #10005 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: #10005 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: #10005 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: #10005 Select[SplitBy[PhDAndDates,First],Length[#]>1 ]//Take[#,5] //Column For example: #10005 Entity["MGPPerson", "100896"]["Degrees"] ... who has these two PhDs: #10005 EntityValue[%,{"Date","DegreeType","SchoolName"}] While having two PhDs is not unheard of, having three is unique: #10005 Tally[Length/@SplitBy[PhDAndDates,First]] In particular: #10005 Select[SplitBy[PhDAndDates,First],Length[#]===3 ] Select the first PhDs of advisees and make a set of replacement rules to their first PhD dates: #10005 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: #10005 Take[times=-Subtract@@@(AdvisorsAndFirstStudentPhDDates/.FirstPhDDateRules),10] The data contains a small number of discrepancies where students allegedly received their PhDs prior to their advisors: #10005 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: #10005 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. (8)

4. New Wolfram Language Books: Dont Stop Learning Just Because Its Summer., 31 [−]

Its not really late summer unless youre armed with an apple and a good book. Theres been a recent slew of incredible books utilizing the capabilities of the Wolfram Language that make sure your coding knowledge never stops growing and your reading list stays stocked. (And be sure to check the farmers market for those apples.)


Beginning Artificial Intelligence with the Raspberry Pi

Your Raspberry Pis standard system software comes with the Wolfram Language, and using this bundle to break into the world of AI will complete your computing platform toolbox. Read about how author Donald J. Norris uses the Wolfram Language for deep machine learning demonstrations. You can also consider ExternalEvaluate (an exciting new feature made available in Version 11.3 of the Wolfram Language) as a way of incorporating skills taught in Python into your Wolfram Notebooks.

Numerical Solutions of Initial Value Problems Using Mathematica

Do you only have time for a quick read? Authors Sujaul Chowdhury and Ponkog Kumar Das have got you covered! Study numerical solutions to differential equations of elementary physics problems using Euler, second-order Runge–Kutta methods and Mathematica. The authors explore using Mathematica for dealing with complex numbers, solving linear equations and dealing with matrices—complex topics handled well in 58 pages.

A Mathematica Primer for Physicists

Mathematica was created by a physicist, and with Jim Napolitanos recent book you can learn how easy it is to use Mathematica for physics. Starting with the basics, this book spans across vectors and matrices, random number generation and data analysis, differential and integral calculus and beyond, all while explaining problem solving in a step-by-step fashion.


Group Theory in Solid State Physics and Photonics: Problem Solving with Mathematica

While group theory and its application to solid-state physics is a commonly broached topic, authors Wolfram Hergert and R. Matthias Geilhufe provide a deeper understanding through extensive use of Mathematica tools. Giving unique tools to the photonics community as well as offering a newly developed Mathematica package (GTPack, which is available for download), this book is an important addition to every physicists bookshelf.

Mathematica for Bioinformatics: A Wolfram Language Approach to Omics

If you thought Mathematica and the Wolfram Language were only useful in mathematics and physics, George Mias is here to tell you otherwise! Written using the latest version of the Wolfram Language, this book will help you learn the basics of the language and how it can be applied to bioinformatics using detailed coding examples, a wide variety of bioinformatics-specific scenarios and dynamically interactive tools built with the Wolfram Language. Geared to address the needs of the full spectrum of bioinformaticians, this book will lead you to coding expertise in under 400 pages.

Classical Mechanics with Mathematica, second edition

With over 30 years of teaching experience, authors Antonio Romano and Addolorata Marasco have developed the perfect textbook to give students an overview of the history of classical mechanics while incorporating modern developments. After reading this two-part text, youll come away with an understanding of tensor algebra, Euclidean and symplectic vector spaces, Lagrangian and Hamiltonian dynamics, HamiltonJacobi theory and impulsive dynamics, among many other topics. This second edition has been completely revised and updated, and now includes completely new chapters as well as almost 200 exercises. Included with your purchase of this text are several downloadable Wolfram Notebooks to help your students in understanding some of the more complex materials.


Discrete Mathematics through the Use of VilCretas Package

Enrique V?lchez Q. uses VilCretas, a package he created, to develop procedures for elementary learning of discrete mathematics. Learn about algorithm analysis, graph theory, finite-state automata and so much more using 230 commands available for addition to the Wolfram Language. This Spanish book is also available in English.

Introduccion a la Programacion en Mathematica: para ingenieros civiles y mecánicos

(Title in English: Introduction to Programming in Mathematica: For Civil and Mechanical Engineers)

You already know that the Wolfram Language can be used beyond math and physics, so you may have guessed that engineering is one of the many other fields in which the language is quite useful. Luis E. Su?rez teaches readers how to use Mathematica within mechanical and civil engineering; several examples focusing on vibrations and structural mechanics are also included.

Hands-on Start to Wolfram Mathematica with the Wolfram Language, second edition (in Chinese)

With their book now available in Chinese (as well as English and Japanese), authors Cliff Hastings, Kelvin Mischo and Michael Morrison have expanded their catch-all guide to learning and understanding Mathematica. Leave any of your remaining questions behindwith this book in hand, all youll need to do is go forth and program!


5. Big O and Friends: Tales of the Big, the Small and Every Scale in Between., 26 [−]
One of the many beautiful aspects of mathematics is that often, things that look radically different are in fact the sameor 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 functionsa common trick in mathematicsasymptotic 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 Bachmanns work (leading to the name BachmannLandau 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 well 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: #10005 AsymptoticLessEqual[x,x^2,x->?] Even though for many values of , because eventually will become bigger and continue increasing in size: #10005 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: #10005 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: #10005 AsymptoticEqual[10^5 x,x,x->?] The linear polynomials are not asymptotically equal to the quadratic one, however: #10005 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. #10005 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): #10005 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: #10005 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: #10005 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 : #10005 AsymptoticLess[n,n Log[n],n->?] Conversely, any algorithm with run time , such as bubble sort, will be slower for large inputs, as : #10005 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 convergencefor example, at infinity: #10005 AsymptoticEqual[1/n,ArcCot[n], n->?] It is well known that the sum of , known as the harmonic series, diverges: #10005 DiscreteLimit[Sum[1/n,{n,1,k}],k->?] Thus, must also diverge: #10005 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 : #10005 {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: #10005 {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 Hardys 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 Stirlings approximation for the factorial function: #10005 s[n_]:=Sqrt[2? n] (n/E)^n This function is asymptotically equivalent to the factorial function: #10005 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: #10005 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: #10005 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 Taylors 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: #10005 {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: #10005 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: #10005 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: #10005 AsymptoticGreater[x^m,x^n,x->0,Assumptions->m0],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: #10005 {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: #10005 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 Bessels equation, the asymptotic scale is needed to approximate the integraland Airys equation leads to the scale . We can verify that each of these indeed forms a scale by creating a small wrapper around AsymptoticGreater: #10005 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: #10005 AsymptoticScaleQ[{1/x^2,1/x,1,x,x^2},x->0] #10005 AsymptoticScaleQ[{x^(1/2),x^(5/2),x^(9/2),x^(13/2)},x->0] The last two, however, are asymptotic scales at ?: #10005 AsymptoticScaleQ[{E^?/?^(1/2),E^?/?^(3/2),E^?/?^(5/2)},?->?] #10005 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: #10005 AsymptoticScaleQ[{n^3,n^2,n,1},n->?] #10005 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 expansionsfamilies 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! Download this post as a Wolfram Notebook. (1)

6. Four Minecraft Projects with the Wolfram Language., 24 [−]
A couple of weeks ago I shared a package for controlling the Raspberry Pi version of Minecraft from Mathematica (either on the Pi or from another computer). You can control the Minecraft API from lots of languages, but the Wolfram Language is very well aligned to this taskboth because the rich, literate, multiparadigm style of the language makes it great for learning coding, and because its high-level data and computation features let you get exciting results very quickly. Today, I wanted to share four fun Minecraft project ideas that I had, together with simple code for achieving them. There are also some ideas for taking the projects further. If you havent already installed the MinecraftLink package, follow the instructions here. Rendering a Photo in Minecraft The Minecraft world is made up of blocks of different colors and textures. If we arrange these appropriately, we can use the colors to create grainy pictures. I want to automate this process of converting pictures to Minecraft blocks. The first step is to start a new world in Minecraft on the Raspberry Pi, then load the MinecraftLink package: #10005 ImageQ]],{Entity["MinecraftBlock", "Glass"],Entity["MinecraftBlock", "Leaves"],Entity["MinecraftBlock", "Cobweb"],Entity["MinecraftBlock", "Sand"],Entity["MinecraftBlock", "Gravel"],Entity["MinecraftBlock", "Snow"],Entity["MinecraftBlock", "Fire"],Entity["MinecraftBlock", "WaterStationary"]}] Here are the images that we have: #10005 Magnify[{#["Image"],#} /@available,0.6] Most blocks (subject to lighting) are the same on all faces, but a few have different textures on their side faces than their top faces. I plan to look at all blocks from the side, so I want to figure out what the blocks average side-face color is. To do this, I created the following mask for the position of the side-face pixels of the gold block: #10005 mask=Erosion[DominantColors[,4,"CoverageImage"][[2]],2] Because all the images have the same shape and viewpoint, I can apply that mask to every block to pick out their front-face pixels: #10005 mask RemoveAlphaChannel[Entity["MinecraftBlock","WoodBirch"]["Image"]] To make sure I am using a like-for-like measurement, I remove the transparency layer (AlphaChannel) and put them all into the same color space. Then I just ask for the average pixel value and convert that back to an average color (working in HSB color gives more perceptually correct averaging of colors): #10005 averageColor[block_]:=Hue[ImageMeasurements[ColorConvert[RemoveAlphaChannel[block["Image"],LightBlue],"HSB"],"Mean",Masking->mask]] #10005 colors=Map[averageColor,available] We can now look at our available palette: #10005 ChromaticityPlot[colors] You can see from this plot of colors in the visible color space that we have rather poor coverage of high-saturation colors, and something of a gap around the green/cyan border, but this is what we have to work with. Now we need a function that takes a color and picks out the block name that is nearest in color. The Wolfram Language already knows about perceptual color distance, so Nearest handles this directly: #10005 getName[color_]:=First[Nearest[MapThread[Rule,{colors,available}],color]]; For example, the block that is closest to pure red is wool orange: #10005 getName[Red] Now we need a function that will take a picture and drop its resolution to make it more blocky and simplify the image to use only the colors that are available to us: #10005 toBlockColors[img_,size_]:=ColorQuantize[ImageResize[img,size],colors]; Lets now apply that to a well-known picture: #10005 toBlockColors[,50] Now we just have to count through the pixels of that image, find the name of the block with the nearest color to the pixel and place it in the corresponding place in the Minecraft world: #10005 putPicture[{x0_,y0_,z0_},img_]:= Block[{dims=ImageDimensions[img]}, Do[ MinecraftSetBlock[{dims[[1]]-x+x0,y+y0,z0},getName[RGBColor[ImageValue[img,{x,y}]]]], {x,dims[[1]]},{y,dims[[2]]}]]; Find a big open space... And run the program on a simple image: #10005 putPicture[{30,0,0},toBlockColors[,50]] You can use Import to bring images into the system, but the Wolfram Language provides lots of images as part of its Entity system. For example, you can fetch famous works of art: #10005 Entity["Artwork", "AmericanGothic::GrantWood"]["Image"] Here is a detail from American Gothic (Grant Woods sister) in blocks: #10005 putPicture[{30,0,0},toBlockColors[ImageTake[Entity["Artwork", "AmericanGothic::GrantWood"]["Image"],{25,75},{10,50}],50]] You can even, at an incredibly low frame rate, make an outdoor movie theater by streaming frames from your webcam onto the wall. Here is me working on this blog post! #10005 While[True,putPicture[{30,0,0},toBlockColors[CurrentImage[],50]]] An interesting challenge would be to reverse this process to generate a map of the Minecraft world. You would need to scan the surface block type at every position in the Minecraft world and use the color map created to color a single pixel of the output map. Recreating the Real World in Minecraft This project sounds quite hard, but thanks to the built-in data in the Wolfram Language, it is actually very simple. Lets suppose I want to create my home country of the United Kingdom in Minecraft. All I need to do is place a grid of blocks at heights that correspond to heights of the land in the United Kingdom. We can get that data from the Wolfram Language with GeoElevationData: #10005 ListPlot3D[Reverse/@GeoElevationData[Entity["Country", "UnitedKingdom"]],PlotRange->{-10000,20000},Mesh->False] You will see that the data includes underwater values, so we will need to handle those differently to make the shape recognizable. Also, we dont need anywhere near as much resolution (GeoElevationData can go to a resolution of a few meters in some places). We need something more like this: #10005 ListPlot3D[Reverse/@GeoElevationData[Entity["Country", "UnitedKingdom"],GeoZoomLevel->3],PlotRange->{0,5000},Mesh->False] Now lets make that into blocks. Lets assume I will choose the minimum and maximum heights of our output. For any given position, I need to create a column of blocks. If the height is positive, this should be solid blocks up to the height, and air above. If the height is negative, then it is solid up to the point, water above that until we reach a given sea level value and then air above that. #10005 createMapColumn[{x_,y_,z_},seaLevel_,min_,max_]:=(MinecraftSetBlock[{{x,min,z},{x,y,z}},"Dirt"];If[y>=seaLevel,MinecraftSetBlock[{{x,y,z},{x,max,z}},Entity["MinecraftBlock", "Air"]], MinecraftSetBlock[{{x,y,z},{x,seaLevel-1,z}},Entity["MinecraftBlock", "WaterStationary"]];MinecraftSetBlock[{{x,seaLevel,z},{x,max,z}},"Air"]]); Now we just need to create a column for each position in our elevation data. All the work is in transforming the numbers. The reversing and transposing is to get the coordinates to line up with the compass correctly, QuantityMagnitude gets rid of units, and the rest is vertical scaling: #10005 MinecraftElevationPlot[data0_,{x0_,seaLevel_,z0_}, maxHeight_:5]:= Block[{data=QuantityMagnitude[Reverse[Map[Reverse,Transpose[data0]]]],scale, min,dims}, dims=Dimensions[data]; scale= maxHeight/Max[Flatten[data]]; min= Round[scale*Min[Flatten[data]]]; Do[createMapColumn[{Round[x0+i],Floor[scale data[[i,j]]+seaLevel],z0+j},Round[seaLevel], seaLevel+min,Round[maxHeight+seaLevel]],{i,dims[[1]]},{j,dims[[2]]}]] Before we start, we can use the following code to clear a large, flat area to work on and put the camera high in the air above the action: #10005 MinecraftSetBlock[{{-40,-10,-40},{40,0,40}},"Grass"]; MinecraftSetBlock[{{-40,0,-40},{40,50,40}},"Air"]; #10005 MinecraftSetCamera["Fixed"]; MinecraftSetCamera[{0,25,0}] And now we can place the map: #10005 MinecraftElevationPlot[GeoElevationData[Entity["Country", "UnitedKingdom"],GeoZoomLevel->2],{-15,0,-15},5] You can just see that the land is higher in mountainous Scotland. You can see that better with the camera in the usual position, but the coastline becomes harder to see. Alternatively, here is the view of the north ridge of Mount Everest, as seen from the summit: #10005 MinecraftSetCamera["Normal"] MinecraftElevationPlot[GeoElevationData[GeoDisk[Entity["Mountain", "MountEverest"],3 mi],GeoZoomLevel->9],{-15,-18,-15},30] A nicer version of this might switch materials at different heights to give you snowcapped mountains, or sandy beaches. I will leave that for you to add. Rendering a CT Scan in Minecraft If you are unlucky enough to need an MRI or CT scan, then you might end up with 3D image data such as this built-in example: #10005 Show[ExampleData[{"TestImage3D","CThead"}],BoxRatios->1] This might seem complex, but it is actually a simpler problem than the photo renderer we did earlier, because color isnt very meaningful in the CT world. Our simplest approach is just to drop the scans resolution, and convert it into either air or blocks. #10005 Binarize[ImageResize[ExampleData[{"TestImage3D","CThead"}],{80,80,80}]] We can easily find the coordinates of all the solid voxels, which we can use to place blocks in our world: #10005 Position[ImageData[%],1] We can wrap all of that into a single function, and add in an initial position in the Minecraft world. I added the small pause because if you run this code from a desktop computer, it will flood the Minecraft server with more requests than it can handle, and Minecraft will drop some of the blocks: #10005 fixCoordinates[{a_,b_,c_}]:={c,-a,b} (*Mapping coordinate systems*) #10005 minecraftImage3D[img_Image3D,pos_List,block_,threshold_:Automatic]:=( MinecraftSetBlock[{pos,pos+ImageDimensions[img]},"Air"];Map[(Pause[0.01];MinecraftSetBlock[pos+#,block]) ,fixCoordinates/@Position[ImageData[Binarize[img,threshold]],1]];) And here it is in action with the head: #10005 minecraftImage3D[ ImageResize[ExampleData[{"TestImage3D","CThead"}],{40,40,40}],{0,40,0},"GoldBlock"] But one thing to understand with 3D images is that there is information inside the image at every level, so if we change the threshold for binarizing then we can pick out just the denser bone material and make a skull: #10005 Binarize[ImageResize[ExampleData[{"TestImage3D","CThead"}],{80,80,80}],0.4] #10005 minecraftImage3D[ ImageResize[ExampleData[{"TestImage3D","CThead"}],{40,40,40}],{0,40,0},"GoldBlock",0.4] An interesting extension would be to establish three levels of density and use the glass block type to put a transparent skin on the skull. I will leave that for you to do. You can find DICOM images on the web that can be imported into the Wolfram Language with Import, but bewaresome of those can be quite large files. Automatic Pyramid Building The final project is about creating new game behavior. My idea is to create a special block combination that triggers an automatic action. Specifically, when you place a gold block on top of a glowstone block, a large pyramid will be built for you. The first step is to scan the surface blocks around a specific point for gold and return a list of surface gold block positions found: #10005 scanForGold[{x0_,y0_,z0_}]:=Block[{goldPos={}, height = MinecraftGetHeight[{x,z}]}, Do[Pause[0.1];If[MinecraftGetBlock[{x,height-1,z}]===Entity["MinecraftBlock","GoldBlock"],AppendTo[goldPos,{x,height-1,z}]],{x,x0-1,x0+1},{z,z0-1,z0+1}]; goldPos]; Next, we look under each of the gold blocks that we found and see if any have glowstone under them: #10005 checkGoldForGlowstone[goldPos_]:=FirstCase[goldPos,{x_,y_,z_}/;MinecraftGetBlock[{x,y-1,z}]===Entity["MinecraftBlock","GlowstoneBlock"]] Now we need a function that performs the resulting actions. It posts a message, removes the two special blocks and sets the pyramid: #10005 pyramidActions[found_]:=(MinecraftChat["Building Pyramid"]; MinecraftSetBlock[{found,found-{0,1,0}},"Air"]; MinecraftSetBlock[found-{0,1,0},"GoldBlock",Pyramid[],RasterSize->12]); We can now put all of that together into one function that scans around the current player and runs the actions on the first matching location. The PreemptProtect is a bit subtle. Because I am going to run this as a background task, I need to make sure that I dont perform two actions at once, as the messages going back and forth to the Minecraft server may get muddled: #10005 pyramidCheck[]:=PreemptProtect[Block[{found=checkGoldForGlowstone[scanForGold[MinecraftGetTile[]]]},If[Not[MissingQ[found]],pyramidActions[found]]]] All that is left is to run this code repeatedly every five seconds: #10005 task=SessionSubmit[ScheduledTask[pyramidCheck[],5]] I place the blocks like this... ... walk up within one block of the special column and wait for a couple of seconds, until this happens... To stop the task, you can evaluate... #10005 TaskRemove[task] Well, thats the end of my short series on Minecraft coding in the Wolfram Language. There are lots of fun knowledge domains and computation areas that I could have injected into Minecraft. I had thought of using reading surface blocks and constructing a 3D mini-map of the world using the Wolfram Languages data visualization, or creating a solar system of orbiting planets using the differential equation solver. I also considered creating a terrain generator using 3D cellular automata or fractals. But I do have a day job to do, so I will leave those for others to try. Do post your own project ideas or code on Wolfram Community. Download this post as a Wolfram Notebook. (0)

7. Getting to the Point: Asymptotic Expansions in the Wolfram Language., 19 [−]
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 devils 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: #10005 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: #10005 sol = AsymptoticDSolveValue[deqn, y[x], {x, 0, 8}] Here is a plot that compares the approximate solution with the exact solution : #10005 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: #10005 nsol[n_]:=Callout[AsymptoticDSolveValue[{y''[x]+y[x]==0,y[0]==1,y'[0]==0},y[x],{x,0,n}],n] #10005 Plot[{nsol[4],nsol[8],nsol[12],nsol[16],nsol[20],Cos[x]}//Evaluate,{x,0,3Pi},PlotRange->{-2,5}] Next, consider Bessels equation of order , which is given by: #10005 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: #10005 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: #10005 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 : #10005 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: #10005 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: #10005 AsymptoticDSolveValue[airyode, y[x], {x, ?, 3}] The components of this solution correspond to the asymptotic expansions for AiryAi and AiryBi at Infinity: #10005 s1 = Normal[Series[AiryAi[x], {x, ?, 4}]] #10005 s2 = Normal[Series[AiryBi[x], {x, ?, 4}]] The following plot shows that the approximation is very good for large values of : #10005 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 CauchyKovalevskaya 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: #10005 eqn={3 (y^?)[x]^2+4 x (y^?)[x]-y[x]+x^2==0,y[0]==1}; #10005 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 NavierStokes equations of fluid dynamics. Prandtls 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. Prandtls 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: #10005 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 : #10005 psol = AsymptoticDSolveValue[eqn, y[x], x, {?, 0, 1}] For this example, an exact solution can be computed using DSolveValue as follows: #10005 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!): #10005 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: #10005 AsymptoticEquivalent[dsol, psol,?->0,Direction->-1,Assumptions->0 Axis, FillingStyle -> Yellow] Laplaces method gives the following simple result for the leading term in the integral of from 0 to Infinity, for large values of the parameter : #10005 AsymptoticIntegrate[f[x], {x, 0, ?}, {?, ?, 1}] The following inputs compare the value of the approximation for with the numerical result given by NIntegrate: #10005 % /. {? -> 30.} #10005 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: #10005 AsymptoticIntegrate[f[x], {x, 0, ?}, {?, ?, 2}] The approximate answer now agrees very closely with the result from NIntegrate: #10005 % /. {? -> 30.} The British mathematicians Sir George Gabriel Stokes and Lord Kelvin modified Laplaces 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: #10005 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: #10005 Plot[Re[f[x]/. {?->50}],{t,0,?},Filling->Axis,FillingStyle->Yellow] The method of stationary phase gives a first-order approximation for this integral: #10005 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 : #10005 int/. ?->5000. #10005 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 : #10005 aint=AsymptoticIntegrate[E^(-t)/(1+x t),{t,0,Infinity},{x,0,8}] The term in the asymptotic expansion is given by: #10005 a[n_]:=(-1)^n n! x^n #10005 Table[a[n],{n,0,8}] SumConvergence informs us that this series is divergent for all nonzero values of : #10005 SumConvergence[a[n],n] However, for any fixed value of sufficiently near 0 (say, ), the truncated series gives a very good approximation: #10005 aint/.x-> 0.05 #10005 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: #10005 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: #10005 Integrate[E^(-t)/(1+x t),{t,0,Infinity},Assumptions-> x>0] #10005 Sum[a[n],{n,0,Infinity},Regularization->"Borel"] Both these results give essentially the same numerical value as the asymptotic expansion with eight terms: #10005 {%,%%}/.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: #10005 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: #10005 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: #10005 N[sol, 80] #10005 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. Download this post as a Wolfram Notebook. (3)

8. Why Is Sickle Cell Anemia Common in Areas with Malaria? Teaching Life Science with Modeling., 12 [−]

Life science teaches us to answer everything from “How can vaccines be used to indirectly protect people who haven’t been immunized?” to “Why are variations in eye color almost exclusively present among humans and domesticated animals?” You can now learn to answer these questions by using modeling with Wolfram’s virtual labs. Virtual labs are interactive course materials that are used to make teaching come alive, provide an easy way to study different concepts and promote student curiosity.

Cat with different eye colors

Two such virtual labs were created by MathCore’s summer intern, Anna Palmer, based on learning objectives from biology units 3 and 4 of the Victorian Certificate of Education (VCE) in the state of Victoria, Australia. Hear her talk about how she created them:

Population Models in Genetics

You’ve probably seen or heard about commercial kits where you send in a DNA sample to a company that then tells you about different things, such as your ancestry or predispositions for different diseases and genetic traits. Increasingly, we have become interested in our own genes and what stories our traits can tell us about ourselves and our ancestors. But we seldom stop to think how these genetic traits came to be in the first place.

To understand the ancestry that genetic kits tell you about, we need to go back to biology class. We need to understand how different traits, such as eye color, evolve in populations over time; the most well-known cases of this are natural selection and Darwin’s famous phrase “survival of the fittest.” But what do these actually mean?

Survival of the fittest

Contrary to what you may think, “survival of the fittest” does not just mean that only the fittest members of a species will survive, but rather, it refers to the ability of an organism to survive to reproduction. If an organism does not survive to reproduction, it cannot pass on its genes to the next generation. Thus, if a group of organisms with a particular trait has a decreased chance of surviving to reproduction, there is a decreased chance that the genes for this trait will be passed on to the next generation. This in turn may lead to the extinction of the trait.

As Anna mentioned in the video, she created a system model that explains this. In the following model, two populations with different traits (A and B) compete for the same resources. The different traits will be differently adapted to the environment, and not all born with a particular trait will reach maturity. If they reach maturity, however, they will pass on their genes to a new generation.

System model of population traits

By running this model, biology students can experiment with how different things in the model affect the outcome. In the virtual lab, this model has been embedded in a Wolfram Language notebook that contains explanations, questions and interactive interfaces using the Manipulate function, such as this:

Interactive population interface

By playing around with the interface, students can explore how even very slight differences in the survivability can cause a particular trait to overtake another within just a few generations. With this, students can try different “what-if” scenarios and instantly see if the results match their hypotheses.

Apart from ancestry, many people are also interested in knowing if they are predisposed toward certain diseases or not. There are many genes that influence just this. For example, a genetic variance causing sickle cell anemia actually protects against another disease, malaria. This explains why the gene for sickle cell anemia is found in about 7% of the population in malaria-stricken regions, but is virtually nonexistent elsewhere. In the same virtual lab, a model that explains this relationship is included.

System model of sickle cell anemia

In teaching, visualizing the complex relationship between two different diseases might be hard to do on paper; with a model and the Wolfram Language, it’s easy. Wolfram SystemModeler and the Wolfram Language make it possible for students to interact with the simulations, try different scenarios and set up experiments to instantly see if the results match their hypotheses.

Interactive sickle cell anemia model

We made this virtual lab available to students, educators and hobbyists alike. You can download the notebook here and learn about population genetics for yourself. In the download, you will find models of the scenarios described here, as well as a notebook providing you with the background and exercises that you can complete.

Population Models in Infectious Diseases

One of the things I really like about using SystemModeler is how easy it is to reuse the components you have created. This is especially useful in education, where the same concept can be found in many different settings. As an example, the types of population models that were used for the previous virtual lab are not only useful to illustrate genetic changes across multiple generations; among other things, they can also be used to explain how infectious diseases spread within populations. Anna developed a virtual lab for this too.

With vaccination versus without vaccination

The most well known of these models, the susceptible-infectious-recovered (SIR) model, describes a disease that transmits from an infected person to a susceptible person. After a certain amount of time, the infected person recovers from the disease and can’t be infected again. In these types of diseases, our immune system is what keeps us from falling back into the infected category. The SIR model has proven very useful at explaining everything from smallpox to seasonal influenza.

By getting vaccinated, you take a shortcut that moves you from the susceptible stage into the recovered stage, without going through the infected stage. This is illustrated in the “With Vaccination” model shown above.

In this virtual lab, interactive interfaces that allow the students to adjust things such as the rate of infections, the duration of the sickness and the vaccinated rate are provided:

Interactive model

By comparing two scenarios (no vaccination and low vaccination), students can use the model to explain and understand things such as herd immunity. Even though there is a larger percentage of susceptible people in the first scenario, there is clearly a lower rate of infection since the vaccinated people provide an indirect form of immunity to the non-immune individuals, called herd immunity.

Of course, this exercise (together with other exercises explaining infectious diseases) is available as a virtual lab that can be used by students, educators and hobbyists alike. You can download the virtual lab notebook here and learn about it for yourself!

Biology and genetics are just two of the areas where we have created virtual labs to explain different concepts, and we are looking to create even more. Do you have a good idea for an educational area where modeling could be applied? Let us know by sending us an email at wsmcourseware@wolfram.com.


9. Programming Minecraft on the Raspberry Pi., 05 [−]
The standard Raspbian software on the Raspberry Pi comes with a basic implementation of Minecraft and a full implementation of the Wolfram Language. Combining the two provides a fun playground for learning coding. If you are a gamer, you can use the richness of the Wolfram Language to programmatically generate all kinds of interesting structures in the game world, or to add new capabilities to the game. If you are a coder, then you can consider Minecraft just as a fun 3D rendering engine for the output of your code. Installation The first step is to make sure that you have all the right components. Make sure that you have the latest version of Raspbian and the Wolfram Language. You do this by connecting your Raspberry Pi to the network, opening the Terminal app and typing the following: sudo apt-get update. sudo apt-get dist-upgrade Now open Mathematica on the Pi, or another computer, and type: #10005 PacletInstall["MinecraftLink"] followed by Shift + Return to evaluate it. If all went well, we are ready to start. Using the Link The MinecraftLink library adds a small set of new commands to the Wolfram Language for connecting to a running Raspberry Pi Minecraft game. Start by launching Minecraft on the Raspberry Pi, and then start a fresh game or open an existing one. In the Wolfram Language, load the library by evaluating the following: #10005 50] Anything you can create in the Wolfram Language can be made into blocks. Here is a plot of the function Sin[x]: #10005 MinecraftSetBlock[pos,"Dirt",Plot[Sin[x],{x,0,12},Axes->False],RasterSize->18] #10005 MinecraftSetBlock[pos,"Air",Plot[Sin[x],{x,0,12},Axes->False],RasterSize->18] You can also control the orientation of rasterized images with an option Orientation. If the content is a 3D geometry, then it will be rasterized in 3D: #10005 MinecraftSetBlock[pos,"Wood",Sphere[],RasterSize->50] #10005 MinecraftSetBlock[pos,"Air",Sphere[],RasterSize->50] There are lots of 3D geometry primitives, and they can be combined in many ways. Here are some cuboids, a pyramid and a sphere to make a house: #10005 (*Main house frame*)MinecraftSetBlock[{pos,pos+{8,3,8}},"Wood"]; (*Windows*)MinecraftSetBlock[{pos+{1,0,0},pos+{7,3,8}},"Glass"]; (*Make it hollow*)MinecraftSetBlock[{pos+{1,0,1},pos+{7,3,7}},"Air"]; (*Add a doorway*)MinecraftSetBlock[{pos+{4,0,0},pos+{4,1,0}},"Air"]; (*Add a roof*) MinecraftSetBlock[pos+{0,4,0},"WoodPlanksSpruce",Pyramid[],RasterSize->12]; (*Decorate with gold ball*) MinecraftSetBlock[pos+{3,8,2},"GoldBlock",Sphere[],RasterSize->5];) OK, Im not much of an architect! We can look at our creation from the air by controlling the camera: #10005 MinecraftSetCamera["Fixed"]; MinecraftSetCamera[{0,25,6}]; #10005 MinecraftSetCamera["Normal"] Reacting to Events Finally, we can interact with blocks that you hit using the right mouse button while holding a sword. The left mouse button just places and smashes blocks, but the right mouse button creates events that wait for us to read and act on them. You can read these with: #10005 MinecraftHitHistory[] This shows that since the game started, I have done two of these special hits, each time on the same block at {1, 2, 2}, on face number 1 (the top of the block). I am player 1, but there could be multiple players. I can fetch these pieces of information by position and name. For example, HitHistory[1] is the last hit, and we extract its "Position" information and use that coordinate in MinecraftGetBlock to discover the type of block that was most recently hit: #10005 MinecraftGetBlock[HitHistory[-1]["Position"]] And we can clear the data with: #10005 MinecraftClearHits[] As a simple example, lets monitor this list every second and create an explosive super hit. I will define the explosion first. It is a function that takes a position and places a large sphere of air at that position: #10005 explosion[event_]:=MinecraftSetBlock[event["Position"]-{2,2,2},"Air",Ball[],RasterSize->8]; Now I create a scheduled task to run every second, and apply that function to the hit history: #10005 task=SessionSubmit[ ScheduledTask[ Map[explosion,MinecraftHitHistory[]];MinecraftClearHits[],1]] And now when I strike the ground in front of my house with my sword, using the right mouse button, a huge hole appears... I can remove the monitoring task with: #10005 TaskRemove[task] There are a few more commands in the MinecraftLink package that you can read about in the documentation after you have installed the link. As well as giving you a simple programming interface to Minecraft, similar to other languages, the Wolfram Language contains hundreds of high-level functions that let you develop much more exciting projects quickly; you might want to check out some of the 3D geometry, 3D image processing and built-in data sources as a starting point. I will return soon with a few projects of my own. Download this post as a Wolfram Notebook. (6)

10. The Shape of the Vote: Exploring Congressional Districts with Computation., 26 [−]
In the past few decades, the process of redistricting has moved squarely into the computational realm, and with it the political practice of gerrymandering. But how can one solve the problem of equal representation mathematically? And what can be done to test the fairness of districts? In this post Ill take a deeper dive with the Wolfram Languageusing data exploration with Import and Association, built-in knowledge through the Entity framework and various GeoGraphics visualizations to better understand how redistricting works, where issues can arise and how to identify the effects of gerrymandering. Rules of Apportionment In the US House of Representatives, each state is assigned a number of representatives based on its population through the process of apportionment (or reapportionment). On the surface, the rules for this process are simple: each state gets at least one representative, and representative seats must be redistributed at least once per decennial census. Apportionment has been tried using various mathematical methods throughout history. Since the 1940 Census, representatives have been assigned using the method of equal proportions (the HuntingtonHill method). This means that the next available slot goes to the state with the highest priority , defined as: #10005 TraditionalForm[Subscript[A, n]==P/GeometricMean[{n,n-1}]] where P is the population of the state and n is the number of districts already assigned to the state. You might recognize the denominator as the geometric mean of and . Its straightforward to implement symbolically: #10005 Priority[pop_,n_]:=N[pop/GeometricMean[{n,n-1}]] Formula in hand, Id like to run a simulation to compare to the current apportionment plan. First Ill pull the 2010 population data from the Wolfram Knowledgebase (excluding the District of Columbia): #10005 states=Complement[all US states with District of Columbia administrative divisions["Entities"],{Entity["AdministrativeDivision", {"DistrictOfColumbia", "UnitedStates"}]}]; #10005 statenames=StringDrop[#["Name"],-15] /@states; #10005 popdata=AssociationThread[statenames->Table[QuantityMagnitude[Dated[s,2010]["Population"]],{s,states}]]; #10005 RandomChoice[Normal@popdata] Its worth noting that these population counts are slightly different from the official reapportionment numbers, which include overseas residents for each state. The discrepancy is too small to make a difference in my apportionment computations, but it could be a topic for a more detailed exploration. To start my simulation, I give each state one representative. The initial 50 are actually assigned before applying the formula, so Ill set those initial priority values at Infinity: #10005 init=Thread[statenames->?]; From there, districts are assigned based on successively smaller priority values. Historically, no state has received more than 55 seats, so Ill set the upper limit at 60: #10005 pvalues=Flatten@Table[Normal@Priority[popdata,i],{i,2,60}]; Since only 435 seats are available, the rest can be dropped: #10005 app=TakeLargestBy[Join[init,pvalues],Values[#] ,435]; Heres a function that displays the apportionment data on a map: #10005 DistrictWeightMap[apportionment_]:=GeoRegionValuePlot[KeyMap[Interpreter["USState"],apportionment]//Normal,GeoRange->Entity["Country", "UnitedStates"],GeoProjection->"Mercator",GeoLabels->(Text[Style[#4,FontFamily->"Arabic Transparent",White,Medium],#3] ), ImageSize->1200,ColorFunction->(Which[#540, GeoLabels->(Text[Style[#4,"Text",White,10,FontFamily->"Arabic Transparent"],#3] ),ColorRules->({_?Positive->Green,_?Negative->Red,_->Gray})] If I reapportion using the current population estimates, Texas and South Carolina both gain a seat, while Wisconsin and Pennsylvania both lose one: #10005 latestpopdata=AssociationThread[statenames->Table[QuantityMagnitude[s["Population"]],{s,states}]]; #10005 latestpvalues=Flatten@Table[Normal@Priority[latestpopdata,i],{i,2,60}]; #10005 latestapp=TakeLargestBy[Join[init,latestpvalues],Values[#] ,435]; #10005 DistrictDifferenceMap[ReverseSort@Counts[Keys@latestapp],ReverseSort@Counts[Keys@app]] But population growth also affects the apportionment process in other ways. While the number of districts increased steadily for many years, it has remained essentially constant at 435 since 1913 (a limit codified in 1929). As a result, modern representatives have much larger constituencies than in the paston average, these have nearly quadrupled over the last century: #10005 uspophistory=Dated[Entity["Country", "UnitedStates"],All]["Population"]; #10005 DateListPlot[TimeSeriesWindow[uspophistory,{"1918",Today}]/435.,ColorFunction->"DarkRainbow",PlotRange->Full,PlotTheme->"Detailed"] Some states also end up with much more populous districts than others. Many have argued that this violates the established one person, one vote principle by, for instance, giving voters in Wyoming (with around 500,000 voters per representative) more federal voting power than those in Montana (with about 900,000 voters per representative): #10005 popperdist=ReverseSort@Association@Table[Interpreter["USState"][s]->N[popdata[s]/Counts[Keys@app][s]],{s,statenames}]; #10005 GeoRegionValuePlot[popperdist,GeoProjection->"Mercator",GeoRange->Entity["Country", "UnitedStates"],ColorFunction->"TemperatureMap"] A Congressional Apportionment Amendment was drafted (but never ratified) that set an initial guideline of [not] less than one Representative for every forty thousand persons. Heres what the breakdown would look like if we used that guideline today: #10005 newapp=TakeLargestBy[Join[init,Flatten@Table[Normal[Priority[#,i] /@popdata],{i,2,1000}]],Values[#] ,Floor[Total[popdata]/40000.]]; #10005 DistrictWeightMap[newapp] While the original limitation of 40,000 citizens per representative is perhaps no longer viable (one can imagine how chaotic an 8,000-member legislature would be), adding more seats certainly reduces the population spread among the districts: #10005 newpopperdist=ReverseSort@Association@Table[Interpreter["USState"][s]->N[popdata[s]/Counts[Keys@newapp][s]],{s,statenames}]; #10005 GeoRegionValuePlot[newpopperdist,GeoProjection->"Mercator",GeoRange->Entity["Country", "UnitedStates"],ColorFunction->"TemperatureMap"] Of course, apportionment is just the first step. Adding more seats would also mean adding more districtsand that would likely make the next stage a lot more complicated. Redistricting by the Numbers Since populations migrate and fluctuate, government officials are constitutionally required to redraw congressional districts following reapportionment. On its surface, this seems straightforward: divide each state into areas of equal population. But the reality can be deceptively complex. First, using a naive approach, the number of ways to divide a population into equal parts is huge. Suppose you wanted to split a group of 50 people into non-overlapping groups of 10: #10005 Times@@Binomial[Range[50.,10,-10],10]/2 This issue scales up with the size of the population; with the current population of the US, the number of ways to divide it into 435 equal districts (ignoring all other constraints) is truly astounding: #10005 (Times@@Binomial[Range[#1,#2,-#2],#2]/#2!) @@{QuantityMagnitude[Entity["Country", "UnitedStates"]["Population"]],435} Then theres the problem of actually drawing sensible districts with roughly equal population in each state. Congressional maps are usually drawn and approved by state legislatures, who must meet varying requirements for contiguousness, compactness and other qualities associated with fair districts. In a recent Wolfram Community post, Professor Marco Thiel explores a computational approach to drawing unbiased districts; here is how his algorithm splits up Iowa: The latest district maps are available through the Wolfram Knowledgebase: #10005 current=KeyDrop[GroupBy[EntityList["USCongressionalDistrict"],#["USState"] ],{"DistrictOfColumbia",Missing["NotApplicable"],Missing["NotAvailable"]}]; This makes it easy to (roughly) check the equal population requirement; the districts within each state differ by less than one percent, on average: #10005 distpop=Table[DeleteMissing[#["Population"] /@current[s]],{s,statenames}]; #10005 Mean@Table[If[Length[v]>1,N@StandardDeviation[v]/Mean@v,0.],{v,distpop}] In some cases, the maps have a nice geometric aesthetic, with shapes that fit together like a childrens puzzle. This type of map tends to follow county lines, only straying when necessary to satisfy the equal population requirement. The quintessential example of this is Iowa: #10005 iacounties=EntityClass["AdministrativeDivision", "USCountiesIowa"]; #10005 Show[GeoListPlot[List/@current["Iowa"],PlotLegends->None],GeoListPlot[iacounties,PlotStyle->Directive[EdgeForm[Blue],FaceForm[Opacity[0]]]]] Unfortunately, this isnt the case in most states. By contrast, here is North Carolinas notoriously jagged map: #10005 nccounties=EntityClass["AdministrativeDivision", "USCountiesNorthCarolina"]; #10005 Show[GeoListPlot[List/@current["NorthCarolina"],PlotLegends->None],GeoListPlot[nccounties,PlotStyle->Directive[EdgeForm[Blue],FaceForm[Opacity[0]]]]] This kind of irregular shape is considered one of the main indications of deliberate manipulation of districts (and indeed, North Carolinas map is currently being contested in court), but thats not to say that every oddly shaped district is gerrymandered. Crooked borders often evolve slowly as the demography of areas change subtly over time. Drawing on Experience: Historical Maps I wanted to dig a bit deeper, so I thought Id take a look at historical congressional maps that are readily available for viewing and analysis. The TIGER/Shapefile format (with a .zip extension) can be directly imported for a GeoGraphics object containing all the districts combined: #10005 Import["http://cdmaps.polisci.ucla.edu/shp/districts001.zip"] For inspecting individual districts, the files also contain detailed data in key-value pairs. I like using Association in this situation, since it lets me reference elements by name: #10005 c1=Association@First@Import["http://cdmaps.polisci.ucla.edu/shp/districts001.zip","Data"]; #10005 Keys@c1 The "LabeledData" element contains ordered information about individual districts: #10005 ld=Association@c1["LabeledData"]; Keys@ld From there, I can create entries that associate each state name with its district numbers and geometry: #10005 entries= #[[3]]|>|> /@ Transpose[{ ld["STATENAME"], ld["DISTRICT"], Polygon[Cases[#,_GeoPosition,All]] /@c1["Geometry"] }]; Lastly, I consolidate all entries from each state: #10005 statenames=Union[Keys@entries]//Flatten; #10005 districts=Association@Table[Merge[Sort@Select[entries,StringMatchQ[First@Keys@#,s] ],Association],{s,statenames}]; From here, I can easily examine districts on a per-state basis: #10005 GeoListPlot[List/@Values@districts["Virginia"],PlotLegends->None] Here is the original districting map for the entire country, drawn by the First United States Congress: #10005 Show@@Table[GeoListPlot[List/@Values@d,PlotLegends->None],{d,districts}] I put it all together into a function that can import the full district data from any past Congress into an Association for easy exploration: #10005 CongressionalMapData[congressnumber_]:= Module[{baseURL="http://cdmaps.polisci.ucla.edu/shp/",raw,ld,entries,statenames},raw=Association@First@Import[baseURL"districts"StringPadLeft[ToString[congressnumber],3,"0"]".zip","Data"]; ld=Association@raw["LabeledData"]; entries= #[[3]]|>|> /@Transpose[{ld["STATENAME"],ld["DISTRICT"], Polygon[Cases[#,_GeoPosition,All]] /@raw["Geometry"]}]; statenames=Union[Keys@entries]//Flatten; Association@Table[Merge[Sort@Select[entries,StringMatchQ[First@Keys@#,s] ],Association],{s,statenames}] ] Rather than having to reference by Congress number, its easier to reference by year: #10005 CongressNumber[year_]:=Floor[(year-1787)/2.] CongressionalMapData[year_?(#>1700 )]:=CongressionalMapData[CongressNumber[year]] Lastly, heres a function for visualizing all districts in a state: #10005 DistrictMap[statedata_]:=GeoListPlot[Table[{s},{s,statedata}],GeoLabels->(Tooltip[#1,FirstPosition[statedata,#1][[1,1]]] ),PlotLegends->None] This makes it easy to look at individual districts for a given state and year. Looking at the map from 100 years ago, I was surprised to learn that Illinois used to have over 70% more districts: #10005 dist1918=CongressionalMapData[1918]; #10005 N@Length@current["Illinois"]/Length@dist1918["Illinois"] This included one at-large representative that represented the entire state, rather than a particular district or area. In this data, such districts are numbered 0: #10005 GeoGraphics[dist1918["Illinois",0]] In general, it seems like the population has shifted away from the Midwest region: #10005 DistrictDifferenceMap[Length/@current,Length/@dist1918] Importing the full set of maps took me about 40 minutes and most of the RAM on my laptop: #10005 allmaps=Table[CongressionalMapData[cnum],{cnum,114}]; Heres a complete history of reapportionment counts: #10005 frames = Table[{DistrictWeightMap[Length /@ Values /@ allmaps[[i]]], 1789 + 2 i - 1}, {i, 114}]; ListAnimate[Labeled[#1, Style[#2, "Section"], Top] @@@ frames] With the full dataset, I can look at the history of districts for a particular state, which can give some insights about its development. New York is an interesting case: it started with 10 districts, and continued gaining seats with population growth until it peaked at 44 districts in the mid-20th century. Since then, its been losing seats due to population shifts and the 435-member cap on the House. The map has changed nearly every ten-year cycle, indicating that internal demographics have shifted as well: #10005 nydists=Table[{i,allmaps[[CongressNumber[i],"New York"]]},{i,1793,2013,10}]; #10005 ListAnimate[ Labeled[DistrictMap[#2], Style[ToString[#1] ": " Capitalize@IntegerName[Length[#2]] " Districts", "Section"], Top] @@@ nydists, AnimationRepetitions -> 1, AnimationRunning -> False] New Hampshire sits on the other end of the spectrum, having gone through only minimal changes since its original map. It actually kept the same two-district plan for the eight cycles between 1880 and 1960. The simplest explanation is that, unlike New York, this states demographics have remained fairly constant (and its population growth average): #10005 nhdists=Table[{i,allmaps[[CongressNumber[i],"New Hampshire"]]},{i,1793,2013,10}]; #10005 ListAnimate[ Labeled[DistrictMap[#2], Style[ToString[#1] ": " Capitalize@IntegerName[Length[#2]] If[Length[#2] == 1, " District", " Districts"], "Section"], Top] @@@ nhdists, AnimationRepetitions -> 1, AnimationRunning -> False] The maps also illustrate some notable historical events. When the American Civil War broke out, Virginia seceded from the Union. But a group of Unionists in the northwestern part of the state broke from this decision, taking three districts from the state to form West Virginia: #10005 GeoListPlot[{Values[CongressionalMapData[1859]["Virginia"]], Values[CongressionalMapData[1863]["West Virginia"]]}] And of course, when the war was over, the two states remained separate: #10005 GeoListPlot[{Values[CongressionalMapData[1869]["Virginia"]], Values[CongressionalMapData[1869]["West Virginia"]]}] After the war, population counts grew in many southern states because of freed slaves, giving them more national voting power: #10005 dist1859=CongressionalMapData[1859]; dist1873=CongressionalMapData[1873]; #10005 DistrictDifferenceMap[Length/@dist1873,Length/@dist1859] In the late 20th century, some states started adjusting maps to create majority-minority districts designed to ensure appropriate representation and voting power for minority groups (as required by the Voting Rights Act of 1965). Opponents of this practice claim that it constitutes racial gerrymandering; in some cases, the Supreme Court has agreed. For instance, after gaining three seats in 1990, Texas attempted to draw new majority-minority districts to represent both Hispanic and African American voters. In Bush v. Vera, the court ruled that two of the new districts (the 29th and 30th) and one newly manipulated district (the 18th) violated compactness principles too severely: #10005 dist1993=CongressionalMapData[1993]; Row@Table[Labeled[GeoGraphics[{Green,dist1993["Texas",i]},ImageSize->150],Style[i,"Text",Darker@Green,Bold],Top],{i,{18,29,30}}] Legislators were forced to redraw the maps: #10005 dist1997=CongressionalMapData[1997]; Row@Table[Labeled[GeoGraphics[{Green,dist1997["Texas",i]},ImageSize->150],Style[i,"Text",Darker@Green,Bold],Top],{i,{18,29,30}}] This indicates that while some level of affirmative racial gerrymandering may be acceptable, the shape of a district must still be sensible. Of course, plenty of minority-majority districts exist naturally because of concentrated minority populations. Many of these are in southern regions with large African American populations: #10005 mm=Import["https://en.wikipedia.org/wiki/List_of_majority-minority_United_States_congressional_districts","Data"]; aalist=mm[[1,1,4,3;;27]]; GeoRegionValuePlot[Table[ current[[StringDelete[aalist[[d,3]]," "],aalist[[d,4]]]]->Quantity[aalist[[d,2]]],{d,Length@aalist}],GeoRange->{{40.,25.}, {-95.,-75.}},GeoProjection->"Mercator"] There are also a number of southwest regions with Hispanic/Latino-majority districts: #10005 hisplist=mm[[1,1,6,3;;27]]; GeoRegionValuePlot[Table[ current[[StringDelete[hisplist[[d,3]]," "],hisplist[[d,4]]]]->Quantity[hisplist[[d,2]]],{d,Length@hisplist}],GeoRange->{{38,25},{-120,-95}},GeoProjection->"Mercator"] Maps are sometimes adjusted to preserve communities of interest, such as separating rural and urban populations or keeping a major business in the same district as its workers. States with large metropolitan areas can be especially difficult to draw lines for because of the high population density in those areas. Here in Illinois, the city of Chicago makes up a full 21% of the population: #10005 N[Entity["City", {"Chicago", "Illinois", "UnitedStates"}]["Population"]/Entity["AdministrativeDivision", {"Illinois", "UnitedStates"}]["Population"]] A look at the map shows that the city itself sprawls across nearly half the states 18 districts in order to distribute that population: #10005 Show[GeoListPlot[List/@Most[current["Illinois"]],PlotLegends->None], GeoGraphics[{FaceForm[Directive[Opacity[1.],Black]],EdgeForm[White],Entity["City", {"Chicago", "Illinois", "UnitedStates"}]["Polygon"]}],GeoRange->Entity["City", {"Chicago", "Illinois", "UnitedStates"}]] Looking at historic maps of the area, its clear this wasnt always the case. Just after the Civil War, the state had 14 districts, with Chicago mostly enclosed in just one: #10005 dist1865=CongressionalMapData[1865]; Length@dist1865["Illinois"] #10005 Show[GeoListPlot[List/@Values@dist1865["Illinois"],PlotLegends->None], GeoGraphics[{FaceForm[Directive[Opacity[1.],Black]],EdgeForm[White],Dated[Entity["City", {"Chicago", "Illinois", "UnitedStates"}],1823]["Polygon"]}],GeoRange->Entity["City", {"Chicago", "Illinois", "UnitedStates"}]] From this perspective, its also clear that the current Illinois districts fall into the jagged category, which is often the case when mapmakers start using more complex factors to draw their maps. Since modern redistricting is often done using heavy-duty GIS software with detailed maps and high-resolution census data overlays, it can be difficult to tell what the reasoning was for districts shapesor what kinds of manipulation might have taken place. But theres growing concern that these manipulations might be damaging to the democratic process. Gerrymandering and the Supreme Court Throughout Americas 200-year history, countless legislative bodies (and at least one independent commission) have been accused of partisan gerrymanderingand some of these accusations have gone all the way to the Supreme Court. But to explore this issue effectively, I need to look at more than just maps. I found comprehensive election data in PDF format from the Clerk of the House. I tried various methods for importing these; in the end I created a package that uses string patterns to sort through election information: #10005 (KeySort@N[#/Total[#]] /@ilvotes)[[All,1]]],ColorFunction->(Blend[{Red,Blue},#] ), PlotRange->{0,1}], GeoGraphics[{FaceForm[Directive[Opacity[1.],Green]],EdgeForm[White],Entity["City", {"Chicago", "Illinois", "UnitedStates"}]["Polygon"]}]] And aside from a few purple bi-state areas, the irregular districts in Chicago appear to tip the balance for Democrats. While no case has been brought forth in Illinois, most critics point to the earmuff-shaped fourth district as a prime example of extreme gerrymandering: #10005 GeoGraphics[{Green,Polygon@current[["Illinois",4]]}] Shape-based arguments have historically dominated in gerrymandering cases, and its easy to see whyliterally. Anyone can look and get a general sense of how complex a shape is. But there are also some geometric tests for measuring the compactness of a district. In his Community post, Marco Thiel tests out a few techniques that involve computing the ratio of a regions area to the area of a circumscribed shape: The range considered acceptable for each test can be subjective, but each measure gives a value between 0 and 1. Looking at the distribution of each test among the states, you can get a good sense of whats average: #10005 Multicolumn[{CloudGet["https://wolfr.am/vF9vSryp"], CloudGet["https://wolfr.am/vF9vSHh1"], CloudGet["https://wolfr.am/vF9vT2uh"], CloudGet["https://wolfr.am/vF9vTegh"]}, ItemSize -> Full] Here are some of the least compact districts in the country, according to Marcos computations: Application of these and similar geometric tests has led several courts to strike down district maps that lack compactness (like in Texas). But theres no single way to measure compactness, and some odd shapes are due to natural boundaries and other non-political factors. Aside from that, both legislators and courts have been reluctant to make any strong statements about partisan gerrymandering because of the inherent political implications: any law or ruling that seems to favor a particular party could be highly criticized. So the fact that the Supreme Court took two cases on this topic (in addition to one on racial gerrymandering) is a pretty big deal. The first case, Gill v. Whitford, takes a practical approach to the problem: if partisan gerrymandering is the issue, they reason, perhaps it needs a partisan-based solution. Originating in a Wisconsin state court, the plaintiffs presented a case in October 2017 based on a new measure of partisan bias proposed by Nicholas Stephanopoulos and Eric McGhee called efficiency gap. The formula is best summarized as the difference in the total number of wasted votes for each partyincluding votes cast for a losing candidate and surplus votes cast for a winning candidateover the total votes cast: #10005 TraditionalForm[EG==(HoldForm@(Subscript[lost, A]+Subscript[surplus, A])-HoldForm@(Subscript[lost, B]+Subscript[surplus, B]))/(total votes)] By assuming equal population per district and a two-party system, this formula is conveniently reduced to the difference between a partys seat margin (percentage of seats over 50%) and twice its vote margin: #10005 TraditionalForm[EG=="seat margin" - 2 *"vote margin"] From the data I collected, I can easily compute the seat margins and vote margins: #10005 SeatMargin[electiondata_]:=With[{pv=PartyVotes[electiondata]},N@(Counts[Flatten@Keys[TakeLargest[#,1] /@pv]]-Length@pv/2)/Length@pv] VoteMargin[electiondata_]:=N@#/Total[#] @Merge[PartyVotes[electiondata],Total]-.5 For congressional districts, the efficiency gap is given in seats. Here's an implementation of the simplified efficiency gap formula with positive numbers indicating a Democratic advantage and negative indicating a Republican advantage: #10005 EfficiencyGap[electiondata_]:=Length[GroupBy[electiondata,"District"]] *(KeySort[SeatMargin[electiondata]]-2 KeySort[VoteMargin[electiondata]]) From a legal standpoint, the argument is that the wasted votes constitute a violation of the voters rights under the Equal Protection Clause. According to the authors, an advantage of two or more seats could indicate district manipulation in a given state. The paper points out a few states with large congressional efficiency gaps in recent cycles: #10005 Table[With[{data=GroupBy[RepresentativeVotesDataset[state,{1998,2016}],"Year"]},DateListPlot[Transpose[{DateRange[{1998},{2016},2yr],Table[EfficiencyGap@data[[i]],{i,Length@data}][[All,1]]}],PlotTheme->"Scientific"]],{state,{"Michigan","Michigan","North Carolina","Ohio","Pennsylvania","Texas","Virginia"}}] Although Gill v. Whitford deals with state legislative districts, Wisconsins congressional districts seem to show a strong trend toward Republican dominance over the past 20 years as well: #10005 widata=GroupBy[RepresentativeVotesDataset["Wisconsin",{1998,2016}],"Year"]; #10005 DateListPlot[Transpose[{DateRange[{1998},{2016},2yr],Table[EfficiencyGap@widata[[i]],{i,Length@widata}][[All,1]]}],PlotTheme->"Scientific"] You can see this effect on the maps, where many previously contentious areas now run more solidly red: #10005 widists=Table[CongressionalMapData[i]["Wisconsin"],{i,2000,2016,4}]; #10005 wivotes=Table[PartyVotes[RepresentativeVotesDataset["Wisconsin",i]],{i,2000,2016,4}]; #10005 Grid[{Text/@Range[2000,2016,4], Table[GeoRegionValuePlot[Thread[Values[widists[[i]]]->(KeySort@N[#/Total[#]] /@wivotes[[i]])[[All,1]]], PlotLegends->None, ColorFunction->(Blend[{Red,Blue},#] ), ImageSize->100],{i,Length@widists}]}] In Benisek v. Lamone (coming from Maryland), the legal argument instead hinges on the First Amendment: casting a vote is considered a form of expression (i.e. speech), and the claim is that the offending district dilutes the votes of Republican voters, thus reducing the value of those voters speech. While this case presents no particular standard for computing the extent of partisan gerrymandering, it does provide a fresh legal route for applying any standard that might be instated. Either way, the efficiency gap test shows a rather prominent Democratic trend in Maryland: #10005 mddata=GroupBy[RepresentativeVotesDataset["Maryland",{1998,2016}],"Year"]; #10005 DateListPlot[Transpose[{DateRange[{1998},{2016},2yr],Table[KeySort@EfficiencyGap@mddata[[i]],{i,Length@mddata}][[All,1]]}],PlotTheme->"Scientific"] And a look at the district in question shows that its latest map is far from compact: #10005 mddists=Table[CongressionalMapData[i]["Maryland"],{i,2000,2016,8}]; #10005 mdvotes=Table[PartyVotes[RepresentativeVotesDataset["Maryland",i]],{i,2000,2016,8}]; #10005 Grid[{Text/@Range[2000,2016,8],GeoGraphics/@Transpose[{GeoStyling[Blend[{Red,Blue},#]] /@(KeySort@N[#/Total[#]] /@mdvotes[[All,6]])[[All,1]],mddists[[All,6]]}]}] The Supreme Court also picked up another case in January 2018, this time about racial gerrymandering. Like Bush v. Vera, the case comes from Texas, but this time its centered around negative racial gerrymandering. Republican lawmakers are appealing a lower courts ruling that the states latest maps discriminate against racial minorities. The efficiency gap doesnt exactly translate to this case, but one could conceive of a similar measure based on the wasted votes of racial minorities. Suffice it to say, the gerrymandering issue is coming to a head. With these three cases combinedas well as recent decisions in North Carolina and Pennsylvania, a ballot initiative in Michigan and all kinds of academic discussions around the countrythe stage is set for the Supreme Court to make changes in how redistricting is regulated. Unfortunately, theyve opted to pass on both partisan gerrymandering cases on technical grounds, so we will likely have to wait until next session to get a major decision. Gerrymandering is a complex subject with a deep history, and this post only scratches the surface. Exploring with the Wolfram Language helped me pull everything together easily and discover a lot of intricacies I wouldnt have otherwise found. Now that I've collected all the data in one place, I invite you to do your own exploration. Go find out the history of your district, explore measures of fairness and partition states as you see fitjust dont forget to go out and vote this November! Download this post as a Wolfram Notebook.

: 1.video/mp4 2.video/mp4 3.video/mp4


   RSS- () — RSSfeedReader
: 10

Books (1)
Computational Thinking (3)
Data Analysis and Visualization (3)
Data Repository (1)
Design (2)
Developer Insights (2)
Education (1)
Machine Learning (1)
Mathematics (3)
Other Application Areas (6)
Raspberry Pi (3)
Recreational Computation (4)
Software Development (3)
SystemModeler (1)
Wolfram Language (3)

2018-08-16, . (1)
2018-08-09, . (1)
2018-08-02, . (1)
2018-07-31, . (1)
2018-07-26, . (1)
2018-07-24, . (1)
2018-07-19, . (1)
2018-07-12, . (1)
2018-07-05, . (1)
2018-06-26, . (1)

Aaron Enright (1)
Brian Wood (1)
Chapin Langenheim (1)
Devendra Kapadia (1)
Erez Kaminski (1)
Itai Seggev (1)
Jon McLoone (2)
Patrik Ekenberg (1)
Swede White (1)