WeBWorK Problems

simplifying radical expressions

simplifying radical expressions

by Bruce Yoshiwara -
Number of replies: 19
Our course (elementary algebra) has a section about simplifying radicals. I'd like, for example to mark “sqrt(27a^5 b^4)” as incorrect and mark “3a^2 b^2 sqrt(3a)” as correct.

I haven't figured out how to accomplish this. Any suggestions?

Thanks.
In reply to Bruce Yoshiwara

Re: simplifying radical expressions

by Paul Pearson -
Hi,

As a work around, I would load the macro loadMacros("contextLimitedPolynomial.pl"); and use Context("LimitedPolynomial-Strict")->variables->are(x=>"Real",a=>"Real"); and then have students take the square root of something that is already a square (such as sqrt(16 x^2 a^4)) so that the answer is a polynomial (such as 4 x a^2, disregarding the absolute value issues). You may also want to disable the square root function (see http://webwork.maa.org/wiki/DisableFunctions).

Good luck,

Paul Pearson
In reply to Paul Pearson

Re: simplifying radical expressions

by Bruce Yoshiwara -
Paul,

Thanks for the suggestion. However, I'm not satisfied with the idea of just omitting a whole class of standard exercises.

Actually, we're trying out WeBWorK as an alternative to an answer key--our students do not have answers at the back of their text, but by submitting answers to WeBWorK, they can still check if they have the correct answer (for the skills type of exercises).

I may resort to multiple choice.
In reply to Bruce Yoshiwara

Re: simplifying radical expressions

by Michael Schroeder -
I suppose one option would be to ask the students to ask the student

Simplify this expression as X\sqrt{Y}.

X=

Y=

It's not pretty, but then you could check to see if it's correct.


In reply to Michael Schroeder

Re: simplifying radical expressions

by Bruce Yoshiwara -
Yes, that would work.

I do fear that a large percentage of our elementary algebra students will not recognize that a placeholder like X can stand for an algebraic expression (say 2a+3b) rather than always representing a specific real number.

So I may try showing \square\sqrt{\square}, and then ask students to enter an expression that would go in Box 1 and and expression that would go in Box 2.
In reply to Bruce Yoshiwara

Re: simplifying radical expressions

by Davide Cervone -
One question I have is how specific you are about what you want the students to be able to enter as a valid answer. For example, is 3a*b*a*b*sqrt(3a) acceptable? How about 3a^(4/2)b^(4/2)(3a)^(1/2)?

If you wanted them to simplify sqrt(a^3), would you expect abs(a)sqrt(a)? (I would, unless you had specifically indicated that a >= 0). Would you want to accept 3|a|^2|b^2|sqrt(3a) for your answer to the question in your problem, even though the absolute values are redundent here)?

Your answers to these questions would affect how I think you should handle the problem. Note that these will also be relevant to using the \Square\sqrt{\Square} approach as well.

I suspect that Paul has the right idea: you will want a specialized context that helps enforce the correct format. Or at least, you will want to analyze the parse tree. Depending on how strict the format is, that can be more or less difficult.

Davide
In reply to Davide Cervone

Re: simplifying radical expressions

