-
Notifications
You must be signed in to change notification settings - Fork 2
Feature/sig fig #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Feature/sig fig #15
Changes from all commits
d309e65
d8ea34a
62a2d6c
4730319
1d61b31
90e68a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,47 @@ | ||
| from numpy import spacing | ||
|
|
||
|
|
||
| def _is_number(value): | ||
| return isinstance(value, int) or isinstance(value, float) | ||
|
|
||
|
|
||
| def _round_to_sig_figs(value, sig_figs): | ||
| if value == 0: | ||
| return 0.0 | ||
| return float(f"{value:.{sig_figs}g}") | ||
|
|
||
|
|
||
| def _result(is_correct, real_diff, allowed_diff, feedback=""): | ||
| return { | ||
| "is_correct": bool(is_correct), | ||
| "real_diff": real_diff, | ||
| "allowed_diff": allowed_diff, | ||
| "feedback": feedback, | ||
| } | ||
|
|
||
|
|
||
| def _evaluate_sig_figs(response, answer, sig_figs): | ||
| if not _is_number(response): | ||
| return _result(False, None, None, "Please enter a number.") | ||
|
|
||
| rounded_answer = _round_to_sig_figs(answer, sig_figs) | ||
| rounded_response = _round_to_sig_figs(response, sig_figs) | ||
| is_correct = abs(rounded_response - rounded_answer) <= spacing(abs(rounded_answer)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need to discuss with Phil what we mean by sig figs. Example: Is the response correct to 3 sig fig? Is this a scientific question of expressing a number to 3 sig fig? If so then no, the response is not correct. It should have been rounded to 0.123. If we're asking for the two numbers to be similar after rounding to 3 s.f. that's a different criterion? Similarly 0.1230 could be interpreted as equal to 0.123, or not equal because it implies a different level of precision. (e.g. https://ccnmtl.columbia.edu/projects/mmt/frontiers/web/chapter_5/6665.html) I suspect that we need two types of sig fig checking? The computational type and the scientific type? The latter would need to operate on the string before it is converted to a float or integer as a prelude. I also wonder if we should incorporate feedback when there is a level of known equality (this point applies to the wider isSimilar), e.g. 'correct within the allowed tolerance'.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the feedback, I am happy to discuss with Phil further. However, I think the link you gave provides the detail that I missed in my first implementation. Currently, the implementation doesn't support trailing 0s for decimal numbers as the conversion will truncate them. I think adding feedback throughout would also be beneficial. I'll make the edits. |
||
|
|
||
| return _result(is_correct, abs(response - answer), None) | ||
|
|
||
|
|
||
| def _evaluate_tolerance(response, answer, relative_tolerance, absolute_tolerance): | ||
| allowed_diff = absolute_tolerance + relative_tolerance * abs(answer) + spacing(answer) | ||
|
|
||
| if not _is_number(response): | ||
| return _result(False, None, allowed_diff, "Please enter a number.") | ||
|
|
||
| real_diff = abs(response - answer) | ||
|
|
||
| return _result(real_diff <= allowed_diff, real_diff, allowed_diff) | ||
|
|
||
|
|
||
| def evaluation_function(response, answer, params) -> dict: | ||
| """ | ||
| Function used to grade a student response. | ||
|
|
@@ -21,31 +63,27 @@ def evaluation_function(response, answer, params) -> dict: | |
| to output the grading response. | ||
| """ | ||
|
|
||
| rtol = params.get("rtol", 0) | ||
| atol = params.get("atol", 0) | ||
| sig_figs = params.get("significant_figures", params.get("sig_figs")) | ||
| relative_tolerance = params.get("relative_tolerance", params.get("rtol", 0)) | ||
| absolute_tolerance = params.get("absolute_tolerance", params.get("atol", 0)) | ||
|
|
||
| is_correct = None | ||
| real_diff = None | ||
| uses_tolerance = any( | ||
| key in params | ||
| for key in ("relative_tolerance", "rtol", "absolute_tolerance", "atol") | ||
| ) | ||
|
|
||
| if not (isinstance(answer, int) or isinstance(answer, float)): | ||
| raise Exception("Answer must be a number.") | ||
| if sig_figs is not None and uses_tolerance: | ||
| raise Exception( | ||
| "significant_figures/sig_figs cannot be used together with " | ||
| "relative_tolerance/rtol or absolute_tolerance/atol." | ||
| ) | ||
|
|
||
| real_diff = None | ||
| allowed_diff = atol + rtol * abs(answer) | ||
| allowed_diff += spacing(answer) | ||
| is_correct = False | ||
| feedback = "" | ||
| if not (isinstance(response, int) or isinstance(response, float)): | ||
| feedback = "Please enter a number." | ||
| else: | ||
| real_diff = abs(response - answer) | ||
| allowed_diff = atol + rtol * abs(answer) | ||
| allowed_diff += spacing(answer) | ||
| is_correct = bool(real_diff <= allowed_diff) | ||
| if sig_figs is not None and (not isinstance(sig_figs, int) or sig_figs < 1): | ||
| raise Exception("significant_figures must be a positive integer.") | ||
|
|
||
| return { | ||
| "is_correct": is_correct, | ||
| "real_diff": real_diff, | ||
| "allowed_diff": allowed_diff, | ||
| "feedback": feedback, | ||
| } | ||
| if not _is_number(answer): | ||
| raise Exception("Answer must be a number.") | ||
|
|
||
| if sig_figs is not None: | ||
| return _evaluate_sig_figs(response, answer, sig_figs) | ||
| return _evaluate_tolerance(response, answer, relative_tolerance, absolute_tolerance) | ||
Uh oh!
There was an error while loading. Please reload this page.