Difference between revisions of "Introduction to MathObjects"

From WeBWorK_wiki
Jump to navigation Jump to search
(Expand and update MathObject Methods and Properties)
(Added some MathObject Methods)
Line 506: Line 506:
   
 
: For a Formula, it is a little more complicated. If the Formula is an explicit Point, Vector, Matrix, Interval, etc. (e.g., <code>Formula("<x,x+1>")</code>), then the value is an array of Formulas that are the coordinates. If the Formula is an expression (e.g., <code>Formula("<x,1> + <2x,-x>")</code> or <code>Formula("norm(<x,x+1>)"</code>) then the value is just the original Formula.
 
: For a Formula, it is a little more complicated. If the Formula is an explicit Point, Vector, Matrix, Interval, etc. (e.g., <code>Formula("<x,x+1>")</code>), then the value is an array of Formulas that are the coordinates. If the Formula is an expression (e.g., <code>Formula("<x,1> + <2x,-x>")</code> or <code>Formula("norm(<x,x+1>)"</code>) then the value is just the original Formula.
  +
  +
* <code>getFlag("flag-name")</code> or <code>getFlag("flag-name",default)</code>
  +
: Returns the object's value for a given context flag. The value can come from a variety of locations, which are searched in the following order (the first that has a property with the given flag name will have that propery's value returned): the object itself, the Formula that created it (if it was the result of an <code>eval()</code> call, for example), the AnswerHash associated with the object (if it was the source for an answer checker; this gives access to the flags passed to the <code>cmp</code> method), the Context in which the object was created, the currently active Context, or the default value (if given as the second argument), otherwise the return value is <code>undef</code>. This is useful in custom answer checkers for finding out the settings of things like the tolerances, the flags passed to the answer checker, and so on.
  +
  +
* <code>with(name => value)</code>
  +
: This copies the object, and sets its property with the given name to the given value. You can supply multiple name/value pairs separated by commas. This gives you the ability to initialize the object when you create it, or to make a copy with specific settings. E.g.
  +
  +
$f = Formula("sqrt(x-10)")->with(limits => [10,12]);
  +
$a = Real(pi/2)->with(period => 2*pi);
  +
  +
* typeMatch
   
 
* <code>class</code> and <code>type</code>
 
* <code>class</code> and <code>type</code>
Line 517: Line 528:
 
Formula("3x+1")->class; # produces "Formula"
 
Formula("3x+1")->class; # produces "Formula"
 
Formula("3x+1")->type; # produces "Number"
 
Formula("3x+1")->type; # produces "Number"
 
* <code>getFlag("flag name")</code>: Returns the value of one of the object's internal flags. For example: <code>$a->getFlag("tolerance");</code> (see [[ContextFlags]] for a partial list).
 
* with
 
   
 
* <code>TeX</code>: Returns a string which represents the object as a TeX math expression.
 
* <code>TeX</code>: Returns a string which represents the object as a TeX math expression.

Revision as of 20:48, 30 July 2012

What are MathObjects?

MathObjects are a set of formal objects that make the manipulation of mathematics within WeBWorK problems more intuitive. They make it possible to define variables as common mathematical objects, such as formulas, real number, complex numbers, intervals, vectors, points, and so on. For example:

   $f = Formula("sin(x^2+6)");
   $a = Real("sqrt(pi/6)");
   $z = Complex("1 + 5i");

These are useful (and powerful) because MathObjects "know" information about themselves; thus, we can add formulas to get new formulas, plug real objects into formulas to get formulas evaluated at those values, calculate derivatives of formulas, add and subtract intervals to form unions and intersections, and much more.

For several reasons it is usually preferable to write the MathObjects above using the Compute() function rather than the individual constructors for the various MathObject types.

   $f = Compute("sin(x^2+6)");
   $a = Compute("sqrt(pi/6)");
   $z = Compute("1 + 5i");

The Compute() function determines the kind of MathObject from the Context and from the syntax of its argument, which is usually a string value that is in the form that a student could type. The Compute function also sets the "correct answer" to be the exact string that it was given, so that if a student were asked to enter a number that matched $a from above and asked to see the correct answer (after the due date), then sqrt(pi/6) would be displayed rather than 0.723601. This gives you more control over the format of correct answers that are shown to students.


Why use MathObjects?

MathObjects are designed to be used in two ways. First, you can use them within your Perl code when writing problems as a means of making it easier to handle formulas and other mathematical items, and in particular to be able to produce numeric values, TeX output, and answer strings from a single formula object. This avoids having to type a function three different ways (as a Perl function, as a TeX string, and as an answer string), making it much easier to maintain a problem, or duplicate and modify it to use a different formula. Since MathObjects also included vectors, points, matrices, intervals, complex numbers, and a variety of other object types, it is easier to work with these kinds of values as well.

More importantly, using MathObjects improves the processing of student input. This is accomplished through special answer checkers that are part of the MathObjects' Parser package (rather than the traditional WeBWorK answer checkers). Each of these checkers has error checking customized to the type of input expected from the student and can provide helpful feedback if the syntax of the student's entry is incorrect. Because the MathObject checkers are part of a unified library, students get consistent error messages regardless of the type of answer that is expected or that they provide. For example, if a student enters a formula in an answer blank where the correct answer is actually a number, she will receive a message indicating that what she typed was a formula but that a number was expected. Thus students are given guidance automatically about this type of semantic problem with their answers. Answer checkers for points and vectors can indicate that the number of coordinates are wrong, or can tell the student which coordinates are incorrect (at the problem-author's discretion).

Answer checkers are available for each of the types of values that are part of the MathObjects library (numbers, complex numbers, infinities, points, vectors, intervals, sets, unions, formulas, lists of numbers, lists of points, lists of intervals, lists of formulas returning numbers, lists of formulas returning points, and so on). They are typically invoked using the notation $mathObject->cmp(). The pg/macros directory includes a number of extensions to the MathObjects library. Macro files that begin with parser typically define new classes of objects; e.g., parserParametricLine.pl defines a special class that handles parametric lines in arbitrary dimensions. These are documented in the Specialized parsers page.


MathObject Contexts

Although a problem may include several answer blanks, the problem generally has a collection of variables that it defines, values that it uses, and so on; these form the "context" of the problem. For example, if the problem is concerned with a function of x and y and its partial derivatives, then the context of the problem includes variable x and y, and so all the answer blanks within the problem should recognize that x and y have meaning within the problem, even if the answer blank is only asking for a number. Suppose a student is asked to enter the value of a partial derivative at a particular point and, rather than giving a number, enters the formula for the derivative (not evaluated at the point). He should not be told "x is undefined", as would have been the case with WeBWorK's traditional answer checkers, since x actually is defined as part of the problem. Such messages serve to confuse the student rather than help him resolve the problem.

MathObjects solves this issue by using a Context object to maintain the context of a problem. That way, all the answer blanks will know about all the variables and values defined within the problem, and can issue appropriate warning messages when a student uses them inappropriately. For example, in the situation described above where the student entered the unevaluated derivative where the value at a point was requested, he will get the message "Your answer is not a number (it seems to be a formula returning a number)", which should help him figure out what he has done wrong.

Thus one of the main purposes for the Context is to maintain the set of variables and values that are part of the problem as a whole. Another key function of the Context is to tell the answer checkers what functions and operations are available within a student answer, and what the various symbols the student can type will mean. For example, if you are asking a student to compute the value of 6 / 3, then you may want to restrict what the student can type so that she can't enter / in her answer, and must type an actual number (not an expression that is evaluated to a number). Or if you are asking a student to determine the value of sin(pi/6), you might want to restrict his answer so that he can't include sin(), but you do allow arithmetic operations. Such restrictions are also part of the context of the problem, and so are maintained by MathObjects as part of the Context.

In a similar way, some symbols mean different things in different problems. For example in a problem dealing with intervals, (4,5) means the interval between 4 and 5, while in a multi-variable calculus setting, it might mean a single point in the xy-plane. Or in a problem on complex numbers, the value i means the square root of -1, while in vector calculus, it would mean the coordinate unit vector along the x-axis. Or in a problem on inequalities, 5 < x would use < as the "less-than" sign, while in a vector calculus problem, <5,6,7> would mean the vector with coordinates 5, 6, and 7. The Context determines how these symbols will be interpreted by the MathObjects library.

MathObjects comes with a collection of predefined Contexts that you can call on to set the meanings of symbols like these to be what you need for your problems. The default Context is

   Context("Numeric");

and it will be sufficient to use the default context for most first-semester calculus problems. There are Contexts for complex numbers, inequalities, vectors, and so forth; see the list of basic contexts, and the links at the bottom of this document, for further information. The pg/macros directory includes a number of specialized Contexts as well; the files beginning with context define these, and are described on the Specialized contexts page. See the Introduction to contexts for information about how to modify an existing context (e.g., to add variables to it, or to restrict the functions that can be used).

It is possible to use more than one context within the same problem. This is discussed (need a link for this).


How to create a MathObject

In order to use MathObjects in a problem you are writing, include MathObjects.pl in your loadMacros() call. For example:

   loadMacros(
     "PGstandard.pl",
     "MathObjects.pl",
     "PGcourse.pl",
   );

Once this is done, there are several ways to create MathObjects in your problem:

  • By calling a constructor function for the type of object you want (e.g., Real(3.5) or Complex("3+4i"))
  • By calling Compute() to parse a string and return the resulting object (e.g., Compute("3+4i"))
  • By calling a method of an existing MathObject that returns another object (e.g., Formula("sin(x)")->eval(x => pi/2))
  • By combining existing MathObjects via mathematical operations (e.g., $x = Formula("x"); $f = $x**2 + 2*$x + 1)

Here are some examples:

   $a = Real(3.5);   or
   $a = Compute(3.5);   or
   $a = Compute("3.5");
   $b = Complex(3, 4);   or
   $b = Complex("3 + 4i");   or 
   $b = Compute("3 + 4i");
   $v = Vector(4,5,8);   or
   $v = Vector([4,5,8]);   or
   $v = Vector("<4,5,8>");  or
   $v = Compute("<4,5,8>");
   $f = Formula("sin(x^2) + 6");  or
   $f = Compute("sin(x^2) + 6");

Here, $a (defined any of the three ways) represents a real number and $b (defined by any of the three) represents a complex number; $v will be a vector, and $f is a formula.

In general, the Compute() variant is preferred because you enter what you want the student to type, and Compute() generates the proper MathObject to handle that (just as it will when it processes the student answer). Another important feature of Compute() is that the input string also serves as the correct answer when a student requests answers after the due date, which lets you put the correct answer in exactly the form you want it to appear. For example if you use

   Compute("<cos(pi/6),sin(pi/6), pi/6>")->cmp

the correct answer will be presented to the student as <cos(pi/6),sin(pi/6), pi/6>, while

   Vector("<cos(pi/6),sin(pi/6), pi/6>")->cmp

would show <0.866025,0.5,0.523599> as the correct answer.


The MathObject types

The MathObjects library defines the following object types:

Real

The Real class implements real numbers with "fuzzy" comparison (governed by the same tolerances and settings that control student answer checking). For example, Real(1.0) == Real(1.0000001) will be true, while Real(1.0) < Real(1.0000001) will be false. Reals can be added, subtracted, and so on, and the results will still be MathObject Reals. Similarly, sin(), sqrt(), ln(), and the other functions return Real objects if their arguments are Reals. For example:
   Context("Numeric");
   
   $a = Real(2);
   $b = $a + 5;     # same as Real(7);
   $c = sqrt($a);   # same as Real(sqrt(2));
This allows you to compute with Reals just as you would with native Perl real numbers.
The value pi can be used in your Perl code to represent the value of [math]\pi[/math]. Note that you must use -(pi) for [math]-\pi[/math] in Perl expressions (but not in strings that will be parsed by MathObjects, such as student answers or arguments to Compute()). For instance:
   $a = pi + 2;              # same as Real("pi + 2");
   $b = 2 - (pi);            # same as Real("2 - pi");
   $c = sin(pi/2);           # same as Real(1);
   $d = Compute("2 - pi");   # parens only needed in Perl expressions

Infinity

The Infinity class handles the positive infinity of the extended reals. This value can be negated and used in intervals, but can be added to Reals (or other MathObjects) and can't be used as an argument to functions like sin() or sqrt(). In Perl code, you can use Infinity to obtain this value, or -(Infinity) to obtain its negation. E.g.,
   Context("Numeric");
   
   $Inf = Infinity;
   $MInf = -(Infinity);
   $I = Interval("(",0,Infinity,"]");   # easier as Interval("(0,infinity]");

Complex

The Complex class implements complex numbers with "fuzzy" comparison (due to the fact that the real and complex parts are handled using MathObject Reals). As with Real objects, you can add, subtract, multiply, and perform the other usual arithmetic operations on Complex objects, and can compute sin(), sqrt() and the other standard functions on Complex arguments. In addition, there are arg() and mod(), which return the argument or modulus of a Complex value, and Re() and Im() that return its real or imaginary part. The conj() function returns the complex conjugate of a Complex object. The value i represents [math]\sqrt{-1}[/math] and can be used in your Perl expressions to produce complex numbers. Note that you must use -(i) to obtain [math]-i[/math] in Perl expressions (but not in strings parsed by MathObjects, such as student answers or the arguments to Compute()).
   Context("Complex");
   
   $z = Complex(2,3);
   $z = Complex([2,3]);
   $z = 2 + 3 * i;
   $z = Complex("2 + 3i");
   $z = Compute("2 + 3i");
   
   $w = sin($z);      # same as Compute("sin(2+3i)");
   $w = conj($z);     # same as Complex("2 - 3i");
   $w = $z**2;        # same as Compute("(2+3i)^2");
   $w = $z + 5*i;     # same as Complex("2 + 8i");
   $w = $z - (i);     # parens needed in Perl expressions

Point

The Point class implements points in [math]{\bf R}^n[/math] for arbitrary [math]n[/math]. Typically, Points are delimited by parentheses, but that can be controlled by settings in the Context. Points (of the same dimension) can be added and subtracted, or multiplied and divided by scalars. The answer checker for Points can give students hints about the coordinates that are wrong, and about whether the number of coordinates is correct.
   Context("Point");
   
   $p = Point(3,0,-2);
   $p = Point([3,0,-2]);
   $p = Point("(3,0,-2)");
   $p = Compute("(3,0,-2)");
   
   $q = $p + Point(1,3,7);   # same as Point(4,3,5);
   $q = $p + [1,3,7];        # same as above
   $q = 3*$p;                # same as Point(9,0,-6);
   $p = $p/2;                # same as Point(3/2,0,-1);
It is possible to create Points in [math]{\bf C}^n[/math], though there is no pre-defined Context that makes this easy to do.

Vector

The Vector class implements vector quantities in [math]{\bf R}^n[/math] for arbitrary [math]n[/math]. Typically, vectors are delimited by angle brackets, as in <-2,4,0>, but that can be controlled by settings in the Context. Vectors can also be produced using the coordinate unit vectors, i, j, and k, as in the examples below. Vectors (of the same dimension) can be added and subtracted, or multiplied and divided by scalars. The answer checker for Vectors can give students hints about the coordinates that are wrong, and about whether the number of coordinates is correct.
   Context("Vector");
   
   $v = Vector(-2,4,0);
   $v = Vector([-2,4,0]);
   $v = Vector("<-2,4,0>");
   $v = Compute("<-2,4,0>");
   $v = Vector("-2i + 4j");
   $v = Compute("-2i + 4j");
   $v = -2*i + 4*j;
   
   $u = $v + Vector(3,1,-1);     # same as Vector(1,5,-1);
   $u = $v + [3,1,-1];           # same as Vector(1,5,-1);
   $u = 3*$v;                    # same as Vector(-6,12,0);
   $u = $v/2;                    # same as Vector(-1,2,0);
The dot product can be obtained by using a period (.) between two Vectors, and the cross product in [math]{\bf R}^3[/math] is formed by an x between two Vectors in Perl code, or by >< in student answers or other strings parsed by MathObjects. The absolute value of a Vector is its length, which can also be obtained from the norm() function. A unit vector in the same direction (as a non-zero Vector) can be obtained from the unit() function.
   $a = $u . $v;                 # dot product
   $a = Compute("$u . $v");
   
   $w = $u x $v;                 # cross product
   $w = Compute("$u >< $v");
   
   $b = norm($v);                # length of a vector
   $b = abs($v);                 # same
   $b = Compute("|$v|");
   $b = Compute("abs($v)");
   $b = Compute("norm($v)");
   
   $w = unit($v);                # unit vector in direction of $v
   $w = Compute("unit($v)");
If a Vector is combined with a Point, the Point will first be converted to a Vector, and then combined to form a Vector result:
   $v = Vector(3,1,-1);
   $p = Point(1,-3,5);
   $t = Formula("t");
   
   $w = $v + $p;         # same as Vector(4,-2,4);
   
   $f = $p + $t * $v;    # a parametric line through $p in the direction of $v
   $f = Formula("(1,-3,5) + t<3,1,-1>");
There is a special constructor, ColumnVector() which will produce a Vector object that displays vertically in TeX output. This is useful for matrix-vector multiplication, or systems of equations, for example. A ColumnVector acts just like a regular vector in terms of the operations and functions described above; its only difference is that it displays vertically.
   $v = ColumnVector(5,-3,-2);
In addition to the Vector context, there is also Vector2D which is specifically for vectors in the plane. Here, i and j are defined as two-dimensional vectors, and k is not defined. (In Vector context, i and j are 3-dimensional, and k is available as well).
The file pg/macros/parserVectorUtils.pl includes some useful routines for obtaining things like non-zero points and vectors, or equations of lines and planes. See the POD documentation for details.
It is possible to create Vectors in [math]{\bf C}^n[/math], though there is no pre-defined Context that makes this easy to do.

Matrix

The Matrix class implements matrices (in any dimension) of real entries, though the most common use is 2-dimensional ones (i.e., [math]n\times m[/math] matrices). A 1-dimensional Matrix is effectively a Vector. Typically, the rows of a Matrix are comma-separated lists of numbers delimited by square brackets, and a Matrix is a comma separated list of rows delimited by square brackets. E.g., [[1,2,3],[4,5,6]] is a [math]2\times 3[/math] matrix whose first row has entries 1, 2 and 3, and whose second row has entries 4, 5 and 6. Matrices can be combined by addition, subtraction, and multiplication, when the dimensions match up properly, and square matrices can have integer powers taken of them (meaning repeated multiplication). You can perform scalar multiplication and division, and also matrix-vector multiplication.
   Context("Matrix");
   
   $M = Matrix([1,2,3],[4,5,6]);
   $M = Matrix([[1,2,3],[4,5,6]]);
   $M = Matrix("[[1,2,3],[4,5,6]]");
   $M = Compute("[[1,2,3],[4,5,6]]");
   
   $M1 = Matrix([1,2],[3,4]);
   $M2 = Matrix([5,6],[7,8]);
   $v = Vector(9,10);
   
   $M3 = $M1 + $M2;     # same as Matrix([6,8],[10,12]);
   $A = $M1 * $M2;      # usual matrix multiplication
   $B = $M1 ** 2;       # same as $M1 * $M1
   $C = 3 * $M1;        # same as Matrix([3,6],[9,12]);
   $D = $M1 / 2;        # same as Matrix([.5,1],[1.5,2]);
   
   $u = $M1 * $v        # matrix-vector multiplication
The value of $u above will be a [math]2\times 1[/math] matrix, which is effectively a column vector but it is not a Vector object, it is a Matrix object. You can force it to be a Vector object using $u = Vector($M1 * $v); or $u = ColumnVector($m1 * $v) instead.
Students can enter Matrix-valued answers using the bracket notation above, or you can add Matrix-valued constants to the Context of your problem and have them write answers as matrix expressions in terms of those constants. Alternatively, you can use $M->ans_array() rather than ans_rule() to create an array of answer blanks where the student enters the entries of the matrix individually.
Matrices have a number of methods that can be used to compute things like determinants, inverses, transposes, and so on. See ```(need reference)``` for complete details, but some examples are
   $a = $M1->det;
   $A = $M1->transpose;
   $B = $M1->inverse;
   $t = $M1->trace;
   $v = $M1->row(1);        # the first row of the matrix
   $v = $M1->column(1);     # the first column of the matrix
   $a = $M1->element(2,1);  # the element in the second row, first column
Note that $M1->row(1) and $M1->column(1) produce Matrix objects, not Vectors. You can coerce them to Vectors via Vector($M1->row(1)) and ColumnVector($M1->column(1)).
It is possible to create Matrices of complex numbers, but there is no pre-defined Context that makes this easy to do.
Higher-dimensional Matrices can be obtained as a comma-separated list of lower-dimensional matrices delimited by square brackets. For example, Matrix([[[1,2],[3,4]],[[5,6],[7,8]]]) is a [math]2\times 2\times 2[/math] matrix (a 3-dimensional matrix).

List

The List class implements arbitrary lists of other MathObjects. There is no restriction on what can be placed in a list, including other lists, other than what can be specified in the Context. For example, 1,<2,3>,5-2i,DNE is a valid List. The answer checker for lists allows you to specify if the order of the list matters or not, and students can enter their answers in any order by default. Lists are useful when you don't want to give away the number of answers a student may have to come up with, for example, or when you don't want to have to worry about the order of the answers.
   $L = List(1,3+i,"DNE",Vector(3,2,1));
   $L = List("1,3+i,DNE,<3,2,1>");       # provided i and Vectors are in the context
   $L = Compute("1,3+i,DNE,<3,2,1>");    # same caveat
Lists can also be enclosed in delimiters, e.g., (1,DNE), and you can have lists of lists, as in (1,increases),(3.5,decreases). If you include delimiters in a list, then students must as well, and if you leave them off, students must as well. If you create a list via the List() constructor as in the first example above, then this assumes no delimiters are needed. There are flags for the answer checker that you can use to change how students must enter delimiters.

Interval

The Interval class implements intervals on the real line. They can be open or closed at each end, and can be infinite. For example, (0,infinity) is the set of [math]x[/math] where [math]x \gt 0[/math] and [-1,1] is the set of [math]x[/math] where [math]-1\le x\le 1[/math]. The interval (-infinity,infinity) is the entire real line (and the constant R refers to this set in the Interval Context). The individual point [math]a[/math] on the line can be represented as [a,a], but this is better handled via a Set (i.e., {a}).
   Context("Interval");
   
   $I = Interval("[",0,Infinity,")");    # the hard way
   $I = Interval("[0,infinity)");        # the easy way
   $I = Compute("[0,infinity)");
The union of two Intervals is represent by an upper-case U in student answers and parsed strings and by addition or the Union() constructor in Perl code. Differences of intervals can be obtained via subtraction. Intervals can be combined with Sets or Unions in these ways as well.
   $U = Interval("(-infinity,-1]") + Interval("[1,infinity)");
   $U = Union(Interval("(-infinity,-1]"),Interval("[1,infinity)"));
   $U = Union("(-infinity,-1] U [1,infinity)");
   $U = Compute("(-infinity,-1] U [1,infinity)");
   
   $S = Interval("(-infinity,1]") - Interval("[-1,1)");   # same as Compute("(-infinity,-1) U {1}");
   $S = Compute("(-infinity,1] - [-1,1)");                # same as above
   
   $S = Compute("R - (-1,1)");                            # same as $U above
Intersections of Intervals (or Sets or Unions) can be obtained via the intersect method of an Interval. There is no built-in method for students to form intersections (though one could be added to the Context by hand). There are other methods for determining if one Interval is contained in another, or intersects another, or is a subset of another, etc. These methods can be applied to Sets and Unions in addition to Intervals.
   $I1 = Interval("(-infinity,1]");
   $I2 = Interval("(-1,5]");
   
   $I3 = $I1->intersect($I2);                # same as Interval("(-1,1]");
   
   $I1->contains($I2);                       # returns false
   $I3->isSubsetOf($I2);                     # returns true
   $I1->intersects($I2);                     # returns true
The answer checker for Intervals can give hints about whether each endpoint is correct or not, and about whether the type of endpoint (open/closed) is correct or not. These features can be controlled through flags for the answer checker.

Set

The Set class implements finite sets of real numbers. Sets are enclosed in curly braces, and can contain arbitrarily many real numbers, in any order. The empty set is formed by open and close braces with no numbers between them, i.e., {}.
   Context("Interval");
   
   $S = Set(0,sqrt(2),pi,-7);
   $S = Set([0,sqrt(2),pi,-7]);
   $S = Set("{0,sqrt(2),pi,-7}");
   $S = Compute("{0,sqrt(2),pi,-7}");
Sets can be combined with each other and with Intervals via a Union (represented by an upper-case U in student answers and parsed strings, and by addition or the Union() constructor in Perl code). Differences of Sets and other Sets, Intervals, or Unions can be obtained via subtraction.
   $U = Set(0,1,2) + Set(2,pi,sqrt(2));         # same as Set(0,1,2,pi,sqrt(2));
   $U = Set("{0,1,2} U {2,pi,sqrt(2)}");
   $U = Union("{0,1,2} U {2,pi,sqrt(2)}");
   $U = Compute("{0,1,2} U {2,pi,sqrt(2)}");
   $W = Set(0,1,2) + Interval("(1,2)");         # same as Compute("{0} U [1,2]");
   
   $S = Set(0,1,2) - Set(2,pi);                 # same as Set(0,1);
   $S = Compute("{0,1,2} - {2,pi}");            # same as above
   
   $S = Compute("{0,1,2} - [1,2)");             # same as Set(1,2);
Intersections of Sets with other Sets, Intervals, or Unions can be obtained via the intersect method of a Set. There is no built-in method for students to form intersections (though one could be added to the Context by hand). There are other methods for determining if one Set is contained in another, or intersects one, or is a subset of another, etc.
   $S1 = Set(1,2,3,4);
   $S2 = Set(3,4,5);
   
   $S3 = $S1->intersect($S2);           # same as Set(3,4);
   
   $S1->contains($S2);                  # returns false
   $S3->isSubsetOf($S2);                # returns true
   $S1->intersects($S2);                # returns true
The answer checker for Sets reports a warning if an element is entered twice in the set (e.g., {1,1,2}), but this can be controlled by a flag on the answer checker.

Union

The Union class implements finite unions of Intervals and Sets. Unions are formed via the U symbol in student answers or parsed strings, or by addition or the Union() constructor in Perl code. The order of the Sets and Intervals in the Union does not matter.
   Context("Interval");
   
   $U = Interval("[-1,1]") + Interval("(5,infinity)");
   $U = Union(Interval("[-1,1]"),Interval("(5,infinity)"));
   $U = Union("[-1,1] U (5,infinity)");
   $U = Compute("[-1,1] U (5,infinity)");
Differences of Unions with Sets, Intervals, or other Unions can be obtained via subtraction.
   $W = Union("(0,1) U (2,5)") - Interval("(3,4)");    # same as Union("(0,1) U (2,3] U [4,5)");
   $W = Compute("(0,1) U (2,5) - (3,4)");            # same as above
   
Intersections of Unions with other Sets, Intervals, or Unions can be obtained via the intersect method of a Union. There is no built-in method for students to form intersections (though one could be added to the Context by hand). There are other methods for determining if one Union is contained in another, or intersects one, or is a subset of another, etc.
   $U1 = Union("(0,3] U {4,5}");
   $U2 = Union("[3,4] U {0}");
   
   $U3 = $U1->intersect($U2);           # same as Set(3,4);
   
   $U1->contains($U2);                  # returns false
   $U3->isSubsetOf($U2);                # returns true
   $U1->intersects($U2);                # returns true
When Unions are created as part of the code of a problem, things like overlapping intervals are reduced automatically so that (-1,2) U [0,3] will be converted to (-1,3]. In general, however, students must enter unions of non-overlapping intervals and sets, though this can be controlled by answer checker flags. You can turn off automatic reduction by setting the flag reduceUnions to 0 in the Context:
   Context()->flags->set(reduceUnions => 0);
This will preserve the Union in whatever form it was originally created (this is set to 0 for student answers so that automatic reductions are not performed). A second flag, reduceUnionsForComparison, determines whether Unions are reduced (temporarily) before they are compared for equality, or whether the structures must match exactly.

String

The String class adds the ability to have special words or phrases be recognized by the MathObjects library. The two pre-defined words are DNE (for "does not exist") and NONE. By default, strings are not case sensitive, so dne and DNE (and Dne, and dNe, etc.) are all considered to be the same thing. The allowed strings are part of the Context, and you can add your own (or remove the two pre-defined ones), and you can make case-sensitive strings if you desire. See the String Context changes section for details.
   $ans = String("DNE");
   $ans = Compute("DNE");
   
   $ans = Compute("DNE,NONE");  # a list of strings
If a string answer is used where something other than a number could have been the answer (e.g., as a response to "At what points [math](x,y)[/math] is [math]\sqrt{x^2+y^2}[/math] undefined?"), then you should indicate the type of answer that might be expected when you create the answer checker by setting the typeMatch parameter to an instance of that type. E.g.,
   ANS(String("NONE")->cmp(typeMatch => Point(0,0)));
This will make sure that syntax checking and error messages are appropriate for the kind of answer that the student might submit.

Formula

A Formula object represents an expression whose result is one of the MathObject types defined above. These act like functions in that you can evaluate them at different values of the variables that they use, or substitute other expressions for the variables (to form compositions).
   $f = Formula("2x^2+3x-5");
   
   $a = random(2,5,1);
   $b = random(5,9,1);
   $f = Formula("x^2 + $a x + $b");
The variables used in a Formula must be declared in the Context. The Numeric Context has x pre-defined, but you can add more, as in the following example:
   Context("Numeric");
   Context()->variables->add(y => 'Real');
   
   $f = Formula("x^2 + 2xy + y^2");
Formulas can be added, subtracted, multiplied, etc, to obtain new Formula objects, and functions like sin(), sqrt(), and so on will return Formula objects when their arguments are formulas:
   $x = Formula("x");
   $f = 3* $x**2 - 2 * $x + 5;    # same as Formula("3x^2 - 2x + 5");
   $g = $x - 5;
   $h = $f / $g;                  # same as Formula("(3x^2 - 2x + 5) / (x - 5)");
   $g1 = sin($g)                  # same as Formula("sin(x-5)");
Formulas can produce any type of MathObject as its result, including points, vectors, intervals, etc. For example,
   Context("Vector");
   $f1 = Formula("<2x+1,1-x,x^2>");           # a vector-valued formula
   
   Context()->variables->add(t => 'Real');
   $f2 = Formula("(1,3,-2) + t <4,-1,2>");    # a parametric line
   
   Context("Interval");
   $f3 = Formula("(x,2x+1]");                 # an interval-valued formula
If you substitute values as coefficients in a formula, you may end up with things like 1 x^2 + -3 x + 0, but Formulas have a reduce method that can be used to remove coefficients of 1, simplify addition of negatives, remove sums or products of 0, and so forth. E.g.,
   $f = Formula("1 x^2 + -3 x + 0")->reduce;  # same as Formula("x^2 - 3x");
There are a number of reduction rules, and you can enable or disable them individually (reference needed). Note, however, that MathObjects is not a full computer algebra system, and the reduction rules are mainly geared toward improving the output, not solving equations or performing algebraic manipulations to simplify the expression.
Given a Formula, you may want to evaluate the Formula at a particular value of its variable; that can be accomplished by the eval() method. The arguments to eval() assign values to all the variables of the Formula, and it returns the value of the Formula for those inputs. For example:
   $f = Formula("x^2 + 2x + 1");
   $a = $f->eval(x=>2);            # f at x=2, or Real(9)
   
   $g = Formula("xy + x + y");
   $b = $g->eval(x=>2,y=>3);       # g at (x,y) = (2,3), or Real(11);
The substitute() method is similar to eval(), but in this case, the specified variables are replaced by the values that are given. The values need not be constants; they could be Formulas, in which case the result is the composition of the two Formulas. Note that not all the variables need to be substituted (unlike with eval(), where all variables must be given a value).
   $f = Formula("x^2 + 2x + 1");
   $g1 = $f->substitute(x => "y");            # same as Formula("y^2 + 2y + 1");
   $g2 = $f->substitute(x => "2x-1");         # same as Formula("(2x-1)^2 + 2(2x-1) + 1");
   
   $h = Formula("xy + x + y");
   $h1 = $h->substitute(x => "y");            # same as Formula("y*y + y + y") or Formula("y^2 + 2y");
   $h2 = $h->substitute(x => 2, y => "3x");   # same as Formula("2(3x) + 2 + 3x");
If you need to evaluate a Formula multiple times (for example, to produce a graph), it is not very efficient to use eval(). Instead, you can use the perlFunction method to generate a native Perl function that you can call to evaluate the Formula. If given no arguments, perlFunction() returns an anonymous code reference to a subroutine that evaluates the Formula; the parameters to the subroutine are the values of the variables (in alphabetical order). If a single argument is given, it is the name to give to the subroutine; if two arguments are given, the second is an array reference that lists the order of the variables to use for the arguments to the subroutine.
   $f = Formula("x^2+y");
   
   $F = $f->perlFunction;            # anonymous subroutine reference
   $a = &{$F}(2,3);                  # value of $f for x=2 and y=3, i.e., Real(7)
   $f->perlFunction("F");            # subroutine named F
   $a = F(2,3);                      # again, Real(y)
   $f->perlFunciton("G",["y","x"]);  # change order of variables in arguments to G
   $a = $G(2,3);                     # value of $f for y=2 and x=3, i.e.,  Real(11);
You can obtain the derivative of a Formula using the D() method. If the Formula has more than one variable, then you need to indicate which one to differentiate by; you do this by giving the variable name as an argument to the method. You can get second or third derivatives (or higher) either by using D() a second time or third time, or by including more variable names in the call to D(). For example:
   $f = Formula("3x^2-5x+2");
   $df = $f->D('x');                    # same as Formula("3*(2x)-5");
   $df_4 = $df->eval(x=>4);             # f'(4), i.e., Real(19);
   $ddf = $df->D('x');                  # same as Formula("6");
   $ddf = $f->D('x','x');               # same as above
   
   $f = Formula("x^2 + 4xy^2 + y^3");
   $fx = $f->D('x');                    # same as Formula("2x+4y^2");
   $fxy = $fx->D('y');                  # same as Formula("4*(2y)");
   $fxy = $f->D('x','y');               # same as above
See the Using Math Obejcts POD documentation for more information about Formula objects and how to use them.

Other types

The pg/macros directory contains a number of extensions to MathObjects, including files that define specialized MathObject types. The ones that begin with parser typically define a new object class. For example, parserParametricLine.pl defines a constructor ParametricLine() for creating a special object that checks if a student's answer is a given parametric line or not, even if it is parameterized differently. These files usually contain documentation within them; see the POD documentation for versions that you can read on line.


MathObject Answer Checkers

MathObject Methods and Properties

There are a number of methods that are common to all MathObjects, which are described below. Some classes have additional methods, and many of these are described above. You call a method on a MathObject as you would a method for any Perl object, using the -> operator with the MathObject on the left and the method name and its arguments (if any) on the right. E.g.,

   $mathObject->method;              # when there are no arguments
   $mathObject->method($arg);        # for one argument
   $mathObject->method($arg1,$arg2); # for two arguments
   # and so on

The common methods include:

  • cmp
Returns an answer checker for the MathObject. The cmp method can accept a number of settings that control the tolerances for the comparison, special options of the comparison (e.g., parallel vectors rather than equal vectors), the types of error messages produced (e.g., messages about individual coordinates that are wrong), and other features of the check. These are described in the MathObjects Answer Checkers POD documentation. All of the answer checkers are defined in the file pg/lib/Value/AnswerChecker.pm.
  • value
Returns an array containing the data that represents the object. For a Real, it is just the perl real number that corresponds to it; for a Complex number, it is the real and imaginary parts (as Reals). For an Infinity, it is the string needed to obtain the Infinity. For a Point or Vector, it is the coordinates of the Point or Vector (as MathObjects). For a Matrix, it is an array of rows of the Matrix, where the rows are references to arrays of MathObjects. For an Interval, it is the two endpoints (as Reals) followed by strings that are the open and close parentheses or brackets for the Interval. For a Set, it is an array of the elements of the set (as Reals). For a Union, it is an array of the Sets and Intervals that make up the Union. For a String, it is a perl string representing the value of the string.
For a Formula, it is a little more complicated. If the Formula is an explicit Point, Vector, Matrix, Interval, etc. (e.g., Formula("<x,x+1>")), then the value is an array of Formulas that are the coordinates. If the Formula is an expression (e.g., Formula("<x,1> + <2x,-x>") or Formula("norm(<x,x+1>)") then the value is just the original Formula.
  • getFlag("flag-name") or getFlag("flag-name",default)
Returns the object's value for a given context flag. The value can come from a variety of locations, which are searched in the following order (the first that has a property with the given flag name will have that propery's value returned): the object itself, the Formula that created it (if it was the result of an eval() call, for example), the AnswerHash associated with the object (if it was the source for an answer checker; this gives access to the flags passed to the cmp method), the Context in which the object was created, the currently active Context, or the default value (if given as the second argument), otherwise the return value is undef. This is useful in custom answer checkers for finding out the settings of things like the tolerances, the flags passed to the answer checker, and so on.
  • with(name => value)
This copies the object, and sets its property with the given name to the given value. You can supply multiple name/value pairs separated by commas. This gives you the ability to initialize the object when you create it, or to make a copy with specific settings. E.g.
   $f = Formula("sqrt(x-10)")->with(limits => [10,12]);
   $a = Real(pi/2)->with(period => 2*pi);
  • typeMatch
  • class and type
These are two methods that help you determine what kind of MathObject you are working with. They can be useful in custom answer checkers if you want to know more about what kind of object a student answer is. The class method tells you the class of object (like Real, Complex, Point, Formula, etc.), while the type method tells you what kind of return value a Formula has (non-Formulas are considered constant-valued Formulas when computing the type). The class is essentially the package name from the Value package of the MathObject, while the type is the package name from the Parser package name for the result of the Formula.
   Real(5)->class;          # produces "Real"
   Real(5)->type;           # produces "Number"
   Point(1,2)->class;       # produces "Point"
   Point(1,2)->type;        # produces "Point"
   
   Formula("3x+1")->class;  # produces "Formula"
   Formula("3x+1")->type;   # produces "Number"
  • TeX: Returns a string which represents the object as a TeX math expression.
  • string: Returns a string similar to that used to create the object. May include extra parentheses.
  • perl
Returns a string which represents the object as Perl source code.

Experimenting with MathObejcts

One way to experiment with MathObjects is to use the on-line PG labs to write example code and see what it produces. You can copy any of the examples above and paste them into the lab to check the results. This makes it easy to test code without having to write complete problem files and save them in a course. One lab lets you get the output from a line of PG code, while another lets you try out full problems to see how they work.


Files defining MathObjects

The MathObjects library is contained in two Perl packages: the Parser package and the Value package. The first of these implements the parser that converts student answers (or strings passed to Compute() or the MathObject constructor functions) into the corresponding MathObjects. The second implements the mathematical operations and functions for the various object types. The files that define the Parser package are pg/lib/Parser.pm and the files in the directory pg/lib/Parser/, while the Value package is defined in pg/lib/Value.pm and the directory pg/lib/Value/. There are also two macro files pg/macro/Parser.pl and pg/macro/Value.pl that define the object constructors and other values needed by PG problems that use MathObjects. The pg/macros/MathObjects.pl file is a wrapper that loads these two, and is the file that you need to include in your loadMacros() call in order to use MathObjects in a problem you are writing.

The pg/lib/Value/ directory includes files for each of the MathObject types (e.g., Real.pm, Complex.pm, etc.). These define the various object types and how they operate. For example, these implement how operations like addition and multiplication work for each object class, how functions like sin() and sqrt() work, how to compare two objects for equality and numeric order, how to display the object in TeX and string form, how to covert the object to Perl code, and so on.

The pg/lib/Parser/ directory contains files that make it possible to break up a string into its various tokens and map them to build the parse tree for the expression. For example, there are binary-operator and unary-operator classes that form the nodes of an expression tree. There are also function classes to handle function calls, list classes to handle points, vectors, and so on, and classes for the various values that can be produced, like numbers and strings. The Context stores information that the parser uses to create the parse tree, so the various entities like variables, constants, strings, operators, functions, parens, and so on that you can set in the Context correspond directly to the classes defined in pg/lib/Parser. The Parser classes are related to the ones defined in the Value package, but are not the same. The former are used to build the parse tree, while that latter are used to compute specific instances of the objects.


List of Basic Contexts

A Context is a table of values that provides defaults for the Parser and for MathObjects created while the Context is in force. As a quick example: in Numeric context the answer [math](4,5)[/math] is interpreted as a point in the two dimensional plane while in Interval context it is interpreted as the real values [math]x[/math] satisfying [math]4 \lt x \lt 5[/math].

  • Define context using: Context("Numeric");
  • To obtain the current context: $context = Context();
  • Context names: defined in pg/lib/Parser/Context/Default.pm
    • Numeric: no Matrix, Complex or Vector (or Interval) type is allowed.
    • Complex: no Matrix or Vector type is allowed. Can't use "<" to compare complex numbers.
    • Point: Nearly the same as the Vector Context below, but the angle bracket notation is not allowed and vector operations on points are not defined. This is useful if you wish to force students to perform the vector calculations before entering their answer.
    • Vector: i, j, and k are defined as unit Vectors, no Complex numbers are allowed.
    • Vector2D: i and j are defined as unit Vectors, no Complex numbers are allowed.
    • Matrix: square brackets define Matrix instead of Point or Interval
    • Interval: similar to Numeric context, but (,) and [,] create Real Intervals rather than Lists. {,} creates finite sets of Reals.
    • Full: For internal use. This context is used to seed the others.
      • pi is defined
      • i is square root of minus one, but j and k are unit Vectors
      • Matrix, Vector and Complex are all defined.
      • x is a variable

When first using MathObjects it's easiest to use the standard "Numeric" context, however as you begin to search for better ways to ask questions and to evaluate student responses you will find that customizing the context is a powerful way to proceed. There is more on this subject in the following documents.

See also