WeBWorK Problems

function comparison upToConstant and custom checkers

function comparison upToConstant and custom checkers

by Gavin LaRose -
Number of replies: 6
Hi all,

I want to have a checker that accepts either of two possible formulas, either of which are valid to within a constant. For example, the correct answers are x and 2*x, but any vertical shift of these should be marked correct as well.

Ignoring the vertical shift, I did this with $g1 = Compute("x");
$g2 = Compute("2*x");
...
ANS( $g1->cmp( checker=>sub {
my ( $c, $s, $self ) = @_;
return $s == $c || $s == $g2; } ) );
I would allow the vertical shift for a single answer checker with $g1 = Compute("x");
...
ANS( $g1->cmp( upToConstant => 1 ) );
but trying to add this to the comparison I had before, ANS( $g1->cmp( upToConstant => 1,
checker=>sub {
my ( $c, $s, $self ) = @_;
return $s == $c || $s == $g2; } ) );
results in a Context mismatch error.

Can someone tell me where to set the comparison flag in the Context so that it gets inherited by both functions when the answer checking is done?

Thanks,
Gavin
In reply to Gavin LaRose

Re: function comparison upToConstant and custom checkers

by Gavin LaRose -
p.s. I've found a related question: I want to have an answer that's an equation and make it allow addition of constants. For example, "write the equation of a circle centered on the origin." What I'd like is to mark this with
ANS( ImplicitEquation("x^2+y^2=1")->cmp( upToConstant=>1 ) ); (after loading and installing the ImplicitEquation Context, of course) but that (obviously) doesn't work.

Thanks,
Gavin
In reply to Gavin LaRose

Re: function comparison upToConstant and custom checkers

by Davide Cervone -
This will not be easy, and I don't see a way to accomplish it with the current tools. What you may need to do is write a custom checker that splits the student answer at the equal sign and compares the left and right halves separately. ImplicitEquation is not a very reliable checker, and it would not be easy to modify it to do what you need.

Davide
In reply to Gavin LaRose

Re: function comparison upToConstant and custom checkers

by Davide Cervone -
One way to do this is to use adaptive parameters. Here's an example:
    ANS($f1->cmp(checker=> sub {
      my ($correct, $student, $self) = @_;
      my $context = Context()->copy;
      $context->flags->set(no_parameters=>0);
      $context->variables->add('C0'=>'Parameter');
      my $C0 = Formula($context,'C0');
      $student = Formula($context,$student);
      return $C0+$g1 == $student || $C0 + $g2 == $student;
    }));

Here, we copy the current context and then allow adaptive parameters in new formulas (since they are not allowed while students are entering answers). Then we define C0 as a new adaptive parameter, and create a formula for it. Then we put the student answer into the new context. Finally, we compare the student answer to the correct possibilities with the adaptive parameter added to them.

I think that should do it.

Davide

In reply to Davide Cervone

Re: function comparison upToConstant and custom checkers

by Gavin LaRose -
Hi Davide,

Thanks! That works like a charm, though for some reason I have to put the test of equality in that order; the command return $student == $C0 + $g1 || $student == $C0 + $g2; tests as false when the adaptive parameter has to be nonzero.

My solution for the other problem, with the ImplicitEquation up to a constant, was to use a MultiAnswer construct and have the student first specify the value of the arbitrary constant that s/he is using.

Gavin
In reply to Gavin LaRose

Re: function comparison upToConstant and custom checkers

by Davide Cervone -
The order of the operand with the == operator actually is important. Adaptive parameters are only allowed on the left-hand side (and are treated as zero on the right). This is because the standard checker compares the correct answer is on the left and the student on the right, and only the correct answer is allowed to have adaptive parameters. It would be possible to loosen this requirement, but it could lead to unforeseen problems.

As for the ImplicitEquation problem, I did think of a way to do it. If you are looking for [math]f(x,y)=g(x,y)[/math] and want the student answer [math]f_1(x,y)=g_1(x,y)[/math] to match "up to a constant", then I take this to mean that the solutions for [math]f(x,y)=g(x,y)[/math] must also be solutions for [math]f_1(x,y)=g_1(x,y)+k[/math] for some constant [math]k[/math]. In other words, [math]f_1(x,y)-g_1(x,y) = k[/math] for all the [math](x,y)[/math] where [math]f(x,y)=g(x,y)[/math].

