openwebwork / pg

Problem rendering engine for WeBWorK
http://webwork.maa.org/wiki/Category:Authors
Other
45 stars 76 forks source link

Context Fraction and non-terminating decimal reals #429

Open drdrew42 opened 5 years ago

drdrew42 commented 5 years ago

Using Context(“Fraction”) and only the associated context default values, I have found that responses that use decimal approximations in response to an answer blank that is assigned a Fraction object for comparison are unable to achieve a valid result whenever the decimal form is non-terminating (regardless of how many significant figures are provided, and despite the default tolerance).

In such a case, no error is provided by default - but using the AnswerHashInfo option, an error appears in the student_formula portion of the answer hash as follows: Unable to determine stringify for this item Can’t locate object method “string” via package “context::Fraction::Real” at /opt/WeBWorK/pg/lib/Parser/Number.pm line 66

I have attempted to add a variety of string methods to the context::Fraction::Real segment of contextFraction.pl to no avail. There’s still something I’m not grasping about perl inheritance, perhaps...

@dpvc any ideas?

Context(“Fraction”);
$ans = Fraction(1,3);
BEGIN_PGML
Try a decimal approximation of [`\frac{1}{3}`]: [_______]{$ans}
END_PGML
dpvc commented 5 years ago

I've made a pull request to fix the issue. It turns out to be due to how the fraction context promotes reals to fractions: it makes a "fraction" as the real over 1, but it did so by taking the perl real from the student answer, not the MathObject. That meant that it wasn't a fuzzy real, and so the comparison was being done using exact (perl real) comparisons. The patch keeps the fraction using a MathObject, so when the comparison is made, it uses the fuzzy math that takes the tolerances into account.

You could take the patched contextFraction.pl from the pull request and put it in your templates/macros directory in order to get the updated version right away.

drgrice1 commented 5 years ago

I question the validity of this issue, and the pull request that Davide has submitted. It seems to me that if you use the fraction context and the answer is 1/3, then 0.33333 should be incorrect. In my opinion, you are using the wrong context for this problem. You should do:

Context("Numeric");
$ans = Compute("1/3");
BEGIN_PGML
Try a decimal approximation of [`\frac{1}{3}`]: [_______]{$ans}
END_PGML

Of course to do this properly to not accept 1/3 or 33333/100000 as correct you would have to use a custom checker and something like bizarro arithmetic.

If Davide's pull request is accepted, then problems that are using context fraction in what I think is the correct way to expect exact fractional answers will no longer work correctly.

drdrew42 commented 5 years ago

contextFraction.pl supports several contexts with a range of configuration defaults. What you’re describing is Context(“Fraction-NoDecimal”), a context where decimals should be considered incorrect by default.

TIMTOWTDI, but the vanilla Fraction context deftly handles reduction of fractions without manually dividing out GCFs, defaults to displaying correct answers as fractions, converts decimal values to fractions, and offers a range of optional enforcement rules for fractions (completely reduced or otherwise) without resorting to bizarro arithmetic. I explicitly chose the vanilla Fraction context because I wanted several of these features, but didn’t want to specifically require or avoid decimal approximation by students.

Thanks @dpvc - If I follow correctly, the specific error message regarding the string method was a byproduct of the Real answer object being promoted to a Fraction object for comparison, and (I’m not sure why it’s being called, but) the string method failed due to the fraction object having been constructed with a non-Object numerator - which thereby has no string method which can be called?

drgrice1 commented 5 years ago

That is not how I see it. The Fraction-NoDecimal does not work the way I am thinking. The point is to allow a decimal, but only an exact decimal. For example 0.25 would be correct for 1/4. However, 0.33333 would not be correct for 1/3.

drdrew42 commented 5 years ago

Perhaps we have our wires crossed here.

contextFraction.pl is explicit about the intended behavior within each “flavor” of Fraction context which it provides. PR #430 is consistent with the stated intent and will not interfere with existing problems.

