From c4b33fe56117d36cac2860adfbcca70937dad3b2 Mon Sep 17 00:00:00 2001 From: Marcus Messer Date: Mon, 17 Nov 2025 16:28:09 +0000 Subject: [PATCH 1/5] Added a test for mu, and switched transforming prefix to standard to use the symbol instead of the name --- app/context/physical_quantity.py | 2 +- app/evaluation_tests.py | 24 +++++++++++++++++++ .../physical_quantity_evaluation_tests.py | 4 ++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/app/context/physical_quantity.py b/app/context/physical_quantity.py index ecd4f51..7469a84 100644 --- a/app/context/physical_quantity.py +++ b/app/context/physical_quantity.py @@ -561,7 +561,7 @@ def transform_prefixes_to_standard(expr): # Match the alternative prefix either attached to or followed by spaces before a unit # Examples matched: "km", "k m", "microsecond", "micro second" pattern = rf'(? Date: Tue, 18 Nov 2025 09:35:52 +0000 Subject: [PATCH 2/5] Reverted change --- app/context/physical_quantity.py | 2 +- app/tests/physical_quantity_evaluation_tests.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/context/physical_quantity.py b/app/context/physical_quantity.py index 7469a84..ecd4f51 100644 --- a/app/context/physical_quantity.py +++ b/app/context/physical_quantity.py @@ -561,7 +561,7 @@ def transform_prefixes_to_standard(expr): # Match the alternative prefix either attached to or followed by spaces before a unit # Examples matched: "km", "k m", "microsecond", "micro second" pattern = rf'(? Date: Tue, 18 Nov 2025 11:01:27 +0000 Subject: [PATCH 3/5] Removed old TODO statement --- app/preview_tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/preview_tests.py b/app/preview_tests.py index 4af6b63..472f78d 100644 --- a/app/preview_tests.py +++ b/app/preview_tests.py @@ -93,7 +93,6 @@ def test_natural_logarithm_notation(self): ("e * x", True, False, "e * x", "E*x"), ("E", True, False, "E", "E",), ("ER_2", True, False, "ER_2", "E*R_2",), - # TODO: add exp (0), (1), (2) and (x) ("exp(1)", False, True, "e^{1}", "exp(1)"), ("e**1", False, True, "e^{1}", "E**1"), ("e^{1}", True, True, "e^{1}", "E"), From 8b5d9b69eef7ec5e7640f95b74790de07534ac99 Mon Sep 17 00:00:00 2001 From: Marcus Messer Date: Tue, 18 Nov 2025 11:02:20 +0000 Subject: [PATCH 4/5] Fixed micro to be able to be used as a prefix --- app/context/physical_quantity.py | 2 +- app/tests/physical_quantity_evaluation_tests.py | 4 ++-- app/utility/physical_quantity_utilities.py | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/context/physical_quantity.py b/app/context/physical_quantity.py index ecd4f51..c460a80 100644 --- a/app/context/physical_quantity.py +++ b/app/context/physical_quantity.py @@ -561,7 +561,7 @@ def transform_prefixes_to_standard(expr): # Match the alternative prefix either attached to or followed by spaces before a unit # Examples matched: "km", "k m", "microsecond", "micro second" pattern = rf'(? Date: Wed, 19 Nov 2025 10:39:16 +0000 Subject: [PATCH 5/5] Fixed preview for physical quantities --- app/context/physical_quantity.py | 103 +----------------- app/evaluation_tests.py | 4 +- .../physical_quantity_preview.py | 5 +- app/utility/physical_quantity_utilities.py | 101 +++++++++++++++++ 4 files changed, 107 insertions(+), 106 deletions(-) diff --git a/app/context/physical_quantity.py b/app/context/physical_quantity.py index c460a80..28348de 100644 --- a/app/context/physical_quantity.py +++ b/app/context/physical_quantity.py @@ -6,7 +6,7 @@ from copy import deepcopy from ..utility.physical_quantity_utilities import ( SLR_quantity_parser, - SLR_quantity_parsing + SLR_quantity_parsing, expression_preprocess ) from ..preview_implementations.physical_quantity_preview import preview_function from ..feedback.physical_quantity import feedback_string_generators as physical_quantity_feedback_string_generators @@ -476,107 +476,6 @@ def feedback_procedure_generator(parameters_dict): graphs.update({label: graph}) return graphs -def preprocess_legacy(expr, parameters): - prefix_data = {(p[0], p[1], tuple(), p[3]) for p in set_of_SI_prefixes} - prefixes = [] - for prefix in prefix_data: - prefixes = prefixes + [prefix[0]] + list(prefix[-1]) - prefix_short_forms = [prefix[1] for prefix in prefix_data] - unit_data = set_of_SI_base_unit_dimensions \ - | set_of_derived_SI_units_in_SI_base_units \ - | set_of_common_units_in_SI \ - | set_of_very_common_units_in_SI \ - | set_of_imperial_units - unit_long_forms = prefixes - for unit in unit_data: - unit_long_forms = unit_long_forms + [unit[0]] + list(unit[-2]) + list(unit[-1]) - unit_long_forms = "(" + "|".join(unit_long_forms) + ")" - # Rewrite any expression on the form "*UNIT" (but not "**UNIT") as " UNIT" - # Example: "newton*metre" ---> "newton metre" - search_string = r"(? "kilometre" - search_string = prefixes + " " + unit_long_forms - match_content = re.search(search_string, expr) - while match_content is not None: - expr = expr[0:match_content.span()[0]] + " " + "".join(match_content.group().split()) + expr[ - match_content.span()[ - 1]:] - match_content = re.search(search_string, expr) - unit_short_forms = [u[1] for u in unit_data] - short_forms = "(" + "|".join(list(set(prefix_short_forms + unit_short_forms))) + ")" - # Add space before short forms of prefixes or unit names if they are preceded by numbers or multiplication - # Example: "100Pa" ---> "100 Pa" - search_string = r"[0-9\*\(\)]" + short_forms - match_content = re.search(search_string, expr) - while match_content is not None: - expr = expr[0:match_content.span()[0] + 1] + " " + expr[match_content.span()[0] + 1:] - match_content = re.search(search_string, expr) - # Remove space after prefix short forms if they are preceded by numbers, multiplication or space - # Example: "100 m Pa" ---> "100 mPa" - prefix_short_forms = "(" + "|".join(prefix_short_forms) + ")" - search_string = r"[0-9\*\(\) ]" + prefix_short_forms + " " - match_content = re.search(search_string, expr) - while match_content is not None: - expr = expr[0:match_content.span()[0] + 1] + match_content.group()[0:-1] + expr[match_content.span()[1]:] - match_content = re.search(search_string, expr) - # Remove multiplication and space after prefix short forms if they are preceded by numbers, multiplication or space - # Example: "100 m* Pa" ---> "100 mPa" - search_string = r"[0-9\*\(\) ]" + prefix_short_forms + "\* " - match_content = re.search(search_string, expr) - while match_content is not None: - expr = expr[0:match_content.span()[0] + 1] + match_content.group()[0:-2] + expr[match_content.span()[1]:] - match_content = re.search(search_string, expr) - # Replace multiplication followed by space before unit short forms with only spaces if they are preceded by numbers or space - # Example: "100* Pa" ---> "100 Pa" - unit_short_forms = "(" + "|".join(unit_short_forms) + ")" - search_string = r"[0-9\(\) ]\* " + unit_short_forms - match_content = re.search(search_string, expr) - while match_content is not None: - expr = expr[0:match_content.span()[0]] + match_content.group().replace("*", " ") + expr[ - match_content.span()[1]:] - match_content = re.search(search_string, expr) - - return expr - -def transform_prefixes_to_standard(expr): - """ - Transform ONLY alternative prefix spellings to standard prefix names. - Ensure there's exactly one space after the prefix before the unit. - Works for both attached (e.g. 'km') and spaced (e.g. 'k m') forms. - """ - - for prefix_name, symbol, power, alternatives in set_of_SI_prefixes: - for alt in alternatives: - if not alt: - continue - - # Match the alternative prefix either attached to or followed by spaces before a unit - # Examples matched: "km", "k m", "microsecond", "micro second" - pattern = rf'(? Result: unit_sympy = res_parsed.unit.content_string() if unit is not None else "" sympy_out = value_sympy+separator_sympy+unit_sympy else: - res_parsed = quantity_parsing(response, params, parser, "response") + _, res_pre_processed, _ = expression_preprocess("response", response, params) + res_parsed = quantity_parsing(res_pre_processed, params, parser, "response") latex_out = res_parsed.latex_string sympy_out = response diff --git a/app/utility/physical_quantity_utilities.py b/app/utility/physical_quantity_utilities.py index 0da5989..33a074d 100644 --- a/app/utility/physical_quantity_utilities.py +++ b/app/utility/physical_quantity_utilities.py @@ -493,3 +493,104 @@ def SLR_quantity_parsing(expr, parameters, parser, name): tag_handler = set_tags(parameters.get("strictness", "strict")) return PhysicalQuantity(name, parameters, quantity[0], parser, messages=[], tag_handler=tag_handler) + +def expression_preprocess(name, expr, parameters): + if parameters.get("strictness", "natural") == "legacy": + expr = preprocess_legacy(expr, parameters) + return True, expr, None + + expr = transform_prefixes_to_standard(expr) + + return True, expr, None + +def preprocess_legacy(expr, parameters): + prefix_data = {(p[0], p[1], tuple(), p[3]) for p in set_of_SI_prefixes} + prefixes = [] + for prefix in prefix_data: + prefixes = prefixes + [prefix[0]] + list(prefix[-1]) + prefix_short_forms = [prefix[1] for prefix in prefix_data] + unit_data = set_of_SI_base_unit_dimensions \ + | set_of_derived_SI_units_in_SI_base_units \ + | set_of_common_units_in_SI \ + | set_of_very_common_units_in_SI \ + | set_of_imperial_units + unit_long_forms = prefixes + for unit in unit_data: + unit_long_forms = unit_long_forms + [unit[0]] + list(unit[-2]) + list(unit[-1]) + unit_long_forms = "(" + "|".join(unit_long_forms) + ")" + # Rewrite any expression on the form "*UNIT" (but not "**UNIT") as " UNIT" + # Example: "newton*metre" ---> "newton metre" + search_string = r"(? "kilometre" + search_string = prefixes + " " + unit_long_forms + match_content = re.search(search_string, expr) + while match_content is not None: + expr = expr[0:match_content.span()[0]] + " " + "".join(match_content.group().split()) + expr[ + match_content.span()[ + 1]:] + match_content = re.search(search_string, expr) + unit_short_forms = [u[1] for u in unit_data] + short_forms = "(" + "|".join(list(set(prefix_short_forms + unit_short_forms))) + ")" + # Add space before short forms of prefixes or unit names if they are preceded by numbers or multiplication + # Example: "100Pa" ---> "100 Pa" + search_string = r"[0-9\*\(\)]" + short_forms + match_content = re.search(search_string, expr) + while match_content is not None: + expr = expr[0:match_content.span()[0] + 1] + " " + expr[match_content.span()[0] + 1:] + match_content = re.search(search_string, expr) + # Remove space after prefix short forms if they are preceded by numbers, multiplication or space + # Example: "100 m Pa" ---> "100 mPa" + prefix_short_forms = "(" + "|".join(prefix_short_forms) + ")" + search_string = r"[0-9\*\(\) ]" + prefix_short_forms + " " + match_content = re.search(search_string, expr) + while match_content is not None: + expr = expr[0:match_content.span()[0] + 1] + match_content.group()[0:-1] + expr[match_content.span()[1]:] + match_content = re.search(search_string, expr) + # Remove multiplication and space after prefix short forms if they are preceded by numbers, multiplication or space + # Example: "100 m* Pa" ---> "100 mPa" + search_string = r"[0-9\*\(\) ]" + prefix_short_forms + "\* " + match_content = re.search(search_string, expr) + while match_content is not None: + expr = expr[0:match_content.span()[0] + 1] + match_content.group()[0:-2] + expr[match_content.span()[1]:] + match_content = re.search(search_string, expr) + # Replace multiplication followed by space before unit short forms with only spaces if they are preceded by numbers or space + # Example: "100* Pa" ---> "100 Pa" + unit_short_forms = "(" + "|".join(unit_short_forms) + ")" + search_string = r"[0-9\(\) ]\* " + unit_short_forms + match_content = re.search(search_string, expr) + while match_content is not None: + expr = expr[0:match_content.span()[0]] + match_content.group().replace("*", " ") + expr[ + match_content.span()[1]:] + match_content = re.search(search_string, expr) + + return expr + +def transform_prefixes_to_standard(expr): + """ + Transform ONLY alternative prefix spellings to standard prefix names. + Ensure there's exactly one space after the prefix before the unit. + Works for both attached (e.g. 'km') and spaced (e.g. 'k m') forms. + """ + + for prefix_name, symbol, power, alternatives in set_of_SI_prefixes: + for alt in alternatives: + if not alt: + continue + + # Match the alternative prefix either attached to or followed by spaces before a unit + # Examples matched: "km", "k m", "microsecond", "micro second" + pattern = rf'(?