Tweaking Mathematica for a Calculus Laboratory

Mathematica in Education
Vol.2 No.4
(October 1993)

by
John Costango and Barry Tesman
Department of Mathematics and Computer Science
Dickinson College

As part of an ongoing project of calculus reform at Dickinson College, we have developed introductory calculus courses using a microcomputer laboratory.  Most recently, our work has centered on developing MathematicaÐbased labs for the second semester of calculus.
     Although Mathematica's capabilites as a research tool and a programming environment surpass most other computer algebra systems currently available, its usefulness as an educational tool often falls short.  In practice in an introductory classroom setting, Mathematica users are not yet initiates with the language of calculus or the language of Mathematica, but rather find both new, confusing, and sometimes alienating.  Essentially we tried to make Mathematica output as clear as possible so that a beginning calculus student could make the most of the results rather than be impeded or confused by them.
     In this article, we will show how we worked with Mathematica's shortcomings as introductory laboratory software in three areas:  factorization of quadratic polynomials, polar plotting, and plotting of discontinuous functions.  In no case do we claim to be fixing "bugs" in Mathematica, nor do we believe that Mathematica should have been necessarily designed differently.  Instead we believe that in our labs Mathematica must speak first and foremost to the students of calculus.

Factor

Mathematica's Factor command will factor many polynomials.  For example:

Factor[x^2-x-6]

Factor[-21*2^(1/2) - 21*x - 2^(5/2)*x - 
		4*x^2 + 2^(1/2)*x^2 + x^3]

However, Mathematica will not factor other similar polynomials.

Factor[x^2-6]

The intricacies of Mathematica's internal algorithms used for Factor are unknown to us, but it seems obvious that Mathematica is only set up to factor over the integers.  A  student may be confused and/or annoyed at Mathematica's seeming inability to factor polynomials that they themselves could factor with paper and pencil.
    With all due respect to the consistency of the system, however, we felt that Factor would serve our purposes better if it factored quadratics completely over the real numbers.  We first noted this importance while developing a set of commands which 
"walk" students through the integration technique of partial fraction decomposition.  The following code selectively alters (by applying specific conditions) Mathematica's internal algorithms to give us the desired result:

RealFactorableQuadraticQ::usage=
	"RealFactorableQuadraticQ[poly] returns True if the 
	polynomial is a qradratic factorable over the reals 
	and False otherwise.";

RealFactorableQuadraticQ[expr_]:=
	Module[{},
		If[PolynomialQ[expr,Variables[expr][[1]]] &&
		   Exponent[expr,Variables[expr][[1]]]==2 &&
		   Coefficient[expr,Variables[expr][[1]]]^2-
		    4*Coefficient[expr,Variables[expr][[1]],2]*
		    Coefficient[expr,Variables[expr][[1]],0]>=0,
		   Return[True],
		   Return[False],
		   Return[False]]
		  ]

Unprotect[Factor];

Factor[expr_?RealFactorableQuadraticQ]:=
 	(Variables[expr][[1]]-
		Together[(-Coefficient[expr,Variables[expr][[1]],1]+
		  Sqrt[Coefficient[expr,Variables[expr][[1]],1]^2-
		  4*Coefficient[expr,Variables[expr][[1]],2]*
		  Coefficient[expr,Variables[expr][[1]],0]]/
		  2*Coefficient[expr,Variables[expr][[1]],2])])*
	(Variables[expr][[1]]+
		Together[(-Coefficient[expr,Variables[expr][[1]],1]+
		  Sqrt[Coefficient[expr,Variables[expr][[1]],1]^2-
		  4*Coefficient[expr,Variables[expr][[1]],2]*
		  Coefficient[expr,Variables[expr][[1]],0]]/
		  2*Coefficient[expr,Variables[expr][[1]],2])])

Protect[Factor];

By including this code, Mathematica will now factor all quadratic polynomials over the real numbers.  For example, we return to the last example from above.

Factor[x^2-6]

PolarPlot

