Difference between revisions of "Introduction to Contexts"

From WeBWorK_wiki
Jump to navigation Jump to search
m (Correct spelling of "infinite")
 
(43 intermediate revisions by 10 users not shown)
Line 1: Line 1:
== Basic Contexts ==
+
== What is a Context? ==
The Context determines how variables are interpreted,
 
sets the default variable(s),
 
determines default constants available in the problem,
 
and sets appropriate default system values such as the tolerance for student errors, etc.
 
   
It also adjusts the error messages for student responses so that, for example, "4i +5j +6k" will
 
  +
Every webwork problem has a collection of variables and constants that are part of the problem, and a setting in which these reside. For example, some problems use vector-valued formulas, and others might be about intervals on the real line. Some questions allow students to perform numerical calculations as part of their answers, while others require that they simplify their answers to a single number before entering it. In some questions a less-than and greater-than signs might be used to describe intervals (e.g., <code>x &lt; 5</code>) while in others they are used to delimit vectors (as in <code>&lt;3,-1,2&gt;</code>). Similarly, in a problem on vectors, the letter <code>i</code> might represent the coordinate unit vector along the <math>x</math>-axis, while in a question on complex numbers, <code>i</code> might represent the complex number <math>\sqrt{-1}</math>.
result in a syntax error message in "Numeric" context but not in "Vector" context.
 
   
Usually we select a Context by including
 
  +
All of these features represent the ''context'' of the problem, and MathObjects maintains this information in a special Context object. The Context object (or simply Context) holds information about what variables are defined and their types and domains, what constants are available, what functions can be applied and what operations performed, what words (like <code>NONE</code> and <code>DNE</code>) are available, and what interpretation to give to the various symbols and delimiters that the student can type.
  +
  +
There are predefined contexts for working with things like real numbers, complex numbers, intervals, vectors, matrices, and so on. There are also a large number of extensions that implement specialized Contexts for particular purposes: scientific notation, currency values, inequalities, fractions, polynomials, chemical reactions, and so forth. It is possible to modify an existing Context to suit the needs of your problems, and you can even create your own custom Contexts (though this is an advanced topic).
  +
  +
  +
== Selecting a Context ==
  +
  +
Each Context has a name, and you select a context by adding the command
   
 
Context("name");
 
Context("name");
   
at the top of a problem file, where <code>name</code> is one of:
 
  +
at the top of a problem file, where <code>name</code> is the name of the desired Context. The pre-defined contexts are listed on the [[Common Contexts]] page, and these include <code>Numeric</code>, <code>Complex</code>, <code>Vector</code>, <code>Matrix</code>, and <code>Interval</code>.
  +
  +
