## Sage Beginner’s Guide

Unlock the full potential of Sage for simplifying and automating mathematical computing

## Confusion alert: Sage plots and matplotlib

The 2D plotting capabilities of Sage are built upon a Python plotting package called matplotlib. The most widely used features of matplotlib are accessible through Sage functions. You can also import the matplotlib package into Sage, and use all of its features directly. This is very powerful, but it’s also confusing, because there’s more than one way to do the same thing. To further add to the confusion, matplotlib has two interfaces: the command-oriented Pyplot interface and an object-oriented interface. The examples in this chapter will attempt to clarify which interface is being used.

## Plotting in two dimensions

Two-dimensional plots are probably the most important tool for visually presenting information in math, science, and engineering. Sage has a wide variety of tools for making many types of 2D plots.

## Plotting symbolic expressions with Sage

We will start by exploring the plotting functions that are built in to Sage. They are generally less flexible than using matplotlib directly, but also tend to be easier to use.

## Time for action – plotting symbolic expressions

Let’s plot some simple functions. Enter the following code:

p1 = plot(sin, (-2*pi, 2*pi), thickness=2.0, rgbcolor=(0.5, 1, 0),

legend_label=’sin(x)’)

p2 = plot(cos, (-2*pi, 2*pi), thickness=3.0, color=’purple’,

alpha=0.5, legend_label=’cos(x)’)

plt = p1 + p2

plt.axes_labels([‘x’, ‘f(x)’])

show(plt)

If you run the code from the interactive shell, the plot will open in a separate window. If you run it from the notebook interface, the plot will appear below the input cell. In either case, the result should look like this:

### What just happened?

This example demonstrated the most basic type of plotting in Sage. The *plot* function requires the following arguments:

graphics_object = plot(callable symbolic expression, (independent_var, ind_var_min, ind_var_max))

The first argument is a callable symbolic expression, and the second argument is a tuple consisting of the independent variable, the lower limit of the domain, and the upper limit. If there is no ambiguity, you do not need to specify the independent variable. Sage automatically selects the right number of points to make a nice curve in the specified domain. The *plot* function returns a graphics object. To combine two graphics objects in the same image, use the + operator: *plt = p1 + p2*. Graphics objects have additional methods for modifying the final image. In this case, we used the *axes_labels* method to label the x and y axes. Finally, the *show* function was used to finish the calculation and display the image.

The *plot* function accepts optional arguments that can be used to customize the appearance and format of the plot. To see a list of all the options and their default values, type:

sage: plot.options {'fillalpha': 0.5, 'detect_poles': False, 'plot_points': 200, 'thickness': 1, 'alpha': 1, 'adaptive_tolerance': 0.01, 'fillcolor': 'automatic', 'adaptive_recursion': 5, 'exclude': None, 'legend_label': None, 'rgbcolor': (0, 0, 1), 'fill': False}

Here is a summary of the options for customizing the appearance of a plot:

**Keyword****Description**alphaTransparency of the line (0=opaque, 1=transparent)fillTrue to fill area below the linefillalphaTransparency of the filled-in area (0=opaque, 1=transparent)fillcolorColor of the filled-in areargbcolorColor of the line

Sage uses an algorithm to determine the best number of points to use for the plot, and how to distribute them on the x axis. The algorithm uses recursion to add more points to resolve regions where the function changes rapidly. Here are the options that control how the plot is generated:

**Keyword****Description**adaptive_recursionMax depth of recursion when resolving areas of the plot where the function changes rapidlyadaptive_toleranceTolerance for stopping recursiondetect_polesDetect points where function value approaches infinity (see next example)excludeA list or tuple of points to exclude from the plotplot_pointsNumber of points to use in the plot

**Specifying colors in Sage**

There are several ways to specify a color in Sage. For basic colors, you can use a string containing the name of the color, such as red or blue. You can also use a tuple of three floating-point values between 0 and 1.0. The first value is the amount of red, the second is the amount of green, and the third is the amount of blue. For example, the tuple (0.5, 0.0, 0.5) represents a medium purple color.

Some functions “blow up” to plus or minus infinity at a certain point. A simplistic plotting algorithm will have trouble plotting these points, but Sage adapts.

## Time for action – plotting a function with a pole

Let’s try to plot a simple function that takes on infinite values within the domain of the plot:

pole_plot = plot(1 / (x – 1), (0.8, 1.2), detect_poles=’show’,

marker=’.’)

print(“min y = {0} max y = {1}”.format(pole_plot.ymax(),

pole_plot.ymin()))

pole_plot.ymax(100.0)

pole_plot.ymin(-100.0)

# Use TeX to make nicer labels

pole_plot.axes_labels([r’$x$’, r’$1/(x-1)$’])

pole_plot.show()

The output from this code is as follows:

### What just happened?

