WeBWorK Problems

must_have_filter

must_have_filter

by Nandor Sieben -
Number of replies: 6
I'd like to continue the discussion at

http://wwrk.maa.org/moodle/mod/forum/discuss.php?d=1667

The following problem in the NAU library used to work. It still works but the must_have_filter creates a warning message if the not allowed string is used in the answer. The warning asks the students to report it and makes the screen pink. Was there a change in the way pre_filters are handled?

-------------------

Warning -- there may be something wrong with this question. Please inform your instructor including the warning messages below.

WeBWorK Warnings

WeBWorK has encountered warnings while processing your request. If this occured when viewing a problem, it was likely caused by an error or ambiguity in that problem. Otherwise, it may indicate a problem with the WeBWorK system itself. If you are a student, report these warnings to your professor to have them corrected. If you are a professor, please consult the warning output below for more information.


Warning messages

  • Evaluation error: Answer AnSwEr2:
  • SYNTAX :: sin (t) is not allowed in this answer


"PGchoicemacros.pl",
"PGanswermacros.pl",
"PGauxiliaryFunctions.pl",
"PGgraphmacros.pl",
"PGasu.pl"
);

TEXT(&beginproblem);
$showPartialCorrectAnswers = 0;



## TeX formulas on LHS
@leftTex=('(1-\sin(t))(1+\sin(t))',
'(1-\cos(t))(1+\cos(t))',
'(\csc(t)-1)(\csc(t)+1)',
'(\sec(t)-1)(\sec(t)+1)',
'(\tan(t)-\sec(t))(\tan(t)+\sec(t))',
'(\cot(t)-\csc(t))(\cot(t)+\csc(t))'
);
$size=scalar @leftTex;

## formulas on RHS
@answer=('(cos(t))^2','(sin(t))^2','cot(t)^2','tan(t)^2','-1','-1');

## strings forbidden in the answer by prefilter
@left=('sin','cos','csc','sec','tan','cot');


## load answer evaluator
for($i=0;$i<$size;$i++){
$mycmp[$i] = fun_cmp($answer[$i],var=>'t');
$mycmp[$i]->install_pre_filter(must_have_filter($left[$i],
'no',
"$left[$i] (t) is not allowed in this answer"
));
}

## Choose 3 from a pool of identities
@slice=NchooseK($size,3);

($q1,$q2,$q3) = @leftTex[@slice] ;
($ans1,$ans2,$ans3)= @mycmp[@slice];

BEGIN_TEXT


Simplify each expression.
$PAR
\( $q1 \) = \{ans_rule(15)\}
$BR
\( $q2 \) = \{ans_rule(15)\}
$BR
\( $q3 \) = \{ans_rule(15)\}
$BR



END_TEXT

ANS( $ans1);
ANS( $ans2);
ANS( $ans3);

ENDDOCUMENT();


In reply to Nandor Sieben

Re: must_have_filter

by Davide Cervone -
Well, the must_have_filter uses the throw_error method of the AnswerHash to signal when the student has included the illegal string, and that causes the PG Translator to report the error in the way you indicate. It looks like PGasu.pl includes an additional filter that catches these errors and processes them properly, so you may need to add that into your code by adding
    $mycmp[$i]->install_post_filter(~~&catch_errors_filter);
after the
   $mycmp[$i]->install_pre_filter(must_have_filter($left[$i],...));
command.

