From a6a32cb5997051b1c006ea1ecc8a56fb98f1ac12 Mon Sep 17 00:00:00 2001 From: mgreminger Date: Tue, 23 Jan 2024 20:35:54 -0600 Subject: [PATCH 1/4] feat: add sheet setting to disable symbolic expression simplification Disabling symbolic expression simplification can speed up calculations for sheets with complex expressions. Does not impact numeric results. --- public/dimensional_analysis.py | 29 +++++++++++++++++++++-------- src/App.svelte | 18 ++++++++++++++---- src/sheet/Sheet.ts | 8 +++++--- src/types.ts | 1 + 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/public/dimensional_analysis.py b/public/dimensional_analysis.py index b211c8a3..55d04f96 100644 --- a/public/dimensional_analysis.py +++ b/public/dimensional_analysis.py @@ -440,6 +440,7 @@ class StatementsAndSystems(TypedDict): statements: list[InputStatement] systemDefinitions: list[SystemDefinition] customBaseUnits: NotRequired[CustomBaseUnits] + simplifySymbolicExpressions: bool def is_code_function_query_statement(statement: InputAndSystemStatement) -> TypeGuard[CodeFunctionQueryStatement]: return statement.get("isCodeFunctionQuery", False) @@ -1680,19 +1681,27 @@ def subs_wrapper(expression: Expr, subs: dict[str, str] | dict[str, Expr | float return cast(Expr, expression.xreplace(subs)) -def get_evaluated_expression(expression: Expr, parameter_subs: dict[Symbol, Expr]) -> tuple[ExprWithAssumptions, str | list[list[str]]]: +def get_evaluated_expression(expression: Expr, + parameter_subs: dict[Symbol, Expr], + simplify_symbolic_expressions: bool) -> tuple[ExprWithAssumptions, str | list[list[str]]]: expression = cast(Expr, expression.xreplace(parameter_subs)) expression = replace_placeholder_funcs(expression, "sympy_func") expression = cast(Expr, expression.doit()) if not is_matrix(expression): - symbolic_expression = custom_latex(cancel(expression)) + if simplify_symbolic_expressions: + symbolic_expression = custom_latex(cancel(expression)) + else: + symbolic_expression = custom_latex(expression) else: symbolic_expression = [] for i in range(expression.rows): row = [] symbolic_expression.append(row) for j in range(expression.cols): - row.append(custom_latex(cancel(expression[i,j]))) + if simplify_symbolic_expressions: + row.append(custom_latex(cancel(expression[i,j]))) + else: + row.append(custom_latex(cast(Expr, expression[i,j]))) evaluated_expression = cast(ExprWithAssumptions, expression.evalf(PRECISION)) return evaluated_expression, symbolic_expression @@ -1762,7 +1771,8 @@ def get_hashable_matrix_units(matrix_result: MatrixResult) -> tuple[tuple[str, . return tuple(rows) def evaluate_statements(statements: list[InputAndSystemStatement], - custom_base_units: CustomBaseUnits | None) -> tuple[list[Result | FiniteImagResult | list[PlotResult] | MatrixResult], dict[int,bool]]: + custom_base_units: CustomBaseUnits | None, + simplify_symbolic_expressions: bool) -> tuple[list[Result | FiniteImagResult | list[PlotResult] | MatrixResult], dict[int,bool]]: num_statements = len(statements) if num_statements == 0: @@ -1986,7 +1996,7 @@ def evaluate_statements(statements: list[InputAndSystemStatement], else: expression = cast(Expr, item["expression"].doit()) - evaluated_expression, symbolic_expression = get_evaluated_expression(expression, parameter_subs) + evaluated_expression, symbolic_expression = get_evaluated_expression(expression, parameter_subs, simplify_symbolic_expressions) dimensional_analysis_expression, dim_sub_error = get_dimensional_analysis_expression(dimensional_analysis_subs, expression) if not is_matrix(evaluated_expression): @@ -2092,13 +2102,15 @@ def evaluate_statements(statements: list[InputAndSystemStatement], return combine_plot_results(results_with_ranges[:num_statements], statement_plot_info), numerical_system_cell_unit_errors -def get_query_values(statements: list[InputAndSystemStatement], custom_base_units: CustomBaseUnits | None): +def get_query_values(statements: list[InputAndSystemStatement], + custom_base_units: CustomBaseUnits | None, + simplify_symbolic_expressions: bool): error: None | str = None results: list[Result | FiniteImagResult | list[PlotResult] | MatrixResult] = [] numerical_system_cell_errors: dict[int, bool] = {} try: - results, numerical_system_cell_errors = evaluate_statements(statements, custom_base_units) + results, numerical_system_cell_errors = evaluate_statements(statements, custom_base_units, simplify_symbolic_expressions) except DuplicateAssignment as e: error = f"Duplicate assignment of variable {e}" except ReferenceCycle as e: @@ -2190,6 +2202,7 @@ def solve_sheet(statements_and_systems): statements: list[InputAndSystemStatement] = cast(list[InputAndSystemStatement], statements_and_systems["statements"]) system_definitions = statements_and_systems["systemDefinitions"] custom_base_units = statements_and_systems.get("customBaseUnits", None) + simplify_symbolic_expressions = statements_and_systems["simplifySymbolicExpressions"] system_results: list[SystemResult] = [] equation_to_system_cell_map: dict[int,int] = {} @@ -2234,7 +2247,7 @@ def solve_sheet(statements_and_systems): error: str | None results: list[Result | FiniteImagResult | list[PlotResult] | MatrixResult] numerical_system_cell_errors: dict[int, bool] - error, results, numerical_system_cell_errors = get_query_values(statements, custom_base_units) + error, results, numerical_system_cell_errors = get_query_values(statements, custom_base_units, simplify_symbolic_expressions) # If there was a numerical solve, check to make sure there were not unit mismatches # between the lhs and rhs of each equality in the system diff --git a/src/App.svelte b/src/App.svelte index 92f231d9..0d6e6895 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -50,6 +50,7 @@ SkipToContent, HeaderUtilities, HeaderGlobalAction, + Checkbox, Content, SideNav, SideNavMenuItem, @@ -827,7 +828,12 @@ statements.push(...endStatements); - return {statements: statements, systemDefinitions: systemDefinitions, customBaseUnits: $config.customBaseUnits}; + return { + statements: statements, + systemDefinitions: systemDefinitions, + customBaseUnits: $config.customBaseUnits, + simplifySymbolicExpressions: $config.simplifySymbolicExpressions + }; } function checkParsingErrors() { @@ -1087,9 +1093,8 @@ Please include a link to this sheet in the email to assist in debugging the prob // old documents in database will not have the insertedSheets property or a config property $insertedSheets = sheet.insertedSheets ?? []; $config = sheet.config ?? getDefaultConfig(); - if (!$config.customBaseUnits) { - $config.customBaseUnits = getDefaultBaseUnits(); - } + $config.customBaseUnits = $config.customBaseUnits ?? getDefaultBaseUnits(); // customBaseUnits may not exist + $config.simplifySymbolicExpressions = $config.simplifySymbolicExpressions ?? true; // simplifySymboicExpressions may not exist if (!$history.map(item => item.hash !== "file" ? getSheetHash(new URL(item.url)) : "").includes(getSheetHash(window.location))) { $history = requestHistory; @@ -2630,6 +2635,11 @@ Please include a link to this sheet in the email to assist in debugging the prob + $mathCellChanged = true} + /> Date: Tue, 23 Jan 2024 21:37:13 -0600 Subject: [PATCH 2/4] test: add test for disabling automatic expression simplification --- tests/test_basic.spec.mjs | 13 ------------- tests/test_number_format.spec.mjs | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/tests/test_basic.spec.mjs b/tests/test_basic.spec.mjs index c04157b0..db2a41dc 100644 --- a/tests/test_basic.spec.mjs +++ b/tests/test_basic.spec.mjs @@ -1295,19 +1295,6 @@ test('Check parsing error handling impact on displayed results', async () => { }); -test('Symbolic result canceling', async () => { - - await page.setLatex(0, String.raw`-F-F_{B}-F_{W}-\frac{F\cdot l_4-F_{B}\cdot l_2+F_{W}\cdot l_3}{l_1+l_2}+\frac{F\cdot l_1+F\cdot l_2+F\cdot l_4+F_{B}\cdot l_1+F_{W}\cdot l_1+F_{W}\cdot l_2+F_{W}\cdot l_3}{l_1+l_2}\ =`); - - await page.waitForSelector('.status-footer', { state: 'detached'}); - - // check query result in cell 1 - let content = await page.textContent('#result-value-0'); - expect(content).toBe('0'); - -}); - - test('Test single character square root', async () => { await page.locator('#cell-0 >> math-field.editable').type('sqrt4'); diff --git a/tests/test_number_format.spec.mjs b/tests/test_number_format.spec.mjs index 3754876b..3fd14e5f 100644 --- a/tests/test_number_format.spec.mjs +++ b/tests/test_number_format.spec.mjs @@ -89,6 +89,26 @@ test('Test symbolic format', async () => { }); +test('Test disabling automatic expressions simplification', async () => { + // first test that automatic simplification is on by default + await page.setLatex(0, String.raw`-F-F_{B}-F_{W}-\frac{F\cdot l_4-F_{B}\cdot l_2+F_{W}\cdot l_3}{l_1+l_2}+\frac{F\cdot l_1+F\cdot l_2+F\cdot l_4+F_{B}\cdot l_1+F_{W}\cdot l_1+F_{W}\cdot l_2+F_{W}\cdot l_3}{l_1+l_2}\ =`); + + await page.waitForSelector('.status-footer', { state: 'detached'}); + + // check query result in cell 1 + let content = await page.textContent('#result-value-0'); + expect(content).toBe('0'); + + // turn off automatic simplification + await page.getByRole('button', { name: 'Sheet Settings' }).click(); + await page.locator('label').filter({ hasText: 'Automatically Simplify Symbolic Expressions' }).click(); + await page.getByRole('button', { name: 'Confirm' }).click(); + + // check query result in cell 1 + content = await page.textContent('#result-value-0'); + expect(content).toBe(String.raw`- F - F_{B} - F_{W} - \frac{F l_{4} - F_{B} l_{2} + F_{W} l_{3}}{l_{1} + l_{2}} + \frac{F l_{1} + F l_{2} + F l_{4} + F_{B} l_{1} + F_{W} l_{1} + F_{W} l_{2} + F_{W} l_{3}}{l_{1} + l_{2}}`); + +}); test('Test auto exponent', async () => { await page.setLatex(0, String.raw`\frac{2}{3}=`); From 5d19ad64a07e8d7991fa2cebfd9b21c69ec38286 Mon Sep 17 00:00:00 2001 From: mgreminger Date: Tue, 23 Jan 2024 21:55:56 -0600 Subject: [PATCH 3/4] test: fix symbolic simplification test --- tests/test_number_format.spec.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_number_format.spec.mjs b/tests/test_number_format.spec.mjs index 3fd14e5f..53f46465 100644 --- a/tests/test_number_format.spec.mjs +++ b/tests/test_number_format.spec.mjs @@ -104,6 +104,8 @@ test('Test disabling automatic expressions simplification', async () => { await page.locator('label').filter({ hasText: 'Automatically Simplify Symbolic Expressions' }).click(); await page.getByRole('button', { name: 'Confirm' }).click(); + await page.waitForSelector('.status-footer', { state: 'detached'}); + // check query result in cell 1 content = await page.textContent('#result-value-0'); expect(content).toBe(String.raw`- F - F_{B} - F_{W} - \frac{F l_{4} - F_{B} l_{2} + F_{W} l_{3}}{l_{1} + l_{2}} + \frac{F l_{1} + F l_{2} + F l_{4} + F_{B} l_{1} + F_{W} l_{1} + F_{W} l_{2} + F_{W} l_{3}}{l_{1} + l_{2}}`); From 55c1a5610a9a4e4d9b4a2e46d452e9b8104891e6 Mon Sep 17 00:00:00 2001 From: mgreminger Date: Wed, 24 Jan 2024 22:50:11 -0600 Subject: [PATCH 4/4] fix: include simplifySymbolicExpressions in default config check and reset --- src/App.svelte | 6 ++++-- src/sheet/Sheet.ts | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/App.svelte b/src/App.svelte index 0d6e6895..c02bd3ac 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -2619,7 +2619,9 @@ Please include a link to this sheet in the email to assist in debugging the prob primaryButtonText="Confirm" secondaryButtonText="Restore Defaults" on:click:button--primary={() => modalInfo.modalOpen = false} - on:click:button--secondary={() => {mathCellConfigDialog?.resetDefaults(); baseUnitsConfigDialog?.resetDefaults();}} + on:click:button--secondary={() => {mathCellConfigDialog?.resetDefaults(); + baseUnitsConfigDialog?.resetDefaults(); + $config.simplifySymbolicExpressions = true;}} bind:open={modalInfo.modalOpen} > {#if modalInfo.mathCell} @@ -2638,7 +2640,7 @@ Please include a link to this sheet in the email to assist in debugging the prob $mathCellChanged = true} + on:check={() => $mathCellChanged = true} />