This Sage worksheet is one of the tutorials developed for the MAA PREP Workshop "Sage: Using Open-Source Mathematics Software with Undergraduates" (funding provided by NSF DUE 0817071).
This tutorial will cover the following topics (and various others throughout):
We will motivate our examples using basic matrices and situations one might want to handle in everyday use of matrices in the classroom.
Making a new matrix is not too hard in Sage.
Some commands are available right off the bat, like derivatives are. This is the determinant.
{{{id=4| det(A) /// -2 }}}But some things are not available this way - for instance a row-reduced echelon form. We can 'tab' after this to make sure.
{{{id=104| r /// }}}So, as we've already seen, many of the commands in Sage are "methods" of objects. This is a huge advantage, once you get familiar with it. First, we do the determinant again.
{{{id=5| A.det() /// -2 }}}Then we do the row-reduced echelon form.
{{{id=106| A.rref() /// [1 0] [0 1] }}}It is very important to keep in the parentheses. Things that would be legal without them would be called 'attributes', but Sage prefers stylistically to hide them, since math is made of functions and not elements of sets. (Or so a category-theorist would say.)
{{{id=8| # Won't work A.det ///To find out what you can do, don't forget that you can use the 'tab' key.
{{{id=12| A. /// Traceback (most recent call last): File "And it is always helpful to read the documentation to tell if you need an argument (here, one of the columns).
{{{id=6| A.column(1) /// (2, 4) }}}Notice that this gives the SECOND column! You might have thought that this would give the first column, but Sage (along with the Python programming language) begins numbering at zero. This is another very important point.
Incidentally, sometimes you will have surprises. Subtle changes in an object can affect what commands are available, or what their outcomes are.
{{{id=107| A.echelon_form() /// [1 0] [0 2] }}}This is because our original matrix had only integer coefficients, and you can't make the last entry one via elementary operations unless you multiply by a rational number!
{{{id=13| B = A.change_ring(QQ); B.echelon_form() /// [1 0] [0 1] }}}We've already seen one or two examples of a fundamental object called a list. You should think of a list as an ordered set, where the elements of the set can be pretty much anything - including other lists.
{{{id=53| my_list=[2,'Grover',[3,2,1]]; my_list /// [2, 'Grover', [3, 2, 1]] }}}Although we won't dwell on it here, it is possible to access any elements of such a list quite easily using square brackets - as long as you remember that the counting starts at zero!
{{{id=119| my_list[0]; my_list[2]; my_list[0:1] /// 2 [3, 2, 1] [2] }}}However, our main reason for introducing this is more practical, as we'll now see.
One of the best uses of the computer in the classroom is to quickly show tedious things. One of the most tedious things to do by hand in linear algebra is taking powers of matrices. Here we make the first four powers of our matrix by hand.
{{{id=110| A = matrix([[1,2],[3,4]]) A^0; A^1; A^2; A^3; A^4; A^5 /// [1 0] [0 1] [1 2] [3 4] [ 7 10] [15 22] [ 37 54] [ 81 118] [199 290] [435 634] [1069 1558] [2337 3406] }}}This is not terrible, but it's not exactly nice either, particularly if you might want to do something with these new matrices.
Instead, we can do what is known as a loop construction. It is extremely important that we include both the colon in the first line and the indentation in the second line, for that is the basic syntactical structure of Python.
{{{id=112| for i in [0,1,2,3,4]: A^i /// [1 0] [0 1] [1 2] [3 4] [ 7 10] [15 22] [ 37 54] [ 81 118] [199 290] [435 634] }}}The syntax is vaguely mathematical - for $i$ in the set [0,1,2,3,4], return $A^i$. Recall that the square brackets create a list, and note that the powers of the original matrix come in the same order as the list.
This is better, but still not perfect. It would be best to make it quicker to write. There are two ways to do this in Sage:
{{{id=66| for i in [0..4]: det(A^i) /// 1 -2 4 -8 16 }}} {{{id=114| for i in range(5): det(A^i) /// 1 -2 4 -8 16 }}}These ways of constructing lists are very useful (and demonstrate that, like many Sage/Python things, that counting begins at zero in things like 'range'). The examples below show that one can get step sizes other than one as well.
{{{id=115| range(3, 23, 2); [3,5..21] /// [3, 5, 7, 9, 11, 13, 15, 17, 19, 21] [3, 5, 7, 9, 11, 13, 15, 17, 19, 21] }}}It is also important to emphasize that the 'range' command does not include its last value! Confirm this in the examples above.
This all works well. However, after a short time this will seem tedious as well (you may have to trust us on this). It turns out that there is a very powerful way to create such lists in a way that very strongly resembles the so-called set builder notation, called a list comprehension.
We start with a relatively easy example: $$\{n^2\mid n\in\ZZ, 3 \leq n \leq 12\}$$
This is a natural for the list comprehension, and can be very powerful when used in Sage.
{{{id=116| [n^2 for n in range(3, 13)] /// }}}This sort of turns the loop around. The notation is easiest if you think of it mathematically; "The set of $n^2$, for (any) $n$ in the range between 3 and 13."
This is phenomenally useful. Here is another example, from groups: $G = S_4$, $T=\{|g|\mid g\in G\}$
{{{id=69| G = SymmetricGroup(4) orders = [g.order() for g in G] orders.sort() orders /// [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4] }}}And plotting!
{{{id=55|
plot([x^n for n in [2..6]],(x,0,1))
///
}}}
Now we apply it to the example we were doing in the first place. Notice we now have a nice concise description of all determinants of these matrices, without the syntax of colon and indentation.
{{{id=121| [det(A^i) for i in [0..4]] /// [1, -2, 4, -8, 16] }}}It is often the case that Sage can do something, but doesn't have a simple command for it. For instance, you might want to take a matrix and output the square of that matrix minus the original matrix.
{{{id=77| A = matrix([[1,2],[3,4]]) A^2-A /// [ 6 8] [12 18] }}}How might one do this for other matrices? Of course, you could just always do $A^2-A$ again and again. But this would be tedious and hard to follow, as with so many things that motivate a little programming. Here is how we solve this problem.
{{{id=129| def square_and_subtract(mymatrix): """ Return `A^2-A` """ return mymatrix^2-mymatrix /// }}}The 'def' command has created a new function called 'square_and_subtract'. It should even be available using tab-completion! If you type 'square_and_subtract?', then the documentation string should appear.
Here are things to note about its construction:
File: /tmp/tmpP9p_cQ/___code___.py
Type: <type ‘function’>
Definition: square_and_subtract(mymatrix)
Docstring:
Return A^2-A
You may have noticed that there is nothing that requires the input 'mymatrix' to be a matrix. Sage will just try to square whatever you give it and subtract the original thing.
{{{id=135| square_and_subtract(sqrt(5)) /// -sqrt(5) + 5 }}}Try to define a function which inputs a matrix and returns the determinant of the cube of the matrix. (There are a few ways to do this, of course!)
{{{id=132| /// }}} {{{id=133| /// }}}Before we finish the tutorial, there are a few things that often trip people up, mostly related to symbols, which should be pointed out.
One thing is that it's possible to clobber constants!
{{{id=80| i /// 4 }}}Can you figure out why $i=4$? Look carefully above to see when this happened.
By the way, this isn't necessarily bad (for instance, if you are only dealing with real matrices!), but definitely is something a Sage user needs to know. Also, it's possible to restore symbolic constants.
{{{id=84| reset('i') i; i^2 /// I -1 }}} {{{id=83| type(e) ///Variables are another thing to keep in mind. In order to maintain maximum flexibility while not allowing things to happen which shouldn't, only $x$ is predefined, nothing else.
{{{id=86| type(x) /// }}} {{{id=87| type(y) /// }}}Finally, there are a few names which are "reserved" by Python/Sage, and which aren't allowed as variable names. It's not surprising that 'for' is not allowed, but neither is 'lambda' ($\lambda$)!
{{{id=138| var('lambda') /// lambda }}} {{{id=140| lambda^2-1 /// Traceback (most recent call last): File "There are lots of ways to get around this. One popular, though annoying, way is this.
{{{id=141| var('lambda_') /// lambda_ }}} {{{id=143| lambda_^2-1 /// lambda_^2 - 1 }}}Note that showing the expression still shows the Greek letter.
{{{id=145| show(lambda_^2-1) ///There are several things which are useful to know about, but which are not always introduced immediately in programming. We give a few examples here, but they are mainly here to make sure you have seen them so that they are not completely surprising when they come up again.
We saw the "block" structure of Python earlier, with the indentation. This gives the opportunity to introduce conditional statements and comparisons.
{{{id=123| B = matrix([[0,1,0,0],[0,0,1,0],[0,0,0,1],[0,0,0,0]]) for i in range(5): # all integers from 0 to 4, remember if B^i==0: # We ask if the power is the zero matrix print i /// 4 }}}Another useful concept is that of a dictionary. This can be thought of as a mathematical mapping from "keys" to "values". The order is not important and not guaranteed. A dictionary is delimited by curly brackets and correspondence is indicated by colons.
Again, we will not give a big example. What if one wants to specify a matrix using just the nonzero entries? A dictionary is a great way to do this. This one puts 3 as an entry in the $(2,3)$ spot, for example (remember, this is the third row and fourth column, since we start with zero!).
{{{id=48| D = {(2,3):3, (4,5):6, (6,0):-3} C = matrix(D) C /// [ 0 0 0 0 0 0] [ 0 0 0 0 0 0] [ 0 0 0 3 0 0] [ 0 0 0 0 0 0] [ 0 0 0 0 0 6] [ 0 0 0 0 0 0] [-3 0 0 0 0 0] }}}That was a lot easier than inputting the whole matrix!
Sometimes it does matter how you define the same elements in Sage; we saw that above with matrices over the rationals versus integers, for instance. We will not go in great depth about this, either, but it is worth knowing about.
"It depends on what the meaning of the word 'is' is." –Bill Clinton, 1998
{{{id=73| a = 2 b = 2/1 c = 2.0 d = 2 + 0*I e = 2.0 + 0.0*I /// }}}Notice that each of these has or does not have $I=\sqrt{-1}$, decimal points, or division.
{{{id=124| print parent(a) print parent(b) print parent(c) print parent(d) print parent(e) /// Integer Ring Rational Field Real Field with 53 bits of precision Symbolic Ring Symbolic Ring }}}At times this will become important. Finally, sometimes one has to be careful about making changes to objects, as the '=' sign doesn't just say things are equal, but passes along a "reference" to the object it is made equal to.
{{{id=91| A = matrix(QQ,[[1,2],[3,4]]) B = A C = copy(A) /// }}} {{{id=93| A[0,0]=987 /// }}} {{{id=94| show([A,B,C]) ///