The Graphics`Graphics package contains Mathematica code which suppots several different types of plots:  LogPlot, LogLogPlot, PolarPlot, etc.  The code for these  commands is rather simple, relying primarily on Mathematica's Plot and ParametricPlot functions.  Here is Mathematica's code for PolarPlot:

??PolarPlot

Mathematica's code allows the user to plot a polar function r = f[q], but it doesn't really provide a polar plot of r per se.  Rather it superimposes r = f[q]'s polar representation onto a Cartesian grid.  Consequently, the Cartesian picture of r = f[q] eclipses the entire notion of a radius function dependent upon rotation about a pole.  We tell our students not to think of polar functions as {x,y} coordinate pairs, but instead to visualize the sweep of q and the reach of the corresponding r.  But how can we think to illustrate the fundamental differences between a Cartesian and polar plot when the PolarPlot command does nothing but convert a polar function to a parametric representation and proceed to plot the parametric equations on a Cartesian grid?
     Mathematica's succint code aids only those users for whom the general shape and visual appearance of the function is more important than the {r,q} relationship.
     Even though it has its disadvantages (for example, it is slower), our PolarPlot code results in generally superior graphics over Mathematica's original version.  Notably, PolarPlot shows polar functions on a polar grid with radial lines at 0, Pi/4, Pi/2, 3Pi/4, 5Pi/4, and 3Pi/2.  Furthermore, the AspectRatio is permanently fixed at 1 (not 1/GoldenRatio, the default for Mathematica's PolarPlot) and thus does not distort curvature.
     Below are two graphs of the same polar function r = 3*Cos[2*Sin[q]] over the interval 0 ² q ² 2Pi.

CosTesFig.1.eps

<< /Users/wellin/MathInEd/InitStuff.m

Needs["Graphics`Graphics`"]

PolarPlot[3Cos[2Sin[q]],{q,0,2Pi}];

CosTesFig.2.eps

<< /Users/wellin/MathInEd/Vol.2_No.4/L.Hall/CostangoTesman/DCPolarPlot.m

DCPolarPlot[3Cos[2Sin[q]],{q,0,2Pi}];

The DCPolarPlot output clearly shows that we graphed on a polar grid; the aspect ratio and the plot range do not distort the shape of the graph; and the graph reinforces the dependence of r on q and the importance of the pole.
     There are disadvantages with our code.  Because the plot area must be square, our PolarPlot will sometimes return graphs with large areas of blank space while Mathematica's PolarPlot will usually restrict the graph area to the bare minimum (thus allowing for more detail).  The only time this becomes a real factor, however, is when students need to plot several functions with radically different radii (e.g., r = 3*Cos[q] and r = 125 will look just like the graph of r = 125 because the r = 3Cos[q] will be too small to appear) or functions which are not centered around the pole.
     Although we think that such cases will rarely arise in our classes, the disadvantages can often result in useless graphs.  To "correct" these problems we added an option called PolarGrid to the PolarPlot Option list.  PolarGrid defaults to True, but when set to False, PolarPlot will return Mathematica's Cartesian version of the plot.  Occasionally PolarGrid->False will result in the superior graph.
     Our PolarPlot code for plotting a single function, r = f[q], follows.  We have similar code for plotting a list of polar functions.

DCPolarPlot (Dickinson College PolarPlot) code.

BeginPackage["DCPolarPlot`"];

DCPolarPlot::usage="DCPolarPlot[f[q],{q,a,b}] gives a polar plot 
	of r=f[q] over [a,b] where q is in radian measure.";
PolarGrid::usage="PolarGrid is an option for DCPolarPlot.  
	PolarGrid->True shows the function on a polar grid.  
	PolarGrid->False shows the function on a Cartesian grid.";

Options[DCPolarPlot]={PolarGrid->True,PlotStyle->RGBColor[0,0,0]};

Begin["`Private`"];

