diff --git a/app/context/physical_quantity.py b/app/context/physical_quantity.py index 28348de..3155ac5 100644 --- a/app/context/physical_quantity.py +++ b/app/context/physical_quantity.py @@ -285,7 +285,8 @@ def quantity_match(unused_inputs): if parsing_params.get('rtol', 0) > 0 and (ans != 0): value_match = bool(abs(float((ans-res)/ans)) < parsing_params['rtol']) elif parsing_params.get('atol', 0) > 0 or (ans == 0): - value_match = bool(abs(float(ans-res)) < parsing_params['atol']) + answer_unit_factor = float(parameters["reserved_expressions"]["answer"]["quantity"].converted_unit_factor) + value_match = bool(abs(float(ans-res)) < parsing_params['atol']*answer_unit_factor) substitutions = [(key, expr["standard"]["unit"]) for (key, expr) in reserved_expressions] unit_match = is_equal(lhs, rhs, substitutions) diff --git a/app/tests/physical_quantity_evaluation_test.py b/app/tests/physical_quantity_evaluation_test.py index 21516b2..accd0b2 100644 --- a/app/tests/physical_quantity_evaluation_test.py +++ b/app/tests/physical_quantity_evaluation_test.py @@ -398,6 +398,148 @@ def test_answer_zero_value(self): result = evaluation_function(res, ans, params, include_test_data=True) assert result["is_correct"] is False + + def test_physical_quantity_with_atol_and_kg_unit(self): + ans = "20 kg" + params = { + 'atol': 0.05, + 'strict_syntax': False, + 'physical_quantity': True, + 'elementary_functions': True, + } + res = "19.97 kg" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is True + res = "19.5 kg" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is False + + def test_physical_quantity_with_atol_and_km_unit(self): + ans = "5 km" + params = { + 'atol': 0.05, + 'strict_syntax': False, + 'physical_quantity': True, + 'elementary_functions': True, + } + res = "5.03 km" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is True + res = "5.2 km" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is False + + def test_physical_quantity_with_abs_tol_and_non_base_unit(self): + ans = "20 kg" + res = "19.97 kg" + params = { + 'absolute_tolerance': 0.05, + 'strict_syntax': False, + 'physical_quantity': True, + 'elementary_functions': True, + } + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is True + + def test_physical_quantity_with_atol_and_imperial_mass_unit(self): + ans = "10 lb" + params = { + 'atol': 0.05, + 'strict_syntax': False, + 'physical_quantity': True, + 'elementary_functions': True, + } + res = "10.03 lb" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is True + res = "10.2 lb" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is False + + def test_physical_quantity_with_atol_and_imperial_length_unit(self): + ans = "3 mile" + params = { + 'atol': 0.05, + 'strict_syntax': False, + 'physical_quantity': True, + 'elementary_functions': True, + } + res = "3.02 mile" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is True + res = "3.1 mile" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is False + + def test_physical_quantity_with_atol_and_compound_unit_force(self): + ans = "10 kg m/s^2" + params = { + 'atol': 0.02, + 'strict_syntax': False, + 'physical_quantity': True, + 'elementary_functions': True, + } + res = "10.01 kg m/s^2" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is True + res = "10.05 kg m/s^2" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is False + + def test_physical_quantity_with_atol_and_compound_unit_density(self): + ans = "2.5 kg/m^3" + params = { + 'atol': 0.01, + 'strict_syntax': False, + 'physical_quantity': True, + 'elementary_functions': True, + } + res = "2.505 kg/m^3" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is True + res = "2.52 kg/m^3" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is False + + def test_physical_quantity_with_atol_and_mixed_units_kg_g(self): + ans = "1 kg" + params = { + 'atol': 0.01, + 'strict_syntax': False, + 'physical_quantity': True, + 'elementary_functions': True, + } + res = "998 g" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is True + res = "950 g" + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is False + + def test_physical_quantity_with_rtol_and_kg_unit(self): + ans = "20 kg" + res = "21 kg" + params = { + 'rtol': 0.1, + 'strict_syntax': False, + 'physical_quantity': True, + 'elementary_functions': True, + } + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is True + + def test_physical_quantity_with_rtol_and_imperial_mass_unit(self): + ans = "10 lb" + res = "10.9 lb" + params = { + 'rtol': 0.1, + 'strict_syntax': False, + 'physical_quantity': True, + 'elementary_functions': True, + } + result = evaluation_function(res, ans, params, include_test_data=True) + assert result["is_correct"] is True + @pytest.mark.parametrize( "ans,res", [ diff --git a/app/utility/physical_quantity_utilities.py b/app/utility/physical_quantity_utilities.py index 4b89f4a..1b73df9 100644 --- a/app/utility/physical_quantity_utilities.py +++ b/app/utility/physical_quantity_utilities.py @@ -102,7 +102,7 @@ def revert_content(node): value_latex = self.value_latex_string if self.value_latex_string is not None else "" unit_latex = self.unit_latex_string if self.unit_latex_string is not None else "" self.latex_string = value_latex+separator+unit_latex - self.standard_value, self.standard_unit, self.expanded_unit, self.dimension = self._all_forms() + self.standard_value, self.standard_unit, self.expanded_unit, self.dimension, self.converted_unit_factor = self._all_forms() return def _rotate(self, direction): @@ -217,6 +217,7 @@ def _all_forms(self): converted_unit = None expanded_unit = None converted_dimension = parse_expression("1", parsing_params) + converted_unit_factor = parse_expression("1", parsing_params) if self.unit is not None: converted_unit = self.unit.copy() expanded_unit = self._expand_units(converted_unit) @@ -236,7 +237,7 @@ def _all_forms(self): converted_dimension = parse_expression(converted_dimension, parsing_params) if converted_value is not None: converted_value = parse_expression(converted_value, parsing_params) - return converted_value, converted_unit, expanded_unit, converted_dimension + return converted_value, converted_unit, expanded_unit, converted_dimension, converted_unit_factor def SLR_generate_unit_dictionaries(units_string, strictness):