In this chapter we will discuss some of the basic concepts that we need to know to program in Mathematica using the Wolfram Language. We will also cover some useful notions for generating efficient code. In particular, we will learn how to code using pure functions, very important if you want to create compact programs that take full advantage of the power of Mathematica. We will also include examples of some of the most commonly used programming functions and describe the fundamental ideas behind package development, essential to build new applications that incorporate user-defined functionality. Finally, we will provide guidance on tools available for large projects or to develop programs that can be run from a browser.
As we have seen already in previous chapters, Mathematica is not only a program to perform symbolic and numeric calculations. It is a complete technical system that can be used to develop anything: from traffic control applications to sophisticated image manipulation interfaces. All of this is possible thanks to the power of the Wolfram Language, the high-level general-purpose programming language used by Mathematica. To start using it we need to know its fundamentals and that’s what we are going to learn in this chapter. This section will cover the basic components of the language.
Everything in Mathematica is an expression, including notebooks. All expressions are written in the form: Head [..., {}]. Head is always capitalized and followed by a pair of enclosing brackets containing elements separated by commas. Those elements sometimes go inside braces. Here is one example:
Head[a + b + c]
Plus
Instead of x + y + z we write:
Plus[x, y, z]
x + y + z
With FullForm we can see the basic form of expressions: One head, Plus in this case, and elements separated by commas inside brackets:
FullForm[x + y + z]
Plus[x, y, z]
Cells are also expressions. Shown below is a copy of this cell displaying its contents. This was done by selecting the copied cell and clicking on: Cell ▶ Show Expression.
Cell[TextData [{
"Cells are also expressions. Shown below is a copy of this cell displaying its contents.
This was done by selecting the copied cell and \
clicking on: ",
StyleBox["Cell",
FontWeight-> "Bold"],
" \ [FilledRightTriangle] ",
StyleBox["Show Expression",
FontWeight -> "Bold"],
". "}], "Texto AM",
CellChangeTimes->{{3.627622*^9, 3.627622`*^9}, {3.6396977`*^9, 3.6396977*^9},
{3.6396978`*^9, 3.6396978*^9}, {3.6396979*^9, 3.6396979*^9}, {3.63969798*^9,
3.63969798`*^9}}]
All objects in Mathematica are one of the following types:
Numbers: (i) Integers: Any number without a decimal separator, (ii) Reals: Any number with a decimal separator, (iii) Rationals: the ratio between two integers such as 3/4 o 27/8, (iv) Complex: The sum of a real number and an imaginary number: 3 + 2 .
Symbols: A sequence of letters, digits or symbols not starting with a number (we will describe them later).
Strings: Any expression between double quotation marks: “this is a string”.
SparseArrays: Matrices or lists where most of the elements have the same value:
MatrixForm[SparseArray[{i_, i_} :→ RandomInteger[8], {4, 4}]]
Graphs: A collection of objects, joined by vertices in pairs.
Mathematica internally always separates expressions first into their atomic components before computing them.
Symbols is the name of one of the types of atomic objects in Mathematica. They can be: letters, digits, combinations of letters and digits not starting with a digit, and special symbols such as: $, π, ∞, , , . Upper and lower-case letters are distinguished. Once a symbol has been created during a session, it will remain in memory unless explicitly removed. An alternative is to use functions that automatically delete a symbol once it’s been used.
If we have a list and we’d like to know the type of objects that it contains, we apply Head to each of the list elements. This can be done using Map (We will refer later to this important programming function).
{Integer, Rational, Real, Complex, Complex,
Power, Symbol, List, Symbol, Graphics, Image}
The power of Mathematica can be seen specially when we use it to create programs. Readers with experience in procedural programming using languages such as C or FORTRAN, sometimes use the same approach with Mathematica even though it may not be the most appropriate one. Because of this, if you are a programmer we would like to encourage you not to replicate the procedural paradigm when programming with Mathematica. It will be worth the effort.
Let’s see an example for creating a function to calculate the factorial of a number using different programming styles without using the built-in factorial function n!:
Using a procedural style, similar to C or FORTRAN, we could do as follows:
factorial1[n_] :=
(temp = 1; Do[temp = temp counter, {counter, 1, n}]; temp)
factorial1[5]
120
With Mathematica we can create a function that virtually transcribes the definition of factorial (note the use of “=” in the first definition and of “:=” in the second one; the reason will be explained later):
factorial2[0] = 1;
factorial2[n_] := n factorial2[n - 1]
factorial2[5]
120
Normally, the functional style besides being easier to understand is also more efficient.
The next example consists of building, using different programming styles, a command to add the elements of a list of consecutive integer numbers and compare execution times (we use the function AbsoluteTiming to show the absolute number of seconds in real time that have elapsed, together with the result obtained). We avoid directly applying the formula for calculating the sum of an arithmetic series which would be the easiest and most effective way.
We start with a list consisting of the first one million integers.
list = Range[{106];
A procedural method (traditional) to add the elements of this list is:
AbsoluteTiming[sum = 0;
Do, {i, Length[list]}];
sum]
{0.819956, 500 000 500 000}
The functional method (using different instructions in each case) is much more efficient.
We perform the same operation in three different ways and display the execution time. In the first case we use Total, a built-in function. If available, these functions are usually the fastest way to get things done in Mathematica. Later on in the chapter, we will explain Apply (or @@) in more detail.
AbsoluteTiming[Total[list]]
{0.00111397, 500 000 500 000}
AbsoluteTiming[Apply[Plus, list]]
{0.131744, 500 000 500 000}
In this example we build a function, using different styles, to calculate the sum of the square roots of n consecutive integers:
A classical but inefficient way of doing this in Mathematica, is:
n = 50; sum = 0.0; Do[sum = sum + N[Sqrt[i]], {i, 1, n}];
sum
239.036
In the functional style we transcribe almost literally the traditional notation in Mathematica. This approach is not only simpler but also more effective.
Why do we use “//N”? To find out, see what happens after removing it from the previous function definition.
Another way of programming where Mathematica displays its power and flexibility is rule-based programming.
For example: To convert {{a1, b1, c1},..., {ai, bi, ci},...} into {a1/(b1 + c1), ...., {ai/(bi + ci)}, ...} we just create the corresponding replacement rule.
list = {{a1, b1, c1}, {a2, b2, c2}, {a3, b3, c3}};
list /. {a_, b_, c_} → a/(b + c)
The previous approach is very useful to perform operations, such as reordering, on lists.
The following function uses rules-based programming to calculate the distance between two points in a two-dimensional space.
There’s a specific function, EuclideanDistance, to calculate distances. However, Mathematica has thousands of functions and sometimes it is not easy to find the most appropriate one. Often the idea is to create a program to perform an operation. It doesn’t matter if that approach is not the most efficient one. Later on you can always optimize the program if it’s going to be used frequently or if its computing time is excessive.
A frequently asked question is how to differentiate between "=" (Set) and ":=" (SetDelayed).
Examine the following example carefully, after clearing the variables from memory:
Clear ["Global`*"]
x = RandomReal[]
y := RandomReal[]
0.486034
x - x
0.
y - y
0.00130115
What has happened? When "=" is used in f = exp, the result of evaluating exp is assigned to f immediately. From then on, whenever f is called, the program will always replace f with the value of exp. In the previous example, the value of x was stored right away, while in the case of y, what gets stored is the function that will be executed every time it’s called. Because of that, when calculating y-y, RandomReal [] is executed twice, returning each time a different result.
The following expression generates a list of 5 random integers between 0 and 5. We call it data1. These numbers have been stored and they will always be used anytime data1 is called.
data1 = RandomInteger[{5}, 5]
{5, 3, 0, 4, 2}
Now we type the same above expression but using “:=”. We name this new list data2. From now on, whenever data2 is called, the expression that generates the 5 random numbers will be executed anew. What is stored is the function RandomInteger[{5},5] not the actual numbers it generates.
data2 := RandomInteger[{5}, 5]
The difference can be seen by calling and executing the above defined expressions.
data1
{5, 3, 0, 4, 2}
data2
{0, 1, 0, 2, 1}
In the following example we combine “:=” with “=”. What happens?
data3a := data3b = RandomInteger[{5}, 5]
data3a
{5, 2, 4, 2, 3}
data3b
{5, 2, 4, 2, 3}
data3b
{5, 2, 4, 2, 3}
data3a
{3, 3, 3, 2, 0}
data3b
{3, 3, 3, 2, 0}
Notice that in the example that we saw previously, we combined “=” and “=:” to calculate the factorial. The use of “=” takes precedence over “:=”.
factorial[0] = 1;
factorial[n_] := n factorial[n-1]
factorial[5]
120
It’s normal to use “:=”when defining functions. However, in many cases it is also possible to use "=" or ":=" without affecting the final result.
The same role that “ =” y “:=” play when defining functions, “→” and “: → (or :>)” play when making replacements:
Immediate replacement (Rule).
{x, x, x} /. x -> RandomReal[]
{0.587342, 0.587342, 0.587342}
Delayed replacement (RuleDelayed).
{x, x, x} /. x :→ RandomReal[]
{0.255118, 0.107796, 0.380331}
Clear["Global`*"]
In this section we are going to see some functions that are very useful when performing operations on lists, matrices and vectors. We already mentioned some of them in Chapter 2.
Any time we want to do something in Mathematica, it’s possible that the program already has a built-in function to perform the operation.
In this example we sum consecutively all the elements in a list.
Accumulate[{a, b, c, d, e}]
{a, a + b, a + b + c, a + b + c + d, a + b + c + d + e}
As mentioned previously, we will not always find the specific function for what we want to do. However, there are versatile functions useful to know that we can use to perform a specific computation. When operating with lists FoldList is such a function.
Here we use FoldList as an alternative to Accumulate.
Rest[FoldList[Plus, 0, {a, b, c, d, e}]]
{a, a + b, a + b + c, a + b + c + d, a + b + c + d + e}
Using any of the two previous functions we can build a simple expression to simulate a random walk, also known as the drunk man’s walk. Let’s suppose that a drunk man tries to cross the street and each step he takes can randomly go in any direction. Many physical phenomena, such as Brownian motion, are variations of random walks (The Mathematica built-in function is RandomWalkProcess).
The function below is a very simple case of a random walk along a line. Each step has a maximum length of 1 and can go toward both ends of the line with equal probability. To simulate it, we generate random numbers between −1 and 1 (if it goes in one direction we use + and in the opposite direction –) What will be the distance covered after n steps?
RandomWalk[steps_] := Accumulate[RandomReal[{-1, 1}, {steps}]]
We create a function to graphically represent the distance covered after n steps. In this case we assume n = 500.
ListPlot[RandomWalk[500], Filling → Axis]
Matrix operations appear very often when performing algebraic calculations. The use of SparseArray speeds up computations significantly.
Generate a random matrix:
mat = RandomInteger[10, {5, 5}]
{{9, 10, 6, 2, 7}, {7, 4, 2, 10, 9},
{6, 10, 10, 3, 1}, {1, 0, 5, 10, 9}, {7, 10, 9, 1, 1}}
The next command displays the previous output in matrix format and evaluates its transpose, inverse, determinant and eigenvalues. We use TabView to place the outputs under different labels visible after clicking on the corresponding tab.
TabView[
{"Matrix A" → MatrixForm[mat], "Transpose" → MatrixForm[Transpose[mat]],
"Inverse" → MatrixForm[Inverse[mat]], "Determinant" → Det[mat],
"Eigenvalues" → N[Eigenvalues[mat]]}]
Next, we create an equation of the form m x = b, where m corresponds to the matrix mat. We will solve it and check that the solution is correct.
We generate matrix b randomly.
b = RandomInteger[10, {5}]
{2, 10, 1, 4, 0}
We solve the equation using LinearSolve.
xvec = LinearSolve[mat, b]
We verify that the solution is correct ("." is used for matrix multiplication).
mat.xvec
{2, 10, 1, 4, 0}
In matrix operations it is very frequent to have matrices where only few elements have non-zero values. These kinds of matrices are called sparse arrays. In these cases is recommended to use SparseArray.
We build a tridiagonal matrix 5×5 whose main diagonal elements are 9s and its adjacent 1s.
We show it numerically and represent it graphically using MatrixPlot.
MatrixForm[Normal[s]]
MatrixPlot[s]
We build a linear system of 5 equations of the form s x = b, with s and b the matrices previously defined.
We solve the equation and check the calculation time:
Timing [LinearSolve[s, b]]
We repeat the same process but for a system of 50000 variables and 50000 equations, of the form s x = b where s and b are called sLarge and bLarge respectively. We use “;” to avoid displaying the output since it would take too much space.
Timing[bLarge = RandomReal[1, {50 000}];]
{0., Null}
Timing[sLarge = SparseArray[
{{i_, i_} → 9, {i_, j_} /; Abs[i - j] == 1 → 1}, {50 000, 50 000}];]
{0.453125, Null}
MatrixPlot[sLarge, ColorFunction → "Rainbow"]
We solve the system sLarge x = bLarge and see that the calculation time is extremely short.
Timing[xvec = LinearSolve[sLarge, bLarge];]
{0., Null}
We don’t show the result but we can check that the answer is correct by verifying that mx − b = 0. With Chop we eliminate the insignificant terms, unavoidable in numerical calculations involving decimal numbers.
Norm[sLarge.xvec - bLarge] // Chop
0
We’ve seen that Mathematica manipulates basic objects located at the head of each expression (Head).
Clear["Global`*"]
Remember that with FullForm we can see the basic form of expressions. The example below shows that a/b internally is a multiplied by b−1.
FullForm[a/b]
Times [a, Power [b, -1]]
Here we apply FullForm to a more complicated expression.
FullForm[1.5 Sin[2 Pi/4]]
1.5`
We get the result of the operation but not the structure of the initial expression as we wanted. This is because the calculation is done first and then FullForm is applied to the result, just a number. To avoid the execution of the calculation we use HoldForm.
FullForm[HoldForm[1.5 Sin[2 Pi/4]]]
HoldForm[Times[1.5`, Sin [Times [2, Times [Pi, Power [4, -1]]]]]]
The structure of any expression adopts a tree-like format. Here we represent the previous expression in such a format using TreeForm.
TreeForm[HoldForm[1.5 Sin[2 Pi/4]]]
If we analyze the previous tree we can see that the first expression is 1/4. Internally it is represented as 4−1. Next, that expression is multiplied by Pi, the result multiplied by 2, and so on until reaching the top of the tree.
Trace enables us to see the order in which operations are executed:
Trace[1.5 Sin[2 Pi/4]]
The previous output seems to duplicate certain steps. However, with FullForm we can see that it’s actually the basic structure that is changing. So when we see
FullForm[%]
List[
List[List[List[List[HoldForm[Power[4, -1]], HoldForm[Rational[1, 4]]],
HoldForm[Times[Pi, Rational [1, 4]]],
HoldForm[Times[Rational[1, 4], Pi]]],
HoldForm[Times[2, Times[Rational [1, 4], Pi]]],
HoldForm[Times[2, Rational[1, 4], Pi]],
HoldForm[Times[Rational[1, 4], 2 Pi]],
HoldForm[Times[Rational[1, 2], Pi]]],
HoldForm[Sin[Times[Rational[1, 2], Pi]]], HoldForm[1]],
HoldForm[Times[1.5`, 1], HoldForm[1.5`]]
With a simple transformation rule we can modify the previous expression so that multiplication becomes addition.
HoldForm[1.5 Sin [2 Pi/4]] /. {Times → Plus}
The next function shows a red circumference of radius 1.
gr = Graphics[{Red, Circle[{0, 0}, 1]}]
Its internal form is as follows:
FullForm[gr]
Graphics[List[RGBColor[1, 0, 0], Circle[List [0, 0], 1]]]
TreeForm[gr]
If you move the cursor over an individual frame you will notice that it displays the result of the operations until that frame.
A small transformation will allow us to modify the expression so that instead of a red circumference (Circle), we get a red circle (Disk).
gr /. Circle → Disk
Once we know the structure of an expression, we will be able to modify it. Additionally, when encountering outputs from operations that we don’t understand, their internal form can give us clues about how the program works.
Clear["Global`*"]
In this section we have selected some of the most commonly used functions for programming.
Apply[f, list] or f@@list applies f to list.
Apply[f, {a, b, c}]
f[a, b, c]
f @@ {a, b, c}
f[a, b, c]
Apply[f, {{a, b}, c}]
f[{a, b}, c]
We use FullForm to see how Apply works:
FullForm[f @@ {x, y, z}]
f[x, y, z]
Here Apply is used to multiply the elements of a list:
Times @@ {a, b, c, d}
a b c d
Have you realized that the previous pattern can be used to compute the factorial of a number? For example 5!
Apply[Times, Range [5]]
120
Apply (@@@) at level 1:
Apply[f, {{a, b, c}, {d, e}}, {1}]
{f[a, b, c], f[d, e]}
f @@@ {{a, b, c}, {d, e}}
{f[a, b, c], f[d, e]}
Plus @@@ {{a, b, c}, {d, e}}
{a + b + c, d + e}
Using @ in succession enables the creation of composite functions as shown in this example:
f@g@h@x
f[g[h[x]]]
f[x_] := dx^2;g[y_] := c Exp[y];h[z_] := a Cos[bz];
f@g@h@x
c2 d 2 a Cos [{bx]
Clear[f, g, h]
Map[f, list] or f/@list applies f to each of the elements of list. This is probably the most frequently used function in programming.
Map[g, {a, b, c}]
{g [a], g[b], g[c]}
g/@ {a, b, c}
{g[a], g[b], g[c]}
Map [Sin, func[x, y, 0, π/2, 90°]]
func[Sin[x], Sin[y], 0, 1, 1]
Notice that f with Map is applied to each element of the expression while with Apply actually changes the entire expression by replacing its head.
{Map[f, {a, b, c}], Apply[f, {a, b, c}]}
{{f[a], f[b], f[c]}, f[a, b, c]}
Next we apply Map at level 1:
{Map[f, {{a, b, c}, {d, e}}, 1]
{f[{a, b, c}], f[{d, e}]}
Map at level 2:
Map[f, {{a, b, c}, {d, e}}, {2}]
{{f[a], f[b], f[c]}, {f[d], f[e]}}
Map at levels 1 and 2:
Map[f, {{a, b, c}, {d, e}}, 2]
{f[{f[a], f[b], f[c]}], f[{f[d], f[e]}]}
MapAll or f//@ applies f to each of the expressions.
MapAll[f, {{a, b}, {c}, {{d}}}]
f[{f[{f[a], f[b]}], f[{f[c]}], f[{f[{f[d]}]}]}]
f //@ {{a, b}, {c}, {{d}}}
f[{f[{f[a], f[b]}], f[{f[c]}], f[{f[{f[d]}]}]}]
In the case of listable functions, Map is implicitly included in them as this example shows.
Map[Sin, {a, b, c}]
{Sin[a], Sin[b], Sin[c]}
Sin[{a, b, c}]
{Sin[a], Sin[b], Sin[c]}
See also: MapAt, MapThread and Outer.
MapAt[f, {a, b, c, d}, 2]
{a, f[b], c, d}
MapThread[f, {{a, b, c}, {x, y, z}}]
{f[a, x], f[b, y], f[c, z]}
Outer[f, {a, b}, {x, y, z}]
{{f[a, x], f[a, y], f[a, z]}, {f[b, x], f[b, y], f[b, z]}}
In the next example we apply MapIndexed together with Labeled to identify the position of each term.
MapIndexed[Labeled, D[Sin[x]^4, {x, 4}]]
A related function is Thread.
Thread[{a, b, c} -> {x, y, z}]
{a → x, b → y, c → z}
The most commonly used iterative functions in Mathematica are Nest, FixedPoint, Fold and Catch along with their extensions NestList, NestWhile, FoldList and FixedPointList.
People with experience in other programming languages tend to make iterations using For and Do (also available in Mathematica) but normally we should avoid them and use the commands in the previous paragraph. In the examples that follow and throughout the rest of the book we will do just that.
NestList is specially useful for iterative calculations.
f[x_] := 1 + x^2
NestList[f, x, 4]
[x, 1 + x2, 1 + (1 + x2)2, 1 + (1 + (1 + x2)2)2, 1 + [1 + (1 + (1 + x2)2)2)2}
In this example we use NestList to build an iterative formula for computing the future value of an initial investment of n at an annual interest rate i for j years and show its growth year by year. We assume an initial capital of €100 and an annual interest rate of 3% for 10 years.
interest[n_] := (1 + 0.03) n
NestList[interest, 100, 10]
{100, 103., 106.09, 109.273, 112.551,
115.927, 119.405, 122.987, 126.677, 130.477, 134.392}
The concept of a pure function is absolutely fundamental to program properly in Mathematica. Pure functions behave like operators telling arguments what to do. Initially they may appear cryptic but their use will end up showing us how powerful they are. Therefore, it’s convenient to start using them as soon as the opportunity arises.
After removing all variables from memory, we are going to demonstrate several ways of defining the same pure function:
Clear["Global`*"]
Function[u, 3 + u][x]
3 + x
Function[3 + #][x]
3 + x
(3 + #) &[x]
3 + x
In this last case, instead of Function we have used the # symbol and to finish defining the function we have added & at the end. Since the combination of # and & represent dummy variables we don’t need to store those variables in memory, reducing the computation time. Next we are going to see several examples using this notation.
This function squares its argument and adds 3 to it.
#2 + 3&[x]
3 + x2
An example of a pure function with two arguments (note the use of #1 and #2):
(#1^2 + #2^4) &[x, y]
x2 + y4
We define an operator, name it f, and see its behavior.
f = (3 + #) &
3 + #1 &
{f[a], f[b]}
{3 + a, 3 + b}
The same result can be obtained with Map (/@):
f /@ {a, b}
{3 + a, 3 + b}
(#1^2 + 1 &) /@ func[x, y, 1, 4]
func[{1 + x2, 1 + y2, 2, 17]
There are many ways to define a function in Mathematica as shown below for the function f (x ) = x2 + sin(x) − 3.
f1[x_] := x^2 + Sin[x] - 3; f1[1]
-2 + Sin[1]
f2 = Function[{x}, x^2 + Sin[x] - 3]; f2[1]
-2 + Sin[1]
f3 = Function[#^2 + Sin[#] - 3]; f3[1]
-2 + Sin[1]
f4 = (#^2 + Sin[#] - 3) &; f4[1]
-2 + Sin[1]
(#^2 + Sin[#] - 3) &[1]
-2 + Sin[1]
In practice, although we can often use several methods to solve problems, certain approaches may return incorrect results. In the example below, given a list of pair of elements: {{a1, b1}, {a2, b2}, ...} we’d like the second element of each pair, bi, to be multiplied by a constant k. To do this we can apply a rule:
list1 = {{a1, b1}, {a2, b2}, {a3, b3}};
list1 /. {a_, b_} → {a, kb}
{{a1, b1 k}, {a2, b2 k}, {a3, b3 k}}
The rule works fine except if the list has exactly two sublists. In that case, b1 and b2 are interpreted as sublist 1 and sublist 2 respectively.
list2 = {{a1, b1}, {a2, b2}};
list2 /. {a_, b_} → {a, k b}
{{a1, b1}, {a2 k, b2 k}}
To avoid this unexpected behavior, we use a combination of a pure function and Apply at level 1 (the short form for Apply [f, expr,{1}] is @@@).
({#1, k #2}) & @@@ list2
{{a1, b1 k}, {a2, b2 k}}
In this example we want to solve the equation below step-by-step expressing the result as a function of x.
y + x == 2;
Let’s see the internal form of the previous expression.
FullForm[y + x == 2]
Equal[Plus[x, y], 2]
We want to solve for y by moving x to the right side. That is equivalent to subtracting x from both sides of the equal sign (note that is necessary to use parenthesis to apply the pure function to the entire equation).
# - x & /@ (y + x == 2)
y == 2 - x
Another way of doing it would be as follows (remember that % always refers to the last Out[t]).
y + x == 2;
# - x & /@ %
y == 2 - x
Next, we generate a list of random values first.
data = RandomReal[10, 20];
Then, we draw three lines, red, green and blue, each one representing the median, the mean and the geometric mean of the data respectively. We use MapThread to define the beginning and the end of each line.
lines =
MapThread[{#1, Line[{{0, #2}, {20, #2}}]} &, {{Red, Green, Blue},
{Median[data], Mean[data], GeometricMean[data]}}];
Finally, we combine the data with the generated lines.
ListPlot[data, Epilog → lines]
Here we calculate
{0.328125, 7.45367 × 106}
rootsum2[n_] := Total[
Timing[rootsum2[50 000]]
{0.328125, 7.45367×106}
In the next example we use NestList to compute a continuous fraction.
NestList[1 + 1/(1 + #) &, a, 4]
The previous method, with a = 1, can be used to calculate
Nest[1 + 1 / (1 + #) &, 1, 10] // N
1.41421
We use NestList to simulate a random walk like the one described earlier. The individual steps can be forward or backward with equal probability. We randomly generate 1 s and -1 s, 1 meaning a step forward and -1 indicating a step backward. Starting at an arbitrary point, we want to know the position after n steps.
RandomWalk[initial_, nsteps_] :=
NestList[# + If [RandomReal[1] < 0.5, - 1, 1] &, initial, nsteps]
ListPlot[RandomWalk[3, 100], Filling → Axis]
FixedPointList is similar to NestList except that it will execute until the solution converges.
FixedPointList[1 + 1 / (1 + #) &, 0.1]
{0.1, 1.90909, 1.34375, 1.42667, 1.41209, 1.41458, 1.41415, 1.41422,
1.41421, 1.41421, 1.41421, 1.41421, 1.41421, 1.41421, 1.41421, 1.41421,
1.41421, 1.41421, 1.41421, 1.41421, 1.41421, 1.41421, 1.41421}
In this example we use FixedPoint to implement the famous Newton’s method (http://mathworld.wolfram.com/NewtonsMethod.html), where x0 is the initial point.
newton[f_, x0_] :=
FixedPoint[# - f[#]/f'[#] &, N[x0], 10]
We apply the previous function to solve Log[x] − 5/x = 0 (remember that Log[x] represents the natural logarithm of x).
newton[(Log[#] - 5/#) &, 0.2]
3.76868
A common problem in programming languages is how to avoid conflicts among variables. Be careful and do not confuse the mathematical concept of variable with its concept in programming, the one we use here. In this case, when we mention variables we refer to assignments or definitions made for later use. If we create a variable and later on use the same name to define a new one, conflicts may arise between them.
If you have executed all the cells until here, you’ll have the following list of used names:
Names["Global`*"]
{a, a1, a2, a3, b, b1, b2, b3, bLarge, c, c1, c2, c3, counter, d, data,
data1, data2, data3a, data3b, distance, e, f, f1, f2, f3, f4, factorial,
factorial1, factorial2, func, g, gr, h, i, initial, interest, j,
k, lines, list, list1, list2, mat, n, newton, nsteps, RandomWalk,
rootsum, rootsum2, s, sLarge, steps, sum, temp, u, x, x0, xvec, y, z}
Symbols starting with $ that you haven’t defined may appear. They correspond to internal assignments done by the program. In addition, if you used Clear[“Global`*”] earlier during the session, you will have deleted the previous assignments but not their names. For example: a will not have any value assigned to it but RandomWalk will. You still have not cleared the assignment, but in both cases the symbols still exist in the global context.
?a
RandomWalk[initial_, nsteps_] :=
NestList[#1 + If[RandomReal[1] < 0.5, -1, 1] &, initial, nsteps]
With Clear[“Global`*”] all the assignments made will be deleted but not the variable names themselves. They can be removed as follows:
Remove["Global`*"]
Names["Global`*"]
{}
?a
Information: Symbol a not found.
? RandomWalk
Information: Symbol RandomWalk not found.
However, a more logical way to proceed is to avoid global variables and use local variables instead, that is, variables that only have meaning inside an expression.
Let’s define the following variable in a global context.
y = 3
3
Let’s use y within a local context. To create local variables we will use Module or With. For example, in the next expression, y appears but in this case it’s a local variable, defined using With, and its use is restricted to operations inside the defined expression.
f1[x_] := With[{y = x + 1}, 1 + y + y^2]
If we apply the previous function when x = 3, since y = x + 1 then y = 4 (local variable), that is replaced by 1 + y + y^2.
f1[3]
21
f2[x_] := Module[{y}, y = x + 1; 1 + y + y^2]
f2[3]
21
Nevertheless, that doesn’t affect the global variable y that will still be y = 3.
y
3
The main difference between Module and With is that with Module we explicitly indicate what variables are local while when using With, replacement rules are assigned to the local symbols, that is: With [{x = x0, y = y0, …}, expr]. In many cases you can use them interchangeably.
Another related command is Block, very similar to Module. The main difference is that while Module creates temporary assignments for the specified variables during the evaluation, Block suspends any existing assignments for those variables and restores the assignments once the evaluation is done.
Module[{y}, Expand[(1 + y) ^2]]
1 + 2 y$1313 + y$13132
Block[{y}, Expand[(1 + y) ^2]]
16
You can find an interesting comparison between them, using the new function Inactive, in: http://www.wolfram.com/mathematica/new-in-10/inactive-objects/optimize-code.html.
If we want to remove the definition assigned to y from the global context we can use Clear.
Clear[y]
Now y doesn’t have any assignments:
y
y
As we’ve been doing, if we want to eliminate all the previously defined assignments we can use:
Clear["Global`*"]
Remember that unless you exit the session, even though you create a new notebook the functions previously defined during the session will still be active.
There’s another way to limit the use of variables to a specific context by choosing in the menu bar: Notebook’s Default Context. With this method you will be able to restrict the variables to a notebook or a group of cells. For example:
Select all the cells corresponding to this section and in the main menu choose: Evaluation ▶ Notebook's Default Context ▶ Unique to Each Cell Group.
Define the function below:
f[x_] := 3x
f[3]
9
If you execute the same function in other section of the notebook you’ll see that is not defined.
Clear["Global`*"]
A predicate is a function that can take two values: {True, False}. This type of function is called Boolean. Figure 3.1 shows all the built-in symbols in Mathematica used to represent Boolean functions and their corresponding Wolfram Language command:
Predicates are used when defining conditions. These can appear under very different situations.
Let’s use f to define a function whose behavior will be different depending on whether we are using 1 or 2 arguments:
f[x_] := 0.2 x^2
f[x_, y_] := 0.2 x^2 y^3
f[3]
1.8
f[3, 5]
225.
A frequently used format when defining conditions is “_h” to indicate that the replacement will happen for any expression whose head is h as the following examples show:
Clear[f, g]
f[x] + g[x] /. x_f -> 1/x
Map[Head, {a, 3, 1/3, 21/3.0, 21/3, 3 I, }]
{Symbol, Integer, Rational, Real, Power, Complex, Symbol}
{a, 3, 1/3, 20.33, 21/3, 3 I, } /. x_Integer → x^2
{a, 9,
Notice that 21/3 is being interpreted as an integer. The reason is that when the head is Power the check is done over the base, that in this case is an integer.
In a function, the easiest way to limit a parameter n so that the function will not be executed unless the parameter meets the condition cond, is_cond.
In this example n is required to be an integer.
Clear[f]
f[x_, n_Integer] := x^n
f[3.2, -2]
0.0976562
f[3.2, 2.1]
f[3.2, 2.1]
Another way to establish a condition is with_?Test (guide/TestingExpressions). As a matter of fact, this is best way to do it since it’s the fastest one.
In this example, n must be a non-negative integer (n ≥ 0).
g[x_, (n_Integer) ?NonNegative] := x^n
g[3.2, 2]
10.24
g[3.2, -2]
g[3.2, -2]
A condition can also be defined using “/;”, the function f: =exp/;cond is evaluated only if the condition is met. Other equivalent forms are: f/;cond:=exp and f:>exp/;cond.
In the next example the function takes different forms depending on the selected region (the same can be done with Piecewise, as we will see later on):
Clear[f]
f[x_] := x^2 /; x > 0
f[x_] := x /; -2 ≤ x <= 0
f[x_] := "x must be bigger than or equal to -2" /; x < -2
We apply the function to different values of x.
{f[0.2], f[3], f [-3]}
{0.04, 9, x must be bigger than or equal to -2}
In the next example we combine several conditions that must be met to apply the binomial coefficients formula
The function below calculates the binomial coefficients if n is an integer, r is an integer ≥0, and n >= r (implying that n≥0).
binomial[n_Integer, (r_Integer) ?NonNegative] /; n >= r :=
n! / (r!*(n - r)!)
We add a message that will be displayed if the conditions are not met.
binomial[n_, r_] :=
"Check that n is an integer, r is a non-negative integer and n >= r."
We test the function for several cases.
binomial[3, 2]
3
binomial[2, 3]
Check that n is an integer, r is a non-negative integer and n >= r.
binomial[-3, 2]
Check that n is an integer, r is a non-negative integer and n >= r.
binomial [3, -0.5]
Check that n is an integer, r is a non-negative integer and n >= r.
In the following case we establish the condition that n has to be either integer, rational or complex to return n2. The function will be returned unevaluated in any other case.
squareofnumber[n_Integer | n_Rational | n_Complex] := n^2
Map[squareofnumber, {3, 3/2, 1.3, 3 + 4 I, I,
{9,
squareofnumber[π], squareofnumber[{a, b}], squareofnumber[a]}
We can also define conditions with the following functions (see the program’s help): If, Which, Switch and Piecewise.
In this example the function is defined using intervals.
Plot[
Piecewise[{{x^3 + 6, x <- 2}, {x, - 2 ≤ x ≤ 0}, {x^2, x > 0}}], {x, - 5, 5}]
The use of /; combined with FreeQ is particularly useful. For example we create a function that will consider everything that is not x as a constant.
f[c_ x_, x_] := cf[x, x] /; FreeQ[c, x]
{f[3x, x], f[ax, x], f[(1 + x) x, x]}
{3f[x, x], af[x, x], f[x(1 + x), x]}
Clear[f, g]
A double underscore__or BlankSequence[]indicates a sequence of one or more expressions as shown in this example:
f[x_Symbol, p__Integer] := Apply[Plus, x ^{p}]
f[y, 1, 2, 3]
y + y2 + y3
f[y]
f[y]
A triple underscore__or BlankNullSequence[] enables the use of any number of arguments. This is especially useful when defining optional arguments as we’ll see later on.
g[x_Symbol, p_ _ _Integer] := Apply[Plus, x^{p}]
g[y, 1, 2, 3]
y + y2 + y3
g[y]
0
Here it is an elegant example (from Power Programming with Mathematica: The Kernel by D.B. Wagner) of sorting the elements of a list using the triple underscore pattern.
sorter[{a_ _ _, b_, c_, d_ _ _} /; b > c] := sorter[{a, c, b, d}]
sorter[x_] := x
sorter[{5, 2, 7, 1, 4}]
{1, 2, 4, 5, 7}
Certainly, we could have used Sort instead of sorter.
Sort[{5, 2, 7, 1, 4}]
{1, 2, 4, 5, 7}
In this example we use Cases to select the list elements of the form xy.
Cases[{1, x,
The pattern “_.” makes it possible to define an argument that can be omitted. By using x^y_. (note the dot after the underscore), x is also selected since the argument of y can be omitted.
Cases[{1, x,
In the examples that follow we use DeleteCases to eliminate those elements of a list that meet certain criteria.
We remove the elements that are decimals (we use Real, different from Reals that refers to the real numbers).
DeleteCases[{1, 1, x, 2, 3, y, 3/5, 2.7, Pi, 9, y}, _Real]
[1, 1, x, 2, 3, y,
We eliminate from the list those sublists containing 0 as their last term.
DeleteCases[
{{{1, 1}, 0}, {{2, 3}, 1}, {{y, 3/5}, .7}, {{Pi, 9}, z}}, {a_, 0}]
{{{2, 3}, 1}, {
The number of significant digits of a number x used in calculations is called precision. To know the precision used in a calculation used Precision [x]. Accuracy (Accuracy [x]) is the number of significant digits to the right of the decimal point. Precision is a relative error measure while accuracy measures the absolute error.
By default N uses machine precision.
Clear ["Global`*"]
Precision[N[π]]
MachinePrecision
Precision[N[π, 40]]
40.
However, if we don’t use a decimal approximation, the precision of Pi is infinite.
Precision[π]
∞
WorkingPrecision controls the precision level during internal calculations while PrecisionGoal determines the precision in the final result. Other options related to accuracy and precision are: Accuracy, AccuracyGoal and Tolerance.
Besides knowing how to use the options mentioned above, there’s an important issue that you should know: Mathematica has different rules for working with accuracy and precision. An approximate quantity is identified by the presence of a decimal point. Therefore, 0.14 and 14.0 are approximate numbers and we will refer to them as decimals. If we rationalize 0.14 we get 14/100, the ratio of two integers. When doing operations with integers or combinations of them (rationals, integer roots, etc.) Mathematica generates exact results.
For example:
is different from:
There are several functions to convert a decimal number into an exact one.
For example: Round [x] returns the closest integer to x, Floor [x] the closest integer to x that is less than or equal to x, and Ceiling[x] the closest integer to x that is greater than or equal to x.
{Round[3.3], Floor[3.3], Ceiling[3.3]}
{3, 3, 4}
In the next example we are going to see the difference between operating with integers (or expressions composed of integers, such as fractions) and operating with decimal approximations.
We create the following function:
f[x_] := 11 x - a
We iterate it and check that we always get the same result:
NestList [f, a/10, 30] // Short
However, when we replace 1/10 with its decimal approximation 0.1, we can clearly see the errors associated with machine precision.
NestList[f, 0.1a, 20]
{0.1 a, 0.1 a, 0.1 a, 0.1 a, 0.1 a, 0.1 a, 0.1 a, 0.1 a, 0.1 a,
0.1 a, 0.1 a, 0.100002 a, 0.100025 a, 0.100279 a, 0.103066 a,
0.133729 a, 0.471014 a, 4.18116 a, 44.9927 a, 493.92 a, 5432.12 a}
This effect is not associated to the way Mathematica operates. It will happen with any program performing the same operation using machine precision. For example, try to replicate the previous calculation, replacing a with a positive integer (for example 3), in a program such as Microsoft® Excel and you will see that the errors multiply.
f[x_] := 11x - 3
NestList[f, 0.3, 20]
{0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3,
0.3, 0.299995, 0.299949, 0.299443, 0.293868, 0.232543,
-0.442028, -7.86231, -89.4854, -987.34, -10 863.7}
Rationalize converts a decimal number to the nearest rational with the smallest denominator.
Rationalize[0.3]
When used in the previous iteration we eliminate the numerical errors since Mathematica uses infinite precision for calculations without decimal numbers.
NestList[f, Rationalize[0.3], 30] // Short
Let’s show another more complicated example where we will see as well the effect of using decimal approximations instead of exact calculations.
Consider the following system of differential equations:
x1'(t) = 1 - 2.5 x1(t) + x2(t)
x2'(t) = 2x1(t) - x2(t)
Initial Conditions : x1(0) = x1(0) = 0
We solve it using DSolve. Depending on the machine precision of your computer, you may find numerical problems when visualizing the solutions.
Clear[x1, x2]
Now we solve it rationalizing the decimal coefficients since Mathematica computes with integers and rationals using infinite precision. The numerical problem is fixed.
Clear[x1, x2]
{{x1[t_], x2[t_]}} = {x1[t], x2[t]} /. DSolve[
{x1'[t_] == 1 - Rationalize[2.5] x1[t] + x2[t],
{x1'[t_]'[t_] == 2 x1[t] - x2[t], x1[0] == 0, x2[0] == 0},
{x1[t], x2[t]}, t] // ExpandAll
Plot[{x1[t], x2[t]}, {t, 0, 100}]
Another situation where it is common to experience difficulties similar to the previous ones is when performing matrix operations (matrix inversion, resolution of systems of differential equations with decimal coefficients, etc.) Therefore, it is often desirable to rationalize the decimal matrix elements; although you may have to consider the increase in computation time.
The power of Mathematica becomes particularly evident in the precision with which you can do calculations and the ability to choose the most appropriate method or algorithm given the required computation.
NIntegrate is used for numerical integrations and like almost all other calculation-related functions, unless indicated otherwise, will choose by default a computation method. However, you can select a different method or, for a given method, choose the most appropriate parameters. In the example that follows we compare three methods after showing graphically the method of quadrature. Note the use of Defer.
f[x_] := Exp[-x]
Show[Plot[f[x], {x, -3, 3}],
DiscretePlot[f[x], {x, -3, 3}, ExtentSize → Left],
PlotLabel → Style[Defer
NIntegrate[f[x], {x, -3, 3), Method → #, PrecisionGoal → 2] & /@
{Automatic, {"RiemannRule", "Type" → "Left"}, "TrapezoidalRule"}
{20.0357, 20.2215, 20.036}
Here we use EvaluationMonitor to display the value of x used in each iteration.
Print["x = ", x]
x = x
NIntegrate[f[x], {x, -1, 1}, Method → "TrapezoidalRule",
PrecisionGoal → 2, EvaluationMonitor :→ Print["x = ", x]]
x = -1.
x = -0.75
x = -0.5
x = -0.25
x = 0.
x = 0.25
x = 0.5
x = 0.75
x = 1.
2.35045
The next function is a piecewise function, that is, it takes different values in each of the intervals.
Clear["Global`*"]
To represent it graphically Mathematica uses different calculation methods depending on the interval.
Plot[fun, {x, -10, 10}, ImageSize → 300]
We can control the level of precision as follows:
N[Integrate[Sin[2000 x] x^2, {x, 1, 2}], 10]
0.001275015530
N[NIntegrate[Sin[2000 x] x^2, {x, 1, 2}], 10]
0.00127502
NIntegrate[Sin[2000 x] x^2, {x, 1, 2}, WorkingPrecision -> 10]
0.001275015530
Next, we present an example of a differential equation in which the solving method (tutorial/NDSolveOverview) depends on the value of δ. NDSolve chooses the best algorithm given δ. We use Manipulate, a function that we will refer to extensively in the next chapter.
Manipulate[
res = NDSolve[{y'[t] == y[t]2 - y[t]3, y[0] == δ}, y, {t, 0, 2/δ}];
p1 = Plot[(y /. First[res])[t], {t, 0, 2/δ}, Mesh → All],
{{δ, 0.01}, 0.0001, 0.01}]
Another interesting aspect of Mathematica is its use of previously calculated results. Before showing it with an example, it would be better to quit the session to remove any previous information stored in memory.
Quit[]
In the next example we show the calculation time of a complicated definite integral for different integration limits.
Timing[(f1[x1_] := Integrate[Sin[x^3], {x, 0, x1}];
Table[f1 [i], {i, 1, 10}]);]
{3.67188, Null}
We repeat the same exercise using “=” instead of “:=”
Timing[(f2[x1_] = Integrate[Sin[x^3], {x, 0, x1}];
Table[f2[i], {i, 1, 10}]);]
{1.07813, Null}
The calculation time is faster in this last case. It seems logical, since in the first example the integral f1 is calculated 10 times, each time applying a different integration limit, while in the second case f2 provides the solution to the integral as a function of the integration limit x1, and then replaces the value of x1 directly in the solution 10 times.
The surprise comes when we repeat the calculation of f1. The calculation time has been reduced from the first time we used the function.
Timing[(f1[x1_] := Integrate[Sin[x^3], {x, 0, x1}];
Table[f1[i], {i, 1, 10}]);]
{1.03125, Null}
And even more surprising: if instead of f1 we define a new function identical to f1 named f3, the calculation time is still faster than the first time we computed f1.
Timing[(f3[x1_] := Integrate[Sin[x^3], {x, 0, x1}];
Table[f3[i], {i, 1, 10}]);]
{0.90625, Null}
The reason behind this behavior is that for functions such as: Integrate, Simplify and others, Mathematica keeps a cache of previous results that it will use later if necessary to make calculations more efficient (http://library.wolfram.com/infocenter/Conferences/5832/).
If we now delete the cache, the calculation takes longer:
ClearSystemCache,[]; Timing[(f3[x1_] := Integrate[Sin[x^3], {x, 0, x1}];
Table[f3[i], {i, 1, 10}]);]
{3.5625, Null}
In many situations we can significantly reduce the computation time by using Compile. Normally it’s used with functions that only need to evaluate numerical arguments. This approach enables the program to save time by avoiding checks related to symbolic operations. The command generates a compiled function that applies specific algorithms to numerical calculations.
Example: We use Compile to calculate the following function that only accepts real values as arguments:
cf = Compile[{{x, _Real}}, x^2 - 1/(1 + x) Sin[x]]
CompiledFunction evaluates the expression using the compiled code:
cf[Pi]
9.8696
We can now represent graphically the compiled version of the function:
Plot[cf[x], {x, -1, 3}]
If you need to create highly efficient programs for calculations that could take hours or even days, it’s recommended to try several alternative approaches first. It’s not unusual for two commands generating the same result to have very different computation times (a carefully crafted programming instruction can be tens or sometimes even thousands of times faster than one that is not).
The function CloudDeploy deploys objects to the Wolfram Cloud. Let’s take a look at some examples.
The command below creates a web page with a 100-point font message:
CloudDeploy[Style["My first cloud program", 100]]
CloudObject[{https://www.wolframcloud.com/objects/e4f106f2-25fc-4cb8-ad22-14
The first time we execute it, Mathematica will ask for our Wolfram ID and password as shown in Figure 3.2:
Clicking on the hyperlink just created will automatically take us to the web page shown in Figure 3.3:
We can deploy any Mathematica function to the cloud. Figure 3.4 shows the deployment of a dynamic interface:
CloudDeploy[Manipulate[Plot[Sin[x (1 + kx)], {x, 0, 6}], {k, 0, 2}]]
CloudObject[{https://www.wolframcloud.com/objects/7c9e2c32-4e66-4d38-88cf-40
For further information:
http://reference.wolfram.com/language/guide/CloudFunctionsAndDeployment.html
Mathematica’s capabilities can be extended substantially with the use of packages. Packages are conceptually similar to what other programming languages call subroutines or subprograms. They basically involve the creation of functions, often complex, that can be called when needed without having to define them again.
Clear["Global`*"]
A package has the following structure :
BeginPackage["name of the package`"]
f::usage = "f brief description", … (here we would include the headings of the functions that we are going to use with a brief description, that will be seen by the user later on).
Begin["`Private`"]
f[args]= value, … (here we would have the option to place functions that will be used by f but that will remain private. That is, the user will not be able to access them). f[args] = value, … (here we would program the package functions accessible to the user).
End[]
EndPackage[]
A simple package
We’d like to build the function below and make sure that it’s going to be available for later use in any notebook.
f(n, r, p) =
Write the following in a code cell (Format ▶ Style ▶ Code)
Next, from the main menu bar select File ▶ New ▶ Package. A new window in the package environment will appear (Figure 3.5). After that, copy the previous cell to this new notebook, turn it into an initialization cell (Cell ▶ Cell Properties ▶ Initialization) and save it as a package (File ▶ Save as ▶ Wolfram Mathematica Package) in the Data folder located in our working directory. Use as the name the one defined in BeginPackage: “Binomialpackage.m” (remember to choose the extension .m).
In this environment we can test, modify and even debug the package. Once we see it is working correctly we will save it. A package is an ASCII file. It can also be modified using a text editor such as Notebook (in Windows).
As the last step, we exit the session (to be precise: we exit the kernel) to start a new one. This can be done without closing the notebook by choosing in the menu bar Evaluation ▶Quit Kernel or with the command:
Quit[]
Let’s make the folder Data our working directory.
SetDirectory[FileNameJoin[{NotebookDirectory[], "Data"}]]
C:\Users\guill\Documents\MathematicaBeyond\Data
To use the package we need to load it first (or, for testing purposes, execute it inside a notebook). We can use either << or Needs but in this case we choose the latter since it has the advantage of only loading the package if it hasn’t been loaded previously.
Needs["Binomialpackage`"]
One way to know what functions are available inside the package is as follows:
?Binomialpackage`*
At the time of loading the package, all its functions are executed, in this case just one: Binomialdistribution.
Binomialdistribution[5, 3, 0.1]
0.0081
Binomialdistribution[5, 3, 1.4]
Check that n is an integer, r
is a non-negative integer,n >= r and 0 ≤ p ≤ 1.
If you want the package to be available to all users, copy it to a Mathematica subdirectory named Applications that can be found executing the command $InstallationDirectory or $BaseDirectory.
FileNameJoin[{$InstallationDirectory, "Addons", "Applications"}]
C:\Program Files\Wolfram Research\Mathematica\11.0\Addons\Applications
Here we show a way to build a package with options. For that purpose we use the syntax: f[x_, opts___]. Note the use of the triple underscore symbol (BlankNullSequence) “___” that we have mentioned before.
Before building a function with options remember the replacement rules:
m /. m -> opt1 /. m1 -> opt2
opt1
m1 /. m -> opt1 /. m1 -> opt2
opt2
These rules are applied when defining a function where users can choose different options. One option will be used by default (“Method1”):
Opt1[function1] = Method → "Method1";
f1[x1_, opts_ _ _] := {x1, Method /. Flatten[{opts}] /. Opt1[function1]}
If we don’t write any option or we write Method→ “Method1” then Method 1 is applied:
f1[v1]
{v1, Method1}
The method written in opts will be used in other cases. We have used Flatten[{opts}] to ensure that the function works even if the user writes the option between nested brackets.
f1 [v2, {{Method → "Method2"}}]
{v2, Method2}
Now we will develop a package with options. Let’s suppose that we would like to do an operation between two lists (a = list1, b = list2). The operation f will be different depending on the chosen method: If the method is “Automatic” then f = a * b; if it’s “Method1” then f = a / b; and if it’s “Method2” then f = a + b. If the user doesn’t choose a method then the default method, “Automatic”, will be applied. We will also include a message in case there are problems. For example, if we mistype the name of the method we will get the message “Something is wrong”.
BeginPackage ["PackageWithOptions`"]
(* This is a comment. It doesn't affect the package functionality
and cannot be seen by the user. Comments can be useful to
understand the package if we'd like to modify it later on. *)
(* We associate a message to the function name to be
displayed when we request help about the function. *)
listOperation::usage =
"listOperation [listA, listB, options]. This function
will multiply,divide or add the elements of
two lists depending on the chosen method.";
Options [listOperation]={Method → "Automatic"} ;
Begin "`Private`"]
listOperation [listA_List, listB_List, opts_ _ _]:=
With [{m = Method / . Flatten[{opts}] / . Options[listOperation]},
Module [{a, b, r}, a = listA;
b = listB;
r = a b] /;m = = = "Automatic"];
listOperation[listA_List, listB_List, opts___]:=
With[{m =Method /. Flatten[{opts}]/. Options[listOperation]},
Module[{a, b, r}, a =listA;
b =listB;
r =a/b]/; m ==="Method1"];
listOperation[listA_List, listB_List, opts___]:=
With[{m =Method /. Flatten[{opts}]/. Options[listOperation]},
Module[{a, b, r}, a =listA;
b =listB;
r =a +b]/; m ==="Method2"];
listOperation[listA_, listB_, opts___]:="Something is wrong";
End[]
EndPackage[]
Now, we proceed to save the package under the name “PackageWithOptions”. This can be done once again by copying the cell into a new notebook, changing the cell style to Code, initializing the cell and finally saving the notebook as a Mathematica package with the “.m” extension. It’s recommended to copy the file in the subdirectory Applications.
As mentioned before, if you want the package to be available to all users, copy it to Addons\Applications in:
FileNameJoin [{$InstallationDirectory, "Addons", "Applications"}]
C:\Program Files\Wolfram Research\Mathematica\11.0\Addons\Applications
If you’d like only the current user to access it, place it in the following Applications subdirectory:
$UserBaseDirectory
C:\Users\guill\AppData\Roaming\Mathematica
Needs["PackageWithOptions`"]
? PackageWithOptions`
listOperation[{1, 2, 3}, {1, 2, 3}, {1, 2, 3}]
{1, 4, 9}
{listOperation[{1, 2, 3}, {1, 2, 3}, Method "Method1"]
{1, 1, 1}
{listOperation[{1, 2, 3}, {1, 2, 3}, Method "Method2"]
{2, 4, 6}
listOperation[1, 2]
Something is wrong
In later chapters we will see other examples of real applications of packages.
In the previous section we have learned how to display a very simple error message. If we want to create more detailed ones we can proceed as follows: A message has the form Message [symbol::tag], where symbol is the function associated to the message, and tag is a message identifier.
Let’s create a message associated with a function fu using generalmsg as the tag (as a precaution we remove any previous reference to fu.)
Clear [fu]
fu::generalmsg = "This a message about fu.";
We can see the output type that Message uses.
Message [fu::generalmsg]
fu: This a message about fu.
Let’s define fu so that Message will be generated when fu is called with just one argument.
fu[_] := Message[fu::generalmsg]
fu [0]
fu: This a message about fu.
We define fac to calculate the factorial of a number and we limit the argument to positive integers.
fac [n_Integer? Positive] := n fac[n - 1] ;
[0]= 1;
The following message is generated if we use fac with the wrong argument type.
fac::wrongargs: =
"the argument of fac must be a single positive integer."
fac[___] := Message[fac::wrongargs]
Let’s see some examples with incorrect inputs.
fac [2, 3]
fac: the argument of fac must be a single positive integer.
[2.3]
fac: the argument of fac must be a single positive integer.
We can include more complex messages to display specific information related to the type of error. For example, we can include a particular message in the position defined by a double backquote punctuation mark (``) inside a general message.
fac::error : =
"A positive integer is expected in fac [``]."
fac[wrong_?NumericQ] : = Message [fac::error, wrong]
fac[- 6]
fac: A positive integer is expected in fac [-6].
You can find additional information about the use of messages in: Message.
If you are going to develop an application that includes multiple notebooks, you may find the AuthorTools package useful: AuthorTools/tutorial/MakeProject.
Another alternative way of creating packages would be to use Wolfram’s IDE, Wolfram Workbench (http://www.wolfram.com/products/workbench), which we will cover in Chapter 12.
In addition to Workbench, there’s a complementary set of Mathematica tools that we will cover as well in Chapter 12 such as: WLGM (Wolfram Lightweight Grid Manager) to connect multiple computers and use them in parallel computations and webMathematica, to develop applications that run on a web server and can be accessed from any browser. The latest Mathematica versions also include the possibility of CUDA programming (to take advantage of the GPUs in NVidia graphics cards) and incorporate several CUDA functions optimized for list processing, image processing, and linear algebra and Fourier transforms applications among others.
As in most cases, the best available information comes from Mathematica’s own documentation or Wolfram’s website:
Fast Introduction for Programmers, a short introduction to the Wolfram Language for programmers, is available from Help ▶ Wolfram Documentation ▶ Intro for programmers ≫, or on the web: (http://www.wolfram.com/language/fast-introduction-for-programmers/)
Stephen Wolfram’s book: An Elementary Introduction to the Wolfram Language covers the basic principles of the Wolfram Language. It’s available directly from within the Wolfram documentation: Help ▶ Wolfram Documentation ▶ Introductory Book ≫ or on the web: http://www.wolfram.com/language/elementary-introduction
Wolfram’s reference page for the Wolfram Language: http://www.wolfram.com/language/
Functions and references:
http://reference.wolfram.com/mathematica/tutorial/FunctionsAndProgramsOverview.html
The following sources contain links to Mathematica programming documents available on the Internet. Even though they were written for previous versions, most of their contents are still relevant.
Ted Ersek's collection of Mathematica Tricks:
http://www.verbeia.com/mathematica/tips/Tricks.html
Roman E. Maeder's Mathematica's Programming Language:
http://library.wolfram.com/infocenter/Conferences/183/
Michael A. Morrison’s Mathematica’s Tips, Tricks and Techniques: Syntax
http://www.nhn.ou.edu/~morrison/Mathematica/TipSheets/Syntax.pdf
Leonid Shifrin's Mathematica programming - an advanced introduction:
http://www.mathprogramming-intro.org
There are many books for learning how to program with Mathematica. One of the best ones is Power Programming with Mathematica: The Kernel by David B. Wagner (available as a free download); though written for Mathematica 3, its ideas are still useful. Another one is The Computer Science with Mathematica by Roman E. Maeder. The bible of programming with Mathematica is probably The Mathematica GuideBooks collection written by Michael Trott (in Mathematica 5.) A great reference for those who aspire to become Mathematica programming gurus.
http://packagedata.net/ is a global repository of Mathematica packages that makes it easy for users to find additional functionality in a wide variety of scientific and technical fields.