Skip to content

Commit 472319a

Browse files
added 'proportional to' criteria
1 parent b2d7882 commit 472319a

File tree

5 files changed

+102
-24
lines changed

5 files changed

+102
-24
lines changed

app/context/symbolic.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ def check_criterion(criterion, parameters_dict, generate_feedback=True):
4040
result = check_order(criterion, parameters_dict)
4141
elif label == "CONTAINS":
4242
result = check_contains_symbol(criterion, parameters_dict)
43+
elif label == "PROPORTIONAL_TO":
44+
result = check_proportionality(criterion, parameters_dict)
4345
elif label == "WHERE":
4446
crit = criterion.children[0]
4547
subs = criterion.children[1]
@@ -149,6 +151,20 @@ def check_order(criterion, parameters_dict, local_substitutions=[]):
149151
return result
150152

151153

154+
def check_proportionality(criterion, parameters_dict, local_substitutions=[]):
155+
lhs_expr, rhs_expr = create_expressions_for_comparison(criterion, parameters_dict, local_substitutions)
156+
result = None
157+
if lhs_expr.cancel().simplify().simplify() != 0:
158+
result = (rhs_expr/lhs_expr).cancel().simplify()
159+
elif rhs_expr.cancel().simplify().simplify() != 0:
160+
result = (lhs_expr/rhs_expr).cancel().simplify()
161+
if result == 0 or result is None:
162+
result = False
163+
else:
164+
result = result.is_constant()
165+
return result
166+
167+
152168
def check_contains_symbol(criterion, parameters_dict, local_substitutions=[]):
153169
lhs_expr, rhs_expr = create_expressions_for_comparison(criterion, parameters_dict, local_substitutions)
154170
result = rhs_expr in lhs_expr.atoms()
@@ -335,7 +351,7 @@ def same_symbols(unused_input):
335351
ans = parameters_dict["reserved_expressions"]["answer"]
336352
use_equality_equivalence = isinstance(res, Equality) or isinstance(ans, Equality)
337353

338-
# TODO: Make checking set quivalence its own context that calls symbolic comparisons instead
354+
# TODO: Make checking set equivalence its own context that calls symbolic comparisons instead
339355
if use_set_equivalence is True:
340356
graph.add_evaluation_node(
341357
label,

app/evaluation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def determine_context(parameters):
5555
input_symbols_reserved_codes.append(input_symbol[0])
5656
input_symbols_reserved_aliases += [ip for ip in input_symbol[1] if len(ip.strip()) > 0]
5757

58-
reserved_keywords_codes = {"where", "written as", "contains"}
58+
reserved_keywords_codes = {"where", "written as", "contains", "proportional to"}
5959
reserved_keywords_aliases = {"plus_minus", "minus_plus"}
6060
for re in parameters["reserved_expressions_strings"].values():
6161
reserved_keywords_aliases = reserved_keywords_aliases.union(set(re.keys()))

app/feedback/symbolic.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,8 @@
6969
"EXPONENTIAL": "Response and answer are both written on exponential form.", # None,
7070
"UNKNOWN": "The response is not written on the expected form.",
7171
}[tag]
72+
feedback_generators["PROPORTIONAL_TO"] = lambda tag: lambda inputs: {
73+
"TRUE": None,
74+
"FALSE": None,
75+
"UNKNOWN": None,
76+
}[tag]