Alternatively, you could use the MathObjects' ability to disable individual functions to accomplish the same result. Here is my version of this problem.


    DOCUMENT();

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

    TEXT(beginproblem);

    #############################################

    Context("Numeric")->variables->are(t => 'Real');
    Context()->{error}{msg}{"Function '%s' is not allowed in this context"} =
        "Function '%s' can not be used in your final answer";

    @formulas = (
    #
    #  Data in the form: [formula, answer, disable],
    #
    @formulas = (
      ['(1 - \sin t)(1 + \sin t)', 'cos^2 t', 'sin'],
      ['(1 - \cos t)(1 + \cos t)', 'sin^2 t', 'cos'],
      ['(\csc t - 1)(\csc t + 1)', 'cot^2 t', 'csc'],
      ['(\sec t - 1)(\sec t + 1)', 'tan^2 t', 'sec'],
      ['(\tan t - \sec t)(\tan t + \sec t)', '-1', 'tan'],
      ['(\cot t - \csc t)(\cot t + \csc t)', '-1', 'cot'],
    );

    @F = (); @A = ();
    foreach $i (NchooseK(scalar(@formulas),3)) {
      ($tex,$ans,$disable) = @{$formulas[$i]};
      #
      #  Make a new copy of the context and disable the
      #  function used in the original formula
      #
      $context = Context()->copy;
      $context->functions->disable($disable);
      #
      #  Make an answer checker for the correct answer
      #  and save it
      #
      push(@A,Formula($context,$ans)->cmp);
      #
      #  Save the associated original formula
      #
      push(@F,$tex);
    }


    #############################################

    BEGIN_TEXT
    Simplify each of the following expressions:
    $PAR
    \($F[0]\) = \{ans_rule(15)\}$BR
    \($F[1]\) = \{ans_rule(15)\}$BR
    \($F[2]\) = \{ans_rule(15)\}
    END_TEXT

    #############################################

    ANS(@A);
    $showPartialCorrectAnswers = 0;

    #############################################

    ENDDOCUMENT();

Davide

In reply to Davide Cervone

Re: must_have_filter

by Nandor Sieben -
Thank you, the fix works.
In reply to Nandor Sieben

Re: must_have_filter

by Nandor Sieben -
Davide, I have another somewhat related issue. This problem gives a similar warning. Students suppose to type in a formula for a function that matches the given graph. The problem is that the function they type in might be undefined on the points the evaluator wants to check equality. Is the solution again a custom post filter that avoids the warning? Nandor

#DESCRIPTION
# Name of the file: Exp.pg
# File Created: 7/16/05
# Problem Author: Nandor Sieben
# Location: Northern Arizona University
# Course:
# Recommended trials:
# Recommended value:
#
##ENDDESCRIPTION

##KEYWORDS('function evaluator, string evaluator')

DOCUMENT();
loadMacros("PGstandard.pl",
"Parser.pl",
"PGnauGraphics.pl"
);

TEXT(&beginproblem) ;

Context('Numeric');

$a = list_random(1/3,1/2,2,3);
$b = random(-3,3,1);

$dom = 8;
@gr_lab=(-$dom+1,$dom-1);

@opts = (-$dom, -$dom, $dom, $dom, 'axes'=>[0,0],'grid'=>[$dom, $dom],size=>[250,250]);

$f = Formula("$a^x + $b")->reduce;
$f->{limits} =[-$dom, $dom];

$graphf = init_graph(@opts);
# $fr = new Fun( sub {my $x = shift; $f->perl});
$fr = new Fun($f->perlFunction);
$fr->domain(-$dom,$dom);
$fr->steps(500);
$graphf->fn($fr);
$labelf = new Label(@gr_lab, 'f', 'blue' , 'center', 'center');
$graphf->lb($labelf);

#####################################################

$graph_eval = sub{
my ($orig_in) = @_;

my ($h, $view, $intex, $score, $ans_hash);

$myeval = $f->cmp;
$ans_hash = $myeval->evaluate($orig_in);

if ($orig_in ne ''){
$h = Formula($orig_in)->reduce;
$h->{limits} =[-$dom, $dom];
$hr = new Fun($h->perlFunction);
$hr->domain(-$dom,$dom);
$hr->steps(500);
$graph = init_graph(@opts);
$graph->new_color('dkgreen',0,102,0);
$hr->color('dkgreen');
$graph->fn($fr);
$graph->fn($hr);
$graph->gifName($graph->gifName()."-$orig_in");
$view = Plot($graph);
$$ans_hash{student_ans} = $view;
}
$ans_hash;
};

#####################################################
BEGIN_TEXT
\{ Plot($graphf) \}
$PAR
Find the formula for \(f\).
$PAR
\(f(x)=\) \{ ans_rule(30) \}

$PAR
$BBOLD Hint: $EBOLD
Transform an exponential function with an integer base.


END_TEXT
ANS($graph_eval);
ENDDOCUMENT();

In reply to Nandor Sieben

Re: perlFunction and plots

by Davide Cervone -
Nandor:

The problem here is that the perlFunction method produces a function that will throw an error when it is evaluated outside the domain of the function (i.e., when there is an error in evaluating the function) while the plotting macros expect the function to silently return an undefined value instead.

It is possible to encapsulate the perlFunction in a subroutine that does error trapping and returns the value of the formula when it evaluates without error, or undef if it does, but fortunately you don't have to do that, as PGgraphmacros.pl already contains the code for that if you use plot_functions rather than adding graphical function objects by hand. (If you want to see how it is done, look at pg/macros/PGgraphmacros.pl in the plot_functions subroutine).

There is also a subtle problem with using perlFunction to pass directly to the plotting functions, which is that the function that perlFunction produces will return MathObject Real's rather than plain old perl reals, and the overhead of that within the plotting functions will cause the plots to be produced very slowly (especially since you ask for 500 steps each). Using the plot_functions approach avoids that, as the internal routines convert back to perl reals automatically.

Here is a rewrite of your problem that takes advantage of this (and cleans up a few other items, e.g., since the the image is only 250 pixels wide, it is not necessary to use 500 steps; 250 is the largest needed, and even that is really overkill).


    DOCUMENT();

    loadMacros(
      "PGstandard.pl",
      "Parser.pl",
      "PGnauGraphics.pl",
      "PGgraphmacros.pl",
      "PGcourse.pl",
    );

    TEXT(beginproblem);

    #####################################################

    Context('Numeric');

    $a = list_random(1/3,1/2,2,3);
    $b = random(-3,3,1);

    $dom = 8;
    @gr_lab=(-$dom+3,$dom-1);
    @opts = (-$dom, -$dom, $dom, $dom, axes=>[0,0], grid=>[$dom, $dom], size=>[250,250]);

    $f = Formula("$a^x + $b")->reduce;
    $f->{limits} =[-$dom, $dom];

    $graphf = init_graph(@opts);
    (plot_functions($graphf,"$f for x in <-$dom,$dom> using color:blue"))[0]->steps(250);
    $labelf = new Label(@gr_lab, 'y = f(x)', 'blue' , 'center', 'center');
    $graphf->lb($labelf);

    #####################################################

    $graph_eval = sub{
      my $h = shift;
      my $ans_hash = $f->cmp->evaluate($h);
                                                                                                                         
      if ($h ne '') {
        my $graph = init_graph(@opts);
        $graph->new_color('dkgreen',0,102,0);
        my ($F,$H) = plot_functions($graph,
          "$f for x in <-$dom,$dom> using color:blue",
          "$h for x in <-$dom,$dom> using color:dkgreen",
        );
        $F->steps(250); $H->steps(250);
        $graph->gifName($graph->gifName()."-$orig_in");
        $ans_hash->{student_ans} = Plot($graph);
      }
      $ans_hash;
    };

    #####################################################

    BEGIN_TEXT
    \{ Plot($graphf) \}
    $PAR
    Find the formula for \(f\).
    $PAR
    \(f(x)=\) \{ ans_rule(30) \}
    $PAR
    $BBOLD Hint: $EBOLD
    Transform an exponential function with an integer base.
    END_TEXT

    #####################################################

    ANS($graph_eval);                                                                                                    

    #####################################################

    ENDDOCUMENT();


It's also possible to use the MathObjects answer checker directly and add a post-filter to make the graph. Here's how that might look:


    DOCUMENT();

    loadMacros(
      "PGstandard.pl",
      "Parser.pl",
      "PGnauGraphics.pl",
      "PGgraphmacros.pl",
      "PGcourse.pl",
    );

    TEXT(beginproblem);

    #####################################################

    Context('Numeric');

    $a = list_random(1/3,1/2,2,3);
    $b = random(-3,3,1);

    $dom = 8;
    @gr_lab=(-$dom+3,$dom-1);
    @opts = (-$dom, -$dom, $dom, $dom, axes=>[0,0], grid=>[$dom, $dom], size=>[250,250]);

    $f = Formula("$a^x + $b")->reduce;
    $f->{limits} =[-$dom, $dom];

    $graphf = init_graph(@opts);
    (plot_functions($graphf,"$f for x in <-$dom,$dom> using color:blue"))[0]->steps(250);
    $labelf = new Label(@gr_lab, 'y = f(x)', 'blue' , 'center', 'center');
    $graphf->lb($labelf);

    #####################################################

    BEGIN_TEXT
    \{ Plot($graphf) \}
    $PAR
    Find the formula for \(f\).
    $PAR
    \(f(x)=\) \{ ans_rule(30) \}
    $PAR
    $BBOLD Hint: $EBOLD
    Transform an exponential function with an integer base.
    END_TEXT

    #####################################################

    ANS($f->cmp->withPostFilter(sub {
      my $ans = shift;
      my ($correct,$student) = ($ans->{correct_value},$ans->{student_value});
      if ($student && $correct->typeMatch($student)) {
        my $graph = init_graph(@opts);
        $graph->new_color('dkgreen',0,102,0);
        my ($c,$s) = plot_functions($graph,
          "$correct for x in <-$dom,$dom> using color:blue",
          "$student for x in <-$dom,$dom> using color:dkgreen",
        );
        $c->steps(250); $s->steps(250);
        $graph->gifName($graph->gifName()."-$student");
        $ans->{student_ans} = Plot($graph);
      }
      return $ans;
    }));

    #####################################################

    ENDDOCUMENT();

This latter one has the advantage of reporting syntax errors in the students answers correctly (rather than giving a pink screen), and of not trying to plot things that are not of the proper form (like if the student entered a point rather than a function).

Hope these help out.

Davide

In reply to Davide Cervone

Re: perlFunction and plots

by Dick Lane -
Nandor's 24-Jan-2009 example uses init_graph which probably needs PGgraphmacros.pl to be loaded.  I do like having the student answer plotted.  I would add the following:
    $PAR
    Note: the ${BBOLD}Preview Answer${EBOLD} button
    will show a figure with original curve (in blue) together
    with your function (plotted in green).

My experiments with Davide's first revision, 26-Jan-2009, often had an incorrect plot of the student answer.  E.g., with problem having 2^x as correct answer, I answered 3^x - 2 and viewed a green curve corresponding to 2 + 2^(-x) = 2 + (1/2)^x.  Some earlier malfunctions had led me to guess $h retained the value from first student attempt, but this particular experiment had result inconsistent with that conjecture.

Davide's second version is even more instructive, thanks.
In reply to Davide Cervone

Re: must_have_filter

by Dick Lane -
Davide's 20-Jan-2009 example uses different contexts to disable a single function for each part of this problem.  This helped me improve my understanding of the power involving modification of a context, thanks.