There are a number of Contexts implemented as extensions to the MathObjects library. These are in the <code>[https://github.com/openwebwork/pg/tree/master/macros pg/macros/]</code> directory; files whose names begin with <code>context</code> implement specialized Contexts. Most of these are listed in the [[Specialized contexts]] documentation. In order to use one of these additional Contexts, include the macro file for that Context in the <code>loadMacros()</code> call at the top of your file. For example:
  +
  +
loadMacros(
  +
"PGstandard.pl",
  +
"MathObjects.pl",
  +
"contextLimitedPolynomial.pl",
  +
"PGcourse.pl",
  +
);
  +
  +
loads the <code>contextLimitedPolynomial.pl</code> macro file, which implements the <code>LimitedPolynomial</code> Context, which requires students to specify only "simplified" polynomials in the form <math>a_n x^n + ... + a_0 </math> as their answers. Inspecting files like <code>contextLimitedPolynomial.pl</code> is one way to learn how to modify a context to meet your requirements, though that is an advanced topic.
  +
  +
  +
== Changing Context Settings ==
  +
  +
Since the Context stores information about things like the variables and constants for the problem, you need a way to specify what these are. You do this by using methods of the Context object. In order to obtain a reference to the current Context, use the <code>Context()</code> function with no context name; this returns the context that is active.
  +
  +
$context = Context();
  +
  +
The Context give you access to the various types of information that it stores via methods for each type, such as <code>Context()->variables</code> is a reference to an object that holds the information about the variables in the Context, while <code>Context()->constants</code> is a reference to the object that manages the constants in the Context. Each of these in turn has methods for adding, modifying, removing, and listing the values that they store. For example,
  +
  +
Context()->variables->add(t => 'Real');
  +
  +
adds a new variable <code>t</code> that is a Real value.
  +
  +
All the types of data listed below have the following methods:
  +
  +
{| class="wikitable"
  +
! Method !! Description
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>add(name => data)</code>
  +
| style="padding:5px" | for adding new items
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>are(name => data)</code>
  +
| style="padding:5px" | for removing all current items and adding new ones
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>set(name => data)</code>
  +
| style="padding:5px" | for modifying existing items
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>get(name)</code>
  +
| style="padding:5px" | for getting the data defining a particular item
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>replace(name => data)</code>
  +
| style="padding:5px" | for replacing an existing definition by a new one
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>undefine(name)</code>
  +
| style="padding:5px" | for making an item be undefined, but still recognized
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>redefine(name)</code>
  +
| style="padding:5px" | for making the item be defined again
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>remove(name)</code>
  +
| style="padding:5px" | for deleting a particular item
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>clear</code>
  +
| style="padding:5px" | for removing all current definitions
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>names</code>
  +
| style="padding:5px" | for retrieving the names of all the defined items
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>all</code>
  +
| style="padding:5px" | for retrieving the entire collection of data for all items
  +
  +
|}
  +
  +
These are explained in more detail in the [[Modifying contexts (advanced)]] documentation. Several of these are used in the examples below, and can serve as an introduction to their use.
  +
  +
In addition to direct calls to the data objects, it is possible to modify the Context in other ways. For example, some function calls modify the Context, e.g.,
  +
  +
Parser::Number::NoDecimals(Context());
  +
  +
Also, some macro files provide mechanisms for changing the context, as in the following,
  +
  +
loadMacros("contextLimitedPowers.pl");
  +
Context()->operators->set(@LimitedPowers::OnlyIntegers);
  +
  +
which sets the operators to limit exponents to only integer values.
  +
  +
  +
=== Variables ===
  +
  +
Each Context comes with one or more pre-defined variables. For example the <code>Numeric</code> Context includes the variable <math>x</math>, while the <code>Complex</code> Context has the variable <math>z</math>. The <code>Point</code> and <code>Vector</code> Contexts have variables <math>x</math>, <math>y</math> and <math>z</math>.
  +
  +
To add a new variable to a context, use
  +
  +
Context()->variables->add(name => type);
  +
  +
where <code>name</code> is the name of the variable to be added, and <code>type</code> is the kind of MathObject that is to be stored. The valid types are <code>"Real"</code>, <code>"Complex"</code>, <code>"Point2D"</code>, <code>"Point3D"</code>, <code>"Vector2D"</code>, <code>"Vector3D"</code>, <code>"Parameter"</code>, or an instance of a MathObject of the appropriate type. You can also add multiple variables at once. For example,
  +
  +
Context()->variables->add(y => "Real");
  +
Context()->variables->add(v => Vector(1,2,3,4));
  +
Context()->variables->add(u => "Complex", w => "Complex");
  +
  +
adds a real variable named <code>y</code>, a variable <code>v</code> holding a four-dimensional vector, and two complex variables, <code>u</code> and <code>w</code>.
  +
  +
You can specify the domain of a variable using the <code>set()</code> method, as in the following,
  +
  +
Context()->variables->set(x => {limits => [3,5]});
  +
  +
which sets the limits for <math>x</math> to be from 3 to 5. Again, multiple variables can be set at once.
  +
  +
You can set both the type and the limits at once when you add the variable, as in
  +
  +
Context()->variables->add(t => ["Real",limits => [0,1]]);
  +
  +
If the type of a variable is <code>"Parameter"</code>, then this variable is used as an ''adaptive parameter'', meaning WeBWorK will try to pick a value of the variable so that the student's answer matches the professor's answer with that value set. Note that student's can't enter adaptive parameters of their own; only the correct answer can include them. Also note that adaptive parameters only work in a linear setting, e.g., <math>af+b</math>, where <math>a</math> and <math>b</math> are the adaptive parameters, and <math>f</math> is the basic correct formula. That is, you can adapt for linear multiples, and constant addition. Once the formulas have been compared, the adapted value for the parameter can be obtained using the <code>value()</code> method described below.
  +
  +
A variable can be removed via the <code>remove()</code> method:
  +
  +
Context()->variables->remove("t");
  +
  +
Finally, the names of all the variables in the Context can be displayed by placing
  +
  +
TEXT(join(',',Context()->variables->names));
  +
  +
into the body of a PG problem.
  +
  +
In addition to the methods shown above, the <code>variables</code> object has the following methods:
  +
  +
{| class="wikitable"
  +
! Method !! Description
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>Context()->variables->type("name")</code>
  +
| style="padding:5px" | Returns the type of the variable (this is a typeRef hash; see '''reference needed''').
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>Context()->variables->value("name")</code>
  +
| style="padding:5px" | Returns the value of an adaptive parameter, once it has been used in a Formula comparison.
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>Context()->variables->parameters</code>
  +
| style="padding:5px" | Returns an array of the names of all the adaptive parameters in the Context.
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>Context()->variables->variables</code>
  +
| style="padding:5px" | Returns an array of the names of all the variables (i.e., non-adaptive-parameters) in the Context.
  +
  +
|}
  +
  +
=== Constants ===
  +
  +
The values of variables like <math>\pi</math> and <math>e</math> are stored in the <code>constants</code> data of the Context. The values of <code>pi</code> and <code>e</code> are defined in all the main Contexts. The <code>Vector</code> and <code>Matrix</code> Contexts include <code>i</code>, <code>j</code>, and <code>k</code>, which represent the coordinate unit vectors, while the <code>Complex</code> Context has <code>i</code> defined as the imaginary number <math>i = \sqrt{-1}</math>. The <code>Interval</code> context has a constant <code>R</code> which is equal to the whole real line -- the interval <code>(-infinity,infinity)</code>.
  +
  +
Constants are added to the Context via the <code>add()</code> method. The data is the value for the constant, and you can set several constants at once. For example:
  +
  +
Context()->constants->add(tau => Real(pi*2));
  +
Context()->constants->add(n => Real(3), m => Real(2));
  +
  +
Usually when a Formula containing a constant is parsed, the name of the constant is retained, so <code>Formula("sin(pi)")</code> would remain as <code>sin(pi)</code> rather than <code>0</code>. You can use the <code>set()</code> method to set the <code>keepName</code> parameter for a constant to change this so that the value is substituted into the Formula when it is used. E.g.,
  +
  +
Context()->constants->set(pi => {keepName => 0});
  +
$f = Formula("sin(pi)");
  +
  +
would be the same as having set <code>$f = Formula("0")</code>.
  +
  +
A constant can be removed via the <code>remove()</code> method:
  +
  +
Context()->constants->remove("pi");
  +
  +
Finally, the names of all the constant in the Context can be displayed by placing
  +
  +
TEXT(join(',',Context()->constants->names));
  +
  +
into the body of a PG problem.
  +
  +
In addition to the methods shown above, the <code>constants</code> object has the following methods:
  +
  +
{| class="wikitable"
  +
! Method !! Description
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>Context()->variables->value("name")</code>
  +
| style="padding:5px" | Returns the value of the constant.
  +
  +
|}
  +
  +
=== Strings ===
  +
  +
Most Contexts include some special words that the students can type, like <code>NONE</code> or <code>DNE</code> (for "does not exist"). These can be used in problems that ask for lists that might be empty (where <code>NONE</code> can be used in that case), or for limit problems where the limit might not exist. These two words are available in all the pre-defined contexts, and also the word <code>infinity</code>, which generates the Infinity object, with <code>inf</code> as an alias for the complete word. In general, words like these are not case-sensitive, so <code>INF</code> would produce <code>infinity</code>, as would <code>InfINIty</code> or any other capitalization of the word.
  +
  +
You can add your own words to the Context via the <code>add()</code> method. Usually, there is no data, so you simply use <code>{}</code> as the definition. To make a string case sensitive, use <code>{caseSensitive =>1}</code> as the data. To make a word create infinity, use <code>{infinite => 1}</code>. To make one word mean the same as another, use <code>{alias => "<i>name</i>"}</code>. For example,
  +
  +
Context()->strings->add(A => {}, B => {}, C => {}, D => {});
  +
Context()->strings->add(True => {}, False => {}, T => {alias => "True"}, F => {alias => "False"});
  +
Context()->strings->add(WeBWorK => {caseSensitive => 1});
  +
Context()->strings->add(unbounded => {infinite => 1});
  +
  +
Here, <code>A</code>, <code>B</code>, <code>C</code>, and <code>D</code> are now valid answers for a student to type, but they could also be <code>a</code>, <code>b</code>, <code>c</code>, or <code>d</code>. <code>True</code> and <code>False</code> (with any capitalization) are now available as well, along with <code>T</code> as an alternative to <code>True</code> and <code>F</code> as an alternative for <code>False</code>. The word <code>WeBWorK</code> can be entered, but only with this capitalization (as it should be). Finally, <code>unbounded</code> will produce the positive Infinity object (so <code>-unbounded</code> would be the negative Infinity object).
  +
  +
A string can be removed via the <code>remove()</code> method:
  +
  +
Context()->strings->remove("DNE");
  +
  +
Finally, the names of all the constant in the Context can be displayed by placing
  +
  +
TEXT(join(',',Context()->strings->names));
  +
  +
into the body of a PG problem.
  +
  +
=== Flags ===
  +
  +
Many settings for the Context are stored as ''flags'', which are parameters that control special functions of the parsing process, the answer checkers, or other features of the MathObjects library. For example, the default tolerance for numeric comparisons is stored as a flag, as are the default limits for variable ranges. Other values include things like whether to display Vectors using <math>ijk</math>-notation rather than coordinate form with angle-bracket delimiters, and how to show student answers in the "Entered" and "Preview" columns of the results table when they submit their answers.
  +
  +
You do not usually add new flags (though you could if you wanted to keep track of information for your own custom MathObject classes). The most common actions are to set or get the values of the flags. To set a flag, use the <code>set()</code> with the data being the name of the flag to set and its new value. As with the previous case, you can set several flags at once.
  +
  +
Context()->flags->set(tolerance => .005);
  +
Context()->flags->set(
  +
reduceConstants => 0,
  +
reduceConstantFunctions => 0,
  +
);
  +
Context()->flags->set(formatStudentAnswers=>"parsed");
  +
  +
To get the value of a flag, you could use the <code>get</code> method, but since this is such a common operation, there is a shorthand. For example,
  +
  +
$tol = Context()->flag("tolerance");
  +
  +
gets the current tolerance value from the Context.
  +
  +
Note that MathObject and answer checkers often can override the settings of the Context itself. That means you may need to check the properties of an MathObject and the properties of the active answer checker (if there is one) before resorting to the Context's value. MathObjects gives you an easy way to do that, however, with the <code>[[Common MathObject Methods#getFlag|getFlag()]]</code> method of a MathObject. For example, if you are writing a custom answer checker, you could use
  +
  +
ANS($mo->cmp(checker => sub {
  +
my ($correct,$student,$ansHash) = @_;
  +
my $tol = $correct->getFlag("tolerance",.001);
  +
... (use $tol here) ...
  +
return $score;
  +
}));
  +
  +
to obtain the value of the <code>tolerance</code>. This will be taken from <code>$correct</code> if it was set there, otherwise from the current answer checker's flags as passed to <code>$mo->cmp()</code>, and then from the Context for <code>$mo</code>. If the flag is not set in any of those, the value <code>.001</code> is used.
  +
  +
The names of all the constants in the Context can be displayed by placing
  +
  +
TEXT(join(',',Context()->flags->names));
  +
  +
into the body of a PG problem. Note that different contexts may have different flags, and that some flags that could be set may not currently have any value in the Context. Most of the important flags are described in the [[Context flags]] documentation.
  +
  +
=== Functions ===
  +
  +
The Context includes information about the functions that are available. For example, the <code>Complex</code> Context has <code>Re()</code> and <code>Im()</code> functions, as well as <code>arg()</code>, <code>mod()</code>, and <code>conj()</code>, and the <code>Vector</code> Context has <code>norm()</code> and <code>unit()</code>. These are not available in the <code>Numeric</code> context.
  +
  +
In some problems, you may want to remove some functions so that students can't enter them. For example, if you want to have the student evaluate the sine function at a particular value, you would not want her to be able to use <code>sin()</code> in her answer or that would defeat the purpose. To remove a function, use the <code>disable()</code> method. For instance,
  +
  +
Context()->functions->disable("sin");
  +
  +
makes the <code>sin()</code> function unavailable to the student. (Note: the function is still recognized by MathObjects, but the student will be told it is not available in this problem if it is used. The <code>remove()</code> method would remove the function entirely, making it unknown to MathObjects, so the error message would be less useful to the student.)
  +
  +
To make the function available again, use <code>enable</code>, e.g.
  +
  +
Context()->functions->enable("sin");
  +
  +
You can disable or enable more than one function at a time by listing their names separated by commas. There are also ''categories'' of functions that you can disable or enable all at once. A full list is available in [[Answer Checkers and the Context#Disabling Functions|Answer Checkers and the Context]]. Some of the common ones are <code>Trig</code> to disable all trigonometric functions, <code>Numeric</code> to disable things like <code>ln()</code> and <code>sqrt()</code>, <code>Complex</code> to disable the complex functions, and <code>All</code> to disable ''all'' the functions. For example,
  +
  +
Context()->functions->disable("All");
  +
Context()->functions->enable("sqrt");
  +
  +
would disable all functions and allow only the square root function.
  +
  +
Note that if you disable the <code>sqrt()</code> function, you may want to disable the exponentiation operators so that students can't use <code>a^(1/2)</code> or <code>a**.5</code> to produce square roots. Similarly, if you disable <code>abs()</code>, you would want to remove the absolute value vertical bars so that students can't use <code>|a|</code>. See [[Answer Checkers and the Context]] for details.
  +
  +
It is possible to add new functions in several ways. The <code>[http://webwork.maa.org/pod/pg/macros/parserFunction.html pg/macros/parserFunctions.pl]</code> file implements an easy way to add functions to a Context using Formula objects. Functions written in Perl code can be added to the context using the techniques outlined in the second example on the [[AddingFunctions]] page. Alternatively, functions defined using Perl code can be added to the Context, as described in the [[Modifying Contexts (advanced)#Adding_New_Functions| Adding New Functions]] documentation.
  +
  +
The names of all the functions in the Context can be displayed by placing
  +
  +
TEXT(join(',',Context()->functions->names));
  +
  +
into the body of a PG problem.
  +
  +
In addition to the methods listed above, the <code>functions</code> object has the following:
  +
  +
{| class="wikitable"
  +
! Method !! Description
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>Context()->functions->disable("name")</code>
  +
| style="padding:5px" | Marks the named function(s) or category of functions so that they can't be used in student answers.
  +
  +
|- style="vertical-align:top"
  +
| style="padding:5px; white-space:nowrap" | <code>Context()->functions->enable("name")</code>
  +
| style="padding:5px" | Re-enabled the named function(s) or category of functions so that they ''can'' be used in student answers.
  +
  +
|}
  +
  +
=== Operators ===
  +
  +
The operators that are allowed within a student's answer are controlled by the Context. All the pre-defined Contexts include the standard operators like <code>+</code> and <code>-</code> for addition and subtraction, <code>*</code> and <code>/</code> for multiplication and division, <code>^</code> or <code>**</code> for exponentiation, <code>!</code> for factorial, and <code>,</code> for forming lists. Some contexts include <code>U</code> for taking unions, <code>.</code> for taking dot products, and <code>&gt;&lt;</code> for taking cross products.
  +
  +
In some problems, you may want to remove some operators so that students can't enter them. For example, if you want to have the student compute the value of an expression, you would not want her to be able to include the operations from that expression in her answer. To remove a function, use the <code>undefine()</code> method. For instance,
  +
  +
Context()->operators->undefine("^","**");
  +
  +
makes exponentiation unavailable to the student. (Note: the operations still are recognized by MathObjects, but the student will be told they are not available in this problem if they are used. The <code>remove()</code> method would remove the operators entirely, making them unknown to MathObjects, so the error message would be less useful to the student.)
  +
  +
To make the operator available again, use <code>redefine</code>, e.g.
  +
  +
Context()->operators->redefine("^","**");
  +
  +
Note that multiplication and division have several forms (in order to make a non-standard precedence that allows things like <code>sin(2x)</code> to be entered as <code>sin 2x</code>). So if you want to disable them you need to include all of them. E.g.,
  +
  +
Context()->operators->undefine('*',' *','* ');
  +
Context()->operators->undefine('/',' /','/ ','//');
  +
  +
would be required in order to make multiplication and division unavailable.
  +
  +
The names of all the operators in the Context can be displayed by placing
  +
  +
TEXT(join(',',Context()->operators->names));
  +
  +
into the body of a PG problem.
  +
  +
The [https://github.com/openwebwork/pg/tree/macros pg/macros/] directory contains a number of predefined contexts that limit the operations that can be performed in a student answer. For example, the <code>contextLimitedNumeric.pl</code> file defines contexts in which students can enter numbers, but no operations, so they would have to reduce their answer to a single number by hand. There are limited contexts for complex numbers, points, and vectors, and there are also specialized contexts for entering polynomials, or where powers are restricted in various ways.
  +
  +
=== Reduction Rules ===
  +
  +
When random numbers are used as coefficients in Formulas, it is possible to get situations like <code>1 x^2 + -3 x + 0</code>, and it looks bad to have the coefficient of 1, the <code>+ -</code>, and the <code>+ 0</code> as part of the formula. For that reason, the Formula class has a <code>reduce()</code> method that will normalize the Formula to remove such issues. The reduction rules are controlled through the Context's <code>reduction</code> object. This lists the various reduction rules and determines which ones are active. You can turn these on and off using the <code>set()</code> method, as in
  +
  +
Context()->reductions->set("(-x)-y" => 0);
  +
  +
to turn off the reduction that converts <code>(-x)-y</code> to <code>-(x+y)</code>. As usual, you can set the values for multiple rules in one <code>set()</code> call.
  +
  +
Because the <code>set()</code> call is rather verbose, there are shorthands for turning reduction rules on and off via the <code>reduce()</code> and <code>noreduce()</code> methods. For example,
  +
  +
Context()->noreduce("(-x)-y");
  +
  +
is equivalent to the <code>set()</code> above. Again, you can supply multiple rules at once:
  +
  +
Context()->noreduce("(-x)-y","(-x)+y");
  +
  +
These commands set the reduction rules globally, so they affect all reductions that follow. It is also possible, however, to temporarily suspend certain reduction rules during the reduction process for a specific Formula, as in the following.
  +
  +
$f->reduce("(-x)-y" => 0, "(-x)+y" => 0);
  +
  +
which turns off the reduction rules only for this one reduction.
  +
  +
There are two reductions that occur during the parsing of any formula: the first is that if an operation occurs between two constants, the operation is performed and the result is put into the Formula instead; the second is that if a function call is made on a constant value, the result of the function call is used instead. For example,
  +
  +
$a = 2; $b = 3;
  +
$f = Formula("$a * $b * x + 4 * $a");
  +
$g = Formula("abs(-$b)");
   
* <code>Numeric</code> (variable x, no complex numbers, points, etc.) -- this is the default context
 
  +
would produce the formula <code>6 * x + 8</code> rather than <code> 2 * 2 * x + 4 * 2</code> for <code>$f</code>, and <code>3</code> rather than <code>abs(-3)</code> for <code>$g</code>.
* <code>Complex</code> (variable z, i = \sqrt{-1}, no points, etc.)
 
* <code>Point</code> (another name for Vector context, but angle brackets are not allowed)
 
* <code>Vector</code> (variables x, y, z, angle brackets form vectors, i, j, k are unit coordinate vectors, etc.)
 
* <code>Vector2D</code> (same as Vector, but with i and j in 2D)
 
* <code>Matrix</code> (square brackets form matrices: [[0,1],[1,0]] )
 
* <code>Interval</code> (subsets of the real line: parens and brackets form intervals, finite subsets, (5,6] ,(-infinity,6), {5,6} etc. )
 
   
Another method of selecting a more nuanced context is to include one of the "context" macro packages using the syntax.
 
  +
These operations are controlled by two Context flags (not reduction rules, since they apply to all parsing, not just <code>reduce()</code> calls). These are the <code>reduceConstants</code> and <code>reduceConstantFunctions</code> flags. You can unset these to prevent those reductions from occurring automatically, as in
   
loadMacros("contextLimitedPolynomials.pl");
 
  +
Context()->flags->set(
  +
reduceConstants => 0,
  +
reduceConstantFunctions => 0,
  +
);
  +
$f = Formula("(1+sqrt(5))/2)");
   
This example would specify that only "simplified" polynomials in <code> a_n x^n + ... +a_0 </code> would be allowed as answers. Inspecting the file "contextLimitedPolynomials.pl" is one way to learn how to modify a context to meet your
 
  +
in which case <code>$f</code> would remain <code>(1 + sqrt(5))/2</code> rather than the usual <code>1.61803</code>.
requirements.
 
   
==Common Manipulations of Context==
 
  +
The names of all the reduction rules in the Context can be displayed by placing
   
<p style="border: 1px solid black; padding: 3px; background-color: rgb(238, 238, 238);"> The <strong>Context</strong> of a problem determines how variables are interpreted, sets the default variable(s), determines default constants available in the problem, and sets appropriate default system values such as the tolerance for student errors, etc. </p>
 
  +
TEXT(join(',',Context()->reductions->names));
<p> The most commonly used Contexts are listed in this [context list summary|ContextList]. The following gives the most common changes that we need to make to the Context in a problem. A more advanced list of information about the Context is [also available|$@WIKIVIEWBYID*10@$&amp;page=ModifyingContext], as is an advanced reference page that [lists all Context flags|$@WIKIVIEWBYID*146@$&amp;page=ContextFlags]. </p>
 
===Types Of Context Changes===
 
   
====Context changes for Formulas====
 
  +
into the body of a PG problem. There is also an annotated available on the [[Reduction rules for MathObject Formulas]] page.
<ul>
 
<li> <strong>Adding variables</strong> <br /> <dl> <dt> In the Numeric Context, the default variable is <code>x</code>. To add to the variable(s) available in the Context, use the <code>variables-&gt;add()</code> method to specify the variable(s) to add and its (their) type, as in the following: </dt> <dd> <code>Context()-&gt;variables-&gt;add(y=&gt;'Real'); </code></dd><dd> <code>Context()-&gt;variables-&gt;add(y=&gt;'Real',z=&gt;'Real'); </code></dd><dd> <code>Context()-&gt;variables-&gt;add(z=&gt;'Complex'); </code></dd></dl> </li>
 
<li> <strong>Setting variables</strong> <br /> <dl> <dt> To set the variable(s) in the Context (replacing the default variable(s)), use the <code>variables-&gt;are()</code> method, as shown in the following:</dt> <dd> <code>Context()-&gt;variables-&gt;are(y=&gt;'Real'); </code></dd><dd> <code>Context()-&gt;variables-&gt;are(y=&gt;'Real',z=&gt;'Real'); </code></dd><dd> <code>Context()-&gt;variables-&gt;are(z=&gt;'Complex'); </code></dd></dl> </li>
 
<li> <strong>Setting variable limits</strong> <br /> The limits for the variables in the Context determine the values that may be used to determine the correctness of a Formula. If a Formula is not well-defined on the default range ![-2,2], it may be useful to set a different range for the variable(s) in the Context: <dl> <dt> If the only variable in the Context is <code>x</code>,</dt> <dd> <code>Context()-&gt;variables-&gt;set(x=&gt;{limits=&gt;![-1,1]});</code> </dd> <dt> We can similarly set limits for multiple variables at once, if they have been defined in the Context:</dt> <dd> <code>Context()-&gt;variables-&gt;set(x=&gt;{limits=&gt;![-1,1],y=&gt;{limits=&gt;![-1,1]});</code> </dd> </dl> </li>
 
</ul>
 
====Context changes for Strings====
 
<ul>
 
<li> <strong>Adding Strings to the Context</strong> <br /> By default there are a limited number of Strings that are predefined in the Context. In the Numeric Context (the default) these include <code>inf</code>, <code>infinity</code>, and <code>DNE</code>. Because they exist in the Context, students can enter these as answers without generating error messages (though, of course, the answer may be incorrect). To add other Strings to the Context, we use the <code>strings-&gt;add()</code> method: <dl> <dt> To add the string &quot;Continuous&quot; to the Context, </dt> <dd> <code>Context()-&gt;strings-&gt;add(Continuous=&gt;{});</code><br /> </dd> <dt> To add the strings &quot;True&quot; and &quot;T&quot; to the Context, making &quot;T&quot; be an alias for &quot;True&quot; (so that a student could enter either &quot;True&quot; or &quot;T&quot; as an answer and have either marked correct),</dt> <dd> <code>Context()-&gt;strings-&gt;add(True=&gt;{},T=&gt;{alias=&gt;'True'});</code><br /> </dd> <dt> And to add &quot;True&quot;, &quot;False&quot; and aliases for both,</dt> <dd> <code>Context()-&gt;strings-&gt;add(True=&gt;{},False=&gt;{},T=&gt;{alias=&gt;'True'},F=&gt;{alias=&gt;'False'});</code><br /> </dd> </dl> </li>
 
<li> <strong>Changing case-sensitivity of Strings</strong> <br /> By default, !WeBWorK regards String objects as being case-insensitive. Thus if the correct answer to a problem is the String object &quot;True&quot;, a student could respond &quot;True&quot; or &quot;true&quot; and have either answer marked correct. To change this behavior, we set the <code>caseSensitive</code> flag <em>when adding the variable to the Context</em>: <dl> <dd> <code>Context()-&gt;strings-&gt;add(True=&gt;{caseSensitive=&gt;1});</code> </dd> </dl> </li>
 
</ul>
 
====Other useful Context changes====
 
<ul>
 
<li> <strong>Preserving constant values in Formulas</strong><br /> By default, !WeBWorK will reduce constant expressions that are substituted into Formulas, which is not always what we want to do. This is a somewhat subtle point, which may be easiest to consider in the context of an example. <dl> <dt> Suppose that we have the !WeBWorK code </dt> <dd> <code>$f = Formula(&quot;sin(x)&quot;);<br /> $f0 = $f-&gt;eval(x=&gt;pi/6);</code> </dd> <dt> Then we are substituting the Real value pi/6 into the sine function, and <code>$f0</code> is accordingly a Real value (0.5) which will be displayed as a decimal. We can make !WeBWorK display the unreduced expression sin(pi/6) by setting the <code>reduceConstants</code> flag in the Context and <em>substituting</em> a Formula for <code>x</code> instead of <em>evaluating</em> the expression: </dt> <dd> <code>Context()-&gt;flags-&gt;set(reduceConstants=&gt;0);<br /> $f = Formula(&quot;sin(x)&quot;)<br /> $f0 = $f-&gt;substitute(x=&gt;Formula(&quot;pi/6&quot;));</code> </dd> <dt> will result in the Formula for pi/6 being substituted into the function <code>$f</code> to obtain a new Formula, and when it is shown in the problem the expression will be displayed as <code>sin(pi/6)</code>, not in the reduced form (0.5). </dt> </dl> </li>
 
</ul>
 
   
  +
== See Also ==
   
  +
* [[Common Contexts]]
  +
* [[Context flags]]
  +
* [[Specialized contexts]]
  +
* [[Modifying contexts (advanced)]]
   
  +
[[Category:Contexts]]
 
[[Category:MathObjects]]
 
[[Category:MathObjects]]
 
[[Category:AIMWeBWorK Working Groups]]
 
[[Category:AIMWeBWorK Working Groups]]

Latest revision as of 13:44, 22 February 2023

What is a Context?

Every webwork problem has a collection of variables and constants that are part of the problem, and a setting in which these reside. For example, some problems use vector-valued formulas, and others might be about intervals on the real line. Some questions allow students to perform numerical calculations as part of their answers, while others require that they simplify their answers to a single number before entering it. In some questions a less-than and greater-than signs might be used to describe intervals (e.g., x < 5) while in others they are used to delimit vectors (as in <3,-1,2>). Similarly, in a problem on vectors, the letter i might represent the coordinate unit vector along the [math]x[/math]-axis, while in a question on complex numbers, i might represent the complex number [math]\sqrt{-1}[/math].

All of these features represent the context of the problem, and MathObjects maintains this information in a special Context object. The Context object (or simply Context) holds information about what variables are defined and their types and domains, what constants are available, what functions can be applied and what operations performed, what words (like NONE and DNE) are available, and what interpretation to give to the various symbols and delimiters that the student can type.

There are predefined contexts for working with things like real numbers, complex numbers, intervals, vectors, matrices, and so on. There are also a large number of extensions that implement specialized Contexts for particular purposes: scientific notation, currency values, inequalities, fractions, polynomials, chemical reactions, and so forth. It is possible to modify an existing Context to suit the needs of your problems, and you can even create your own custom Contexts (though this is an advanced topic).


Selecting a Context

Each Context has a name, and you select a context by adding the command

 Context("name");

at the top of a problem file, where name is the name of the desired Context. The pre-defined contexts are listed on the Common Contexts page, and these include Numeric, Complex, Vector, Matrix, and Interval.

There are a number of Contexts implemented as extensions to the MathObjects library. These are in the pg/macros/ directory; files whose names begin with context implement specialized Contexts. Most of these are listed in the Specialized contexts documentation. In order to use one of these additional Contexts, include the macro file for that Context in the loadMacros() call at the top of your file. For example:

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

loads the contextLimitedPolynomial.pl macro file, which implements the LimitedPolynomial Context, which requires students to specify only "simplified" polynomials in the form [math]a_n x^n + ... + a_0 [/math] as their answers. Inspecting files like contextLimitedPolynomial.pl is one way to learn how to modify a context to meet your requirements, though that is an advanced topic.


Changing Context Settings

Since the Context stores information about things like the variables and constants for the problem, you need a way to specify what these are. You do this by using methods of the Context object. In order to obtain a reference to the current Context, use the Context() function with no context name; this returns the context that is active.

   $context = Context();

The Context give you access to the various types of information that it stores via methods for each type, such as Context()->variables is a reference to an object that holds the information about the variables in the Context, while Context()->constants is a reference to the object that manages the constants in the Context. Each of these in turn has methods for adding, modifying, removing, and listing the values that they store. For example,

   Context()->variables->add(t => 'Real');

adds a new variable t that is a Real value.

All the types of data listed below have the following methods:

Method Description
add(name => data) for adding new items
are(name => data) for removing all current items and adding new ones
set(name => data) for modifying existing items
get(name) for getting the data defining a particular item
replace(name => data) for replacing an existing definition by a new one
undefine(name) for making an item be undefined, but still recognized
redefine(name) for making the item be defined again
remove(name) for deleting a particular item
clear for removing all current definitions
names for retrieving the names of all the defined items
all for retrieving the entire collection of data for all items

These are explained in more detail in the Modifying contexts (advanced) documentation. Several of these are used in the examples below, and can serve as an introduction to their use.

In addition to direct calls to the data objects, it is possible to modify the Context in other ways. For example, some function calls modify the Context, e.g.,

   Parser::Number::NoDecimals(Context());

Also, some macro files provide mechanisms for changing the context, as in the following,

loadMacros("contextLimitedPowers.pl");
Context()->operators->set(@LimitedPowers::OnlyIntegers);

which sets the operators to limit exponents to only integer values.


Variables

Each Context comes with one or more pre-defined variables. For example the Numeric Context includes the variable [math]x[/math], while the Complex Context has the variable [math]z[/math]. The Point and Vector Contexts have variables [math]x[/math], [math]y[/math] and [math]z[/math].

To add a new variable to a context, use

   Context()->variables->add(name => type);

where name is the name of the variable to be added, and type is the kind of MathObject that is to be stored. The valid types are "Real", "Complex", "Point2D", "Point3D", "Vector2D", "Vector3D", "Parameter", or an instance of a MathObject of the appropriate type. You can also add multiple variables at once. For example,

   Context()->variables->add(y => "Real");
   Context()->variables->add(v => Vector(1,2,3,4));
   Context()->variables->add(u => "Complex", w => "Complex");

adds a real variable named y, a variable v holding a four-dimensional vector, and two complex variables, u and w.

You can specify the domain of a variable using the set() method, as in the following,

   Context()->variables->set(x => {limits => [3,5]});

which sets the limits for [math]x[/math] to be from 3 to 5. Again, multiple variables can be set at once.

You can set both the type and the limits at once when you add the variable, as in

   Context()->variables->add(t => ["Real",limits => [0,1]]);

If the type of a variable is "Parameter", then this variable is used as an adaptive parameter, meaning WeBWorK will try to pick a value of the variable so that the student's answer matches the professor's answer with that value set. Note that student's can't enter adaptive parameters of their own; only the correct answer can include them. Also note that adaptive parameters only work in a linear setting, e.g., [math]af+b[/math], where [math]a[/math] and [math]b[/math] are the adaptive parameters, and [math]f[/math] is the basic correct formula. That is, you can adapt for linear multiples, and constant addition. Once the formulas have been compared, the adapted value for the parameter can be obtained using the value() method described below.

A variable can be removed via the remove() method:

   Context()->variables->remove("t");

Finally, the names of all the variables in the Context can be displayed by placing

   TEXT(join(',',Context()->variables->names));

into the body of a PG problem.

In addition to the methods shown above, the variables object has the following methods:

Method Description
Context()->variables->type("name") Returns the type of the variable (this is a typeRef hash; see reference needed).
Context()->variables->value("name") Returns the value of an adaptive parameter, once it has been used in a Formula comparison.
Context()->variables->parameters Returns an array of the names of all the adaptive parameters in the Context.
Context()->variables->variables Returns an array of the names of all the variables (i.e., non-adaptive-parameters) in the Context.

Constants

The values of variables like [math]\pi[/math] and [math]e[/math] are stored in the constants data of the Context. The values of pi and e are defined in all the main Contexts. The Vector and Matrix Contexts include i, j, and k, which represent the coordinate unit vectors, while the Complex Context has i defined as the imaginary number [math]i = \sqrt{-1}[/math]. The Interval context has a constant R which is equal to the whole real line -- the interval (-infinity,infinity).

Constants are added to the Context via the add() method. The data is the value for the constant, and you can set several constants at once. For example:

   Context()->constants->add(tau => Real(pi*2));
   Context()->constants->add(n => Real(3), m => Real(2));

Usually when a Formula containing a constant is parsed, the name of the constant is retained, so Formula("sin(pi)") would remain as sin(pi) rather than 0. You can use the set() method to set the keepName parameter for a constant to change this so that the value is substituted into the Formula when it is used. E.g.,

   Context()->constants->set(pi => {keepName => 0});
   $f = Formula("sin(pi)");

would be the same as having set $f = Formula("0").

A constant can be removed via the remove() method:

   Context()->constants->remove("pi");

Finally, the names of all the constant in the Context can be displayed by placing

   TEXT(join(',',Context()->constants->names));

into the body of a PG problem.

In addition to the methods shown above, the constants object has the following methods:

Method Description
Context()->variables->value("name") Returns the value of the constant.

Strings

Most Contexts include some special words that the students can type, like NONE or DNE (for "does not exist"). These can be used in problems that ask for lists that might be empty (where NONE can be used in that case), or for limit problems where the limit might not exist. These two words are available in all the pre-defined contexts, and also the word infinity, which generates the Infinity object, with inf as an alias for the complete word. In general, words like these are not case-sensitive, so INF would produce infinity, as would InfINIty or any other capitalization of the word.

You can add your own words to the Context via the add() method. Usually, there is no data, so you simply use {} as the definition. To make a string case sensitive, use {caseSensitive =>1} as the data. To make a word create infinity, use {infinite => 1}. To make one word mean the same as another, use {alias => "name"}. For example,

   Context()->strings->add(A => {}, B => {}, C => {}, D => {});
   Context()->strings->add(True => {}, False => {}, T => {alias => "True"}, F => {alias => "False"});
   Context()->strings->add(WeBWorK => {caseSensitive => 1});
   Context()->strings->add(unbounded => {infinite => 1});

Here, A, B, C, and D are now valid answers for a student to type, but they could also be a, b, c, or d. True and False (with any capitalization) are now available as well, along with T as an alternative to True and F as an alternative for False. The word WeBWorK can be entered, but only with this capitalization (as it should be). Finally, unbounded will produce the positive Infinity object (so -unbounded would be the negative Infinity object).

A string can be removed via the remove() method:

   Context()->strings->remove("DNE");

Finally, the names of all the constant in the Context can be displayed by placing

   TEXT(join(',',Context()->strings->names));

into the body of a PG problem.

Flags

Many settings for the Context are stored as flags, which are parameters that control special functions of the parsing process, the answer checkers, or other features of the MathObjects library. For example, the default tolerance for numeric comparisons is stored as a flag, as are the default limits for variable ranges. Other values include things like whether to display Vectors using [math]ijk[/math]-notation rather than coordinate form with angle-bracket delimiters, and how to show student answers in the "Entered" and "Preview" columns of the results table when they submit their answers.

You do not usually add new flags (though you could if you wanted to keep track of information for your own custom MathObject classes). The most common actions are to set or get the values of the flags. To set a flag, use the set() with the data being the name of the flag to set and its new value. As with the previous case, you can set several flags at once.

  Context()->flags->set(tolerance => .005);
  Context()->flags->set(
     reduceConstants => 0,
     reduceConstantFunctions => 0,
  );
  Context()->flags->set(formatStudentAnswers=>"parsed");

To get the value of a flag, you could use the get method, but since this is such a common operation, there is a shorthand. For example,

   $tol = Context()->flag("tolerance");

gets the current tolerance value from the Context.

Note that MathObject and answer checkers often can override the settings of the Context itself. That means you may need to check the properties of an MathObject and the properties of the active answer checker (if there is one) before resorting to the Context's value. MathObjects gives you an easy way to do that, however, with the getFlag() method of a MathObject. For example, if you are writing a custom answer checker, you could use

   ANS($mo->cmp(checker => sub {
     my ($correct,$student,$ansHash) = @_;
     my $tol = $correct->getFlag("tolerance",.001);
     ... (use $tol here) ...
     return $score;
   }));

to obtain the value of the tolerance. This will be taken from $correct if it was set there, otherwise from the current answer checker's flags as passed to $mo->cmp(), and then from the Context for $mo. If the flag is not set in any of those, the value .001 is used.

The names of all the constants in the Context can be displayed by placing

   TEXT(join(',',Context()->flags->names));

into the body of a PG problem. Note that different contexts may have different flags, and that some flags that could be set may not currently have any value in the Context. Most of the important flags are described in the Context flags documentation.

Functions

The Context includes information about the functions that are available. For example, the Complex Context has Re() and Im() functions, as well as arg(), mod(), and conj(), and the Vector Context has norm() and unit(). These are not available in the Numeric context.

In some problems, you may want to remove some functions so that students can't enter them. For example, if you want to have the student evaluate the sine function at a particular value, you would not want her to be able to use sin() in her answer or that would defeat the purpose. To remove a function, use the disable() method. For instance,

   Context()->functions->disable("sin");

makes the sin() function unavailable to the student. (Note: the function is still recognized by MathObjects, but the student will be told it is not available in this problem if it is used. The remove() method would remove the function entirely, making it unknown to MathObjects, so the error message would be less useful to the student.)

To make the function available again, use enable, e.g.

   Context()->functions->enable("sin");

You can disable or enable more than one function at a time by listing their names separated by commas. There are also categories of functions that you can disable or enable all at once. A full list is available in Answer Checkers and the Context. Some of the common ones are Trig to disable all trigonometric functions, Numeric to disable things like ln() and sqrt(), Complex to disable the complex functions, and All to disable all the functions. For example,

   Context()->functions->disable("All");
   Context()->functions->enable("sqrt");

would disable all functions and allow only the square root function.

Note that if you disable the sqrt() function, you may want to disable the exponentiation operators so that students can't use a^(1/2) or a**.5 to produce square roots. Similarly, if you disable abs(), you would want to remove the absolute value vertical bars so that students can't use |a|. See Answer Checkers and the Context for details.

It is possible to add new functions in several ways. The pg/macros/parserFunctions.pl file implements an easy way to add functions to a Context using Formula objects. Functions written in Perl code can be added to the context using the techniques outlined in the second example on the AddingFunctions page. Alternatively, functions defined using Perl code can be added to the Context, as described in the Adding New Functions documentation.

The names of all the functions in the Context can be displayed by placing

   TEXT(join(',',Context()->functions->names));

into the body of a PG problem.

In addition to the methods listed above, the functions object has the following:

Method Description
Context()->functions->disable("name") Marks the named function(s) or category of functions so that they can't be used in student answers.
Context()->functions->enable("name") Re-enabled the named function(s) or category of functions so that they can be used in student answers.

Operators

The operators that are allowed within a student's answer are controlled by the Context. All the pre-defined Contexts include the standard operators like + and - for addition and subtraction, * and / for multiplication and division, ^ or ** for exponentiation, ! for factorial, and , for forming lists. Some contexts include U for taking unions, . for taking dot products, and >< for taking cross products.

In some problems, you may want to remove some operators so that students can't enter them. For example, if you want to have the student compute the value of an expression, you would not want her to be able to include the operations from that expression in her answer. To remove a function, use the undefine() method. For instance,

   Context()->operators->undefine("^","**");

makes exponentiation unavailable to the student. (Note: the operations still are recognized by MathObjects, but the student will be told they are not available in this problem if they are used. The remove() method would remove the operators entirely, making them unknown to MathObjects, so the error message would be less useful to the student.)

To make the operator available again, use redefine, e.g.

   Context()->operators->redefine("^","**");

Note that multiplication and division have several forms (in order to make a non-standard precedence that allows things like sin(2x) to be entered as sin 2x). So if you want to disable them you need to include all of them. E.g.,

   Context()->operators->undefine('*',' *','* ');
   Context()->operators->undefine('/',' /','/ ','//');

would be required in order to make multiplication and division unavailable.

The names of all the operators in the Context can be displayed by placing

   TEXT(join(',',Context()->operators->names));

into the body of a PG problem.

The pg/macros/ directory contains a number of predefined contexts that limit the operations that can be performed in a student answer. For example, the contextLimitedNumeric.pl file defines contexts in which students can enter numbers, but no operations, so they would have to reduce their answer to a single number by hand. There are limited contexts for complex numbers, points, and vectors, and there are also specialized contexts for entering polynomials, or where powers are restricted in various ways.

Reduction Rules

When random numbers are used as coefficients in Formulas, it is possible to get situations like 1 x^2 + -3 x + 0, and it looks bad to have the coefficient of 1, the + -, and the + 0 as part of the formula. For that reason, the Formula class has a reduce() method that will normalize the Formula to remove such issues. The reduction rules are controlled through the Context's reduction object. This lists the various reduction rules and determines which ones are active. You can turn these on and off using the set() method, as in

   Context()->reductions->set("(-x)-y" => 0);

to turn off the reduction that converts (-x)-y to -(x+y). As usual, you can set the values for multiple rules in one set() call.

Because the set() call is rather verbose, there are shorthands for turning reduction rules on and off via the reduce() and noreduce() methods. For example,

   Context()->noreduce("(-x)-y");

is equivalent to the set() above. Again, you can supply multiple rules at once:

   Context()->noreduce("(-x)-y","(-x)+y");

These commands set the reduction rules globally, so they affect all reductions that follow. It is also possible, however, to temporarily suspend certain reduction rules during the reduction process for a specific Formula, as in the following.

   $f->reduce("(-x)-y" => 0, "(-x)+y" => 0);

which turns off the reduction rules only for this one reduction.

There are two reductions that occur during the parsing of any formula: the first is that if an operation occurs between two constants, the operation is performed and the result is put into the Formula instead; the second is that if a function call is made on a constant value, the result of the function call is used instead. For example,

   $a = 2; $b = 3;
   $f = Formula("$a * $b * x + 4 * $a");
   $g = Formula("abs(-$b)");

would produce the formula 6 * x + 8 rather than 2 * 2 * x + 4 * 2 for $f, and 3 rather than abs(-3) for $g.

These operations are controlled by two Context flags (not reduction rules, since they apply to all parsing, not just reduce() calls). These are the reduceConstants and reduceConstantFunctions flags. You can unset these to prevent those reductions from occurring automatically, as in

   Context()->flags->set(
     reduceConstants => 0,
     reduceConstantFunctions => 0,
   );
   $f = Formula("(1+sqrt(5))/2)");

in which case $f would remain (1 + sqrt(5))/2 rather than the usual 1.61803.

The names of all the reduction rules in the Context can be displayed by placing

   TEXT(join(',',Context()->reductions->names));

into the body of a PG problem. There is also an annotated available on the Reduction rules for MathObject Formulas page.

See Also