Skip to content
Open
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
31 changes: 23 additions & 8 deletions docs/user.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
# compareSets
### Overview
Checks if a student's set expression matches the correct answer. Can compare both the meaning and exact form of set expressions.
When to Use
For questions involving set operations like union (∪), intersection (∩), and complement.
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`.
17 changes: 11 additions & 6 deletions evaluation_function/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -114,4 +119,4 @@ def evaluation_function(
return Result(
is_correct=False,
feedback_items=[("parse_error", str(e))]
)
)
27 changes: 27 additions & 0 deletions evaluation_function/evaluation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,30 @@ 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"))
Loading