BLD-434: Add fix for negative exponents.
Set default tolerance to 1e-3% for all responsetypes.
This commit is contained in:
committed by
Alexander Kryklia
parent
cbe97d6fe6
commit
1347645ef0
@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes,
|
||||
in roughly chronological order, most recent first. Add your entries at or near
|
||||
the top. Include a label indicating the component affected.
|
||||
|
||||
Blades: Fix comparison of float numbers. BLD-434.
|
||||
|
||||
Blades: Allow regexp strings as the correct answer to a string response question. BLD-475.
|
||||
|
||||
Common: Add feature flags to allow developer use of pure XBlocks
|
||||
|
||||
@@ -35,7 +35,8 @@ from calc import evaluator, UndefinedVariable
|
||||
from . import correctmap
|
||||
from datetime import datetime
|
||||
from pytz import UTC
|
||||
from .util import compare_with_tolerance, contextualize_text, convert_files_to_filenames, is_list_of_files, find_with_default
|
||||
from .util import (compare_with_tolerance, contextualize_text, convert_files_to_filenames,
|
||||
is_list_of_files, find_with_default, default_tolerance)
|
||||
from lxml import etree
|
||||
from lxml.html.soupparser import fromstring as fromstring_bs # uses Beautiful Soup!!! FIXME?
|
||||
import capa.xqueue_interface as xqueue_interface
|
||||
@@ -832,7 +833,7 @@ class NumericalResponse(LoncapaResponse):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.correct_answer = ''
|
||||
self.tolerance = '0' # Default value
|
||||
self.tolerance = default_tolerance
|
||||
super(NumericalResponse, self).__init__(*args, **kwargs)
|
||||
|
||||
def setup_response(self):
|
||||
@@ -1877,7 +1878,7 @@ class FormulaResponse(LoncapaResponse):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.correct_answer = ''
|
||||
self.samples = ''
|
||||
self.tolerance = '1e-5' # Default value
|
||||
self.tolerance = default_tolerance
|
||||
self.case_sensitive = False
|
||||
super(FormulaResponse, self).__init__(*args, **kwargs)
|
||||
|
||||
@@ -2433,7 +2434,7 @@ class ChoiceTextResponse(LoncapaResponse):
|
||||
input_name = child.get('name')
|
||||
# Contextualize the tolerance to value.
|
||||
tolerance = contextualize_text(
|
||||
child.get('tolerance', '0'),
|
||||
child.get('tolerance', default_tolerance),
|
||||
context
|
||||
)
|
||||
# Add the answer and tolerance information for the current
|
||||
@@ -2662,7 +2663,7 @@ class ChoiceTextResponse(LoncapaResponse):
|
||||
|
||||
correct_ans = params['answer']
|
||||
# Set the tolerance to '0' if it was not specified in the xml
|
||||
tolerance = params.get('tolerance', '0')
|
||||
tolerance = params.get('tolerance', default_tolerance)
|
||||
# Make sure that the staff answer is a valid number
|
||||
try:
|
||||
correct_ans = complex(correct_ans)
|
||||
|
||||
@@ -182,7 +182,10 @@ class NumericalResponseXMLFactory(ResponseXMLFactory):
|
||||
response_element = etree.Element('numericalresponse')
|
||||
|
||||
if answer:
|
||||
response_element.set('answer', str(answer))
|
||||
if isinstance(answer, float):
|
||||
response_element.set('answer', repr(answer))
|
||||
else:
|
||||
response_element.set('answer', str(answer))
|
||||
|
||||
if tolerance:
|
||||
responseparam_element = etree.SubElement(response_element, 'responseparam')
|
||||
|
||||
@@ -56,13 +56,11 @@ class ResponseTest(unittest.TestCase):
|
||||
def assert_multiple_grade(self, problem, correct_answers, incorrect_answers):
|
||||
for input_str in correct_answers:
|
||||
result = problem.grade_answers({'1_2_1': input_str}).get_correctness('1_2_1')
|
||||
self.assertEqual(result, 'correct',
|
||||
msg="%s should be marked correct" % str(input_str))
|
||||
self.assertEqual(result, 'correct')
|
||||
|
||||
for input_str in incorrect_answers:
|
||||
result = problem.grade_answers({'1_2_1': input_str}).get_correctness('1_2_1')
|
||||
self.assertEqual(result, 'incorrect',
|
||||
msg="%s should be marked incorrect" % str(input_str))
|
||||
self.assertEqual(result, 'incorrect')
|
||||
|
||||
def _get_random_number_code(self):
|
||||
"""Returns code to be used to generate a random result."""
|
||||
@@ -1070,6 +1068,27 @@ class NumericalResponseTest(ResponseTest):
|
||||
incorrect_responses = ["", "4.5", "3.5", "0"]
|
||||
self.assert_multiple_grade(problem, correct_responses, incorrect_responses)
|
||||
|
||||
def test_floats(self):
|
||||
"""
|
||||
Default tolerance for all responsetypes is 1e-3%.
|
||||
"""
|
||||
problem_setup = [
|
||||
#[given_asnwer, [list of correct responses], [list of incorrect responses]]
|
||||
[1, ["1"], ["1.1"],],
|
||||
[2.0, ["2.0"], ["1.0"],],
|
||||
[4, ["4.0", "4.00004"], ["4.00005"]],
|
||||
[0.00016, ["1.6*10^-4"], [""]],
|
||||
[0.000016, ["1.6*10^-5"], ["0.000165"]],
|
||||
[1.9e24, ["1.9*10^24"], ["1.9001*10^24"]],
|
||||
[2e-15, ["2*10^-15"], [""]],
|
||||
[3141592653589793238., ["3141592653589793115."], [""]],
|
||||
[0.1234567, ["0.123456", "0.1234561"], ["0.123451"]],
|
||||
[1e-5, ["1e-5", "1.0e-5"], ["-1e-5", "2*1e-5"]],
|
||||
]
|
||||
for given_answer, correct_responses, incorrect_responses in problem_setup:
|
||||
problem = self.build_problem(answer=given_answer)
|
||||
self.assert_multiple_grade(problem, correct_responses, incorrect_responses)
|
||||
|
||||
def test_grade_with_script(self):
|
||||
script_text = "computed_response = math.sqrt(4)"
|
||||
problem = self.build_problem(answer="$computed_response", script=script_text)
|
||||
|
||||
@@ -4,16 +4,28 @@ from cmath import isinf
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Utility functions used in CAPA responsetypes
|
||||
default_tolerance = '0.001%'
|
||||
|
||||
|
||||
def compare_with_tolerance(v1, v2, tol):
|
||||
''' Compare v1 to v2 with maximum tolerance tol
|
||||
def compare_with_tolerance(v1, v2, tol=default_tolerance):
|
||||
'''
|
||||
Compare v1 to v2 with maximum tolerance tol.
|
||||
|
||||
tol is relative if it ends in %; otherwise, it is absolute
|
||||
|
||||
- v1 : student result (number)
|
||||
- v2 : instructor result (number)
|
||||
- v1 : student result (float complex number)
|
||||
- v2 : instructor result (float complex number)
|
||||
- tol : tolerance (string representing a number)
|
||||
|
||||
Default tolerance of 1e-3% is added to compare two floats for near-equality
|
||||
(to handle machine representation errors).
|
||||
It is relative, as the acceptable difference between two floats depends on the magnitude of the floats.
|
||||
(http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/)
|
||||
Examples:
|
||||
In [183]: 0.000016 - 1.6*10**-5
|
||||
Out[183]: -3.3881317890172014e-21
|
||||
In [212]: 1.9e24 - 1.9*10**24
|
||||
Out[212]: 268435456.0
|
||||
'''
|
||||
relative = tol.endswith('%')
|
||||
if relative:
|
||||
@@ -28,6 +40,8 @@ def compare_with_tolerance(v1, v2, tol):
|
||||
# `inf <= inf` which is a fail. Instead, compare directly.
|
||||
return v1 == v2
|
||||
else:
|
||||
# v1 and v2 are, in general, complex numbers:
|
||||
# there are some notes about backward compatibility issue: see responsetypes.get_staff_ans()).
|
||||
return abs(v1 - v2) <= tolerance
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
|
||||
@multipleChoiceTemplate : "( ) incorrect\n( ) incorrect\n(x) correct\n"
|
||||
@checkboxChoiceTemplate: "[x] correct\n[ ] incorrect\n[x] correct\n"
|
||||
@stringInputTemplate: "= answer\n"
|
||||
@numberInputTemplate: "= answer +- x%\n"
|
||||
@numberInputTemplate: "= answer +- 0.001%\n"
|
||||
@selectTemplate: "[[incorrect, (correct), incorrect]]\n"
|
||||
@headerTemplate: "Header\n=====\n"
|
||||
@explanationTemplate: "[explanation]\nShort explanation\n[explanation]\n"
|
||||
|
||||
Reference in New Issue
Block a user