Forum archive 2000-2006

Michael Gage - PGanswermacros.pl

Michael Gage - PGanswermacros.pl

by Arnold Pizer -
Number of replies: 0
inactiveTopicPGanswermacros.pl topic started 5/22/2000; 9:57:29 PM
last post 7/17/2000; 10:47:29 AM
userMichael Gage - PGanswermacros.pl  blueArrow
5/22/2000; 9:57:29 PM (reads: 4410, responses: 1)


NAME

    PGanswermacros.pl -- located in the courseScripts directory


SYNPOSIS

    Number Answer Evaluators:
num_cmp() -- uses an input hash to determine parameters
        std_num_cmp(), std_num_cmp_list(), std_num_cmp_abs, std_num_cmp_abs_list()
frac_num_cmp(), frac_num_cmp_list(), frac_num_cmp_abs, frac_num_cmp_abs_list()
arith_num_cmp(), arith_num_cmp_list(), arith_num_cmp_abs, arith_num_cmp_abs_list()
strict_num_cmp(), strict_num_cmp_list(), strict_num_cmp_abs, strict_num_cmp_abs_list()
numerical_compare_with_units() -- requires units as part of the answer
std_num_str_cmp() -- also accepts a set of strings as possible answers
    Function Answer Evaluators:
fun_cmp() -- uses an input hash to determine parameters
        function_cmp(), function_cmp_abs()
function_cmp_up_to_constant(), function_cmp_up_to_constant_abs()
multivar_function_cmp()
    String Answer Evaluators:
str_cmp() -- uses an input hash to determine parameters
        std_str_cmp(), std_str_cmp_list(), std_cs_str_cmp(), std_cs_str_cmp_list()
strict_str_cmp(), strict_str_cmp_list()
ordered_str_cmp(), ordered_str_cmp_list(), ordered_cs_str_cmp(), ordered_cs_str_cmp_list()
unordered_str_cmp(), unordered_str_cmp_list(), unordered_cs_str_cmp(), unordered_cs_str_cmp_list()
    Miscellaneous Answer Evaluators:
checkbox_cmp()
radio_cmp()


DESCRIPTION

This file adds subroutines which create "answer evaluators" for checking answers. Each answer evaluator accepts a single input from a student answer, checks it and creates an output hash %ans_hash with seven or eight entries (the preview_latex_string is optional). The output hash is now being created with the AnswerHash package "class", which is located at the end of this file. This class is currently just a wrapper for the hash, but this might change in the future as new capabilities are added.

                    score           =>  $correctQ,
correct_ans => $originalCorrEqn,
student_ans => $modified_student_ans
original_student_ans => $original_student_answer,
ans_message => $PGanswerMessage,
type => 'typeString',
preview_text_string => $preview_text_string,
preview_latex_string => $preview_latex_string
    $ans_hash{score}            --  a number between 0 and 1 indicating
whether the answer is correct. Fractions
allow the implementation of partial
credit for incorrect answers.
$ans_hash{correct_ans} -- The correct answer, as supplied by the
instructor and then formatted. This can
be viewed by the student after the answer
date.
$ans_hash{student_ans} -- This is the student answer, after reformatting;
for example the answer might be forced
to capital letters for comparison with
the instructors answer. For a numerical
answer, it gives the evaluated answer.
This is displayed in the section reporting
the results of checking the student answers.
$ans_hash{original_student_ans} -- This is the original student answer.
This is displayed
on the preview page and may be used for sticky
answers.
$ans_hash{ans_message} -- Any error message, or hint provided by the
answer evaluator.
This is also displayed in the section reporting
the results of checking the student answers.
$ans_hash{type} -- A string indicating the type of answer evaluator.
This
helps in preprocessing the student answer
for errors.
Some examples:
'number_with_units'
'function'
'frac_number'
'arith_number'
$ans_hash{preview_text_string} -- This typically shows how the student
answer was parsed. It is
displayed on the preview page. For a student
answer of 2sin(3x)
this would be 2*sin(3*x). For string answers
it is typically the
same as $ans_hash{student_ans}.
$ans_hash{preview_latex_string} -- THIS IS OPTIONAL. This is latex version
of the student answer
which is used to show a typeset view on the
answer on the preview
page. For a student answer of 2/3, this would
be \frac{2}{3}.

Technical note: the routines in this file are not actually answer evaluators. Instead, they create answer evaluators. An answer evaluator is an anonymous subroutine, referenced by a named scalar. The routines in this file build the subroutine and return a reference to it. Later, when the student actually enters an answer, the problem processor feeds that answer to the referenced subroutine, which evaluates it and returns a score (usually 0 or 1). For most users, this distinction is unimportant, but if you plan on writing your own answer evaluators, you should understand this point.

Number Answer Evaluators

Number answer evaluators take in a numerical answer, compare it to the correct answer, and return a score. In addition, they can choose to accept or reject an answer based on its format, closeness to the correct answer, and other criteria. There are two types of numerical answer evaluators: num_cmp(), which takes a hash of named options as parameters, and the "mode"_num_cmp() variety, which use different functions to access different sets of options. In addition, there is the special case of std_num_str_cmp(), which can evaluate both numbers and strings.

