- •Credits
- •About the Author
- •About the Reviewers
- •www.PacktPub.com
- •Preface
- •Getting started
- •More advanced graphics
- •Summary
- •Start Sage
- •Installing Sage
- •Starting Sage
- •Prerequisites
- •Installation
- •Summary
- •Command history
- •Working with code
- •Arithmetic operators
- •Strings
- •Functions
- •Functions with keyword arguments
- •Objects
- •Summary
- •Python 2 and Python 3
- •Running scripts
- •Strings
- •List comprehensions
- •Storing data in a dictionary
- •Summary
- •Vectors and vector spaces
- •Creating a vector space
- •Vector operators and methods
- •Decomposing matrices
- •Summary
- •Using graphics primitives
- •Summary
- •Substitutions
- •Finding roots
- •Derivatives
- •Integrals
- •Series and summations
- •Summary
- •Computing gradients
- •Constrained optimization
- •Probability
- •Summary
- •Making our tanks move
- •Unit testing
- •Summary
- •Introducing Python decorators
- •Making interactive graphics
- •Summary
- •Index
Chapter 5
What just happened?
We defined a 3x3 matrix of rational numbers, and used the eigenvalues method to return a list of eigenvalues. We then used the eigenvectors_right method to return a list of tuples that contain data about the eigenvectors. We used a for loop to iterate through the list and print the information in a more readable format. Each element in the list is a tuple with three elements. The first is the eigenvalue, the second is the eigenvector, and the third is the multiplicity of the eigenvalue. Finally, we calculated and displayed the eigenmatrices D and P for matrix A, which satisfy the relation A*P=P*D.
Haveagohero–verifyingtheeigenvaluesandeigenvectors
Let A be an m x n matrix with eigenvalues given by:
For each eigenvalue, there is an eigenvector x, which satisfies the relation:
In the previous example, we found the eigenvalues and eigenvectors for matrix A. Use Sage to verify that each of those eigenvalues and eigenvectors satisfies the relation above.
Decomposing matrices
Another important task in applied mathematics is decomposing a matrix into a combination of special matrices. There are a variety of well-known decompositions (also known as factorizations) that are used to solve various practical problems in applied mathematics.
Timeforaction–computingtheQRfactorization
The QR factorization can be used to solve linear least squares problems. The QR factorization decomposes an m x n matrix A (with m≥n) into two matrices called Q and R, such that A=QR. Q is an m x n matrix with orthonormal columns and R is an n x n matrix that is upper triangular and invertible. In this example, we will see how easy it is to compute the QR factorization with Sage.
# This is an example where it's important to specify the correct ring A = Matrix(RDF, [[1, -1, 4], [1, 4, -2], [1, 4, 2], [1, -1, 0]]) print("Matrix A:")
show(A)
[ 129 ]
Vectors, Matrices, and Linear Algebra
Q, R = A.QR()
print("Matrix with orthonormal basis:") show(Q)
print("Upper diagonal matrix:") show(R)
print("Q*R recovers A:") show(Q*R)
The output should look like this:
[ 130 ]
Chapter 5
What just happened?
We defined a 4 by 3 matrix called A over the ring called RDF, which is a shortcut for RealDoubleField. An RDF object is a double-precision approximation of a floating point number, while a RealField object can have an arbitrary number of bits of precision. This is another example where it is very important to choose the right ring. Matrix decompositions in Sage are only defined for matrices constructed on RDF and its counterpart CDF, or ComplexDoubleField. The QR method returns a tuple containing the matrices Q and R. We printed out the matrices and verified that A = Q*R.
Timeforaction–computingthesingularvaluedecomposition
The singular value decomposition, or SVD, has numerous applications in statistics, signal processing, and numerical analysis. An m x n matrix A is decomposed into three matrices: an m x m unitary matrix U, an m x n diagonal matrix sigma, and an n x n real unitary matrix V. These matrices satisfy the relation:
Here, V* denotes the transpose of the complex conjugate of V.
It's also easy to compute the SVD with Sage:
A = Matrix(RDF, [[1,1], [1,1], [0,0]]) print("Matrix A:")
show(A)
print "SVD:"
U, Sigma, V = A.SVD() print("U:")
show(U)
print("Sigma:")
show(Sigma)
print("V:")
show(V)
print("U.Sigma.V* recovers A:") show(U*Sigma*(V.conjugate().transpose()))
[ 131 ]
Vectors, Matrices, and Linear Algebra
The result should look like this:
What just happened?
As in the previous example, we defined a 4 by 3 matrix called A over the field called RDF. The SVD method returns a tuple containing the matrices U, Sigma, and V. We displayed these matrices, and verified that they satisfy the mathematical relation shown in the introduction. Matrix objects in Sage have methods for computing other decompositions. The method LU computes the LU decomposition, and the method cholesky_decomposition computes the Cholesky decomposition.
The final line of this example shows that methods can be "chained" together. The methods are evaluated in order, from left to right. The reason this works is that the expression v.conjugate() returns a matrix object. We then call the method transpose of this matrix object. In many cases, chaining methods can make your code more concise and readable. Of course, it should be avoided if it makes the code less readable.
[ 132 ]
Chapter 5
AnintroductiontoNumPy
NumPy is a package that turns Python into a powerful numerical computing language. The core of NumPy is a powerful n-dimensional array class. The package also includes tools for numerical linear algebra, Fourier transforms, and many other commonly used numerical methods. To find out more about NumPy, check out http://numpy.scipy.org/.
The current release of Sage is 4.6.1, which includes NumPy version 1.5. Because NumPy is constantly evolving, the latest version of NumPy may differ slightly from the version included with the latest version of Sage. Be aware of this as you are looking at the documentation, especially if you are using a different version of NumPy in other Python code!
Timeforaction–creatingNumPyarrays
The array class is the core of NumPy. Let's explore the various ways that we can create NumPy arrays:
import numpy
print("array:")
a = numpy.array([1,2,3,9,10,11]) print(a)
print("arange:")
b = numpy.arange(0.0, 10.0, 3.0/2) print(b)
print("zeros:")
c = numpy.zeros(5,dtype=int) print(c)
print("ones:")
d = numpy.ones((4,1), dtype=numpy.float64) print(d)
print("ones, 2D array:") e = numpy.ones((3,2)) print(e)
print("empty:")
f = numpy.empty((1,4), dtype=numpy.float32) print(f)
[ 133 ]
Vectors, Matrices, and Linear Algebra
The result should look like this:
What just happened?
In the first line of the script, we used the import statement to make NumPy functions and objects available to Sage. In order to keep NumPy types separate from Sage types with the same name, we access the NumPy types with the syntax numpy.type. In this example, we used several functions to create NumPy arrays. All of these functions accept the optional argument dtype, which specifies the type for the elements in the array (NumPy types are not the same as Sage types). We used the print function instead of the show function to display the arrays we created. Since NumPy objects return only plain text representations, there is no reason to use show to display NumPy objects.
CreatingNumPyarrays
NumPy includes many convenient functions for creating arrays. The array function takes a Python list as an argument, and returns an array with the contents of the list, with each element converted to the specified type. arange is an extension of the range function that we learned about in Chapter 4. The basic syntax is as follows:
new_array = arange([start,] stop [,step] [,dtype])
[ 134 ]
Chapter 5
If only one argument is provided, start is assumed to be zero and step is assumed to be one. If two arguments are given, the first is used as start and the second is assumed to be stop, and step is assumed to be one. ones and zeros return an array of the given shape and the specified type, with every element set to 1 or 0. The shape argument can be an integer or a tuple. An integer creates a "row" array, while a tuple of the form (n,1) creates a "column" array. Tuples of the form (m,n) or (m,n,p) create two-dimensional and threedimensional arrays, respectively. empty is the fastest way to create an array of a specified shape. It allocates the appropriate amount of space for the elements, based on the specified type, but does not initialize the element values. Note that the values in an empty array will be different every time it is created.
NumPytypes
Every NumPy array has a type, and all the elements in the array must have the same type. The following table will help you choose the appropriate type. When choosing a type, the main factors are the maximum value that needs to be stored, the amount of precision required, and the amount of memory required for the array. You must consider the amount of RAM needed to hold the array during calculations, and the amount of disk space required if you are going to save the array to a file. For the simple exercises in this chapter, you will be safe using the default 64-bit types:
bool |
Boolean (True or False) stored as a byte |
int |
Default integer for the platform (normally either int32 or int64) |
int8 |
Byte (-128 to 127) |
int16 |
Integer (-32768 to 32767) |
int32 |
Integer (-2147483648 to 2147483647) |
int64 |
Integer (9223372036854775808 to 9223372036854775807) |
uint8 |
Unsigned integer (0 to 255) |
uint16 |
Unsigned integer (0 to 65535) |
uint32 |
Unsigned integer (0 to 4294967295) |
uint64 |
Unsigned integer (0 to 18446744073709551615) |
float |
Shorthand for float64 |
float32 |
Single precision float: sign bit, 8 bits exponent, 23 bits mantissa |
float64 |
Double precision float: sign bit, 11 bits exponent, 52 bits mantissa |
complex |
Shorthand for complex128 |
complex64 |
Complex number, represented by two 32-bit floats (real and imaginary |
|
components) |
complex128 |
Complex number, represented by two 64-bit floats (real and imaginary |
|
components) |
[ 135 ]
Vectors, Matrices, and Linear Algebra
IndexingandselectionwithNumPyarrays
All of the indexing and slicing tricks that we've learned so far also apply to NumPy arrays.
NumPy adds a few indexing tricks that help with processing numeric data.
Timeforaction–workingwithNumPyarrays
Let's explore some ways to select elements and sub-arrays from NumPy arrays:
import numpy
a = numpy.arange(9.0) print("Array a:") print(a)
a = a.reshape((3,3)) print("Array a, reshaped:") print(a)
print("Selecting an element: {0}".format(a[1,0])) print("Selecting a row: {0}".format(a[1]))
print("Selecting a submatrix:") print(a[1:3,1:3])
b = numpy.arange(9.0, 0.0, -1.0) print("\nArray b: {0}".format(b)) indices, = numpy.where(b > 4.0)
print("Indices of elements of b > 4.0: {0}".format(indices)) print("b > 4.0: {0}".format(b[b > 4.0]))
The output should be as follows:
[ 136 ]
Chapter 5
What just happened?
We used the arange function to create an array with nine floating-point elements. Since we only provided a single argument, NumPy assumes that the first element is zero and the increment is one. We then used the array's reshape method to return a two-dimensional array with three rows and three columns. reshape accepts a tuple that contains the dimensions of the new array. Note that reshape returns a new array instead of modifying the original array, so we have to use the syntax a = a.reshape((3,3)) to overwrite the original array. Elements and subsets of this array were selected using the same slice notation that we used with Sage vectors and matrices.
We created a one-dimensional array to demonstrate how to select elements by value from NumPy arrays. In this case, we specified a negative step, so arange created an array with decreasing values. We used the where function to get a list of indices that met a specific condition. where returns a list of lists of indices, so we used tuple unpacking to obtain a single list. When where is used with multi-dimensional arrays, each list of indices
corresponds to one dimension of the array. The final line of the example shows a shortcut for obtaining the elements of an array that meet a certain criterion.
Haveagohero–replacinglistswithNumPyarrays
In Chapter 4, we used lists and loops to compute the analytical solution to a partial differential equation (see Time for action – computing a solution to the diffusion equation). Go back to that example and replace the lists with NumPy arrays.
Replace the list called x with a NumPy array
Use arange to replace the for loop that was used to define the x coordinates for the calculation
Use empty to create an array to replace the list called ideal_concentration
NumPymatrices
NumPy also includes a matrix class, which is distinct from the two-dimensional array that we created in the previous example.
Timeforaction–creatingmatricesinNumPy
To illustrate some of the similarities and differences between the linear algebra features of Sage and NumPy, we'll repeat an earlier example in which we computed the singular value decomposition of a matrix:
import numpy as np
print "Two ways of creating a Numpy matrix:"
[ 137 ]
Vectors, Matrices, and Linear Algebra
A = np.matrix('1 1; 1 1; 0 0') # Matlab syntax print(A)
A2 = np.matrix([[1,1], [1,1], [0,0]]) print(A2)
print("Singular value decomposition:")
U, s, Vstar = np.linalg.svd(A, full_matrices=False) print("U:")
print(U)
print("s:")
print(s)
print("Transpose of conjugate of V:") print(Vstar)
Sigma = np.diag(s) print("Reconstructed matrix Sigma:") print(Sigma)
print(np.dot(U, np.dot(Sigma, Vstar)))
The result should look like this:
[ 138 ]