We did a few things differently compared to the previous example. We defined a callable symbolic expression right in the plot function. We also used the option *detect_poles=’show’* to plot a dashed vertical line at the x value where the function returns infinite values. The option *marker=’.’* tells Sage to use a small dot to mark the individual (x,y) values on the graph. In this case, the dots are so close together that they look like a fat line. We also used the methods *ymin* and *ymax* to get and set the minimum and maximum values of the vertical axis. When called without arguments, these methods return the current values. When given an argument, they set the minimum and maximum values of the vertical axis.

Finally, we labeled the axes with nicely typeset mathematical expressions. As in the previous example, we used the method *axes_labels* to set the labels on the x and y axes. However, we did two special things with the label strings:

r'$frac{1}{(x-1)}$'

The letter **r** is placed in front of the string, which tells Python that this is a raw string. When processing a raw string, Python does not interpret backslash characters as commands (such as interpreting **n** as a newline). Note that the first and last characters of the string are dollar signs, which tells Sage that the strings contain mark-up that needs to be processed before being displayed. The mark-up language is a subset of TeX, which is widely used for typesetting complicated mathematical expressions. Sage performs this processing with a built-in interpreter, so you don’t need to have TeX installed to take advantage of typeset labels. It’s a good idea to use raw strings to hold TeX markup because TeX uses a lot of backslashes. To learn about the typesetting language, see the matplotlib documentation at:

http://matplotlib.sourceforge.net/users/mathtext.html

## Time for action – plotting a parametric function

Some functions are defined in terms of a parameter. Sage can easily plot parametric functions:

var(‘t’)

pp = parametric_plot((cos(t), sin(t)), (t, 0, 2*pi),

fill=True, fillcolor=’blue’)

pp.show(aspect_ratio=1, figsize=(3, 3), frame=True)

The output from this code is as follows:

### What just happened?

We used two parametric functions to plot a circle. This is a convenient place to demonstrate the *fill* option, which fills in the space between the function and the horizontal axis. The *fillcolor* option tells Sage which color to use for the fill, and the color can be specified in the usual ways. We also demonstrated some useful options for the *show* method (these options also work with the *show* function). The option *aspect_ratio=1* forces the x and y axes to use the same scale. In other words, one unit on the x axis takes up the same number of pixels on the screen as one unit on the y axis. Try changing the aspect ratio to 0.5 and 2.0, and see how the circle looks. The option *figsize=(x_size,y_size)* specifies the aspect ratio and relative size of the figure. The units for the figure size are relative, and don’t correspond to an absolute unit like inches or centimetres. The option *frame=True* places a frame with tick marks around the outside of the plot.

## Time for action – making a polar plot

Some functions are more easily described in terms of angle and radius. The angle is the independent variable, and the radius at that angle is the dependent variable. Polar plots are widely used in electrical engineering to describe the radiation pattern of an antenna. Some antennas are designed to transmit (or receive) electromagnetic radiation in a very narrow beam. The beam shape is known as the radiation pattern. One way to achieve a narrow beam is to use an array of simple dipole antennas, and carefully control the phase of the signal fed to each antenna. In the following example, we will consider seven short dipole antennas set in a straight line:

# A linear broadside array of short vertical dipoles # located along the z axis with 1/2 wavelength spacing var('r, theta') N = 7 normalized_element_pattern = sin(theta) array_factor = 1 / N * sin(N * pi / 2 * cos(theta)) / sin(pi / 2 * cos(theta)) array_plot = polar_plot(abs(array_factor), (theta, 0, pi), color='red', legend_label='Array') radiation_plot = polar_plot(abs(normalized_element_pattern * array_factor), (theta, 0, pi), color='blue', legend_label='Radiation') combined_plot = array_plot + radiation_plot combined_plot.xmin(-0.25) combined_plot.xmax(0.25) combined_plot.set_legend_options(loc=(0.5, 0.3)) show(combined_plot, figsize=(2, 5), aspect_ratio=1)

Execute the code. You should get a plot like this:

### What just happened?

We plotted a polar function, and used several of the plotting features that we’ve already discussed. There are two subtle points worth mentioning. The function *array_factor* is a function of two variables, N and theta. In this example, N is more like a parameter, while *theta* is the independent variable we want to use for plotting. We use the syntax *(theta, 0, pi)* in the *plot* function to indicate that *theta* is the independent variable. The second new aspect of this example is that we used the methods *xmin* and *xmax* to set the limits of the x axis for the graphics object called *combined_plot*. We also used the *set_legend_options* of the graphics object to adjust the position of the legend to avoid covering up important details of the plot.

## Time for action – plotting a vector field

Vector fields are used to represent force fields such as electromagnetic fields, and are used to visualize the solutions of differential equations. Sage has a special plotting function to visualize vector fields.