To make use of this, we form [math]F(x,y) = f_1(x,y)-g_1(x,y)[/math] from the student answer, and evaluate [math]F[/math] at a solution of the correct answer, i.e., an [math](x,y)[/math] where [math]f(x,y) = g(x,y)[/math]. This gives us the value of [math]k[/math], and we ask if [math]F(x,y) = k[/math] matches the correct answer [math]f(x,y)=g(x,y)[/math] (that is, do they have the same solutions) by comparing them as ImplicitEquations.

This is implemented below:


    $f = ImplicitEquation("x^2+y^2=1");

    ANS($f->cmp(checker => sub {
      my ($correct,$student,$ans) = @_;
      return 0 unless defined($correct) && defined($student);
      Value->Error("Your formula doesn't look like an implicit equation")
        unless $student->type eq 'Equality';
      my ($x,$y) = @{$correct->{solutions}[0]};
      my $F = Formula($student->{tree}{lop})-Formula($student->{tree}{rop});
      my $k = $F->eval(x => $x, y => $y);
      return $correct == ImplicitEquation("$F = $k");
    }));

Here, we check to make sure the student answer is an equality (and give an error if not), then look up one of the solutions for the correct answer. The formula $F is the [math]F(x,y)[/math] from above, and $k is its value at one of the solutions of the correct equation. If the ImplicitEquation $F = $k matches the correct answer, then the student's answer is correct "up to a constant".

Note, however, that this would allow answers like x^2+y^2=-3 to match x^2+y^2=1, since there is a constant, [math]k[/math] (in this case [math]k=4[/math]), so that the solutions to [math]x^2+y^2=1[/math] are the same as the solutions to [math]x^2+y^2=-3+k[/math]. If you want the student's answer to have a positive constant, then you need to restrict the allowed values of [math]k[/math]. In this case, you need to make sure [math]k<1[/math]. If your initial equation were [math]x^2+y^2=2[/math], then you would want [math]k<2[/math], and so on.

To insert this into the answer checker, do:


    $f = ImplicitEquation("x^2+y^2=1");

    ANS($f->cmp(checker => sub {
      my ($correct,$student,$ans) = @_;
      return 0 unless defined($correct) && defined($student);
      Value->Error("Your formula doesn't look like an implicit equation")
        unless $student->type eq 'Equality';
      my ($x,$y) = @{$correct->{solutions}[0]};
      my $F = Formula($student->{tree}{lop})-Formula($student->{tree}{rop});
      my $k = $F->eval(x => $x, y => $y);
      return $k < 1 && $correct == ImplicitEquation("$F = $k");
    }));

This is a little unsatisfying, as it means the value 1 has to be in both the original implicit equation, and hard-coded into the answer checker. It would be possible to look up the constant used in the correct answer by evaluating the correct answer at [math](x,y)=(0,0)[/math]. It turns out that the ImplicitEquation object is actually a Formula that is the left-hand-side of the equality minus the right-hand-side, so its value will be the negative of the constant that we need. So here is a version that value directly from the correct answer:
    $f = ImplicitEquation("x^2+y^2=1");

    ANS($f->cmp(checker => sub {
      my ($correct,$student,$ans) = @_;
      return 0 unless defined($correct) && defined($student);
      Value->Error("Your formula doesn't look like an implicit equation")
        unless $student->type eq 'Equality';
      my ($x,$y) = @{$correct->{solutions}[0]};
      my $K = -($correct->eval(x => 0, y => 0));
      my $F = Formula($student->{tree}{lop})-Formula($student->{tree}{rop});
      my $k = $F->eval(x => $x, y => $y);
      return $k < $K && $correct == ImplicitEquation("$F = $k");
    }));

So this will correctly identify a circle no matter which circle is listed as the original correct one, and which one the student enters.

Note that the student (and correct answer) need not be in "standard" form. So, for example, x^2+y^2=5 and x^2=3-y^2 would both be marked as correct. So would (x^2-y^2-4)^2=0, which you might not be happy about, but it is a correct implicit equation for the circle of radius 2. If you want to enforce a particular format, then ImplicitEquation() is not what you want.

Hope that helps.

Davide