diff --git a/docs/user.md b/docs/user.md index 293ff25..e9a6cfa 100644 --- a/docs/user.md +++ b/docs/user.md @@ -1,24 +1,40 @@ # compareSets ### Overview -Checks if a student's set expression matches the correct answer. Can compare both the meaning and exact form of set expressions. +Checks if a student's set expression matches the correct answer. Can compare both the meaning and exact form of set expressions. +Can also compare sets written in set notation containing elements, to see if the elements are the same. When to Use For questions involving set operations like union (∪), intersection (∩), and complement. +### Syntax + +|Operator|Meaning |LaTeX | +|--------|---------|--------------| +|`A u B` or `A \cup B`|`A union B` |$A \cup B$ | +|`A n B` or `A \cap B` |`A intersection B`|$A \cap B$ | +|`A'` |`A complement` |$A^c$| + + ### Examples: -"Express the union of sets A and B" -"Simplify: (A ∪ B) ∩ C" +"Express the union of sets A and B" +"Simplify: (A ∪ B) ∩ C" +"Write the positive integers less than 5 in set notation" -Parameters +### Parameters `is_latex` (optional) -Default: `false` +Default: `false` Description: Set to true if students enter answers in LaTeX format (\cup, \cap). Set to false for plain text. `enforce_expression_equality` (optional) -Default: `false` +Default: `false` Description: `false`: Accepts any mathematically equivalent form (e.g., "A ∪ B" = "B ∪ A") `true`: Requires exact form match + +`is_set_notation` (optional) + +Default: `false` +Description: Set to true if students enter a response in set notation, e.g. `{1,2}`. Set to false if responses are named sets, e.g. `A n B`. diff --git a/evaluation_function/evaluation.py b/evaluation_function/evaluation.py index 7922f91..d436aec 100755 --- a/evaluation_function/evaluation.py +++ b/evaluation_function/evaluation.py @@ -2,7 +2,7 @@ from typing import Any from sympy import simplify_logic, Equivalent from lf_toolkit.evaluation import Result, Params -from lf_toolkit.parse.set import SetParser, LatexPrinter, SymPyBooleanTransformer, ASCIIPrinter +from lf_toolkit.parse.set import SetParser, LatexPrinter, SymPyBooleanTransformer, ASCIIPrinter, SymPyTransformer from .parse import parse_with_feedback, FeedbackException @@ -43,20 +43,22 @@ def evaluation_function( logger.debug("params value=%r", params) parser = SetParser.instance() - sympyTransformer = SymPyBooleanTransformer() # here we want to compare the response set with the example solution set. # we have to do the following steps try: is_latex = params.get("is_latex", False) + is_set_notation = params.get("is_set_notation", False) + transformer = SymPyTransformer() if is_set_notation else SymPyBooleanTransformer() logger.debug("is_latex=%r", is_latex) + logger.debug("is_set_notation=%r", is_set_notation) # 1. convert the `response`, which may be a latex string, to a sympy expression logger.debug("parsing response...") responseSet = parse_with_feedback(response, latex=is_latex) logger.debug("responseSet=%r", responseSet) - responseSetSympy = sympyTransformer.transform(responseSet) + responseSetSympy = transformer.transform(responseSet) logger.debug("responseSetSympy=%r", responseSetSympy) # 2. convert the `answer`, which may be a latex string, to a sympy expression @@ -68,13 +70,16 @@ def evaluation_function( logger.error("failed to parse answer: type=%s value=%r error=%r", type(answer).__name__, answer, e) raise FeedbackException() from e logger.debug("answerSet=%r", answerSet) - answerSetSympy = sympyTransformer.transform(answerSet) + answerSetSympy = transformer.transform(answerSet) logger.debug("answerSetSympy=%r", answerSetSympy) # 3. compare the two sympy expressions w/ simplification enabled. # If they are equal, the sets produced by the two expressions are # semantically equal. However, the expressions may not be equal. - semantic_equal = simplify_logic(Equivalent(responseSetSympy, answerSetSympy)) == True + if is_set_notation: + semantic_equal = responseSetSympy == answerSetSympy + else: + semantic_equal = simplify_logic(Equivalent(responseSetSympy, answerSetSympy)) == True logger.debug("semantic_equal=%r", semantic_equal) # 4. compare the two sympy expressions w/ simplifaction disabled. @@ -114,4 +119,4 @@ def evaluation_function( return Result( is_correct=False, feedback_items=[("parse_error", str(e))] - ) \ No newline at end of file + ) diff --git a/evaluation_function/evaluation_test.py b/evaluation_function/evaluation_test.py index dcea868..2783c42 100755 --- a/evaluation_function/evaluation_test.py +++ b/evaluation_function/evaluation_test.py @@ -126,3 +126,48 @@ def test_syntactic_returns_is_correct_false_de_morgan(self): self.assertEqual(result.get("is_correct"), False) self.assertEqual(result.get("response_latex"), "\\overline{\\left(A \\cup B\\right)}") self.assertTrue(result.get("feedback")) + + def test_set_notation(self): + response, answer, params = "{1,2} u {3,4}", "{1,2,3,4}", Params(is_set_notation=True) + + result = evaluation_function(response, answer, params).to_dict() + + self.assertEqual(result.get("is_correct"), True) + self.assertEqual(result.get("response_latex"), "\\{1,2\\} \\cup \\{3,4\\}") + self.assertFalse(result.get("feedback")) + + def test_set_notation_symbols(self): + response, answer, params = "{a,b} u {c,d}", "{a,b,c,d}", Params(is_set_notation=True) + + result = evaluation_function(response, answer, params).to_dict() + + self.assertEqual(result.get("is_correct"), True) + self.assertEqual(result.get("response_latex"), "\\{a,b\\} \\cup \\{c,d\\}") + self.assertFalse(result.get("feedback")) + + def test_set_notation_false(self): + response, answer, params = "{a,b} u {c,d}", "{a,b,c,d,e}", Params(is_set_notation=True) + + result = evaluation_function(response, answer, params).to_dict() + + self.assertEqual(result.get("is_correct"), False) + self.assertEqual(result.get("response_latex"), "\\{a,b\\} \\cup \\{c,d\\}") + self.assertTrue(result.get("feedback")) + + def test_set_notation1(self): + response, answer, params = "{1,3} u {2,4}", "{1,2,3,4}", Params(is_set_notation=True) + + result = evaluation_function(response, answer, params).to_dict() + + self.assertEqual(result.get("is_correct"), True) + #self.assertEqual(result.get("response_latex"), "\\{a,b\\} \\cup \\{c,d\\}") + self.assertFalse(result.get("feedback")) + + def test_set_notation_intersection(self): + response, answer, params = "{3,5,6,7} n {6,7,8}", "{6,7}", Params(is_set_notation=True) + + result = evaluation_function(response, answer, params).to_dict() + + self.assertEqual(result.get("is_correct"), True) + #self.assertEqual(result.get("response_latex"), "\\{a,b\\} \\cup \\{c,d\\}") + self.assertFalse(result.get("feedback"))