WeBWorK Problems

Custom checker extracting parts of a vector

Custom checker extracting parts of a vector

by John Jones -
Number of replies: 7
I am trying to fix a problem where the answer is a vector field (one with a given curl).  A natural way to proceed seems to be to extract the components of the vector field provided by the student, and then perform tests on those.

The first problem is that the documentation does not seem to indicate how to get components of a vector.  To work around that, I tried using dot products, but that seems to have problems.  To simplify things, I thought I would just check if the first component is "y", but even this throws pg errors:

Context("Vector");

ANS(Vector(Real(0),Real(0),Formula("x+y"))->cmp(
  checker => sub {
    my ($correct,$student,$ansHash) = @_; 
    my $v1 = Compute("$student . Vector(1,0,0)");
    return 1 if Formula($v1) == Formula("y");
    return 0;
  }
));

Moreover, if I enter "<x,0,0>" for the student's answer, it reports
"(<x,0,0>) . Vector(1,0,0)" as what the student entered, and that Vector is not defined in this Context.

Suggestions?

John

In reply to John Jones

Re: Custom checker extracting parts of a vector

by Davide Cervone -
The source of the trouble is the line
my $v1 = Compute("$student . Vector(1,0,0)");
Assuming the student's answer is <x,0,0> as in your example, then you will get
my $v1 = Compute("(<x,0,0>) . Vector(1,0,0)");
which asks the MathObject library to parse the string
"(<x,0,0>) . Vector(1,0,0)"
as it would a student answer. But (as the message indicates) , Vector is not defined in the context, so it complains about that. That is, Vector() can not be used as part of a student answer, and so can't appear in the string passed to Compute(). I think you mean
my $v1 = $student . Vector(1,0,0);
Since $student is already a MathObject, there is no need to turn it into a string and parse it again. And since this is now a perl expression (not a string to be parsed), and Vector() is defined as a perl function, and since . has been overloaded to do dot product when one of its operands is a vector, this will do what you want.

Note that i is predefined as Vector(1,0,0), so you can do

my $v1 = $student . i;
Also, there is no need to use Formula() around $v1 in the next line, as $v1 is already a MathObject, so you can compare it to Formula("y") directly.

With that in mind, your code could be

Context("Vector");

ANS(Vector(Real(0),Real(0),Formula("x+y"))->cmp(
  checker => sub {
    my ($correct,$student,$ansHash) = @_; 
    return ($student . i) == Formula("y");
  }
));
As for the unexpected value in the "entered" column, it turns out that the MathObject answer checkers will replace the entered value by the formula with highlighting when an error occurs during parsing the student answer. In your case, the Compute() caused such an error, and the students answer was replaced by the highlighted formula from that. Since this is due to a programming error in the custom checker, I'm not too worried about that.
In reply to Davide Cervone

Re: Custom checker extracting parts of a vector

by John Jones -
I tried this and get a webwork warning

Please inform your instructor that an error occurred while checking your answer at [PG]/lib/Value/AnswerChecker.pm line 254

but line 254 is a line which spits out this warning message.

John

In reply to John Jones

Re: Custom checker extracting parts of a vector

by Davide Cervone -
Hmmm, works for me. Can you pot the complete problem?
In reply to Davide Cervone

Re: Custom checker extracting parts of a vector

by John Jones -
OK, I can get it to work, but stumbled upon something else I don't understand.  Here is a stripped down problem:

DOCUMENT();

loadMacros("PG.pl","PGbasicmacros.pl","PGanswermacros.pl");

loadMacros("MathObjects.pl");

TEXT(beginproblem());

Context('Vector'); 

BEGIN_TEXT

Answer: \{ ans_rule(30) \}

$BR$BR

Answer: \{ ans_rule(30) \}

END_TEXT

ANS(Vector(Real(0),Real(0),Formula("x+y"))->cmp(

  checker => sub {

    my ($correct,$student,$ansHash) = @_;

    return ($student . i) == Formula("y");

  }

));

Context('Numeric');

ANS(Real(0)->cmp);

ENDDOCUMENT();


This gives me a warning. If I remove the "Context('Numeric')'" line, it

is ok. Why?


John


In reply to John Jones

Re: Custom checker extracting parts of a vector

by Davide Cervone -
OK, here's what is going on: because the vector you have given is a formula (equivalent to Vector("<0,0,x+y>")), there is a post-filter for the answer checker that checks to see if the previous answer is equivalent to the current one. The check also calls the custom checker (to find out if the two are equal), and it is that second time through the checker that is causing the problem.

When the checker is run initially, the context is set to the context of the correct answer (so that things like the tolerances, the variable limits, and so on are taken from the correct context). But it turns out that the test against the previous answer doesn't set the context (this is a bug), and so whatever the last context was for the problem is the one that is in effect for the post-filter.

So when you have Context("Numeric"), that makes the custom checker run in Numeric context rather than Vector context, and things like dot product and the vector i are not defined there.

So one fix is to not use the Context("Numeric"), as you suggest. Personally, I think that is the right solution, since the context is supposed to be for the whole problem, when possible, and you should only change the context if you really need to. That way, if a student types a vector in the place where a number is used, he or she will get an appropriate message to that effect rather than saying that < is undefined.

Alternatively, you could use

ANS(Vector("<0,0,x+y>")->cmp(
  checker => sub {
    my ($correct,$student,$ansHash) = @_;
    Context($correct->context);
    return (Vector(1,0,0) . Vector(1,0,0)) == Formula("y");
  }
));
to get the context set correctly during the second time through.
In reply to John Jones

Re: Custom checker extracting parts of a vector

by Jeremy Sylvestre -
I believe you can extract the components of vector using the MathObject common method value(), which for Vector will return the components of the vector in a perl array. (I also first went with the dot product method to extract components of a vector before I discovered value().)

For example,

(Vector(2,3,5)->value)[0]

should be the same as Real(2).

Not sure what will happen when one of your components is a Formula, but I have used this method to extract components no problem for Complex vectors, using

Context("Complex");
Context()->parens->set("<" => {type => "Vector", close => ">"});

Cheers.
In reply to Jeremy Sylvestre

Re: Custom checker extracting parts of a vector

by Davide Cervone -
This works fine for constant vectors like <1,2,3>, but can be problematic when the answer is a vector formula. In that case, this only works if the student answers an explicit vector, like <x,y,x+y>, but will fail if the student enters 3<x,y,x+y> or <1,0,0>.<x,y,x+y>. So I recommend using the dot product unless you are using one of the restricted vector contexts that prevents vector formulas.

You can also do

    Vector(2,3,5)->extract(1)
rather than
    (Vector(2,3,5)->value)[0]
to get just the first coordinate.

Note also that there is a Complex-Vector context, so no need to set that up yourself. Just use

    Context("Complex-Vector");
There are also Complex-Point and Complex-Matrix contexts.