What you’re describing (distinguishing between decimal representation of 1/3 vs 1/4) does not match any of the intended behavior, but it does describe the substance of the unintended behavior that I originally encountered and came here to remedy.

drgrice1 commented 5 years ago

I don't think that this is as explicit as you claim in the documentation. The documentation is clear on what students will be allowed to enter in each of the "flavors" of the Fraction context, but not so much on how those entries will be compared to the correct answer.

If the intended behaviour is in line with this pull request then I think that should be added to the documentation. I also think, in that case, that it would be nice to add an option to the context to achieve the behaviour that I have described.

Alex-Jordan commented 5 years ago

I am concerned in the same manner as Glen. We have a very large number of problems at low levels (basic math, basic algebra) using the Fraction context, where we expect that 0.5 is an acceptable answers for 1/2, so we do not use a context that denies decimal entry all together.

But we expect that no terminating decimal can count as correct for 1/3. This would change that behavior, and I'm not sure how to respond in our problem code, or how much work it would take to get things back to that behavior (through problem code editing).

When the answer is a Fraction, we want students entering exact answers. If it happens that a terminating decimal exactly equals a fraction, the faculty want that to be OK too.

Could this new behavior be a flag that is off by default? Could there be an addition context defined that uses it?

drdrew42 commented 5 years ago

My impression from the documentation was that the vanilla "Fraction" context was intended to implement the Fraction object without imposing any default restrictions on the evaluation of student answers. From the documentation where it describes the "Fraction" context (emph mine): "there are no restrictions on what is allowed by the student." I have been writing problems that use the Fraction context based on this interpretation; and that is how I've ended up discovering this (what seems to be unintentional) situation wherein student-supplied decimals are handled differently depending on whether or not they are exact.

Furthermore, I have found (based on surveys and user-testing at our institution) that providing explicit directions regarding acceptable forms of student response directly within the problem text has made a significant impact in reducing student frustration with WeBWorK. In addition to explicit directions in the problem text, I have also found that providing a specific error message when these requirements are violated adds yet another layer of awareness. What I wonder here is how you are informing students about which forms are acceptable; especially considering that --in its current state-- this Fraction context provides no specific feedback to students when they attempt to approximate a non-terminating decimal.

A separate context seems most reasonable for those who want to allow decimals, but only in the cases where they are exact representations of the specified value. After all, shouldn't the most basic context provided by this library be the one with the fewest restrictions?

I would suggest a name like "Fraction-ExactDecimal" or "Fraction-NoTolerance", expanding upon the existing hierarchy of restriction:

As with the other restricted fraction contexts, Fraction-ExactDecimal should provide specific feedback when a student answer is rejected because its decimal form cannot possibly be an exact match.

Alex-Jordan commented 5 years ago

there are no restrictions on what is allowed by the student.

There are no restrictions on what may be typed into the input field. There is no blocking by the prefilter for typing a decimal or a trig function. That is different from no restrictions on what is counted correct which is clearly not the intent.

But intentional or not, this has been the behavior for the decade or so that this context has existed. Many problems have been written that rely on that behavior as it is. I argue that the change in behavior is too dangerous to the existing libraries of problems. And I disagree that the documentation says what you interpret it to say.

There is an easy solution though: put the new behavior in a new context (or via a new flag) and clarify the documentation. It should be much easier for you to use a new context/flag on your library than to review all problems that right now use the Fraction context.

By the way, if the ideas is that 0.3333 should count as correct for 1/3 because it is within the regular error tolerance, then why why shouldn't 3333/10000 count as correct as well? Why shouldn't 21/34 as correct for 34/55?

If Davide thinks it possible, an additional check of the following form would be nice. If the answer is wrong, but also is withing the context's error tolerance, produce a feedback message like 'Your answer is close to the correct answer, but not exactly correct.'

Alex-Jordan commented 5 years ago

I had a relevant thought. When you feed a perl real to Fraction(), it uses a continued fraction algorithm to actually make a Fraction Math object that is as close as can be to the given perl real, subject to a cap on an internal value that is computed with each recursion (which has to do with avoiding some machine rounding).

