WeBWorK Problems

showing students which coordinate is wrong in a vector answer

showing students which coordinate is wrong in a vector answer

by Darwyn Cook -
Number of replies: 5
I have the following code inside a calc III problem:

Context("Vector");
Context("Vector")->variables->are(x=>'Real',y=>'Real',theta=>'Real',phi=>'Real');
$r = Vector("$x","$y","$z");
$ru = $r->D('theta')->reduce;
$rv = $r->D('phi')->reduce;
$prod = Vector("$ru >< $rv");

$x,$y,$z are parametric equations defined previously. When a student puts in an incorrect answer the answer is marked wrong, but it does not indicate which coordinate is incorrect. I tried adding the line

Context("Vector");
Context("Vector")->variables->are(x=>'Real',y=>'Real',theta=>'Real',phi=>'Real');
Context("Vector")->flags->set(showCoordinateHints=>1);
$r = Vector("$x","$y","$z");
$ru = $r->D('theta')->reduce;
$rv = $r->D('phi')->reduce;
$prod = Vector("$ru >< $rv");

but then I got the error "theta is not defined in this context". So I tried placing the set flag before the variables declaration:

Context("Vector");
Context("Vector")->flags->set(showCoordinateHints=>1);
Context("Vector")->variables->are(x=>'Real',y=>'Real',theta=>'Real',phi=>'Real');
$r = Vector("$x","$y","$z");
$ru = $r->D('theta')->reduce;
$rv = $r->D('phi')->reduce;
$prod = Vector("$ru >< $rv");

which compiles, but does not change the behavior of the answer checker. I also tried setting the flag inside the answer checker:

ANS($prod->cmp(showCoordinateHints=>1));

which also did not seem to have any effect either.

P.S. I have used vectors successfully before without this issue. I love math objects!

In reply to Darwyn Cook

Re: showing students which coordinate is wrong in a vector answer

by Davide Cervone -
Darwyn:

There are several issues that are affecting your results. I will respond to each in a separate message.

The most important one is that when Vector() has an argument that is a Formula, it returns a Vector-valued formula, and those do not support the coordinate hints. The reason is that the student answer may not be a simple list of coordinates, but rather some more complicated vector equation, where the "coordinates" are not so obvious.

When the student provides an answer for a constant-valued answer, the reduced numeric result is shown in the answer preview area, and so the coordinates of a constant vector would show up and make sense. But for Formula answers (vector or otherwise) the student's preview is the formula, and coordinates may not be apparent, so it could be confusing to discuss coordinates in that case.

On the other hand, you can achieve the result you want by using a custom answer checker. Here is one approach that is very general:

  $ans = Vector("<t,1-t,3t^2>");

  ANS($ans->cmp(checker => sub {
    my ($correct,$student,$ans) = @_;
    return 1 if $correct == $student;
    if (!$ans->{isPreview}) {
      my $m = $correct->length;
      my $I = Value::Matrix->I($m);
      my @errors = ();
      foreach my $i (1..$m) {
        my $e = Vector($I->row($i));
        push (@errors,"Your ".$correct->NameForNumber($i)." coordinate is incorrect")
           unless $correct.$e == $student.$e;
      }
      Value->Error(join("<BR>",@errors)) if @errors;
    }
    return 0;
  }));
Here, you provide a custom answer checker whose job it is to report the incorrect coordinates. The checker first checks if the student's answer is correct, and returns 1 (correct) in that case. Otherwise, if the student didn't press the "Preview" button (when you would not want to give messages about which are correct), you get the length of the vector ($m) and make an identity matrix of that size ($I).

The rows of that matrix will be the unit coordinate vectors of the correct dimension, and dotting a vector with one of those will extract one of the coordinates of the answer. We can use that fact to check the individual coordinates of the student's answer.

To do so, we loop through the indices of the vector (1..$m) and extract a row of the matrix (the vector that has a 1 in position $i and zeros elsewhere), and dot that with the correct and student answers. If they aren't the same, we store an error message indicating that fact. The message uses the NameForNumber() call to turn "1" into "first", "2" into "second" and so on.

Once all the coordinates are checked, we join all the errors together (with BR tags to force them to be on separate lines) and report the errors. If there were no errors (shouldn't happen), or the student is previewing, simply return 0 to indicate the answer is incorrect.

That should have the effect you want. You could even package this checker in a macro file of your own and use loadMacros() to load it. For example, make a file containing

  sub VectorCoordinateHints {
    my ($correct,$student,$ans) = @_;
    ...
    return 0;
  }
  $VectorCoordinateHints = \&VectorCoordinateHints;
load it with loadMacros(), and then use
  ANS($ans->cmp(checker=>$VectorCoordianteHints));
That would make it easier to include into different problems.

Davide

In reply to Darwyn Cook

Re: showing students which coordinate is wrong in a vector answer

by Davide Cervone -
You point out that when you do
  Context("Vector");
  Context("Vector")->variables->are(x=>'Real',y=>'Real',theta=>'Real',phi=>'Real');
  Context("Vector")->flags->set(showCoordinateHints=>1);
  $r = Vector("$x","$y","$z");
  $ru = $r->D('theta')->reduce;
  $rv = $r->D('phi')->reduce;
  $prod = Vector("$ru >< $rv");
you get an error about theta not being defined. The reason is the multiple Context("Vector") calls. Each time you say Context("Vector"), the context gets reset to a copy of the basic vector context, losing any changes you may have made to the context. So the first Context("Vector") gets you a copy of the vector context. Then the next line throws that away and gets another copy of the vector context and adds some variables to it. The next line throws that away, gets another copy of the vector context and sets one of its flags. That context does not include variables you set up in the previous line, since you have discarded that older context.

The way to do this is to use Context() with no name in order to get the current context without changing it. So

  Context("Vector");
  Context()->variables->are(x=>'Real',y=>'Real',theta=>'Real',phi=>'Real');
  Context()->flags->set(showCoordinateHints=>1);
would be what you want.

Of course, this still won't solve the problem, since vector-valued formulas don't show coordinate hints, but it is the correct way to add variables to and set flags in the same context.

Davide

In reply to Darwyn Cook

Re: showing students which coordinate is wrong in a vector answer

by Davide Cervone -
I also wanted to point out that the line
   $r = Vector("$x","$y","$z");
is probably better as
  $r = Vector($x,$y,$z);
provided the values of $x, $y, and $z are defined after the Context("Vector") line. While the first line works, it is less efficient, since WeBWorK first must convert each of $x, $y, and $z to strings, and then reparse the strings into formulas again when building the vector. The second form avoids the extra steps.

The only reason to go through the extra parsing is if the variables were defined in a different context originally. In that case, you might use

  $r = Vector("<$x,$y,$z>");
or even
  $r = Compute("<$x,$y,$z>");
instead.

I'm trying to promote the use of Compute() when the input is a string, since it works just like parsing the student's answer and returns a value of the correct type, whatever that may be.

Davide

In reply to Darwyn Cook

Re: showing students which coordinate is wrong in a vector answer

by Davide Cervone -
I love math objects!
Thanks, I'm glad you are finding them helpful in writing your problems.

Davide

In reply to Davide Cervone

Re: showing students which coordinate is wrong in a vector answer

by Darwyn Cook -
Davide,
Every call of the form Context("Vector") resets the context. Now that you say it, it is completely obvious, and explains many issues I have encountered. I think it is time to go back and rework some of my old code and eliminate some inefficiencies.smile
Thanks!