Numerical Comparison Options

    correctAnswer       --  This is the correct answer that the student answer will
be compared to. However, this does not mean that the
student answer must match this exactly. How close the
student answer must be is determined by the other
options, especially tolerance and format.
    tolerance       --  These options determine how close the student answer
must be to the correct answer to qualify. There are two
types of tolerance: relative and absolute. Relative
tolerances are given in percentages. A relative
tolerance of 1 indicates that the student answer must
be within 1% of the correct answer to qualify as correct.
In other words, a student answer is correct when
abs(studentAnswer - correctAnswer) <= abs(.01*relpercentTol*correctAnswer)
Using absolute tolerance, the student answer must be a
fixed distance from the correct answer to qualify.
For example, an absolute tolerance of 5 means that any
number which is +-5 of the correct answer qualifies as correct.
Final (rarely used) tolerance options are zeroLevel
and zeroLevelTol, used in conjunction with relative
tolerance. if correctAnswer has absolute value less than
or equal to zeroLevel, then the student answer must be,
in absolute terms, within zeroLevelTol of correctAnswer,
i.e.,
abs(studentAnswer - correctAnswer) <= zeroLevelTol.
In other words, if the correct answer is very near zero,
an absolute tolerance will be used. One must do this to
handle floating point answers very near zero, because of
the inaccuracy of floating point arithmetic. However, the
default values are almost always adequate.
    mode            --  This determines the allowable methods for entering an
answer. Answers which do not meet this requirement will
be graded as incorrect, regardless of their numerical
value. The recognized modes are:
'std' (default) -- allows any expression which evaluates
to a number, including those using
elementary functions like sin() and
exp(), as well as the operations
of
arithmetic (+, -, *, /, ^)
'strict' -- only decimal numbers are allowed
'frac' -- whole numbers and fractions are allowed
'arith' -- arithmetic expressions are allowed, but
no functions
Note that all modes allow the use of "pi" and "e" as
constants, and also the use of "E" to represent scientific
notation.
    format          --  The format to use when displaying the correct and