app/tests/example_tests.py

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -508,53 +508,105 @@ def test_syntactical_comparison(self, response, answer, criteria, value, feedbac
508508
assert set(feedback_tags) == set(result["tags"])
509509

510510
@pytest.mark.parametrize(
511-
"response, answer, criteria, value, feedback_tags, additional_params",
511+
"response, value, tags",
512+
[
513+
(
514+
"2a+2b+2c",
515+
True,
516+
[
517+
"response proportional to answer_TRUE",
518+
],
519+
),
520+
(
521+
"a+2b+3c",
522+
False,
523+
[
524+
"response proportional to answer_FALSE",
525+
],
526+
),
527+
(
528+
"pi*(a+b+c)",
529+
True,
530+
[
531+
"response proportional to answer_TRUE",
532+
],
533+
),
534+
(
535+
"x*(a+b+c)",
536+
False,
537+
[
538+
"response proportional to answer_FALSE",
539+
],
540+
),
541+
]
542+
)
543+
def test_custom_comparison_with_criteria_proportional(self, response, value, tags):
544+
params = {
545+
"strict_syntax": False,
546+
"elementary_functions": True,
547+
"criteria": "response proportional to answer",
548+
}
549+
answer = "a+b+c"
550+
result = evaluation_function(response, answer, params, include_test_data=True)
551+
assert result["is_correct"] is value
552+
assert set(tags) == set(result["tags"])
553+
554+
@pytest.mark.parametrize(
555+
"response, value, tags",
512556
[
513557
(
514558
"2*x^2+0.5+0.25*sin(x)^2",
515-
"2*x^2",
516-
"answer <= response, 2+answer > response",
517559
False,
518560
[
519561
"answer <= response_TRUE",
520562
"2+answer > response_UNKNOWN",
521-
],
522-
{
523-
"symbol_assumptions": "('x', 'real')"
524-
}
563+
]
525564
),
565+
]
566+
)
567+
def test_custom_comparison_with_criteria_order(self, response, value, tags):
568+
params = {
569+
"strict_syntax": False,
570+
"elementary_functions": True,
571+
"criteria": "answer <= response, 2+answer > response",
572+
"symbol_assumptions": "('x', 'real')",
573+
}
574+
answer = "2*x^2"
575+
result = evaluation_function(response, answer, params, include_test_data=True)
576+
assert result["is_correct"] is value
577+
assert set(tags) == set(result["tags"])
578+
579+
@pytest.mark.parametrize(
580+
"response, value, tags",
581+
[
526582
(
527583
"pi*n",
528-
"0",
529-
"sin(response)=0, response contains n",
530584
True,
531585
[
532586
"sin(response)=0_TRUE",
533587
"sin(response)=0_SAME_SYMBOLS_TRUE",
534588
"response contains n_TRUE",
535589
],
536-
{
537-
"symbols": {
538-
"n": {
539-
"latex": r"\(n\)",
540-
"aliases": ["i", "k", "N", "I", "K"],
541-
},
542-
},
543-
"symbol_assumptions": "('n', 'integer')"
544-
}
545590
),
546591
]
547592
)
548-
def test_custom_comparison_with_criteria(self, response, answer, criteria, value, feedback_tags, additional_params):
593+
def test_custom_comparison_with_criteria_contains(self, response, value, tags):
549594
params = {
550595
"strict_syntax": False,
551596
"elementary_functions": True,
552-
"criteria": criteria,
597+
"criteria": "sin(response)=0, response contains n",
598+
"symbols": {
599+
"n": {
600+
"latex": r"\(n\)",
601+
"aliases": ["i", "k", "N", "I", "K"],
602+
},
603+
},
604+
"symbol_assumptions": "('n', 'integer')"
553605
}
554-
params.update(additional_params)
606+
answer = "0"
555607
result = evaluation_function(response, answer, params, include_test_data=True)
556608
assert result["is_correct"] is value
557-
assert set(feedback_tags) == set(result["tags"])
609+
assert set(tags) == set(result["tags"])
558610

559611
if __name__ == "__main__":
560612
pytest.main(['-sk not slow', "--tb=line", os.path.abspath(__file__)])

app/utility/criteria_parsing.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
(" *(>=?|<=?|ORDER) *", "ORDER"), # less than (or equal), < (<=), greater than (or equal), > (>=)
1818
(" *where *", "WHERE"),
1919
(" *written +as *", "WRITTEN_AS"),
20+
(" *proportional +to *", "PROPORTIONAL_TO"),
2021
(" *contains *", "CONTAINS"),
2122
(" *; *", "SEPARATOR"),
2223
(" *OTHER *", "OTHER", catch_undefined),
@@ -30,6 +31,10 @@
3031
("BOOL", "EQUAL where EQUAL_LIST", infix),
3132
("BOOL", "RESERVED written as OTHER", infix),
3233
("BOOL", "RESERVED written as RESERVED", infix),
34+
("EQUAL", "OTHER proportional to OTHER", infix),
35+
("EQUAL", "RESERVED proportional to OTHER", infix),
36+
("EQUAL", "OTHER proportional to RESERVED", infix),
37+
("EQUAL", "RESERVED proportional to RESERVED", infix),
3338
("BOOL", "RESERVED contains OTHER", infix),
3439
("BOOL", "RESERVED contains RESERVED", infix),
3540
("EQUAL_LIST", "EQUAL;EQUAL", infix),

0 commit comments

Comments
 (0)