MakeLines[range_List]:=
	Module[{pd,pr,lines},
	pd=range[[1]];pr=range[[2]];
	lines={};
	If[Max[pd]>0&&Max[pr]>0,
		lines=Union[lines,
			{Graphics[{RGBColor[0.559, 0.554, 0.434],
	Line[{{0,0},{Max[Max[pd],Max[pr]],Max[Max[pd],Max[pr]]}}]}]
			}]
	];
	If[Max[pd]>0&&Max[pr]>0,
		lines=Union[lines,
			{Graphics[{RGBColor[0.559, 0.554, 0.434],
				Line[{{0,0},{Max[Max[pd],Abs[Min[pr]]],
				-Max[Max[pd],Abs[Min[pr]]]}}]}]
			}]
	];
	If[Min[pd]<0&&Min[pr]<0,
		lines=Union[lines,
			{Graphics[{RGBColor[0.559, 0.554, 0.434],
				Line[{{0,0},{-Max[Abs[Min[pd]],Abs[Min[pr]]],
				-Max[Abs[Min[pd]],Abs[Min[pr]]]}}]}]
			}]
	];	
	If[Min[pd]<0&&Max[pr]>0,
		lines=Union[lines,
			{Graphics[{RGBColor[0.559, 0.554, 0.434],
				Line[{{0,0},{-Max[Abs[Min[pd]],Max[pr]],
				Max[Abs[Min[pd]],Max[pr]]}}]}]
			}]
	];
	lines=Union[lines,
		{Graphics[{RGBColor[0.559, 0.554, 0.434],
				Line[{{0,Min[pr]},{0,Max[pr]}}],
		 		Line[{{Min[pd],0},{Max[pd],0}}]}]
			}];
	Return[lines]
]

SortTicks[l_List]:=
	Module[{ticks,i},
		ticks={};
		fullticks={};
		For[i=1,i<=Length[l[[1]]],i++,
			If[l[[1,i,2]]>=0 || l[[1,i,2]]<0,
				ticks=Union[ticks,{l[[1,i,1]]}]
			]
		];
		ticks=Union[Map[Abs,ticks],Map[Abs,ticks]];
		Return[ticks]
	]

NotHeadListQ[k_List]:=False;
NotHeadListQ[k_]:=True;

DCPolarPlot[r_?NotHeadListQ,{x_Symbol,a_,b_},opts___Rule]:=
	Module[{pstyle,p,square,ticks,lines,circles},
		pstyle=PlotStyle/.{opts}/.Options[PolarPlot];
		p=ParametricPlot[{r Cos[x],r Sin[x]},{x,a,b},
			Axes->{False,False},
			Ticks->{None,None},
			Frame->True,
			FrameTicks->{Automatic,Automatic},
			PlotStyle->pstyle,
			DisplayFunction->Identity];
		square=Flatten[PlotRange[p]];
		square={{Min[square],Max[square]},
			{Min[square],Max[square]}};
		p=Show[p,PlotRange->square];
		ticks=SortTicks[FullOptions[p,FrameTicks]];
		If[Flatten[ticks]=={},
				ticks={Min[square],Max[square],
					(Min[square]+Max[square])/2}
		];
	    lines=MakeLines[square];
	    circles={};
		For[i=1,i<=Length[ticks],i++,
			{circles=Union[circles,
			 	{Graphics[{RGBColor[0.559, 0.554, 0.434],
			 	 Circle[{0,0},ticks[[i]]]}],
			 	 Graphics[{RGBColor[0.046, 0.038, 0.025],
			 	 Text[N[ticks[[i]],3],{ticks[[i]]/Sqrt[2],
			 	 	ticks[[i]]/Sqrt[2]}],
			 	 Text[N[ticks[[i]],3],{ticks[[i]]/Sqrt[2],
			 	 	-ticks[[i]]/Sqrt[2]}],
				 Text[N[ticks[[i]],3],{-ticks[[i]]/Sqrt[2],
				 	ticks[[i]]/Sqrt[2]}],
				 Text[N[ticks[[i]],3],{-ticks[[i]]/Sqrt[2],
				 	-ticks[[i]]/Sqrt[2]}],
				 Text[N[ticks[[i]],3],{ticks[[i]],0}],
			 	 Text[N[ticks[[i]],3],{-ticks[[i]],0}],
			 	 Text[N[ticks[[i]],3],{0,ticks[[i]]}],
			 	 Text[N[ticks[[i]],3],{0,-ticks[[i]]}]}]
				}]}
		];
		Show[lines,circles,p,DisplayFunction->$DisplayFunction,
			AspectRatio->1,Axes->{False,False},Frame->True,
			Ticks->{None,None},FrameTicks->{None,None},
			PlotRange->square]
	]/;(PolarGrid/.{opts}/.Options[PolarPlot])