submitted answers. This has no effect on how answers are
evaluated; it is only for cosmetic purposes. The
formatting syntax is the same as Perl uses for the sprintf()
function. Format strings are of the form '%m.nx' or '%m.nx#',
where m and n are described below, and x is a formatter.
Esentially, m is the minimum length of the field
(make this negative to left-justify). Note that the decimal
point counts as a character when determining the field width.
If m begins with a zero, the number will be padded with zeros
instead of spaces to fit the field.
The precision specifier (n) works differently, depending
on which formatter you are using. For d, i, o, u, x and X
formatters (non-floating point formatters), n is the minimum
number of digits to display. For e and f, it is the number
of
digits that appear after the decimal point (extra digits
will
be rounded; insufficient digits will be padded with spaces--see
'#' below). For g, it is the number of significant digits
to
display.
The full list of formatters can be found in the manpage
for printf(3), or by typing "perldoc -f sprintf" at a
terminal prompt. The following is a brief summary of the
most frequent formatters:
d -- decimal number
ld -- long decimal number
u -- unsigned decimal number
lu -- long unsigned decimal number
x -- hexadecimal number
o -- octal number
e -- floating point number in scientific notation
f -- floating point number
g -- either e or f, whichever takes less space
Technically, g will use e if the exponent is less than -4
or
greater than or equal to the precision. Trailing zeros are
removed in this mode.
If the format string ends in '#', trailing zeros will
be
removed in the decimal part. Note that this is not a standard
syntax; it is handled internally by WeBWorK and not by Perl
(although this should not be a concern to end users).
The default format is '%0.5f#', which displays as a floating
point number with 5 digits of precision and no trailing zeros.
Other useful format strings might be '%0.2f' for displaying
dollar amounts, or '%010d' to display an integer with leading
zeros. Setting format to an empty string ( " ) means no
formatting will be used; this will show 'arbitrary' precision
floating points.

Default Values (As of 7/24/2000) (Option -- Variable Name -- Value)

    Format                  --  $numFormatDefault       --  "%0.5f#"
Relative Tolerance -- $numRelPercentTolDefault -- .1
Absolute Tolerance -- $numAbsTolDefault -- .001
Zero Level -- $numZeroLevelDefault -- 1E-14
Zero Level Tolerance -- $numZeroLevelTolDefault -- 1E-12

num_cmp()

Compares a number or a list of numbers, using a named hash of options to set parameters. This can make for more readable code than using the "mode"_num_cmp() style, but some people find one or the other easier to remember.

ANS( num_cmp( answer or answer_array_ref, options_hash ) );

    1. the correct answer, or a reference to an array of correct answers
2. a hash with the following keys (all optional):
mode -- 'std' (default) (allows any expression evaluating to
a number)
'strict' (only numbers are allowed)
'frac' (fractions are allowed)
'arith' (arithmetic expressions allowed)
format -- '%0.5f#' (default); defines formatting for the
correct answer
tol -- an absolute tolerance, or
relTol -- a relative tolerance
units -- the units to use for the answer(s)
strings -- a reference to an array of strings which are valid
answers (works like std_num_str_cmp() )
zeroLevel -- if the correct answer is this close to zero,
then zeroLevelTol applies
zeroLevelTol -- absolute tolerance to allow when answer is close
to zero
        debug           --  if set to 1, provides verbose listing of
hash entries throughout fliters.
    Returns an answer evaluator, or (if given a reference to an array of
answers), a list of answer evaluators. Note that a reference to an array of
answers results is just a shortcut for writing a separate <code>num_cmp()</code>
for each
answer.

EXAMPLES:

    num_cmp( 5 )                    --  correct answer is 5, using defaults
for all options
num_cmp( [5,6,7] ) -- correct answers are 5, 6, and 7,
using defaults for all options
num_cmp( 5, mode => 'strict' ) -- correct answer is 5, mode is strict
num_cmp( [5,6], relTol => 5 ) -- correct answers are 5 and 6,
both with 5% relative tolerance
num_cmp( 6, strings => ["Inf", "Minf", "NaN"] )
-- correct answer is 6, "Inf", "Minf",
and "NaN" recognized as valid, but
incorrect answers.
num_cmp( "-INF", strings => ["INF", "-INF"] )
-- correct answer is "-INF", "INF" and
numerical expressions recognized as valid,
but incorrect answers.

"mode"_num_cmp() functions

There are 16 functions total, 4 for each mode (std, frac, strict, arith). Each mode has one "normal" function, one which accepts a list of answers, one which uses absolute rather than relative tolerance, and one which uses absolute tolerance and accepts a list. The "std" family is documented below; all others work precisely the same.

 std_num_cmp($correctAnswer) OR
std_num_cmp($correctAnswer, $relPercentTol) OR
std_num_cmp($correctAnswer, $relPercentTol, $format) OR
std_num_cmp($correctAnswer, $relPercentTol, $format, $zeroLevel) OR
std_num_cmp($correctAnswer, $relPercentTol, $format, $zeroLevel, $zeroLevelTol)
    $correctAnswer  --  the correct answer
$relPercentTol -- the tolerance, as a percentage (optional)
$format -- the format of the displayed answer (optional)
$zeroLevel -- if the correct answer is this close to zero, then zeroLevelTol
applies (optional)
$zeroLevelTol -- absolute tolerance to allow when correct answer is close
to zero (optional)
    std_num_cmp() uses standard mode (arithmetic operations and elementary
functions allowed) and relative tolerance. Options are specified by
one or more parameters. Note that if you wish to set an option which
is later in the parameter list, you must set all previous options.
 std_num_cmp_abs($correctAnswer) OR
std_num_cmp_abs($correctAnswer, $absTol) OR
std_num_cmp_abs($correctAnswer, $absTol, $format)
    $correctAnswer      --  the correct answer
$absTol -- an absolute tolerance (optional)
$format -- the format of the displayed answer (optional)
    std_num_cmp_abs() uses standard mode and absolute tolerance. Options
are set as with std_num_cmp(). Note that $zeroLevel and $zeroLevelTol
do not apply with absolute tolerance.
 std_num_cmp_list($relPercentTol, $format, @answerList)
    $relPercentTol      --  the tolerance, as a percentage
$format -- the format of the displayed answer(s)
@answerList -- a list of one or more correct answers
    std_num_cmp_list() uses standard mode and relative tolerance. There
is no way to set $zeroLevel or $zeroLevelTol. Note that no
parameters are optional. All answers in the list will be
evaluated with the same set of parameters.
 std_num_cmp_abs_list($absTol, $format, @answerList)
    $absTol     --  an absolute tolerance
$format -- the format of the displayed answer(s)
@answerList -- a list of one or more correct answers
    std_num_cmp_abs_list() uses standard mode and absolute tolerance.
Note that no parameters are optional. All answers in the list will be
evaluated with the same set of parameters.
 arith_num_cmp(), arith_num_cmp_list(), arith_num_cmp_abs(), arith_num_cmp_abs_list()
strict_num_cmp(), strict_num_cmp_list(), strict_num_cmp_abs(), strict_num_cmp_abs_list()
frac_num_cmp(), frac_num_cmp_list(), frac_num_cmp_abs(), frac_num_cmp_abs_list()

Examples:

    ANS( strict_num_cmp( 3.14159 ) )    --  The student answer must be a number
in decimal or scientific notation which is within .1 percent of 3.14159.
This assumes $numRelPercentTolDefault has been set to .1.
ANS( strict_num_cmp( $answer, .01 ) ) -- The student answer must be a
number within .01 percent of $answer (e.g. 3.14159 if $answer is 3.14159
or $answer is "pi" or $answer is 4*atan(1)).
ANS( frac_num_cmp( $answer) ) or ANS( frac_num_cmp( $answer,.01 )) --
The student answer can be a number or fraction, e.g. 2/3.
ANS( arith_num_cmp( $answer) ) or ANS( arith_num_cmp( $answer,.01 )) --
The student answer can be an arithmetic expression, e.g. (2+3)/7-2^.5 .
ANS( std_num_cmp( $answer) ) or ANS( std_num_cmp( $answer,.01 )) --
The student answer can contain elementary functions, e.g. sin(.3+pi/2)

std_num_str_cmp()

NOTE:This function is maintained for compatibility. num_cmp() with the

'strings' parameter is slightly preferred.

std_num_str_cmp() is used when the correct answer could be either a number or a string. For example, if you wanted the student to evaluate a function at number of points, but write "Inf" or "Minf" if the function is unbounded. This routine will provide error messages that do not give a hint as to whether the correct answer is a string or a number. For numerical comparisons, std_num_cmp() is used internally; for string comparisons, std_str_cmp() is used. String answers must consist entirely of letters except that an initial minus sign is allowed. E.g. "inf" and "-inf" are valid strings where as "too-big" is not.

 std_num_str_cmp( $correctAnswer ) OR
std_num_str_cmp( $correctAnswer, $ra_legalStrings ) OR
std_num_str_cmp( $correctAnswer, $ra_legalStrings, $relPercentTol ) OR
std_num_str_cmp( $correctAnswer, $ra_legalStrings, $relPercentTol, $format ) OR
std_num_str_cmp( $correctAnswer, $ra_legalStrings, $relPercentTol, $format, $zeroLevel
) OR
std_num_str_cmp( $correctAnswer, $ra_legalStrings, $relPercentTol, $format,
$zeroLevel, $zeroLevelTol )
    $correctAnswer      --  the correct answer
$ra_legalStrings -- a reference to an array of legal strings, e.g. ["str1",
"str2"]
$relPercentTol -- the error tolerance as a percentage
$format -- the display format
$zeroLevel -- if the correct answer is this close to zero, then zeroLevelTol
applies
$zeroLevelTol -- absolute tolerance to allow when correct answer is close
to zero

Examples:

ANS( std_num_str_cmp( $ans, ["Inf", "Minf", "NaN"] ) );

ANS( std_num_str_cmp( $ans, ["INF", "-INF"] ) );

Function Answer Evaluators

Function answer evaluators take in a function, compare it numerically to a correct function, and return a score. They can require an exactly equivalent function, or one that is equal up to a constant. They can accept or reject an answer based on specified tolerances for numerical deviation.

Function Comparison Options

    correctEqn  --  The correct equation, specified as a string. It may include
all basic arithmetic operations, as well as elementary
functions. Variable usage is described below.
    Variables   --  The independent variable(s). When comparing the correct
equation to the student equation, each variable will be
replaced by a certain number of numerical values. If
the student equation agrees numerically with the correct
equation, they are considered equal. Note that all
comparison is numeric; it is possible (although highly
unlikely and never a practical concern) for two unequal
functions to yield the same numerical results.
    Limits      --  The limits of evaluation for the independent variables.
Each variable is evaluated only in the half-open interval
[lower_limit, upper_limit). This is useful if the function
has a singularity or is not defined in a certain range.
For example, the function "sqrt(-1-x)" could be evaluated
in [-2,-1).
    Tolerance   --  Tolerance in function comparisons works exactly as in
numerical comparisons; see the numerical comparison
documentation for a complete description. Note that the
tolerance does applies to the function as a whole, not
each point individually.
    Number of   --  Specifies how many points to evaluate each variable at. This
Points is typically 3, but can be set higher if it is felt that
there is a strong possibility of "false positives."
    Maximum     --  Sets the maximum size of the constant of integration. For
Constant of technical reasons concerning floating point arithmetic, if
Integration the additive constant, i.e., the constant of integration, is
greater (in absolute value) than maxConstantOfIntegration
AND is greater than maxConstantOfIntegration times the
correct value, WeBWorK will give an error message saying
that it can not handle such a large constant of integration.
This is to prevent e.g. cos(x) + 1E20 or even 1E20 as being
accepted as a correct antiderivatives of sin(x) since
floating point arithmetic cannot tell the difference
between cos(x) + 1E20, 1E20, and -cos(x) + 1E20.

Technical note: if you examine the code for the function routines, you will see that most subroutines are simply doing some basic error-checking and then passing the parameters on to the low-level FUNCTION_CMP(). Because this routine is set up to handle multivariable functions, with single-variable functions as a special case, it is possible to pass multivariable parameters to single- variable functions. This usage is strongly discouraged as unnecessarily confusing. Avoid it.

Default Values (As of 7/24/2000) (Option -- Variable Name -- Value)

    Variable            --  $functVarDefault            --  'x'
Relative Tolerance -- $functRelPercentTolDefault -- .1
Absolute Tolerance -- $functAbsTolDefault -- .001
Lower Limit -- $functLLimitDefault -- .0000001
Upper Limit -- $functULimitDefault -- 1
Number of Points -- $functNumOfPoints -- 3
Zero Level -- $functZeroLevelDefault -- 1E-14
Zero Level Tolerance -- $functZeroLevelTolDefault -- 1E-12
Maximum Constant -- $functMaxConstantOfIntegration -- 1E8
of Integration

fun_cmp()

Compares a function or a list of functions, using a named hash of options to set parameters. This can make for more readable code than using the function_cmp() style, but some people find one or the other easier to remember.

ANS( fun_cmp( answer or answer_array_ref, options_hash ) );

    1. a string containing the correct function, or a reference to an
array of correct functions
2. a hash containing the following items (all optional):
var -- either the number of variables or a reference
to an
array of variable names (see below)
limits -- reference to an array of arrays of limits
(see below), or:
mode -- 'std' (default) (function must match exactly),
or:
'antider' (function must match up to a constant)
relTol -- (default) a relative tolerance (as a percentage),
or:
tol -- an absolute tolerance for error
numPoints -- the number of points to evaluate the function
at
maxConstantOfIntegration -- maximum size of the constant of integration
zeroLevel -- if the correct answer is this close to zero,
then
zeroLevelTol applies
zeroLevelTol -- absolute tolerance to allow when answer
is close to zero
test_points -- a list of points to use in checking the function, or a list
of lists when there is more than one variable.
params an array of "free" parameters which can be
used to adapt
the correct answer to the submitted answer. (e.g.
['c'] for
a constant of integration in the answer x^3/3
+ c.
debug -- when set to 1 this provides extra information
while checking the
the answer.
    Returns an answer evaluator, or (if given a reference to an array
of answers), a list of answer evaluators

ANSWER:

    The answer must be in the form of a string. The answer can contain
functions, pi, e, and arithmetic operations. However, the correct answer
string follows a slightly stricter syntax than student answers; specifically,
there is no implicit multiplication. So the correct answer must be "3*x" rather
than "3 x". Students can still enter "3 x".

VARIABLES:

    The var parameter can contain either a number or a reference to an array of
variable names. If it contains a number, the variables are named automatically
as follows: 1 variable -- x
2 variables -- x, y
3 variables -- x, y, z
4 or more -- x_1, x_2, x_3, etc.
If the var parameter contains a reference to an array of variable names, then
the number of variables is determined by the number of items in the array. A
reference to an array is created with brackets, e.g. "var => ['r', 's', 't']".
If only one variable is being used, you can write either "var => ['t']" for
consistency or "var => 't'" as a shortcut. The default is one variable, x.

LIMITS:

    Limits are specified with the limits parameter. You may NOT use llimit/ulimit.
If you specify limits for one variable, you must specify them for all variables.
The limit parameter must be a reference to an array of arrays of the form
[lower_limit. upper_limit], each array corresponding to the lower and upper
endpoints of the (half-open) domain of one variable. For example,
"vars => 2, limits => [[0,2], [-3,8]]" would cause x to be evaluated in [0,2)
and
y to be evaluated in [-3,8). If only one variable is being used, you can write
either "limits => [[0,3]]" for consistency or "limits => [0,3]" as a shortcut.

TEST POINTS:

    In some cases, the problem writer may want to specify the points
used to check a particular function. For example, if you want to
use only integer values, they can be specified. With one variable,
you can specify "test_points => [1,4,5,6]" or "test_points => [[1,4,5,6]]".
With more variables, specify the list for the first variable, then the
second, and so on: "vars=>['x','y'], test_points => [[1,4,5],[7,14,29]]".
    If the problem writer wants random values which need to meet some special
restrictions (such as being integers), they can be generated in the problem:
"test_points=>[random(1,50), random(1,50), random(1,50), random(1,50)]".
    Note that test_points should not be used for function checks which involve
parameters (either explicitly given by "params", or as antiderivatives).

EXAMPLES:

    fun_cmp( "3*x" )    --  standard compare, variable is x
fun_cmp( ["3*x", "4*x+3", "3*x**2"] ) -- standard compare, defaults used for
all three functions
fun_cmp( "3*t", var => 't' ) -- standard compare, variable is t
fun_cmp( "5*x*y*z", var => 3 ) -- x, y and z are the variables
fun_cmp( "5*x", mode => 'antider' ) -- student answer must match up to constant
(i.e., 5x+C)
fun_cmp( ["3*x*y", "4*x*y"], limits => [[0,2], [5,7]] ) -- x evaluated in [0,2)
y evaluated in [5,7)

Single-variable Function Comparisons

There are four single-variable function answer evaluators: "normal," absolute tolerance, antiderivative, and antiderivative with absolute tolerance. All parameters (other than the correct equation) are optional.

 function_cmp( $correctEqn ) OR
function_cmp( $correctEqn, $var ) OR
function_cmp( $correctEqn, $var, $llimit, $ulimit ) OR
function_cmp( $correctEqn, $var, $llimit, $ulimit, $relPercentTol ) OR
function_cmp( $correctEqn, $var, $llimit, $ulimit,
$relPercentTol, $numPoints ) OR
function_cmp( $correctEqn, $var, $llimit, $ulimit,
$relPercentTol, $numPoints, $zeroLevel ) OR
function_cmp( $correctEqn, $var, $llimit, $ulimit, $relPercentTol, $numPoints,
$zeroLevel,$zeroLevelTol )
    $correctEqn     --  the correct equation, as a string
$var -- the string representing the variable (optional)
$llimit -- the lower limit of the interval to evaluate the
variable in (optional)
$ulimit -- the upper limit of the interval to evaluate the
variable in (optional)
$relPercentTol -- the error tolerance as a percentage (optional)
$numPoints -- the number of points at which to evaluate the
variable (optional)
$zeroLevel -- if the correct answer is this close to zero, then
zeroLevelTol applies (optional)
$zeroLevelTol -- absolute tolerance to allow when answer is close to zero
    function_cmp() uses standard comparison and relative tolerance. It takes a
string representing a single-variable function and compares the student
answer to that function numerically.
 function_cmp_up_to_constant( $correctEqn ) OR
function_cmp_up_to_constant( $correctEqn, $var ) OR
function_cmp_up_to_constant( $correctEqn, $var, $llimit, $ulimit ) OR
function_cmp_up_to_constant( $correctEqn, $var, $llimit, $ulimit,
$relpercentTol ) OR
function_cmp_up_to_constant( $correctEqn, $var, $llimit, $ulimit,
$relpercentTol, $numOfPoints ) OR
function_cmp_up_to_constant( $correctEqn, $var, $llimit, $ulimit,
$relpercentTol, $numOfPoints,
$maxConstantOfIntegration ) OR
function_cmp_up_to_constant( $correctEqn, $var, $llimit, $ulimit,
$relpercentTol, $numOfPoints,
$maxConstantOfIntegration, $zeroLevel) OR
function_cmp_up_to_constant( $correctEqn, $var, $llimit, $ulimit,
$relpercentTol, $numOfPoints,
$maxConstantOfIntegration,
$zeroLevel, $zeroLevelTol )
    $maxConstantOfIntegration   --  the maximum size of the constant of
integration
    function_cmp_up_to_constant() uses antiderivative compare and relative
tolerance. All options work exactly like function_cmp(), except of course
$maxConstantOfIntegration. It will accept as correct any function which
differs from $correctEqn by at most a constant; that is, if
$studentEqn = $correctEqn + C
the answer is correct.
 function_cmp_abs( $correctFunction ) OR
function_cmp_abs( $correctFunction, $var ) OR
function_cmp_abs( $correctFunction, $var, $llimit, $ulimit ) OR
function_cmp_abs( $correctFunction, $var, $llimit, $ulimit, $absTol ) OR
function_cmp_abs( $correctFunction, $var, $llimit, $ulimit, $absTol,
$numOfPoints )
    $absTol --  the tolerance as an absolute value
    function_cmp_abs() uses standard compare and absolute tolerance. All
other options work exactly as for function_cmp().
 function_cmp_up_to_constant_abs( $correctFunction ) OR
function_cmp_up_to_constant_abs( $correctFunction, $var ) OR
function_cmp_up_to_constant_abs( $correctFunction, $var, $llimit, $ulimit ) OR
function_cmp_up_to_constant_abs( $correctFunction, $var, $llimit, $ulimit,
$absTol ) OR
function_cmp_up_to_constant_abs( $correctFunction, $var, $llimit, $ulimit,
$absTol, $numOfPoints ) OR
function_cmp_up_to_constant_abs( $correctFunction, $var, $llimit, $ulimit,
$absTol, $numOfPoints,
$maxConstantOfIntegration )
    function_cmp_up_to_constant_abs() uses antiderivative compare
and absolute tolerance. All other options work exactly as with
function_cmp_up_to_constant().

Examples:

    ANS( function_cmp( "cos(x)" ) ) --  Accepts cos(x), sin(x+pi/2),
sin(x)^2 + cos(x) + cos(x)^2 -1, etc. This assumes
$functVarDefault has been set to "x".
ANS( function_cmp( $answer, "t" ) ) -- Assuming $answer is "cos(t)",
accepts cos(t), etc.
ANS( function_cmp_up_to_constant( "cos(x)" ) ) -- Accepts any
antiderivative of sin(x), e.g. cos(x) + 5.
ANS( function_cmp_up_to_constant( "cos(z)", "z" ) ) -- Accepts any
antiderivative of sin(z), e.g. sin(z+pi/2) + 5.

multivar_function_cmp

NOTE:this function is maintained for compatibility. fun_cmp() is

slightly preferred.

usage:

    multivar_function_cmp( $answer, $var_reference, options)
$answer -- string, represents function of several variables
$var_reference -- number (of variables), or list reference (e.g. ["var1","var2"]
)
options:
$limit_reference -- reference to list of lists (e.g. [[1,2],[3,4]])
$relPercentTol -- relative percent tolerance in answer
$numPoints -- number of points to sample in for each variable
$zeroLevel -- if the correct answer is this close to zero, then
zeroLevelTol applies
$zeroLevelTol -- absolute tolerance to allow when answer is close
to zero

String Answer Evaluators

String answer evaluators compare a student string to the correct string. Different filters can be applied to allow various degrees of variation. Both the student and correct answers are subject to the same filters, to ensure that there are no unexpected matches or rejections.

String Filters

    remove_whitespace   --  Removes all whitespace from the string.
It applies the following substitution
to the string:
$filteredAnswer =~ s/\s+//g;
    compress_whitespace --  Removes leading and trailing whitespace, and
replaces all other blocks of whitespace by a
single space. Applies the following substitutions:
$filteredAnswer =~ s/^\s*//;
$filteredAnswer =~ s/\s*$//;
$filteredAnswer =~ s/\s+/ /g;
    trim_whitespace     --  Removes leading and trailing whitespace.
Applies the following substitutions:
$filteredAnswer =~ s/^\s*//;
$filteredAnswer =~ s/\s*$//;
    ignore_case         --  Ignores the case of the string. More accurately,
it converts the string to uppercase (by convention).
Applies the following function:
$filteredAnswer = uc $filteredAnswer;
    ignore_order        --  Ignores the order of the letters in the string.
This is used for problems of the form "Choose all
that apply." Specifically, it removes all
whitespace and lexically sorts the letters in
ascending alphabetical order. Applies the following
functions:
$filteredAnswer = join( "", lex_sort(
split( /\s*/, $filteredAnswer ) ) );

str_cmp()

Compares a string or a list of strings, using a named hash of options to set parameters. This can make for more readable code than using the "mode"_str_cmp() style, but some people find one or the other easier to remember.

ANS( str_cmp( answer or answer_array_ref, options_hash ) );

    1. the correct answer or a reference to an array of answers
2. either a list of filters, or:
a hash consisting of
filters - a reference to an array of filters
    Returns an answer evaluator, or (if given a reference to an array of answers),
a list of answer evaluators

FILTERS:

    remove_whitespace   --  removes all whitespace
compress_whitespace -- removes whitespace from the beginning and end of the
string,
and treats one or more whitespace characters in a row
as a
single space (true by default)
trim_whitespace -- removes whitespace from the beginning and end of the
string
ignore_case -- ignores the case of the letters (true by default)
ignore_order -- ignores the order in which letters are entered

EXAMPLES:

    str_cmp( "Hello" )  --  matches "Hello", "  hello" (same as std_str_cmp() )
str_cmp( ["Hello", "Goodbye"] ) -- same as std_str_cmp_list()
str_cmp( " hello ", trim_whitespace ) -- matches "hello", " hello "
str_cmp( "ABC", filters => 'ignore_order' ) -- matches "ACB", "A B C", but not
"abc"
str_cmp( "D E F", remove_whitespace, ignore_case ) -- matches "def" and "d
e f" but not "fed"

"mode"_str_cmp functions

The functions of the the form "mode"_str_cmp() use different functions to specify which filters to apply. They take no options except the correct string. There are also versions which accept a list of strings.

 std_str_cmp( $correctString )
std_str_cmp_list( @correctStringList )
Filters: compress_whitespace, ignore_case
 std_cs_str_cmp( $correctString )
std_cs_str_cmp_list( @correctStringList )
Filters: compress_whitespace
 strict_str_cmp( $correctString )
strict_str_cmp_list( @correctStringList )
Filters: trim_whitespace
 unordered_str_cmp( $correctString )
unordered_str_cmp_list( @correctStringList )
Filters: ignore_order, ignore_case
 unordered_cs_str_cmp( $correctString )
unordered_cs_str_cmp_list( @correctStringList )
Filters: ignore_order
 ordered_str_cmp( $correctString )
ordered_str_cmp_list( @correctStringList )
Filters: remove_whitespace, ignore_case
 ordered_cs_str_cmp( $correctString )
ordered_cs_str_cmp_list( @correctStringList )
Filters: remove_whitespace

Examples

    ANS( std_str_cmp( "W. Mozart" ) )   --  Accepts "W. Mozart", "W. MOZarT",
and so forth. Case insensitive. All internal spaces treated
as single spaces.
ANS( std_cs_str_cmp( "Mozart" ) ) -- Rejects "mozart". Same as
std_str_cmp() but case sensitive.
ANS( strict_str_cmp( "W. Mozart" ) ) -- Accepts only the exact string.
ANS( unordered_str_cmp( "ABC" ) ) -- Accepts "a c B", "CBA" and so forth.
Unordered, case insensitive, spaces ignored.
ANS( unordered_cs_str_cmp( "ABC" ) ) -- Rejects "abc". Same as
unordered_str_cmp() but case sensitive.
ANS( ordered_str_cmp( "ABC" ) ) -- Accepts "a b C", "A B C" and so forth.
Ordered, case insensitive, spaces ignored.
ANS( ordered_cs_str_cmp( "ABC" ) ) -- Rejects "abc", accepts "A BC" and
so forth. Same as ordered_str_cmp() but case sensitive.

Miscellaneous Answer Evaluators (Checkboxes and Radio Buttons)

These evaluators do not fit any of the other categories.

checkbox_cmp( $correctAnswer )

    $correctAnswer  --  a string containing the names of the correct boxes,
e.g. "ACD". Note that this means that individual
checkbox names can only be one character. Internally,
this is largely the same as unordered_cs_str_cmp().

radio_cmp( $correctAnswer )

    $correctAnswer  --  a string containing the name of the correct radio
button, e.g. "Choice1". This is case sensitive and
whitespace sensitive, so the correct answer must match
the name of the radio button exactly.

Filters

A filter is a short subroutine with the following structure. It accepts an AnswerHash, followed by a hash of options. It returns an AnswerHash

    $ans_hash = filter($ans_hash, %options);

See the AnswerHash.pm file for a list of entries which can be expected to be found in an AnswerHash, such as 'student_ans', 'score' and so forth. Other entries may be present for specialized answer evaluators.

The hope is that a well designed set of filters can easily be combined to form a new answer_evaluator and that this method will produce answer evaluators which are are more robust than the method of copying existing answer evaluators and modifying them.

Here is an outline of how a filter is constructed:

    sub filter{
my $rh_ans = shift;
my %options = @_;
assign_option_aliases(\%options,
'alias1' => 'option5'
'alias2' => 'option7'
);
set_default_options(\%options,
'_filter_name' => 'filter',
'option5' => .0001,
'option7' => 'ascii',
'allow_unknown_options => 0,
}
.... body code of filter .......
if ($error) {
$rh_ans->throw_error("FILTER_ERROR", "Something went wrong");
# see AnswerHash.pm for details on using the throw_error method.
        $rh_ans;    #reference to an AnswerHash object is returned.
}

compare_numbers

std_num_filter

    std_num_filter($rh_ans, %options)
returns $rh_ans

Replaces some constants using math_constants, then evaluates a perl expression.

    std_num_array_filter($rh_ans, %options)
returns $rh_ans

Assumes the {student_ans} field is a numerical array, and applies BOTH check_syntax and std_num_filter to each element of the array. Does it's best to generate sensible error messages for syntax errors. A typical error message displayed in {studnet_ans} might be ( 56, error message, -4).

function_from_string2

is_zero_array

best_approx_parameters

    best_approx_parameters($rh_ans,%options);   #requires the following fields in 
$rh_ans
{rf_student_ans} # reference to the test answer
{rf_correct_ans} # reference to the comparison answer
{evaluation_points}, # an array of row vectors indicating the
points
# to evaluate when comparing the functions
                %options                # debug => 1    gives more error answers
# param_vars => ["] additional parameters used
to adapt to function
)

The parameters for the comparison function which best approximates the test_function are stored in the field {ra_parameters}.

The last $dim_of_parms_space variables are assumed to be parameters, and it is also assumed that the function \&comparison_fun depends linearly on these variables. This function finds the values for these parameters which minimizes the Euclidean distance (L2 distance) between the test function and the comparison function and the test points specified by the array reference \@rows_of_test_points. This is assumed to be an array of arrays, with the inner arrays determining a test point.

The comparison function should have $dim_of_params_space more input variables than the test function.

calculate_difference_vector

    calculate_difference_vector( $ans_hash, %options);
                    {rf_student_ans},   # a reference to the test function
{rf_correct_ans}, # a reference to the correct answer function
{evaluation_points}, # an array of row vectors indicating
the points
# to evaluate when comparing the functions
{ra_parameters} # these are the (optional) additional inputs
to
# the comparison function which adapt it properly
# to the problem at hand.
                    %options        # mode => 'rel' specifies that each element in 
the
# difference matrix is divided by the correct answer.
# unless the correct answer is nearly 0.
)

fix_answer_for_display

evaluatesToNumber

is_numeric_expression

is_a_number

is_a_fraction

phase_pi I often discovered that the answers I was getting, when using the arctan function would be off by phases of pi, which for the tangent function, were equivalent values. This method allows for this. =cut

sub phase_pi { my ($num,%options) = @_; my $process_ans_hash = ( ref( $num ) eq 'AnswerHash' ) ? 1 : 0 ; my ($rh_ans); if ($process_ans_hash) { $rh_ans = $num; $num = $rh_ans->{correct_ans}; } while( ($rh_ans->{correct_ans}) > 3.14159265358979/2 ){ $rh_ans->{correct_ans} -= 3.14159265358979; } while( ($rh_ans->{correct_ans}) <= -3.14159265358979/2 ){ $rh_ans->{correct_ans} += 3.14159265358979; } $rh_ans; }

is_an_arithemetic_expression

math_constants