NES-MAA Sage Workshop 2011

184 days ago by kcrisman

Software Demo: Sage

Speaker: Karl-Dieter Crisman, Gordon College

Thank you to the organizers for the opportunity to bring a tool I very much enjoy using to a wider audience!

 

I usually start my talks about Sage with some easy examples of things you don't really need software to do, but which illustrate that there are nice commands.

2+2 
       
4
4
integrate(x^3,x) 
       
1/4*x^4
1/4*x^4
solve(x^2+1==0,x) 
       
[x == -I, x == I]
[x == -I, x == I]

Something I show a few things that are harder, and possibly even inspired by talks earlier in the conference.

factorial(100) 
       
933262154439441526816992388562667004907159682643816214685929638952175999\
932299156089414639761565182862536979208272237582511852109168640000000000\
00000000000000
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
integral(3*x^2/sqrt(25*x^2-3),x); show(expand(integral(3*x^2/sqrt(25*x^2-3),x))) 
       
3/50*sqrt(25*x^2 - 3)*x + 9/250*log(50*x + 10*sqrt(25*x^2 - 3))
\newcommand{\Bold}[1]{\mathbf{#1}}\frac{3}{50} \, \sqrt{25 \, x^{2} - 3} x + \frac{9}{250} \, \log\left(50 \, x + 10 \, \sqrt{25 \, x^{2} - 3}\right)
3/50*sqrt(25*x^2 - 3)*x + 9/250*log(50*x + 10*sqrt(25*x^2 - 3))
\newcommand{\Bold}[1]{\mathbf{#1}}\frac{3}{50} \, \sqrt{25 \, x^{2} - 3} x + \frac{9}{250} \, \log\left(50 \, x + 10 \, \sqrt{25 \, x^{2} - 3}\right)
G = Graph({1:[2,3],2:[3,4],3:[4]}) show(G) 
       
G.laplacian_matrix() 
       
[ 2 -1 -1  0]
[-1  3 -1 -1]
[-1 -1  3 -1]
[ 0 -1 -1  2]
[ 2 -1 -1  0]
[-1  3 -1 -1]
[-1 -1  3 -1]
[ 0 -1 -1  2]

Or, in this case, I can talk about some of the other open source friends being discussed here today.

%r library("MASS") data(Cars93) mean(Cars93$MPG.city) xtabs( ~ Origin + MPG.city, data=Cars93) 
       
[1] 22.36559
         MPG.city
Origin    15 16 17 18 19 20 21 22 23 24 25 26 28 29 30 31 32 33 39 42 46
  USA      2  3  4  5  8  3  3  4  7  2  2  0  1  2  0  2  0  0  0  0  0
  non-USA  0  0  4  7  2  5  3  3  1  3  4  2  1  4  1  0  1  1  1  1  1
[1] 22.36559
         MPG.city
Origin    15 16 17 18 19 20 21 22 23 24 25 26 28 29 30 31 32 33 39 42 46
  USA      2  3  4  5  8  3  3  4  7  2  2  0  1  2  0  2  0  0  0  0  0
  non-USA  0  0  4  7  2  5  3  3  1  3  4  2  1  4  1  0  1  1  1  1  1
%gap G:=Group([(99,100)]); IsTransitive(G); 
       
<permutation group with 1 generators>
true
<permutation group with 1 generators>
true

And sometimes I even type a few things in my notes, so that I don't forget them.  

\frac{3}{50}\sqrt{25x^2-3x}+\frac{9}{250}\log\left(50x+10\sqrt{25x^2-3}\right)
 

latex(integrate(sin(x)*cos(2*x),x)) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}\verb|-\frac{1}{6}|\phantom{x}\verb|\,|\phantom{x}\verb|\cos\left(3|\phantom{x}\verb|\,|\phantom{x}\verb|x\right)|\phantom{x}\verb|+|\phantom{x}\verb|\frac{1}{2}|\phantom{x}\verb|\,|\phantom{x}\verb|\cos\left(x\right)|
\newcommand{\Bold}[1]{\mathbf{#1}}\verb|-\frac{1}{6}|\phantom{x}\verb|\,|\phantom{x}\verb|\cos\left(3|\phantom{x}\verb|\,|\phantom{x}\verb|x\right)|\phantom{x}\verb|+|\phantom{x}\verb|\frac{1}{2}|\phantom{x}\verb|\,|\phantom{x}\verb|\cos\left(x\right)|

But today I have a slightly different goal than just showing off various fun things you can do with Sage.  

My main use of Sage over the years has not been for student homework or labs, over the years.  Instead, I enjoy using it for quick, accurate, interactive demonstration of difficult concepts.  

Newton's Method

Let me give an example of what I mean.  We've likely all taught Newton's method of root-finding at some point.  

  • Students find it technically easy to master (just memorize the formula)
  • They sometimes have difficulty visualizing it.
  • They really have difficulty discerning why a given guess works or doesn't work.

So let's take a typical example of bad behavior (cubics!) and make it interactive.

  • They can change their initial guess.
  • They can change how many iterations they use.
  • They can even change the function.
%hide #auto def Newton_Iterates(a_function, guess, iterations = 5): ''' Returns a list of the iterations of the Newton-Raphson method approximations to a zero of a_function. INPUT: a_function: a function of one variable start: the starting value of the iteration iterations: (optional) the number of iterations to draw ''' f(x)=a_function(x) deriv=f.derivative() approx(x)=x-f(x)/deriv(x) input = guess iter_list = [input] for i in range(iterations): input = approx(input).n() iter_list.append(input) return iter_list def Newton_Graph(a_function, guess, iterations = 5): ''' Returns a graphics object of a plot of a_function and Newton-Raphson method tangent lines. INPUT: a_function: a function of one variable start: the starting value of the iteration iterations: (optional) the number of iterations to draw xmin: (optional) the lower end of the plotted interval xmax: (optional) the upper end of the plotted interval EXAMPLES: sage: f = lambda x: x^3-3*x^2+2*x sage: show(Newton_Graph(f,.5)) Note: This is very slow with symbolic functions. ''' iter_list = [[guess,0]] f(x)=a_function(x) deriv=f.derivative() approx(x)=x-f(x)/deriv(x) input = guess for i in range(iterations): iter_list.append([input,f(input)]) input=approx(input).n() iter_list.append([input,0]) approx_tangents = line(iter_list,rgbcolor=(1,0,0)) xmin=min([point[0] for point in iter_list])-1 xmax=max([point[0] for point in iter_list])+1 ymin=min([point[1] for point in iter_list])-.5 ymax=max([point[1] for point in iter_list])+.5 basic_plot = plot(a_function, xmin, xmax, color='blue') P=basic_plot + approx_tangents P.show(ymin=ymin,ymax=ymax) def cubic_approx(guess, intercept=0, iterations = 5): f(x)=x^3-3*x^2+2*x+intercept return Newton_Graph(f,guess,iterations=iterations) def cubic_iterate(guess, intercept=0, iterations = 5): f(x)=x^3-3*x^2+2*x+intercept return Newton_Iterates(f,guess,iterations=iterations) @interact def _(guess=RR(.5),iterations=slider(1,20,1,5),intercept=slider(-2,2,.5,0)): root = cubic_iterate(guess,intercept=intercept,iterations=iterations)[-1] html('The starting guess is $%s$'%guess) html('The formula is $x^3-3x^2+2x+%s$'%intercept) html('We use $%s$ iterations of the method'%iterations) html("Newton's method approximates the root as $%s$"%root) cubic_approx(guess,intercept=intercept,iterations=iterations) 
       
guess 
iterations 
intercept 

Click to the left again to hide and once more to show the dynamic interactive window

Naturally, there are other packages which do this.  I'll just make the quick plug that Sage is free, powerful, and can be run off a server in your web browser.  You probably know this, or you wouldn't be here. 

(But go to www.sagemath.org if you didn't know this, and start poking around.)

 

Here are a few other things I and others have done recently that you might like.

Modular exponentiation theorems

#auto @interact def power_table_plot(p=(7,prime_range(50))): P=matrix_plot(matrix(p-1,[mod(a,p)^b for a in range(1,p) for b in srange(p)]),cmap='jet') show(P) 
       

Click to the left again to hide and once more to show the dynamic interactive window

Singular value decomposition and images

%hide #auto import pylab A_image = pylab.mean(pylab.imread(DATA + 'ConnColl.png'), 2) B_image = pylab.mean(pylab.imread(DATA + 'keene.png'), 2) @interact def svd_image(i=(5,(1..50))): u,s,v = pylab.linalg.svd(A_image) A = sum(s[j]*pylab.outer(u[0:,j],v[j,0:]) for j in range(i)) u1,s1,v1 = pylab.linalg.svd(B_image) B = sum(s1[j]*pylab.outer(u1[0:,j],v1[j,0:]) for j in range(i)) g = graphics_array([[matrix_plot(A),matrix_plot(B)],[matrix_plot(A_image),matrix_plot(B_image)]]) show(g,axes=False, figsize=(8,3)) html('<h2>Compressed using %s eigenvalues</h2>'%i) 
       

Click to the left again to hide and once more to show the dynamic interactive window

Linear transformations of "Smiley Guy"

%hide #auto t = var('t') @interact def _(A=matrix(RDF,[[1,0],[0,1]])): pll=A*vector((-0.5,0.5)) plr=A*vector((-0.3,0.5)) prl=A*vector((0.3,0.5)) prr=A*vector((0.5,0.5)) left_eye=line([pll,plr])+point(pll,size=5)+point(plr,size=5) right_eye=line([prl,prr],color='green')+point(prl,size=5,color='green')+point(prr,size=5,color='green') mouth=parametric_plot(A*vector([t, -0.15*sin(2*pi*t)-0.5]), (t, -0.5, 0),color='red')+parametric_plot(A*vector([t, -0.15*sin(2*pi*t)-0.5]), (t,0,0.5),color='orange') face=parametric_plot(A*vector([cos(t),sin(t)]), (t,0,pi/2),color='black')+parametric_plot(A*vector([cos(t),sin(t)]), (t,pi/2,pi),color='lavender')+parametric_plot(A*vector([cos(t),sin(t)]), (t,pi,3*pi/2),color='cyan')+parametric_plot(A*vector([cos(t),sin(t)]),(t,3*pi/2,2*pi),color='sienna') P=right_eye+left_eye+face+mouth html('smiley guy transformed by $A$') P.show(aspect_ratio=1) 
       

Click to the left again to hide and once more to show the dynamic interactive window

Mean Value Theorem

%hide #auto @interact def _(a=2,b=4,g=3*x^2-8*x+4): f(x) = g fprime(x) = diff(f,x) P = plot(f,(x,a,b)) mean = (f(b)-f(a))/(b-a) Q = line([(a,f(a)),(b,f(b))],color='red') try: c = find_root(fprime-mean,a,b) R = plot(fprime(c)*(x-c)+f(c),(x,a,b),color='red') S = points([(a,f(a)),(b,f(b)),(c,f(c))],pointsize=10) html('The mean is $'+latex(mean)+'$ and the value is at $c \\approx '+latex(c)+'$') show(P+Q+R+S) except RuntimeError: S = points([(a,f(a)),(b,f(b))],pointsize=10) html('The mean is $'+latex(mean)+'$ but there is no such value of the derivative') show(P+Q+S,ymax=100,ymin=-100) 
       

Click to the left again to hide and once more to show the dynamic interactive window

%hide #auto %cython from sage.rings.complex_double cimport ComplexDoubleElement cimport numpy as cnumpy from sage.plot.primitive import GraphicPrimitive from sage.misc.decorators import options from sage.misc.misc import srange from sage.symbolic.ring import var cdef inline ComplexDoubleElement new_CDF_element(double w, double v): cdef ComplexDoubleElement z = <ComplexDoubleElement>PY_NEW(ComplexDoubleElement) z._complex.dat[0] = w z._complex.dat[1] = v return z def complex_to_rgb(roots,z_values): """ INPUT: - ``z_values`` -- A grid of complex numbers, as a list of lists OUTPUT: An `N \\times M \\times 3` floating point Numpy array ``X``, where ``X[i,j]`` is an (r,g,b) tuple. EXAMPLES:: sage: from sage.plot.complex_plot import complex_to_rgb sage: complex_to_rgb([[0, 1, 1000]]) array([[[ 0. , 0. , 0. ], [ 0.77172568, 0. , 0. ], [ 1. , 0.64421177, 0.64421177]]]) sage: complex_to_rgb([[0, 1j, 1000j]]) array([[[ 0. , 0. , 0. ], [ 0.38586284, 0.77172568, 0. ], [ 0.82210588, 1. , 0.64421177]]]) """ import numpy cdef unsigned int i, j, imax, jmax, n cdef ComplexDoubleElement zz from sage.rings.complex_double import CDF from sage.plot.colors import rainbow imax = len(z_values) jmax = len(z_values[0]) cdef cnumpy.ndarray[cnumpy.float_t, ndim=3, mode='c'] rgb = numpy.empty(dtype=numpy.float, shape=(imax, jmax, 3)) n = len(roots) R = rainbow(n) R = [Color(col).rgb() for col in R] sig_on() for i from 0 <= i < imax: row = z_values[i] for j from 0 <= j < jmax: zz = row[j][0] alpha = mag_to_lightness(row[j][1]) try: color = R[roots.index(zz)] rgb[i, j, 0] = color[0]*alpha rgb[i, j, 1] = color[1]*alpha rgb[i, j, 2] = color[2]*alpha except: print zz sig_off() return rgb cdef extern from "math.h": double atan(double) double log(double) double sqrt(double) double PI cdef inline double mag_to_lightness(int r): """ Tweak this to adjust how the magnitude affects the color. For instance, changing ``sqrt(r)`` to ``r`` will cause anything near a zero to be much darker and poles to be much lighter, while ``r**(.25)`` would cause the reverse effect. INPUT: - ``r`` - a non-negative real number OUTPUT: A value between `-1` (black) and `+1` (white), inclusive. EXAMPLES: This tests it implicitly:: sage: from sage.plot.complex_plot import complex_to_rgb sage: complex_to_rgb([[0, 1, 10]]) array([[[ 0. , 0. , 0. ], [ 0.77172568, 0. , 0. ], [ 1. , 0.22134776, 0.22134776]]]) """ return .5*atan(log(r+1)) * (4/PI) def newt(func): vari = func.args()[0] fprime = diff(func,vari) return vari-func/fprime class BasinPlot(GraphicPrimitive): def __init__(self, roots, z_values, xrange, yrange, options): """ TESTS:: sage: p = complex_plot(lambda z: z^2-1, (-2, 2), (-2, 2)) """ self.roots = roots self.xrange = xrange self.yrange = yrange self.z_values = z_values self.x_count = len(z_values) self.y_count = len(z_values[0]) self.rgb_data = complex_to_rgb(roots,z_values) GraphicPrimitive.__init__(self, options) def get_minmax_data(self): """ Returns a dictionary with the bounding box data. EXAMPLES:: sage: p = complex_plot(lambda z: z, (-1, 2), (-3, 4)) sage: sorted(p.get_minmax_data().items()) [('xmax', 2.0), ('xmin', -1.0), ('ymax', 4.0), ('ymin', -3.0)] """ from sage.plot.plot import minmax_data return minmax_data(self.xrange, self.yrange, dict=True) def _allowed_options(self): """ TESTS:: sage: isinstance(complex_plot(lambda z: z, (-1,1), (-1,1))[0]._allowed_options(), dict) True """ return {'plot_points':'How many points to use for plotting precision', 'interpolation':'What interpolation method to use'} def _repr_(self): """ TESTS:: sage: isinstance(complex_plot(lambda z: z, (-1,1), (-1,1))[0]._repr_(), str) True """ return "BasinPlot defined by a %s x %s data grid"%(self.x_count, self.y_count) def _render_on_subplot(self, subplot): """ TESTS:: sage: complex_plot(lambda x: x^2, (-5, 5), (-5, 5)) """ options = self.options() x0,x1 = float(self.xrange[0]), float(self.xrange[1]) y0,y1 = float(self.yrange[0]), float(self.yrange[1]) subplot.imshow(self.rgb_data, origin='lower', extent=(x0,x1,y0,y1), interpolation=options['interpolation']) cdef class RootFinder: cdef list roots cdef object newtf cdef ComplexDoubleElement cutoff def __init__(self, roots, newtf): self.newtf = newtf self.roots = roots self.cutoff = CDF(.1) cpdef which_root(self,Varia): cdef int counter cdef ComplexDoubleElement root cdef ComplexDoubleElement varia = Varia for counter from 1<=counter<20: varia = self.newtf(varia) #print z for root in self.roots: if (<ComplexDoubleElement>varia._sub_(root)).abs()<self.cutoff: return root,counter cdef list ls = [] for root in self.roots: ls.append(varia._sub_(root).abs()) return self.roots[ls.index(min(ls))],20 @options(plot_points=3, interpolation='nearest') def basin_plot(list roots, xrange, yrange, **options): r""" ``complex_plot`` takes a complex function of one variable, `f(z)` and plots output of the function over the specified ``xrange`` and ``yrange`` as demonstrated below. The magnitude of the output is indicated by the brightness (with zero being black and infinity being white) while the argument is represented by the hue (with red being positive real, and increasing through orange, yellow, ... as the argument increases). ``complex_plot(f, (xmin, xmax), (ymin, ymax), ...)`` INPUT: - ``f`` -- a function of a single complex value `x + iy` - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values The following inputs must all be passed in as named parameters: - ``plot_points`` -- integer (default: 100); number of points to plot in each direction of the grid - ``interpolation`` -- string (default: ``'catrom'``), the interpolation method to use: ``'bilinear'``, ``'bicubic'``, ``'spline16'``, ``'spline36'``, ``'quadric'``, ``'gaussian'``, ``'sinc'``, ``'bessel'``, ``'mitchell'``, ``'lanczos'``, ``'catrom'``, ``'hermite'``, ``'hanning'``, ``'hamming'``, ``'kaiser'`` """ from sage.plot.plot import Graphics from sage.plot.misc import setup_for_eval_on_grid from sage.ext.fast_callable import fast_callable from sage.rings.complex_double import CDF roots = [CDF(temp) for temp in roots] x = var('x') prefunc = prod([(x-root) for root in roots]) f = fast_callable(prefunc, domain=CDF, expect_one_var=True) newtf = fast_callable(newt(prefunc), domain=CDF, expect_one_var=True) cdef ComplexDoubleElement cutoff = CDF(.1) cdef RootFinder Finder = RootFinder(roots,newtf) f1 = Finder.which_root cdef double t, u ignore, ranges = setup_for_eval_on_grid([], [xrange, yrange], options['plot_points']) xrange,yrange=[r[:2] for r in ranges] sig_on() z_values = [[ f1(new_CDF_element(t, u)) for t in srange(*ranges[0], include_endpoint=True)] for u in srange(*ranges[1], include_endpoint=True)] # print z_values sig_off() g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax'])) g.add_primitive(BasinPlot(roots, z_values, xrange, yrange, options)) return g 

Newton basins

%hide #auto @interact def _(roots='-1,0,1',xmax=2,xmin=-2,ymax=2,ymin=-2,number_points=10): roots = eval('['+roots+']') show(basin_plot([-1,0,1],(xmin,xmax),(ymin,ymax),plot_points=number_points,figsize=10,aspect_ratio='automatic')) 
       
roots 
xmax 
xmin 
ymax 
ymin 
number_points 

Click to the left again to hide and once more to show the dynamic interactive window

It turns out that creating such things for your own students - interactive things you can use in class, a lab, or elsewhere - does not require great amounts of programming talent or other expertise.  

For the rest of the time, I'd like to create one of these "interacts" in a step-by-step fashion for you.  If I'm brave, I'll even do it with your ideas for what you want to show interactively.

 
       
 
       

The following is based on materials developed for the MAA PREP Workshop "Sage: Using Open-Source Mathematics Software with Undergraduates" (funding provided by NSF DUE 0817071).  You can sign up for it again this summer!

Invaluable resources are the Sage wiki http://wiki.sagemath.org/interact (type "sage interact" into Google) and the interact documentation.

 
       

Start by getting the commands for what you want the output to look like.  Here we just want a simple plot.

plot(x^2,(x,-3,3)) 
       

Then abstract out the parts you want to change.  We'll be letting the user change the function, so let's make that a variable f.

f=x^3 plot(f,(x,-3,3)) 
       

Now make this a "def" function.  The "show" or "print" is needed since the output is not automatically printed from within a function.  Note also that we give the variable a default value of x^2.  This is what f is if the user does not specify a value for f.

def myplot(f=x^2): show(plot(f,(x,-3,3))) 
       

Let's test the def function myplot by just calling it.

myplot() 
       

If we call it with a different value for f, we should get a different plot.

myplot(x^3) 
       

Now to make a control to enter the function, we just preface the function with @interact.

@interact def myplot(f=x^2): show(plot(f,(x,-3,3))) 
       

Click to the left again to hide and once more to show the dynamic interactive window

Tech tip: Technically what the @interact does is wrap the function, so the above is equivalent to:

def myplot(..): ...

myplot=interact(myplot)

Note that we can still call our function, even when we've used @interact.  This is often useful in debugging it.

myplot(x^4) 
       

We can go ahead and replace other parts of the expression with variables.  Note the "_" is the function name now.  That is a convention for throw-away names that we don't care about.

@interact def _(f=x^2,a=-3,b=3): show(plot(f,(x,a,b))) 
       

Click to the left again to hide and once more to show the dynamic interactive window

 
       

If we pass ('label', default_value) in for a control, then the control gets the label when printed.

@interact def _(f=('$f$',x^2),a=('lower',-3),b=('upper',3)): show(plot(f,(x,a,b))) 
       

Click to the left again to hide and once more to show the dynamic interactive window

We can specify the type of control explicitly, along with options.

@interact def _(f=input_box(x^2,width=20, label="$f$")): show(plot(f,(x,-3,3))) 
       

Click to the left again to hide and once more to show the dynamic interactive window

Here's another type of control: a color picker.

@interact def _(f=input_box(x^2,width=20), color=color_selector()): show(plot(f,(x,-3,3), color=color)) 
       

Click to the left again to hide and once more to show the dynamic interactive window

 
       
 
       

Here are a bunch of options.  Notice the new controls:

  • range slider, which passes in two values, zoom[0] and zoom[1]
  • True/False get converted to checkboxes
@interact def _(f=input_box(x^2,width=20), color=color_selector(widget='colorpicker', label=""), axes=True, fill=True, zoom=range_slider(-3,3,default=(-3,3))): show(plot(f,(x,zoom[0], zoom[1]), color=color, axes=axes,fill=fill)) 
       

Click to the left again to hide and once more to show the dynamic interactive window

That was a bit ugly because all of the controls were stacked on top of each other.  We can layout the controls in a grid in the top, bottom, left, or right using the 'layout' parameter.

@interact(layout=dict(top=[['f', 'color']], left=[['axes'],['fill']], bottom=[['zoom']])) def _(f=input_box(x^2,width=20), color=color_selector(widget='colorpicker', label=""), axes=True, fill=True, zoom=range_slider(-3,3, default=(-3,3))): show(plot(f,(x,zoom[0], zoom[1]), color=color, axes=axes,fill=fill)) 
       

Click to the left again to hide and once more to show the dynamic interactive window

Control Types

Sage has:

  • boxes
  • sliders
  • range sliders
  • checkboxes
  • selectors (dropdown lists or buttons)
  • grid of boxes
  • color selectors
  • plain text

We illustrate some of these.

@interact def _(frame=checkbox(True, label='Use frame')): show(plot(sin(x), (x,-5,5)), frame=frame) 
       

Click to the left again to hide and once more to show the dynamic interactive window

var('x,y') colormaps=sage.plot.colors.colormaps.keys() @interact def _(cmap=selector(colormaps)): contour_plot(x^2-y^2,(x,-2,2),(y,-2,2),cmap=cmap).show() 
       

Click to the left again to hide and once more to show the dynamic interactive window

var('x,y') colormaps=sage.plot.colors.colormaps.keys() @interact def _(cmap=selector(['RdBu', 'jet', 'gray','gray_r'],buttons=True), type=['density','contour']): if type=='contour': contour_plot(x^2-y^2,(x,-2,2),(y,-2,2),cmap=cmap, aspect_ratio=1).show() else: density_plot(x^2-y^2,(x,-2,2),(y,-2,2),cmap=cmap, frame=True,axes=False,aspect_ratio=1).show() 
       

Click to the left again to hide and once more to show the dynamic interactive window

 
       

By default, ranges are sliders that divide the range into 500 steps (I think that's the right number...)

@interact def _(n=(1,20)): print factorial(n) 
       

Click to the left again to hide and once more to show the dynamic interactive window

You can set the step size to get, for example, just integer values.

@interact def _(n=slider(1,20,step_size=1)): print factorial(n) 
       

Click to the left again to hide and once more to show the dynamic interactive window

Or you can explicitly specify the slider values.

@interact def _(n=slider([1..20])): print factorial(n) 
       

Click to the left again to hide and once more to show the dynamic interactive window

And the slider values don't even have to be numbers!

@interact def _(fun=('function', slider([sin,cos,tan,sec,csc,cot]))): print fun(4.39293) 
       

Click to the left again to hide and once more to show the dynamic interactive window

 
       

Matrices are automatically converted to a grid of input boxes.

@interact def _(m=('matrix', identity_matrix(2))): print m.eigenvalues() 
       

Click to the left again to hide and once more to show the dynamic interactive window

Here's how to get vectors from a grid of boxes.

@interact def _(v=('vector', input_grid(1, 3, default=[[1,2,3]], to_value=lambda x: vector(flatten(x))))): print v.norm() 
       

Click to the left again to hide and once more to show the dynamic interactive window

Sometimes we don't want any updates until we specifically say so.  We can use the auto_update=False option for that.

@interact def _(m=('matrix', identity_matrix(2)), auto_update=False): print m.eigenvalues() 
       

Click to the left again to hide and once more to show the dynamic interactive window

 
       

I hope this was fun!  The take-home message?

  • It doesn't take programming to make interaction!
  • It might take a little to make things look really cool.
  • You can illustrate hard static concepts with such interaction.

 

Remember, this talk is available at http://www.sagenb.org/home/pub/3699

Of course, there are many other great aspects of Sage I haven't mentioned.  

  • Tab-Completion for instant, contextual help
  • It's Open Source - so fast bug fixes or enhancements happen as a matter of course - and you can contribute!
  • It's a combination of research-level quality new code and well-regarded programs such as Maxima, GAP, PARI, R, ... so a gigantic range of functionality comes "out of the box"
  • It's integrated - you can use the Python language, LaTeX, proprietary programs, command-line interface...

But the best thing to do is to try it out or download it! Thank you, and have a great day.

http://www.sagemath.org - the Sage website

http://demo.sagenb.org - try out Sage online!