Skip to content

Commit 354fa3c

Browse files
Merge pull request #201 from lambda-feedback/tr170-basic-generic-syntactical-comparison
Basic check implemented
2 parents e745d85 + 64383f9 commit 354fa3c

File tree

4 files changed

+85
-7
lines changed

4 files changed

+85
-7
lines changed

app/feedback/symbolic_comparison.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,7 @@
9696
feedback_generators["SAME_FORM"] = lambda tag: lambda inputs: {
9797
"CARTESIAN": "Response and answer are both written on Cartesian form.", # None,
9898
"EXPONENTIAL": "Response and answer are both written on exponential form.", # None,
99-
"UNKNOWN": "The response is not written on the expected form.",
99+
"WRITTEN_AS_TRUE": "The response is written in the expected form.",
100+
"WRITTEN_AS_FALSE": "The response is not written in the expected form.",
101+
"UNKNOWN": "The response is not written in the expected form.",
100102
}[tag]

app/symbolic_comparison_evaluation.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from .syntactical_comparison_utilities import patterns as syntactical_forms
2626
from .syntactical_comparison_utilities import is_number as syntactical_is_number
2727
from .syntactical_comparison_utilities import response_and_answer_on_same_form
28+
from .syntactical_comparison_utilities import written_as_answer
2829
from .syntactical_comparison_utilities import attach_form_criteria
2930

3031
from .criteria_graph_utilities import CriteriaGraph
@@ -321,7 +322,6 @@ def same_symbols(unused_input):
321322
for form_label in syntactical_forms.keys():
322323
has_recognisable_form = has_recognisable_form or syntactical_forms[form_label]["matcher"](parameters_dict["original_input"]["answer"])
323324
if has_recognisable_form is True:
324-
325325
graph.attach(
326326
label+"_TRUE",
327327
label+"_SYNTACTICAL_EQUIVALENCE",
@@ -347,30 +347,59 @@ def same_symbols(unused_input):
347347
feedback_string_generator=symbolic_feedback_generators["SYNTACTICAL_EQUIVALENCE"]("FALSE")
348348
)
349349
graph.attach(label+"_SYNTACTICAL_EQUIVALENCE"+"_FALSE", END.label)
350-
351350
graph.attach(
352351
label+"_TRUE",
353352
label+"_SAME_FORM",
354353
summary=str(lhs)+" is written in the same form as "+str(rhs),
355354
details=str(lhs)+" is written in the same form as "+str(rhs)+".",
356355
evaluate=response_and_answer_on_same_form(label+"_SAME_FORM", parameters_dict)
357356
)
358-
359357
for form_label in syntactical_forms.keys():
360358
if syntactical_forms[form_label]["matcher"](parameters_dict["original_input"]["answer"]) is True:
361359
attach_form_criteria(graph, label+"_SAME_FORM", criterion, parameters_dict, form_label)
362-
363360
graph.attach(
364361
label+"_SAME_FORM",
365362
label+"_SAME_FORM"+"_UNKNOWN",
366363
summary="Cannot determine if "+str(lhs)+" and "+str(rhs)+" are written on the same form",
367364
details="Cannot determine if "+str(lhs)+" and "+str(rhs)+" are written on the same form.",
368365
feedback_string_generator=symbolic_feedback_generators["SAME_FORM"]("UNKNOWN"),
369366
)
370-
371367
graph.attach(label+"_SAME_FORM"+"_UNKNOWN", END.label)
372-
373368
graph.attach(label+"_FALSE", label+"_SAME_FORM")
369+
else:
370+
graph.attach(
371+
label+"_TRUE",
372+
label+"_WRITTEN_AS_ANSWER",
373+
summary=str(lhs)+" is written in the same form as "+str(rhs),
374+
details=str(lhs)+" is written in the same form as "+str(rhs)+".",
375+
evaluate=written_as_answer(label+"_WRITTEN_AS_ANSWER", parameters_dict)
376+
)
377+
graph.attach(
378+
label+"_WRITTEN_AS_ANSWER",
379+
label+"_WRITTEN_AS_ANSWER"+"_UNKNOWN",
380+
summary="Cannot determine if "+str(lhs)+" and "+str(rhs)+" are written on the same form",
381+
details="Cannot determine if "+str(lhs)+" and "+str(rhs)+" are written on the same form.",
382+
feedback_string_generator=symbolic_feedback_generators["SAME_FORM"]("UNKNOWN"),
383+
)
384+
graph.attach(label+"_WRITTEN_AS_ANSWER"+"_UNKNOWN", END.label)
385+
graph.attach(
386+
label+"_WRITTEN_AS_ANSWER",
387+
label+"_WRITTEN_AS_ANSWER"+"_TRUE",
388+
summary=str(lhs)+" is written in the same form as "+str(rhs),
389+
details=str(lhs)+" is written in the same form as "+str(rhs)+".",
390+
feedback_string_generator=symbolic_feedback_generators["SAME_FORM"]("WRITTEN_AS_TRUE"),
391+
)
392+
graph.attach(label+"_WRITTEN_AS_ANSWER"+"_TRUE", END.label)
393+
graph.attach(
394+
label+"_WRITTEN_AS_ANSWER",
395+
label+"_WRITTEN_AS_ANSWER"+"_FALSE",
396+
summary=str(lhs)+" is written in the same form as "+str(rhs),
397+
details=str(lhs)+" is written in the same form as "+str(rhs)+".",
398+
feedback_string_generator=symbolic_feedback_generators["SAME_FORM"]("WRITTEN_AS_FALSE"),
399+
)
400+
graph.attach(label+"_WRITTEN_AS_ANSWER"+"_FALSE", END.label)
401+
402+
graph.attach(label+"_FALSE", label+"_WRITTEN_AS_ANSWER")
374403
else:
375404
graph.attach(label+"_FALSE", END.label)
376405
return graph
@@ -846,6 +875,9 @@ def symbolic_comparison(response, answer, params, eval_response) -> dict:
846875