For example, Fraction(sqrt(2)) makes Fraction(131836323,93222358).

And Fraction(0.3333) makes Fraction(3333,10000).

It seems like if a student types in 0.3333 when the expected answer is a Fraction Math Object, the same things should happen as if a problem author used Fraction(0.3333). The result should be Fraction(3333,10000) and that is compared to the correct answer. Which maybe is Fraction(1,3). And I think (a bad assumption?) everyone agrees to have those not count as equivalent.

This is what I always thought was happening actually, and why 0.5 is counted correct for Fraction(1,2). Because it got "promoted" to Fraction(1,2). While 0.3333 got promoted to Fraction(3333,10000) and was declared not equivalent to Fraction(1/3).

drdrew42 commented 5 years ago

That is different from no restrictions on what is counted correct which is clearly not the intent.

I think the documentation for contextFraction clearly needs some clarification. ;P

"Allowing" a student to type a decimal value for a fraction that cannot be expressed exactly as a decimal creates confusion.

Consider the following situation: two students are working together on the same problem - with different random seeds. One student has a problem whose solution is a terminating decimal and he answers first. The two students now share the (mistaken) impression that decimal solutions are acceptable. The second student is hung up on the fact that his non-terminating decimal will not be accepted, regardless of the number of significant digits he provides.

I realize here that I am skewing towards a pedagogical argument rather than strictly software development, but the situation above is precisely what brought me to raise this issue in the first place. However, I also understand the need for consistency with prior functionality as interpreted by content authors up until this point.

It seems like if a student types in 0.3333 when the expected answer is a Fraction Math Object, the same things should happen as if a problem author used Fraction(0.3333).

I disagree with this. There should be a distinction when the student enters 0.3333 vs when they enter 3333/10000 - in the one case, the literal student response conveys an implicit real-fuzziness and in the second, an implicit algebraic-exactness. Measuring each according to the form in which the student responded is (to me) preferable - particularly in a context that allows decimals. To me, "allowing" decimals means subjecting any submitted decimal responses to the stated tolerance.

I realize that ultimately, I am on the "losing" side of this issue - consistency with historical behavior is the more important consideration. However, I would prefer to see this PR incorporated as a flag (or separate context) over the closest alternative - which would seem to be re-casting Fraction-objects as Formula-objects (in a Numeric context with reduceConstants=>0) prior to comparison. And if possible, I would recommend the addition of an error message for decimal responses that cannot possibly get "close enough" - redirecting the student towards an exact response.

Alex-Jordan commented 5 years ago

It is clear that you value the pedagogical aspects, which is good. It's a similar concern that leads someone to view this behavior as a feature rather than a bug, but maybe only in a different setting than your setting. If the curriculum is such that you want exact, fraction answers, but you don't want to penalize a student who recognizes 1/10 as 0.1 and is in the habit of writing that way, then the current behavior is great. Allowing 0.1 when the answer is 1/10 alleviates some of that user frustration that can happen.

I lean away from including formatting instructions at the outset of a problem. It's true that provides complete clarity, but I find that the formatting instructions can outweigh the problem itself. Both in terms of space on the screen as well as cognitive demand with reading comprehension. As long as attempts are unlimited, I prefer to give the formatting corrections in the results table feedback messages, when needed.

More broadly, I like to try to imagine what things would be like if it were a pencil-and-paper assignment. How would the question read, and how would hand-written feedback look for various answers a student would turn in? And then can we really faithfully simulate that?

Here, you have drawn good attention to a place where that fails. How 0.3333 is just incorrect, without the message that a human grader would probably give, like "This is not the exact answer, which is 1/3. Please don't use decimals. Etc." I'd like to hear from @dpvc his thoughts on all of this, but in particular the idea to make a special feedback message when an answer is within the tolerance, but not exact.

drgrice1 commented 4 years ago

I believe that this issue and issue #282 are duplicates. I think we need to reach a consensus on this, and resolve these issues.