by Bruce Yoshiwara -
For the current set, our variable expressions represent positive values, and we do not ask for the absolute values. (We'd certainly accept the absolute values when they make sense.)

And I would also accept a*a or even a^(4/2) instead of a^2, or an exponent of 1/2 in place of a square root. But our students have not yet been exposed to rational exponents in our classes.

In this set of exercises, we're basically looking to see if the student can remove perfect squares from square roots.
In reply to Bruce Yoshiwara

Re: simplifying radical expressions

by Davide Cervone -
OK, I've worked out a sneaky way to check this without having to write a separate context, or walk to parse tree. The idea is to replace the sqrt() in the equations by the number 1 in order to get the part that is outside the sqrt() and compare that that is equal in both the correct and student equations (as well as the whole correct and student equations being equal). Here is the code:


    loadMacros("contextLimitedPowers.pl");

    ###########################
    #
    #  Subclass the numeric functions
    #
    package my::Function::numeric;
    our @ISA = ('Parser::Function::numeric');

    #
    #  Override sqrt() to return a special value (usually 1) when evaluated
    #  effectively eliminating it from the product.
    #
    sub sqrt {
      my $self = shift;
      my $value = $self->context->flag("setSqrt");
      return $value+1 if $value && $_[0] == 1;  # force sqrt(1) to be incorrect
      return $value if $value;
      return $self->SUPER::sqrt(@_);
    }

    #
    #  end of subclass
    #
    package main;

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

    Context("Numeric")->variables->are(
      a => ["Real", limits => [0,2]],
      b => ["Real", limits => [0,2]],
    );
    #
    #  make sqrt() use our subclass
    #
    Context()->functions->set(sqrt=>{class=>'my::Function::numeric'});
    #
    #  Don't allow fractional powers (avoids 1/2 power)
    #  [Could subclass exponentiation to handle that as well]
    #
    LimitedPowers::OnlyPositiveIntegers();


    $F = Formula("sqrt(9a^5b^4)");
    $f = Formula("3a^2b^2sqrt(a)");

    BEGIN_TEXT
    \(\{$F->TeX\}\) = \{ans_rule(20)\}
    END_TEXT

    #
    #  Use a custom checker to check that the answers are equivalent
    #  and that they are still equivalent when sqrt() is replaced by 1
    #  (so the stuff outside the sqrt() is equal in both)
    #
    ANS($f->cmp(checker => sub {
      my ($correct,$student,$ans) = @_;
      return 0 if $ans->{isPreview} || $correct != $student;
      Context()->flags->set(setSqrt => 1);
      delete $correct->{test_values}, $student->{test_values};
      my $OK = ($correct == $student); # check if equal when sqrt's are replaced by 1
      Context()->flags->set(setSqrt => 0);
      Value::Error("You must simplify your answer further") unless $OK;
      return $OK;
    }));

To make this work, we create a subclass of the single-variable functions and replace the sqrt() function so that it will return the value 1 when we set a context flag (and its usual value otherwise). When the flag is set, the sqrt() is effectively removed from the product so the result will be just the stuff outside the square root.

We set up the Numeric context with the variables we want (making sure to use limits that are non-negative), and hook the sqrt() function to our new subclass. We also use the LimitedPowers contexts to prevent the student from entering square roots as powers of 1/2. (It would be possible to subclass the exponentiation operator to handle that like the square roots, but since you said your kids don't know about that yet, I didn't bother with it here).

Once that is done, you make your formulas as usual, but use a custom answer checker to test if they are equal. In this case, you return 0 if the Preview button was used, or if the student answers isn't equivalent to the correct one as formulas. If they are equal, you go on to test if the non-sqrt parts are equal. That is done by setting the setSqrt flag, removing the cached results of evaluating the correct and student formulas (so new values will be computed) and testing if the formulas as equal now that the sqrt() function returns 1. (We reset the flag so sqrt() will work normally again, just in case). Finally, we issue an error message if the non-sqrt parts of the formulas aren't equal (you could remove this if you just want the answers to be marked incorrect silently), and return whether the answers are equal or not.

Note that in its current form, sqrt(ab) could be entered as sqrt(a)sqrt(b) by the student. If you want to prevent that, use a value other than 1 for the value of setSqrt, e.g.

      Context()->flags->set(setSqrt => pi);
so that sqrt(ab) will be replaced by pi, while sqrt(a)sqrt(b) will be replaced by pi*pi, making the student's answer different from the professor's.

I think that should do it for you.

Davide

In reply to Davide Cervone

Re: simplifying radical expressions

by Kenneth Appel -
Davide,
I think that I came in to this conversation a bit late and missed something crucial. I tried your program on sqrt(48)
and did not get what was intended. Since I started out by
blindly copying things, I probably got what I deserved, but here is the situation.

DOCUMENT();

loadMacros(
"PGstandard.pl", # Standard macros for PG language
"MathObjects.pl",
"contextLimitedPowers.pl"
);

# Print problem number and point value (weight) for the problem

TEXT(beginproblem());
# Show which answers are correct and which ones are incorrect
$showPartialCorrectAnswers = 1;

# code essentially from Davide Cervone 4/25/10
###########################
#
# Subclass the numeric functions
#
package my::Function::numeric;
our @ISA = ('Parser::Function::numeric');

#
# Override sqrt() to return a special value (usually 1) when evaluated
# effectively eliminating it from the product.
#
sub sqrt {
my $self = shift;
my $value = $self->context->flag("setSqrt");

return $value+1 if $value && $_[0] == 1; # force sqrt(1) to be incorrect
return $value if $value;
return $self->SUPER::sqrt(@_);
}

#
# end of subclass
#
package main;

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

Context("Numeric")->variables->are(
x => ["Real", limits => [0,2]], # only needed if x is used in the square roots
);
#
# make sqrt() use our subclass
#
Context()->functions->set(sqrt=>{class=>'my::Function::numeric'});
Context()->flags->set(reduceConstantFunctions=>0);
#
#
# Don't allow fractional powers (avoids 1/2 power)
# [Could subclass exponentiation to handle that as well]
#
LimitedPowers::OnlyPositiveIntegers();

$f = Compute("sqrt(48)");
$g=Compute("4*sqrt(3)");
BEGIN_TEXT
$BR
\(\sqrt{48}\)= \{ans_rule(20)\}$PAR
\(4 \sqrt{3}\)= \{ans_rule(20)\}
END_TEXT

#
# Use a custom checker to check that the answers are equivalent
# and that they are still equivalent when sqrt() is replaced by 1
# (so the stuff outside the sqrt() is equal in both)
#
ANS($f->cmp(checker => sub {
my ($correct,$student,$ans) = @_;
return 0 if $ans->{isPreview} || $correct != $student;
#
# Get parsed formula for student and correct answers
#
$student = $ans->{student_formula};
$correct = $correct->{original_formula} if defined $correct->{original_formula};
#
# check if equal when sqrt's are replaced by 1
#
Context()->flags->set(setSqrt => 1);
delete $correct->{test_values}, $student->{test_values};
my $OK = ($correct == $student);
Context()->flags->set(setSqrt => 0);
#
Value::Error("Check to see if your answer is simplified.") unless $OK;
return $OK;
},formatStudentAnswer=>"reduced"));

ANS($g->cmp(checker => sub {
my ($correct,$student,$ans) = @_;
return 0 if $ans->{isPreview} || $correct != $student;
#
# Get parsed formula for student and correct answers
#
$student = $ans->{student_formula};
$correct = $correct->{original_formula} if defined $correct->{original_formula};
#
# check if equal when sqrt's are replaced by 1
#
Context()->flags->set(setSqrt => 1);
delete $correct->{test_values}, $student->{test_values};
my $OK = ($correct == $student);
Context()->flags->set(setSqrt => 0);
#
Value::Error("Check to see if your answer is simplified.") unless $OK;
return $OK;
},formatStudentAnswer=>"reduced"));

ENDDOCUMENT()


Result:

Entered Answer Preview Correct Result Messages
4*sqrt(3) 4\sqrt{3} sqrt(48) incorrect Check to see if your answer is simplified.
sqrt(48) \sqrt{48} 4*sqrt(3) incorrect Check to see if your answer is simplified.
At least one of the answers above is NOT correct.


\sqrt{48}=

4 \sqrt{3}=

Note: You can earn partial credit on this problem.

Edit this problem

Show correct answers
In reply to Kenneth Appel

Re: simplifying radical expressions

by Davide Cervone -
The results you show at the bottom of the message are correct. The point of the answer checker used here is to require the student to enter their answer with the same use of sqrt() as the professor's answer. So 4*sqrt(3) is NOT a correct answer for sqrt(48), and sqrt(48) is NOT a correct answer for 4*sqrt(3). Only sqrt(48) will work for an answer that is specified as sqrt(48) originally.

Of course, the "check to see if your answer is simplified" message assumes that the professor's answer is simplified, and would not be appropriate for an answer of sqrt(48), so you would want to change that in that case.

If this isn't what you are looking for, then if you can describe what you need, perhaps we can achieve that.

Davide
In reply to Davide Cervone

Re: simplifying radical expressions

by Kenneth Appel -
Davide,
I thought that I had followed your directions in the modified program below, but I must have missed something. $f was intended to be the correct answer but
the sqrt(48) which only appears in the answer box is accepted as correct.

DOCUMENT();

loadMacros(
"PGstandard.pl", # Standard macros for PG language
"MathObjects.pl",
"contextLimitedPowers.pl"
);

# Print problem number and point value (weight) for the problem

TEXT(beginproblem());
# Show which answers are correct and which ones are incorrect
$showPartialCorrectAnswers = 1;

# code essentially from Davide Cervone 4/25/10
###########################
#
# Subclass the numeric functions
#
package my::Function::numeric;
our @ISA = ('Parser::Function::numeric');

#
# Override sqrt() to return a special value (usually 1) when evaluated
# effectively eliminating it from the product.
#
sub sqrt {
my $self = shift;
my $value = $self->context->flag("setSqrt");

return $value+1 if $value && $_[0] == 1; # force sqrt(1) to be incorrect
return $value if $value;
return $self->SUPER::sqrt(@_);
}

#
# end of subclass
#
package main;

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

Context("Numeric")->variables->are(
x => ["Real", limits => [0,2]], # only needed if x is used in the square roots
);
#
# make sqrt() use our subclass
#
Context()->functions->set(sqrt=>{class=>'my::Function::numeric'});
Context()->flags->set(reduceConstantFunctions=>0);
#
#
# Don't allow fractional powers (avoids 1/2 power)
# [Could subclass exponentiation to handle that as well]
#
LimitedPowers::OnlyPositiveIntegers();

$f = Compute("sqrt(48)");
# $g=Compute("4*sqrt(3)");
# $h=Compute("3*sqrt(4)");
BEGIN_TEXT
$BR
\(\sqrt{48}\)= \{ans_rule(20)\}$PAR
\(\sqrt{48}\)= \{ans_rule(20)\}$PAR
\(\sqrt{48}\)= \{ans_rule(20)\}
END_TEXT

#
# Use a custom checker to check that the answers are equivalent
# and that they are still equivalent when sqrt() is replaced by 1
# (so the stuff outside the sqrt() is equal in both)
#
ANS($f->cmp(checker => sub {
my ($correct,$student,$ans) = @_;
return 0 if $ans->{isPreview} || $correct != $student;
#
# Get parsed formula for student and correct answers
#
$student = $ans->{student_formula};
$correct = $correct->{original_formula} if defined $correct->{original_formula};
#
# check if equal when sqrt's are replaced by 1
#
Context()->flags->set(setSqrt => 1);
delete $correct->{test_values}, $student->{test_values};
my $OK = ($correct == $student);
Context()->flags->set(setSqrt => 0);
#
Value::Error("Check to see if your answer is simplified.") unless $OK;
return $OK;
},formatStudentAnswer=>"reduced"));

ANS($f->cmp(checker => sub {
my ($correct,$student,$ans) = @_;
return 0 if $ans->{isPreview} || $correct != $student;
#
# Get parsed formula for student and correct answers
#
$student = $ans->{student_formula};
$correct = $correct->{original_formula} if defined $correct->{original_formula};
#
# check if equal when sqrt's are replaced by 1
#
Context()->flags->set(setSqrt => 1);
delete $correct->{test_values}, $student->{test_values};
my $OK = ($correct == $student);
Context()->flags->set(setSqrt => 0);
#
Value::Error("Check to see if your answer is simplified.") unless $OK;
return $OK;
},formatStudentAnswer=>"reduced"));

ANS($f->cmp(checker => sub {
my ($correct,$student,$ans) = @_;
return 0 if $ans->{isPreview} || $correct != $student;
#
# Get parsed formula for student and correct answers
#
$student = $ans->{student_formula};
$correct = $correct->{original_formula} if defined $correct->{original_formula};
#
# check if equal when sqrt's are replaced by 1
#
Context()->flags->set(setSqrt => 1);
delete $correct->{test_values}, $student->{test_values};
my $OK = ($correct == $student);
Context()->flags->set(setSqrt => 0);
#
Value::Error("Check to see if your answer is simplified.") unless $OK;
return $OK;
},formatStudentAnswer=>"reduced"));

ENDDOCUMENT()

The result was


Entered Answer Preview Correct Result Messages
4*sqrt(3) 4\sqrt{3} sqrt(48) incorrect Check to see if your answer is simplified.
sqrt(48) \sqrt{48} sqrt(48) correct
3*sqrt(4) 3\sqrt{4} sqrt(48) incorrect
At least one of the answers above is NOT correct.


\sqrt{48}=

\sqrt{48}=

\sqrt{48}=

Note: You can earn partial credit on this problem.

Edit this problem

Show correct answers

Your score was not recorded because this problem has not been assigned to you.
You have attempted this problem 0 times.
You received a score of 33% for this attempt.
You have unlimited attempts remaining.

This set is hidden from students.

Main Menu

Courses
In reply to Kenneth Appel

Re: simplifying radical expressions

by Davide Cervone -
You are seeing the correct behavior. If $f is the result of Compute("sqrt(48)") then ONLY an answer of "sqrt(48)" is marked correct. Other equivalent answers are marked incorrect with the message "Check to see if your answer is simplified." Other non-equivalent answers are marked incorrect with no message. This is what the results table you have supplied shows actually is occurring.

This also seems not to be what you expect. What is it that you DO expect to happen?

Davide
In reply to Kenneth Appel

Re: simplifying radical expressions

by Davide Cervone -
PS. You should be sure to read my comments in the messages where I explain how the checker works. In particular:

Note that in its current form, sqrt(ab) could be entered as sqrt(a)sqrt(b) by the student. If you want to prevent that, use a value other than 1 for the value of setSqrt, e.g.
      Context()->flags->set(setSqrt => pi);
so that sqrt(ab) will be replaced by pi, while sqrt(a)sqrt(b) will be replaced by pi*pi, making the student's answer different from the professor's.

Note that this means that a correct answer of sqrt(8) could be written as sqrt(4)sqrt(2) when setSqrt is set to 1, but not when set to something else. (A correct answer of 2sqrt(2) could not be written sqrt(4)sqrt(2) with either value of setSqrt, however.) Since the original poster was asking about checking reduced roots, the question of sqrt(8) as a correct answer was not an issue. But since you seem to want to use sqrt(48) as a correct answer, you should be aware of these issues.

Davide

In reply to Davide Cervone

Re: simplifying radical expressions

by Kenneth Appel -
Davide,
I feel particularly dense. I thought that I had changed $f. I made the appropriate changes and now things work perfectly.
Ken
In reply to Bruce Yoshiwara

Re: simplifying radical expressions

by Davide Cervone -
The code in my previous answer could be packaged up into a macro file if you plan to use it in several problems. In that case, I'd recommend that the macro file create its own context, and define a variable that contains the custom checker. Something like the (untested) code saved as contextLimitedSqrt.pl, for example:
    loadMacros("contextLimitedPowers.pl");

    #
    #  Set up the LimitedSqrt context
    #
    sub _contextLimitedSqrt_init {
      my $context = $main::context{LimitedSqrt} = Parser::Context->getCopy("Numeric");
      $context->flags->set(limits => [0,2]);   # no negatives in the radicals
      $context->functions->set(sqrt=>{class=>'my::Function::numeric'});  # override sqrt()
      LimitedPowers::OnlyPositiveIntegers($context);  # don't allow powers of 1/2
      $context->{cmpDefaults}{Formula}{checker} = sub {
        my ($correct,$student,$ans) = @_;
        return 0 if $ans->{isPreview} || $correct != $student;
        Context()->flags->set(setSqrt => 1);
        delete $correct->{test_values}, $student->{test_values};
        my $OK = ($correct == $student); # check if equal when sqrt's are replaced by 1
        Context()->flags->set(setSqrt => 0);
        Value::Error("You must simplify your answer further") unless $OK;
        return $OK;
      };
    }

    ###########################
    #
    #  Subclass the numeric functions
    #
    package my::Function::numeric;
    our @ISA = ('Parser::Function::numeric');

    #
    #  Override sqrt() to return a special value (usually 1) when evaluated
    #  effectively eliminating it from the product.
    #
    sub sqrt {
      my $self = shift;
      my $value = $self->context->flag("setSqrt");
      return $value+1 if $value && $_[0] == 1;  # force sqrt(1) to be incorrect
      return $value if $value;
      return $self->SUPER::sqrt(@_);
    }

    1;

Then in your problem file, use
    loadMacros("contextLimitedSqrt");

    Context("LimitedSqrt")->variables->are(a=>"Real", b=>"Real");

    $f = Formula("3a^5b^2sqrt(a)");

    ... (rest of problem) ...

    ANS($f->cmp);

That should do it.

Davide

In reply to Davide Cervone

Re: simplifying radical expressions

by Bruce Yoshiwara -
I've not had the results I'd hoped.

The correct answer "x sqrt(6)" is marked wrong for the code below. And when the code is altered by specifying that the answer to be 2*sqrt(3), the answer sqrt(12) is still marked correct, although the intent was to require the simplified radical for credit.

Bruce

-------------------
DOCUMENT();

loadMacros(
"PGstandard.pl", # Standard macros for PG language
"MathObjects.pl",
"contextLimitedPowers.pl"
);

# Print problem number and point value (weight) for the problem
TEXT(beginproblem());

# Show which answers are correct and which ones are incorrect
$showPartialCorrectAnswers = 1;

# code essentially from Davide Cervone 3/29/10
###########################
#
# Subclass the numeric functions
#
package my::Function::numeric;
our @ISA = ('Parser::Function::numeric');

#
# Override sqrt() to return a special value (usually 1) when evaluated
# effectively eliminating it from the product.
#
sub sqrt {
my $self = shift;
my $value = $self->context->flag("setSqrt");
return $value+1 if $value && $_[0] == 1; # force sqrt(1) to be incorrect
return $value if $value;
return $self->SUPER::sqrt(@_);
}

#
# end of subclass
#
package main;

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

Context("Numeric")->variables->are(
x => ["Real", limits => [0,2]],
);
#
# make sqrt() use our subclass
#
Context()->functions->set(sqrt=>{class=>'my::Function::numeric'});
#
# Don't allow fractional powers (avoids 1/2 power)
# [Could subclass exponentiation to handle that as well]
#
LimitedPowers::OnlyPositiveIntegers();

$f = Compute("x*sqrt(6)");

BEGIN_TEXT
$BR
9. \{ans_rule(20)\}
END_TEXT

#
# Use a custom checker to check that the answers are equivalent
# and that they are still equivalent when sqrt() is replaced by 1
# (so the stuff outside the sqrt() is equal in both)
#
ANS($f->cmp(checker => sub {
my ($correct,$student,$ans) = @_;
return 0 if $ans->{isPreview} || $correct != $student;
Context()->flags->set(setSqrt => 1);
delete $correct->{test_values}, $student->{test_values};
my $OK = ($correct == $student); # check if equal when sqrt's are replaced
# by 1
Context()->flags->set(setSqrt => 0);
Value::Error("Check to see if your answer is simplified.") unless $OK;
return $OK;
}));


ENDDOCUMENT();
In reply to Bruce Yoshiwara

Re: simplifying radical expressions

by Davide Cervone -
Your two examples of x*sqrt(6) and 2*sqrt(3) are different from your original example of sqrt(9a^5b^4) in a key respect: the square roots in your latest examples are constants of, but in the former they are of formulas. (The a and b are variables in the problem, even though you think of them as constants.) My example code above did not handle this situation, since it was not part of the original example. The problem is that things like sqrt(6) get turned into their numeric equivalents during the parsing phase, so the formula returned for the professor's answer for x*sqrt(6) would be x*2.44949, and the trick about evaluating the square roots would not work (since the square root is gone).

This can be resolved by making the following change: set the reduceConstantFunctions context flag to 0, so that the square root of a constant will be retained in the formula as an actual square root (this is done automatically for student answers, but not professor formulas). This is done using:

    Context()->flags->set(reduceConstantFunctions=>0);
That should make your x*sqrt(6) question work as you desired.

For the 2*sqrt(3), not only is the square root a constant, the whole answer is a constant, so Compute("2*sqrt(3)") returns a MathObject Real (approximately 3.4641), not a formula, and the student's answer of sqrt(12) returns the same real, so they are counted as equal.

This can be resolved by adding the following two lines to the checker:

    $student = $ans->{student_formula};
    $correct = $correct->{original_formula} if defined $correct->{original_formula};
right after the line
    return 0 if $ans->{isPreview} || $correct != $student;
This will use the parsed formula that the student typed rather than the final numeric answer (so sqrt(12) and 2*sqrt(3) will be different formulas), and will use the original parsed formula for the correct answer, if it was created using Compute(), but is a constant. Note that these changes will be OK even for the x*sqrt(6) example, and even for the original sqrt(9a^5b^4) formula, so this is probably the best form to use for your problems.

Note that a student MUST enter sqrt(6) rather than a numeric value, so if she enters x*2.44949, it will be marked wrong (with the error "check to see if your answer is simplified"). Not the best error, but it would take some more work to do better.

Finally, note that the results table will show the student answer in the "entered" column as the numeric result rather than the parsed formula. You may want to change that to reflect the fact that you care about the form, not just the number. Adding formatStudentAnswer=>"reduced" to the cmp() call will do that, even when the correct answer is a constant.

So here is the updated version that should work for all your answers.


    DOCUMENT(); 

    loadMacros(
      "PGstandard.pl", # Standard macros for PG language
      "MathObjects.pl",
      "contextLimitedPowers.pl"
    );

    # Print problem number and point value (weight) for the problem
    TEXT(beginproblem());

    # Show which answers are correct and which ones are incorrect
    $showPartialCorrectAnswers = 1;

    # code essentially from Davide Cervone 4/25/10
    ###########################
    #
    # Subclass the numeric functions
    #
    package my::Function::numeric;
    our @ISA = ('Parser::Function::numeric');

    #
    # Override sqrt() to return a special value (usually 1) when evaluated
    # effectively eliminating it from the product.
    #
    sub sqrt {
      my $self = shift;
      my $value = $self->context->flag("setSqrt");
      return $value+1 if $value && $_[0] == 1; # force sqrt(1) to be incorrect
      return $value if $value;
      return $self->SUPER::sqrt(@_);
    }

    #
    # end of subclass
    #
    package main;

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

    Context("Numeric")->variables->are(
      x => ["Real", limits => [0,2]],    # only needed if x is used in the square roots
    );
    #
    # make sqrt() use our subclass
    #
    Context()->functions->set(sqrt=>{class=>'my::Function::numeric'});
    Context()->flags->set(reduceConstantFunctions=>0);
    #
    # Don't allow fractional powers (avoids 1/2 power)
    # [Could subclass exponentiation to handle that as well]
    #
    LimitedPowers::OnlyPositiveIntegers();

    $f = Compute("x*sqrt(6)");

    BEGIN_TEXT
    $BR
    9. \{ans_rule(20)\}
    END_TEXT

    #
    # Use a custom checker to check that the answers are equivalent
    # and that they are still equivalent when sqrt() is replaced by 1
    # (so the stuff outside the sqrt() is equal in both)
    #
    ANS($f->cmp(checker => sub {
      my ($correct,$student,$ans) = @_;
      return 0 if $ans->{isPreview} || $correct != $student;
      #
      #  Get parsed formula for student and correct answers
      #
      $student = $ans->{student_formula};
      $correct = $correct->{original_formula} if defined $correct->{original_formula};
      #
      # check if equal when sqrt's are replaced by 1
      #
      Context()->flags->set(setSqrt => 1);
      delete $correct->{test_values}, $student->{test_values};
      my $OK = ($correct == $student);
      Context()->flags->set(setSqrt => 0);
      #
      Value::Error("Check to see if your answer is simplified.") unless $OK;
      return $OK;
    },formatStudentAnswer=>"reduced"));

The formatStudentAnswer flag could be set in the Context instead of including it in cmp() if you perfer.

Hope that does it.

Davide

In reply to Bruce Yoshiwara

Re: simplifying radical expressions

by Davide Cervone -
For the current set, our variable expressions represent positive values, and we do not ask for the absolute values.
I hope that you will make that assumption explicit in the statement of your problems (especially if you contribute to the NPL). One of my pet peeves is that incoming Freshmen believe that [math]\sqrt{x^2}=x[/math], which is not true in general, and few of them seem to have seen [math]\sqrt{x^2}=|x|[/math] which is true, at least for real [math]x[/math]. This is a major source of algebraic errors for students in beginning calculus, and is something I would like to see them have a better understanding of when then come into college.

Just my two cent's worth.

Davide