Skip to content

Commit b24b11c

Browse files
authored
structure matching test
1 parent 1132163 commit b24b11c

File tree

1 file changed

+148
-62
lines changed

1 file changed

+148
-62
lines changed

evaluation_function.wl

Lines changed: 148 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,148 @@
1-
(* A function to test whether a response is equal to an answer, \
2-
including to within a given tolerance; input and output as
3-
Associations *)
4-
5-
equalQAssociation =
6-
Function[
7-
Module[{tolerance, correctQ, error},
8-
If[NumericQ[#answer],
9-
tolerance =
10-
If[#params["tolerance_is_absolute"],
11-
#params["tolerance"]
12-
,
13-
#params["tolerance"] * #params["answer"]
14-
];
15-
error = Abs[#answer - #response];
16-
correctQ = TrueQ[error <= tolerance]
17-
,
18-
error = "not applicable";
19-
correctQ = TrueQ[#answer == #response]
20-
];
21-
<|
22-
"command" -> "eval"
23-
,
24-
"result" ->
25-
{
26-
"is_correct" -> correctQ
27-
,
28-
"feedback" ->
29-
If[correctQ,
30-
#params["correct_response_feedback"]
31-
,
32-
#params["incorrect_response_feedback"
33-
]
34-
]
35-
,
36-
"error" -> error
37-
}
38-
|>
39-
]
40-
];
41-
42-
(* A function to test whether a response is equal to an answer, \
43-
including to within a given tolerance; input and output as
44-
JSON strings
45-
46-
Calls equalQAssociation *)
47-
48-
equalQJSON = Function[ExportString[equalQAssociation[ImportString[#,
49-
"JSON"] //. List :> Association], "JSON", "Compact" -> True]];
50-
51-
(* A function to test whether a response is equal to an answer, \
52-
including to within a given tolerance; input and output read in from \
53-
external JSON files
54-
55-
Calls equalQAssociation *)
56-
57-
equalQIO = Function[Export[#2, equalQAssociation[Import[#1, "JSON"] //.
58-
List :> Association], "JSON", "Compact" -> True]];
59-
60-
argv = Rest[$ScriptCommandLine]
61-
62-
equalQIO[argv[[1]], argv[[2]]]
1+
(* ::Package:: *)
2+
3+
(* The basic evaluation function code*)
4+
5+
equalQNumeric[answer_, response_, params_] := Module[{tolerance},
6+
tolerance = If[Lookup[params, "tolerance_is_absolute", False],
7+
Lookup[params, "tolerance", 0],
8+
Lookup[params, "tolerance", 0] * answer
9+
];
10+
error = Abs[answer - response];
11+
<|
12+
"error" -> error,
13+
"is_correct" -> TrueQ[error <= tolerance]
14+
|>
15+
]
16+
17+
equalQOther[answer_, response_, params_] := Module[{correctQ},
18+
<|
19+
"error" -> Null,
20+
"is_correct" -> TrueQ[answer == response]
21+
|>
22+
];
23+
24+
(* Patternize: a function that takes an expression and a list of \
25+
named variables, and converts all unnamed symbols in the expression \
26+
into Optional[..] patterns *)
27+
28+
Options[PatternizeSymbol] = {Atomic -> False};
29+
30+
PatternizeSymbol[a_Symbol, namedVariables_, OptionsPattern[]] /;
31+
Not[MemberQ[namedVariables, a]] := \!\(\*
32+
TagBox[
33+
StyleBox[
34+
RowBox[{"If", "[",
35+
RowBox[{
36+
RowBox[{"OptionValue", "[", "Atomic", "]"}], ",",
37+
RowBox[{"(",
38+
RowBox[{"Optional", "[",
39+
RowBox[{"PatternTest", "[",
40+
RowBox[{
41+
RowBox[{"pattern", "[",
42+
RowBox[{"a", ",",
43+
RowBox[{"Blank", "[", "]"}]}], "]"}], ",", "AtomQ"}], "]"}], "]"}], ")"}], ",",
44+
RowBox[{"(",
45+
RowBox[{"Optional", "[",
46+
RowBox[{"pattern", "[",
47+
RowBox[{"a", ",",
48+
RowBox[{"Blank", "[", "]"}]}], "]"}], "]"}], ")"}]}], "]"}],
49+
ShowSpecialCharacters->False,
50+
ShowStringCharacters->True,
51+
NumberMarks->True],
52+
FullForm]\) /. pattern -> Pattern
53+
54+
PatternizeSymbol[a_, namedVariables_, OptionsPattern[]] := a
55+
56+
ComplexResolve[Optional[a_Symbol] + I Optional[b_Symbol]] :=
57+
Complex[a, b]
58+
59+
ComplexResolve[I Optional[b_Symbol]*Pi] := Complex[0, b]*Pi
60+
61+
ComplexResolve[Complex[0, Optional[b_Symbol]] + Optional[a_Symbol]] :=
62+
Complex[a, b]
63+
64+
ComplexResolve[a_] := a
65+
66+
DepatternizePattern[pattern_Optional] := pattern[[1, 1, 1]]
67+
68+
DepatternizePattern[pattern_] := pattern
69+
70+
ComplexResolve[Optional[a_Symbol] + I Optional[b_Symbol]] :=
71+
Complex[a, b]
72+
73+
ComplexResolve[I Optional[b_Symbol]*Pi] := Complex[0, b]*Pi
74+
75+
ComplexResolve[Complex[0, Optional[b_Symbol]] + Optional[a_Symbol]] :=
76+
Complex[a, b]
77+
78+
ComplexResolve[a_] := a
79+
80+
Options[Patternize] = {Atomic -> False};
81+
82+
Patternize[expression_, namedVariables_, OptionsPattern[]] :=
83+
Map[PatternizeSymbol[#, namedVariables,
84+
Atomic -> OptionValue[Atomic]] &,
85+
MapAll[ComplexResolve, expression], {-1}]
86+
87+
Depatternize[pattern_] := MapAll[DepatternizePattern, pattern]
88+
89+
(*StructureMatchQ: a function that checks whether a user's response \
90+
has the same structure as a given answer template, given a set of \
91+
named variables.*)
92+
93+
inertFunctionRules = {Sin -> fSin, Cos -> fCos, Tan -> fTan,
94+
Sec -> fSec, Csc -> fCsc, Cot -> fCot, ArcSin -> fArcSin,
95+
ArcCos -> fArcCos, ArcTan -> fArcTan, ArcSec -> fArcSec,
96+
ArcCsc -> fArcCsc, ArcCot -> fArcCot, Sinh -> fSinh, Cosh -> fCosh,
97+
Tanh -> fTanh, Sech -> fSech, Csch -> fCsch, Coth -> fCoth,
98+
ArcSinh -> fArcSinh, ArcCosh -> fArcCosh, ArcTanh -> fArcTanh,
99+
ArcSech -> fArcSech, ArcCsch -> fArcCsch, ArcCoth -> fArcCoth,
100+
Exp -> fExp, Log -> fLog};
101+
102+
Options[StructureMatchQ] = {Atomic -> False};
103+
104+
StructureMatchQ[response_, answerTemplate_, namedVariables_,
105+
OptionsPattern[]] :=
106+
Module[{response2, answerTemplate2},
107+
response2 = ReplaceAll[response, inertFunctionRules];
108+
answerTemplate2 = ReplaceAll[answerTemplate, inertFunctionRules];
109+
MatchQ[response2,
110+
Patternize[answerTemplate2, namedVariables,
111+
Atomic -> OptionValue[Atomic]]]]
112+
113+
equalQStructure[answer_, response_, params_] := Module[{namedVariables,correctQ},
114+
namedVariables = ToExpression[Lookup[params,"named_variables",{}],TraditionalForm];
115+
correctQ = StructureMatchQ[
116+
ToExpression[ToString[response],TraditionalForm],
117+
ToExpression[ToString[answer],TraditionalForm],
118+
namedVariables];
119+
<|
120+
"error" -> Null,
121+
"is_correct" -> correctQ
122+
|>
123+
]
124+
125+
(* The evaluation function itself *)
126+
127+
evalQ[answer_, response_, params_] := Module[{},
128+
Which[
129+
Lookup[params,"equality_test","None"] == "structure",
130+
equalQStructure[answer,response,params],
131+
NumericQ[answer],
132+
equalQNumeric[answer, response, params],
133+
True,
134+
equalQOther[answer, response, params]
135+
]
136+
];
137+
138+
EvaluationFunction[answer_, response_, params_] := Module[{tolerance, correctQ, error},
139+
result = evalQ[answer, response, params];
140+
<|
141+
"is_correct" -> result["is_correct"],
142+
"feedback" -> If[result["is_correct"],
143+
Lookup[params, "correct_response_feedback", "Correct!"],
144+
Lookup[params, "incorrect_response_feedback", "Incorrect!"]
145+
],
146+
"error" -> result["error"]
147+
|>
148+
];

0 commit comments

Comments
 (0)