var('x, y') a = plot_vector_field((x, y), (x, -3, 3), (y, -3, 3), color='blue') b = plot_vector_field((y, -x), (x, -3, 3), (y, -3, 3), color='red') show(a + b, aspect_ratio=1, figsize=(4, 4))

You should get the following image:

### What just happened?

The *plot_vector_field* function uses the following syntax:

plot_vector_field((x_function,y_function), (x,x_min,x_max), (y,y_ min,y_max))

The keyword argument *color* specifies the color of the vectors.

## Plotting data in Sage

So far, we’ve been making graphs of functions. We specify the function and the domain, and Sage automatically chooses the points to make a nice-looking curve. Sometimes, we need to plot discrete data points that represent experimental measurements or simulation results. The following functions are used for plotting defined sets of points.

## Time for action – making a scatter plot

Scatter plots are used in science and engineering to look for correlation between two variables. A cloud of points that is roughly circular indicates that the two variables are independent, while a more elliptical arrangement indicates that there may be a relationship between them. In the following example, the x and y coordinates are contrived to make a nice plot. In real life, the x and y coordinates would typically be read in from data files. Enter the following code:

def noisy_line(m, b, x):

return m * x + b + 0.5 * (random() – 0.5)

slope = 1.0

intercept = -0.5

x_coords = [random() for t in range(50)]
y_coords = [noisy_line(slope, intercept, x) for x in x_coords]
sp = scatter_plot(zip(x_coords, y_coords))

sp += line([(0.0, intercept), (1.0, slope+intercept)], color=’red’)

sp.show()

The result should look similar to this plot. Note that your results won’t match exactly, since the point positions are determined randomly.

### What just happened?

We created a list of randomized x coordinates using the built-in *random* function. This function returns a random number in the range 0 <= x < 1. We defined a function called *noisy_line* that we then used to create a list of randomized y coordinates with a linear relationship to the x coordinates. We now have a list of x coordinates and a list of y coordinates, but the *scatter_plot* function needs a list of *(x,y)* tuples. The *zip* function takes the two lists and combines them into a single list of tuples. The *scatter_plot* function returns a graphics object called sp. To add a line object to the plot, we use the following syntax:

sp += line([(x1, y1), (x2,y2)], color='red')

The *+=* operator is a way to increment a variable; *x+=1* is a shortcut for *x = x + 1*. Because the + operator also combines graphics objects, this syntax can be used to add a graphics object to an existing graphics object.

## Time for action – plotting a list

Sometimes, you need to plot a list of discrete data points. The following example might be found in an introductory digital signal processing (DSP) course. We will use lists to represent digital signals. We sample the analogue function cosine(t) at two different sampling rates, and plot the resulting digital signals.

# Use list_plot to visualize digital signals # Undersampling and oversampling a cosine signal sample_times_1 = srange(0, 6*pi, 4*pi/5) sample_times_2 = srange(0, 6*pi, pi/3) data1 = [cos(t) for t in sample_times_1] data2 = [cos(t) for t in sample_times_2]

plot1 = list_plot(zip(sample_times_1, data1), color=’blue’)

plot1.axes_range(0, 18, -1, 1)

plot1 += text(“Undersampled”, (9, 1.1), color=’blue’, fontsize=12)

plot2 = list_plot(zip(sample_times_2, data2), color=’red’)

plot2.axes_range(0, 18, -1, 1)

plot2 += text(“Oversampled”, (9, 1.1), color=’red’, fontsize=12)

g = graphics_array([plot1, plot2], 2, 1) # 2 rows, 1 column

g.show(gridlines=[“minor”, False])

The result is as follows:

### What just happened?

The function *list_plot* works a lot like *scatter_plot* from the previous example, so I won’t explain it again. We used the method *axes_range(x_min, x_max, y_min, y_max)* to set the limits of the x and y axes all at once. Once again, we used the *+=* operator to add a graphics object to an existing object. This time, we added a text annotation instead of a line. The basic syntax for adding text at a given (x,y) position is *text(‘a string’, (x,y))*. To see the options that text accepts, type the following:

sage: text.options {'vertical_alignment': 'center', 'fontsize': 10, 'rgbcolor': (0, 0, 1), 'horizontal_alignment': 'center', 'axis_coords': False}

To display the two plots, we introduced a new function called *graphics_array*, which uses the basic syntax:

graphics_array([plot_1, plot_2, ..., plot_n], num_rows, num_columns)

This function returns another graphics object, and we used the *show* method to display the plots. We used the keyword argument *gridlines=[“minor”, False]* to tell Sage to display vertical lines at each of the minor ticks on the x axis. The first item in the list specifies vertical grid lines, and the second specifies horizontal grid lines. The following options can be used for either element:

“major”Grid lines at major ticks”minor”Grid lines at major and minor ticksFalseNo grid lines

Try playing with these options in the previous example.