WeBWorK Problems

Specifying occurrences of character in answer

Specifying occurrences of character in answer

by Bruce Yoshiwara -
Number of replies: 9

Can authors in WeBWorK specify how often a given character is allowed in answers? As I recall, the MyOpenMath homework system allows the exercise author to specify how often a character is allowed in an answer, with different specifications for different answers in multi-part exercises. (MyOpenMath uses some parameter name like “answertimes”?)

Here’s why I interested in such an option.

I’m in the midst of trying to code WeBWorK exercises to match an open college algebra text (https://aimath.org/textbooks/approved-textbooks/yoshiwara/ ). In many of the exercises, the point is for the students to put answers in a particular format.

The common WeBWorK strategies for coding such problems seem to involve setting an appropriate context, appropriately disabling relevant functions and operations, and/or having students parse their answer and input parts in separate answer boxes.

For example, I just tried to code something for a set of exercises with the instructions, “Simplify by applying the laws of exponents. Write your answers with positive exponents only. Assume that all variables represent positive numbers.” The expressions the student sees involve arithmetic operations on power functions (kx^p) with rational exponents p.

So the answers expected in the textbook would have the form kx^p or k/x^p. The real constant k may or may not be a positive integer, and the positive rational exponent p might or might not be an integer.

I don’t see a way to get WeBWorK to reject equivalent expressions to the desired answer in a single answer box because the correct answer involves multiplication, powers, and perhaps negation and/or division. That is, the desired answer for simplifying -x^(1/3) / x^(5/3) will require the use of both the “-” symbol and the “/” symbol, but I don’t want the student to get credit for copying the given expression nor for entering “-x^(-4/3)”--instructions specify positive exponents.

On the other hand, I don’t want to ask the student to answer in two boxes, namely one for the constant (-1) and another for the exponent, because I don’t want to give away that the answer for this particular problem requires the student to put the power in a denominator—one goal of the exercise is for students to recognize that having a negative exponent corresponds to a reciprocal. (And the textbook typically has several parts to each exercise, one part’s answer will have the power in the denominator and another won’t.)

I’m looking for a way to code to this particular type of problem, and I’m hoping it might smoothly generalize to lots of other exercises that restrict the form of the answer.

So using the MyOpenMath capability in my example, I could specify that the “-” occurs exactly once, “/” occurs exactly twice, and the decimal point "." not occur at all in any correct answer. (Depending on the Context, I might specify that “root” not occur at all.) Or instead of counting occurrences of “/”, perhaps I might specify that “(“ occurs at most once.

Yes I do see issues with MyOpenMath rejecting what should probably be acceptable, and it’s tricky specifying the right restrictions. But I think the optional capability of specifying characters on a per-answer basis offers a lot more flexibility in exercises that require a specific format.

Cheers.

Bruce

In reply to Bruce Yoshiwara

Re: Specifying occurrences of character in answer

by Alex Jordan -
Hi Bruce,

I think you may want to use bizarroArithmetic.pl. Basically, it is designed to capture that answers are numerically correct _and_ are in a certain form. But still give appropriate flexibility for different forms that are within spec. It temporarily changes the meaning of arithmetic operators in a smart way (imho) that generally makes this possible.

If you use bizarroArithmetic.pl, and set it up right, it will accept something like "-1/x^(4/3)" or "1/-x^(4/3)" as correct. But it would tell the student that "-x^(-4/3)" is mathematically equivalent, but not in the specified form. Same for "-x^(1/3) / x^(5/3)".

To use bizarroArithmetic.pl, you might start by reading the documentation at https://github.com/openwebwork/webwork-open-problem-library/blob/master/OpenProblemLibrary/macros/PCC/bizarroArithmetic.pl. (See if you can get a word-wrapped version.)

Then, you need to put some subset of the example lines 17--29 in your problem preamble. And then make a custom answer checker something like lines 54--68 there. Since your exercise is concerned with exponentiation, you might start by limiting use of bizarro to just ** and ^. Note the example checker uses bizarroDiv, but you'd want bizarroPow.

Once you get that far, experiment with several answers you would expect to be correct, and several you would want to be incorrect. If there are some issues still, report back, including your full pg file, and I'll help more directly.


In reply to Alex Jordan

Re: Specifying occurrences of character in answer

by Bruce Yoshiwara -
Hi Alex,

Thanks, that sounds promising. But I'm now looking at https://github.com/openwebwork/pg/blob/master/macros/bizarroArithmetic.pl because I got an error when I tried the url you posted.

Bruce
In reply to Bruce Yoshiwara

Re: Specifying occurrences of character in answer

by Bruce Yoshiwara -
Hi Alex,

I'm not yet able to get bizarroArithmetic to check anything yet. Whatever the student inputs, I get the message that WeBWorK can't generate enough valid points for comparison.

In my first attempt, I trimmed out what I believed should make no difference to the error, and here's what's left.

##DESCRIPTION
## Algebra problem: Rational exponents--incubation time vs mass of egg
##ENDDESCRIPTION

##KEYWORDS('algebra', 'root', 'radical', 'exponent', 'allometry')

## DBsubject(Algebra)
## DBchapter(Functions)
## DBsection(Roots and Radicals)
## Date('2/4/2018')
## Author('Yoshiwara')
## Institution('')
## TitleText1('Modeling, Functions, and Graphs')
## EditionText1('5')
## AuthorText1('Yoshiwara')
## Section1('3.4')
## Problem1('43')

########################################################################
DOCUMENT();

loadMacros(
"PGstandard.pl",
"PGunion.pl",
"imageChoice.pl",
"PGML.pl",
"bizarroArithmetic.pl",
);

Context()->flags->set(reduceConstants=>0);
Context()->flags->set(limits=>[1,2000]);

$f[0] = "-x^(1/3) / (x^(5/3)) ";
$f_display[0] = " \dfrac{ - x^{1/3}}{ x^{5/3} }";
$g[0] = Formula(" -1/x^{4/3} " );

Context()->operators->set(
'**' => {class => 'bizarro::BOP::power', isCommand => 1, perl=>undef},
'^' => {class => 'bizarro::BOP::power', isCommand => 1, perl=>undef},
);
Context()->flags->set(bizarroPow=>1);

BEGIN_PGML
a.
[` [$f_display[0]] =`][__________]
END_PGML
install_problem_grader(~~&std_problem_grader);
ANS($g[0] -> cmp());

ENDDOCUMENT();
In reply to Bruce Yoshiwara

Re: Specifying occurrences of character in answer

by Alex Jordan -
Hi Bruce,

Well, your timing is interesting. I see that behavior too, and something is broken. Meanwhile, an instructor here is writing that a batch of problems that use bizarroArithmetic suddenly no longer work (after years of behaving well).

With the last upgrade of WW, there was a lot of "perl warnings cleanup". Right now I suspect something about that hurt bizarro. Tonight I will investigate, and hopefully have it all sorted out by the end of the weekend.

Point is, bizarro is supposed to work as you've tried and I outlined. And it has worked that way for a while up until seemingly recently. So hang tight.

Alex
In reply to Alex Jordan

Re: Specifying occurrences of character in answer

by Alex Jordan -
OK, I think I still have a different issue to track down regarding local faculty reporting a problem. But the issue with your example is the sequence you are executing the commands. This block:

Context()->operators->set(
'**' => {class => 'bizarro::BOP::power', isCommand => 1, perl=>undef},
'^' => {class => 'bizarro::BOP::power', isCommand => 1, perl=>undef},
);

Needs to happen before you define any Math Objects for the context. In this case, $g[0].

Now, as it turns out (and I often forget when dealing with bizarro), since your problem is about exponents, it is the other relevant things that should get bizarro treatment. In this case, division. Here is what I believe to be a working version of your example. Again, note that I moved the operator settings up earlier. And I've included the custom answer checker. Also I changed the macros you loaded to what I would go with, but maybe you need the originals for what goes on with the full version of the problem.



##DESCRIPTION
## Algebra problem: Rational exponents--incubation time vs mass of egg
##ENDDESCRIPTION

##KEYWORDS('algebra', 'root', 'radical', 'exponent', 'allometry')

## DBsubject(Algebra)
## DBchapter(Functions)
## DBsection(Roots and Radicals)
## Date('2/4/2018')
## Author('Yoshiwara')
## Institution('')
## TitleText1('Modeling, Functions, and Graphs')
## EditionText1('5')
## AuthorText1('Yoshiwara')
## Section1('3.4')
## Problem1('43')

########################################################################
DOCUMENT();

loadMacros(
"PGstandard.pl",
"MathObjects.pl",
"PGML.pl",
"bizarroArithmetic.pl",
);

TEXT(beginproblem());

Context("Numeric");
Context()->flags->set(reduceConstants=>0);
Context()->flags->set(limits=>[1,10]);
Context()->operators->set(
'/' => {class => 'bizarro::BOP::divide', isCommand => 1},
);

$f[0] = "-x^(1/3) / (x^(5/3)) ";
$f_display[0] = " \dfrac{ - x^{1/3}}{ x^{5/3} }";
$g[0] = Formula(" -1/x^(4/3) " );


BEGIN_PGML
a.
[` [$f_display[0]] =`][__________]
END_PGML

ANS($g[0] -> cmp(
checker=>sub{
my ( $correct, $student, $ansHash ) = @_;
return 0 if $ansHash->{isPreview} || $correct != $student;
$student = $ansHash->{student_formula};
$correct = $correct->{original_formula} if defined $correct->{original_formula};
$student = Formula("$student"); $correct = Formula("$correct");
return 0 unless ($correct == $student);
Context()->flags->set(bizarroDiv=>1);
delete $correct->{test_values};
delete $student->{test_values};
my $OK = (($correct == $student) or ($student == $correct));
Context()->flags->set(bizarroDiv=>0);
Value::Error("Your answer is correct, but please simplify it further") unless $OK;
return $OK;
}));


ENDDOCUMENT();



If you are writing this in PTX source, you wnat to put the following in your pg-code element at the end:

$evaluator[0] = $g[0] -> cmp(
checker=>sub{
my ( $correct, $student, $ansHash ) = @_;
return 0 if $ansHash->{isPreview} || $correct != $student;
$student = $ansHash->{student_formula};
$correct = $correct->{original_formula} if defined $correct->{original_formula};
$student = Formula("$student"); $correct = Formula("$correct");
return 0 unless ($correct == $student);
Context()->flags->set(bizarroDiv=>1);
delete $correct->{test_values};
delete $student->{test_values};
my $OK = (($correct == $student) or ($student == $correct));
Context()->flags->set(bizarroDiv=>0);
Value::Error("Your answer is correct, but please simplify it further") unless $OK;
return $OK;
});



And then in the statement, where you put the answer blank, put it like:
var name="$g[0]" evaluator="$evaluator[0]" width="15"
(with surrounding tag marks which I omit here because this forum eats them up and I don't know how to escape that here.)

In reply to Alex Jordan

Re: Specifying occurrences of character in answer

by Bruce Yoshiwara -
Hello Alex,

Thanks!

Actually, the instructions (which I hadn't include in my pared-down example) asks for an expression with only positive exponents, so I'll be working on how to disallow the answer -x^(-4/3).

But now that you've corrected the order of commands, I can experiment with how answers will be graded with different settings.

Cheers.

Bruce

In reply to Bruce Yoshiwara

Re: Specifying occurrences of character in answer

by Alex Jordan -
I think what I posted will do that. Suppose the answer was actually 1/x^4 to simplify the following explanation. Ans suppose 5 is a test point. It will compute a different value for 5^(-4) than for 1/5^4. In the first, you will get the actual decimal value of 0.0016. For the second when bizarro division is on, it first will reduce to 1/625, but then bizarro division will make some decimal that is not equal to 0.0016.

Since x^(-4) is equal to 1/x^4 for regular arithmetic, a submitted answer of x^(-4) passes the first bar in the answer checker.

Since x^(-4) is not equal to 1/x^4 for bizarro arithmetic, a submitted answer of x^(-4) does not pass the second bar, and you get a special feedback message that the answer is numerically correct, but not in the required form.

Meanwhile, a submitted answer of 1/x^4 passes both bars.

You might get "correct" results for things like 1/((x^-4)^(-1)) though, and at the moment I think you have to live with that. Unfortunately just turning on bizarro ^ in addition to / will not help. The bizarro operations are a consistent field structure on R. So if you have both bizarro ^ and bizarro / turned on, then 1/x is equal to x^(-1).

But you could alter the custom checker to just turn on one, then try again with the other. And to get credit, the expression would have to pass three filters instead of just the two in the demonstration.
In reply to Alex Jordan

Re: Specifying occurrences of character in answer

by Bruce Yoshiwara -
Hi Alex,

Thanks so much for your time. I feel a bit guilty to report that when I (believe I had) copied your code and tested it, it accepted -x^(-4/3) as correct. So I did turn on the Pow switches, and -x^(-4/3) is now rejected.

So I'm happy with what I've now have, but apparently you wouldn't expect it to behave differently from what you provided.

Anyway, here's what I now have (which seems to work).

DOCUMENT();

loadMacros(
"PGstandard.pl",
"MathObjects.pl",
"PGML.pl",
"bizarroArithmetic.pl",
);

TEXT(beginproblem());

Context("Numeric");
Context()->flags->set(reduceConstants=>0);
Context()->flags->set(limits=>[1,10]);
Context()->operators->set(
'/' => {class => 'bizarro::BOP::divide', isCommand => 1},
'**' => {class => 'bizarro::BOP::power', isCommand => 1, perl=>undef},
'^' => {class => 'bizarro::BOP::power', isCommand => 1, perl=>undef},
);
#Context()->flags->set(bizarroPow=>1);

$f[0] = "-x^(1/3) / (x^(5/3)) ";
$f_display[0] = " \dfrac{ - x^{1/3}}{ x^{5/3} }";
$g[0] = Formula(" -1/x^(4/3) " );


BEGIN_PGML

THIS PROBLEM IS NOT TO BE ASSIGNED.

Simplify by applying the laws of exponents. Write your answers with positive exponents only.
a.
[` [$f_display[0]] =`][__________]
END_PGML

ANS($g[0] -> cmp(
checker=>sub{
my ( $correct, $student, $ansHash ) = @_;
return 0 if $ansHash->{isPreview} || $correct != $student;
$student = $ansHash->{student_formula};
$correct = $correct->{original_formula} if defined $correct->{original_formula};
$student = Formula("$student"); $correct = Formula("$correct");
return 0 unless ($correct == $student);
Context()->flags->set(bizarroDiv=>1,bizarroPow=>1);
delete $correct->{test_values};
delete $student->{test_values};
my $OK = (($correct == $student) or ($student == $correct));
Context()->flags->set(bizarroDiv=>0,bizarroPow=>1);
Value::Error("Your answer is equivalent, but please use the proper format") unless $OK;
return $OK;
}));


ENDDOCUMENT();
In reply to Bruce Yoshiwara

Re: Specifying occurrences of character in answer

by Alex Jordan -
I'm glad it is working. [If you are a perfectionist, experiment with submissions like -1/((x^(1/3))^4), -(1/x^(4/3)), and 1/(-x^(4/3)). Unsure what to expect, but see if anything makes you unhappy.]

One thing: your custom checker should set bizarroPow to 0 at the end, not to 1. (Copy-paste error no doubt.) In this example here it is irrelevant. But if you use this as a template and have code that follows the answer checker, you would want that flag to have gone back to 0.

Next thing: I removed all the bizarroPow from your example (two places in the checker, and the two lines in the preamble), and it rejected -x^(-4/3) as "Your answer is equivalent, but please use the proper format". So either our systems are behaving differently (quite possible if we are on different branches) or something else was off with what you tried before.

Last thing: I'm confused why with both bizarroDiv and bizarroPow in play, that it rejects -x^(-4/3). But I confirm seeing that same behavior. If you try again and things work out without using bizarroPow, my recommendation would be to remove it. Only because if I investigate and there is something about bizarro that is inconsistent with how it is supposed to behave, I may "fix" that one day, inadvertently breaking your problems.