847876
# TODO: Implement way to define completeness of task other than "all main criteria satisfied"
848877
is_correct = is_correct and main_criteria in criteria_feedback
878+
for tag in criteria_feedback:
879+
if "WRITTEN_AS_ANSWER_FALSE" in tag:
880+
is_correct = False
849881
eval_response.add_criteria_graph(criterion_identifier, graph)
850882

851883
# Generate feedback strings from found feedback

app/symbolic_comparison_evaluation_tests.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,9 @@ def test_disabled_evaluation_nodes(self, response, answer, criteria, value, disa
12341234
("2*e**(2*I)", "2*e^(2*I)", "answer=response", True, ["answer=response_TRUE", "answer=response_SAME_SYMBOLS_TRUE", "answer=response_SYNTACTICAL_EQUIVALENCE_FALSE", "answer=response_SAME_FORM_EXPONENTIAL"], {}),
12351235
("e**(2*I)", "1*e^(2*I)", "answer=response", True, ["answer=response_TRUE", "answer=response_SAME_SYMBOLS_TRUE", "answer=response_SYNTACTICAL_EQUIVALENCE_FALSE", "answer=response_SAME_FORM_EXPONENTIAL"], {}),
12361236
("0.48+0.88*I", "1*e^(0.5*I)", "answer=response", False, ["answer=response_FALSE", "answer=response_SAME_FORM_UNKNOWN"], {}),
1237+
("(x-4)^2-5", "(x-4)^2-5", "answer=response", True, ["answer=response_TRUE", "answer=response_SAME_SYMBOLS_TRUE", "answer=response_WRITTEN_AS_ANSWER_TRUE"], {}),
1238+
("x^2-8x+11", "(x-4)^2-5", "answer=response", False, ["answer=response_TRUE", "answer=response_SAME_SYMBOLS_TRUE", "answer=response_WRITTEN_AS_ANSWER_FALSE"], {}),
1239+
("(x-3)^2-3", "(x-4)^2-5", "answer=response", False, ["answer=response_FALSE", "answer=response_WRITTEN_AS_ANSWER_TRUE"], {}),
12371240
]
12381241
)
12391242
def test_syntactical_comparison(self, response, answer, criteria, value, feedback_tags, additional_params):

app/syntactical_comparison_utilities.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,32 @@ def is_complex_number_on_exponential_form(string):
1818
result = re.fullmatch(is_number_regex+"?\*?(E\^|E\*\*|exp)\(?"+is_number_regex+"*\*?I\)?", string)
1919
return result is not None
2020

21+
def escape_regex_reserved_characters(string):
22+
list = '+*?^$.[]{}()|/'
23+
string = string.replace('\\','\\\\')
24+
for s in list:
25+
string = string.replace(s,'\\'+s)
26+
return string
27+
28+
def generate_arbitrary_number_pattern_matcher(string):
29+
non_numbers = []
30+
number = re.search(is_number_regex, string)
31+
start = 0
32+
end = 0
33+
offset = 0
34+
while number is not None:
35+
start, end = number.span()
36+
start += offset
37+
end += offset
38+
non_numbers.append(escape_regex_reserved_characters(string[offset:start]))
39+
offset = end
40+
number = re.search(is_number_regex, string[offset:])
41+
non_numbers.append(string[offset:])
42+
pattern = is_number_regex.join(non_numbers)
43+
def matcher(comp_string):
44+
result = re.fullmatch(pattern, comp_string)
45+
return result is not None
46+
return matcher
2147

2248
patterns = {
2349
"CARTESIAN": {
@@ -57,3 +83,18 @@ def inner(unused_input):
5783
matches_found.add(label+"_UNKNOWN")
5884
return matches_found
5985
return inner
86+
87+
88+
def written_as_answer(label, parameters_dict):
89+
local_answer = parameters_dict["original_input"]["answer"]
90+
local_response = parameters_dict["original_input"]["response"]
91+
matches_found = set()
92+
93+
def inner(unused_input):
94+
matcher = generate_arbitrary_number_pattern_matcher(local_answer)
95+
if matcher(local_response):
96+
matches_found.add(label+"_TRUE")
97+
else:
98+
matches_found.add(label+"_FALSE")
99+
return matches_found
100+
return inner

0 commit comments

Comments
 (0)