DCPolarPlot[r_,{x_,a_,b_},opts___]:=
	Module[{newopts},
	newopts=Drop[{opts},{Position[{opts},PolarGrid][[1,1]],
		Position[{opts},PolarGrid][[1,1]]}];
	If[newopts=={},
		Return[ParametricPlot[{r Cos[x],r Sin[x]},{x,a,b}]],
		Return[ParametricPlot[{r Cos[x],r Sin[x]},{x,a,b},
			Evaluate[newopts]]]
	]
	]/;(!PolarGrid/.{opts}/.Options[PolarPlot])

End[];

EndPackage[];

Plot

Our most detailed revision of Mathematica's code concerned Mathematica's Plot command.  We found that Mathematica cannot plot discontinuous functions very well.  In fact, Mathematica would produce graphs of a quality which we would not accept from our students.  We wanted to remedy this descrepancy.  Consider the following discontinuous function:

CosTesFig.3.eps

f[x_]:=Which[x<2, x, x>2, x+2]

Plot[f[x], {x, -5, 5}];

From this graph it is impossible to determine the behavior of the function near the point x = 2.  The results clearly do not aid the student with concepts such as continuity or differentiability, for which graphs of discontinuous functions should be quite useful.  Our attempt to create a plot command which would produce more accurate results turned into a very large project which still needs some fine-tuning.  Here is our version of the plot of the above function:

CosTesFig.4.eps

plot[{{x,x<2},{Undefined,x==2},{x+2,x>2}},{x,-5,5}]

Certainly, this output shows in much clearer detail how our function behaves around the point of discontinuity.  The arrowheads indicate that the function continues on to the left and the right in a similar fashion, and the presence of the two open circles at x=2 indicate that the function remains undefined at that point.
     We also decided to improve Mathematica's ability to plot functions whose graphs contain asymptopes.  Consider the Mathematica output for the following function:

CosTesFig.5.eps

g[x_] := Which[x<2, (x^2-2)/(x-2), x>2, Cos[x]/(x-2)];

Plot[g[x], {x,0,2Pi}, PlotRange -> {-10,1}];

CosTesFig.6.eps

plot[{{(x^2-2)/(x-2), x<2}, {Undefined, x==2},
	{Cos[x]/(x-2), x>2}}, {x,0,2Pi}, plotRange -> {-10,1}]

The asymptotes (on a color monitor they appear in red) clearly illustrate the behavior of the function near certain points in the domain and as x approaches ±°.

Conclusion

The work we did with Mathematica's code detracts from its generality.  Mathematica's designers wanted Mathematica to be able to give general responses to a general problem, and it was designed accordingly.  We found that this generality could be confusing to the student of calculus who didn't understand the logic behind the system or who was unable to interpret the results.  It seems that Mathematica should be able to factor quadratic polynomials, and for our purposes we felt that it should.  Mathematica's version of PolarPlot follows the same rules as Plot does:  the command is designed to show you as much of the graph as possible, with the least amount of "blank space."  Our version may have "blank space" at times, but we feel that students will appreciate the polar grid and understand more completely the r, q dependency.  Finally, our reworkings of the Plot command give our students a more complete picture of function behavior.
     If a computer algebra system is to be used as an education tool, rather than a research tool, educators must expect to adapt the environment for the benefit of new users who at first understand neither the system nor the mathematics involved.  Mathematica's interest in generality (which was in many cases the "problem" for our students) was also the strength that allowed us to reorganize the system to suit our needs and the needs of our students.

About the Authors

John Costango is a senior at Dickinson College.  He is double majoring in Mathematics and English.  The Knight Foundation and Dickinson College provided funding for his work on this project.  Barry Tesman is an Assistant Professor in the department of Mathematics and Computer Science at Dickinson College.  He, along with Jack Stodghill and Lee Baric, were supervisors for John's work.

John Costango and Barry Tesman
Department of Mathematics and Computer Science
Dickinson College
Carlisle, PA 17013