Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions app/context/symbolic.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from copy import deepcopy
from sympy import Add, Pow, Mul, Equality, pi, im, I, N, oo
from sympy import re as real_part
from sympy.core.relational import Relational
from sympy.logic.boolalg import BooleanAtom

from ..utility.expression_utilities import (
default_parameters,
Expand Down Expand Up @@ -115,12 +117,21 @@ def do_comparison(comparison_symbol, expression):
return result


def is_inequality_or_boolean(expr):
# Inequalities (e.g. 8 < 9) and booleans (e.g. 5 < 1.43 evaluated to False) are not supported
# and cannot be subtracted, so they cannot be compared like other expressions.
# NOTE: Not checking for Boolean, since Symbol is a subclass of Boolean.
return isinstance(expr, (Relational, BooleanAtom)) and not isinstance(expr, Equality)


def check_equality(criterion, parameters_dict, local_substitutions=[]):
lhs_expr, rhs_expr = create_expressions_for_comparison(criterion, parameters_dict, local_substitutions)
if isinstance(lhs_expr, Equality) and not isinstance(rhs_expr, Equality):
result = False
elif not isinstance(lhs_expr, Equality) and isinstance(rhs_expr, Equality):
result = False
elif is_inequality_or_boolean(lhs_expr) or is_inequality_or_boolean(rhs_expr):
result = False
else:
try:
either_is_infinite = lhs_expr.is_infinite or rhs_expr.is_infinite
Expand Down
11 changes: 10 additions & 1 deletion app/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .utility.evaluation_result_utilities import EvaluationResult
from .utility.preview_utilities import parse_latex
from .context.symbolic import context as symbolic_context
from .context.symbolic import is_inequality_or_boolean
from .context.physical_quantity import context as quantity_context
from .feedback.symbolic import feedback_generators as symbolic_feedback_string_generators

Expand All @@ -33,7 +34,10 @@ class EvaluationError(Exception):


messages = {
"RESERVED_EXPRESSION_MISSING": lambda label: f"Reserved expression `{label}` is not defined."
"RESERVED_EXPRESSION_MISSING": lambda label: f"Reserved expression `{label}` is not defined.",
"RESERVED_EXPRESSION_INEQUALITY": lambda label: (
f"Reserved expression `{label}` is an inequality or evaluates to true/false, which is not supported."
)
}


Expand Down Expand Up @@ -174,6 +178,11 @@ def parse_reserved_expressions(reserved_expressions, parameters, result):
)
else:
raise TaskConfigurationError(symbolic_comparison_internal_messages("PARSE_ERROR")({'x': expr})) from e
if key == "task" and is_inequality_or_boolean(expr_parsed):
raise TaskConfigurationError(
messages["RESERVED_EXPRESSION_INEQUALITY"](label),
f"RESERVED_EXPRESSION_INEQUALITY_{label}"
)
reserved_expressions_dict[key].update({label: expr_parsed})
return success, reserved_expressions_dict

Expand Down
26 changes: 24 additions & 2 deletions app/tests/symbolic_evaluation_test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
import os

from ..evaluation import evaluation_function
from ..evaluation import TaskConfigurationError, evaluation_function
from ..utility.expression_utilities import elementary_functions_names, substitute

# REMARK: If a case is marked with an alternative output, this means that it is difficult in this case to prevent sympy from simplifying for that particular case
Expand Down Expand Up @@ -2144,7 +2144,29 @@ def test_equality_with_both_sides_equal_in_response(self):
}
result = evaluation_function(response, answer, params)
assert result["is_correct"] is False


@pytest.mark.parametrize("response", ["8<9", "x<8", "8>=8", "8<5"])
def test_inequality_response_is_incorrect(self, response):
answer = "8"
params = {
"strict_syntax": False,
"elementary_functions": True,
"relative_tolerance": 0.0005,
}
result = evaluation_function(response, answer, params)
assert result["is_correct"] is False

@pytest.mark.parametrize("answer", ["5<1.43", "x<8"])
def test_inequality_answer_is_task_configuration_error(self, answer):
response = "5<1.43"
params = {
"strict_syntax": False,
"elementary_functions": True,
}
with pytest.raises(TaskConfigurationError) as e:
evaluation_function(response, answer, params)
assert "`answer` is an inequality" in str(e.value)

def test_integral(self):
response = "6y"
answer = "int(3*x*y, (x, 0, 2))"
Expand Down
Loading