QuizGenerator 0.4.2__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. QuizGenerator/contentast.py +809 -117
  2. QuizGenerator/generate.py +219 -11
  3. QuizGenerator/misc.py +0 -556
  4. QuizGenerator/mixins.py +50 -29
  5. QuizGenerator/premade_questions/basic.py +3 -3
  6. QuizGenerator/premade_questions/cst334/languages.py +183 -175
  7. QuizGenerator/premade_questions/cst334/math_questions.py +81 -70
  8. QuizGenerator/premade_questions/cst334/memory_questions.py +262 -165
  9. QuizGenerator/premade_questions/cst334/persistence_questions.py +83 -60
  10. QuizGenerator/premade_questions/cst334/process.py +558 -79
  11. QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +39 -13
  12. QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +61 -36
  13. QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +29 -10
  14. QuizGenerator/premade_questions/cst463/gradient_descent/misc.py +2 -2
  15. QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +60 -43
  16. QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +173 -326
  17. QuizGenerator/premade_questions/cst463/models/attention.py +29 -14
  18. QuizGenerator/premade_questions/cst463/models/cnns.py +32 -20
  19. QuizGenerator/premade_questions/cst463/models/rnns.py +28 -15
  20. QuizGenerator/premade_questions/cst463/models/text.py +29 -15
  21. QuizGenerator/premade_questions/cst463/models/weight_counting.py +38 -30
  22. QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +91 -111
  23. QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +128 -55
  24. QuizGenerator/question.py +114 -20
  25. QuizGenerator/quiz.py +81 -24
  26. QuizGenerator/regenerate.py +98 -29
  27. {quizgenerator-0.4.2.dist-info → quizgenerator-0.6.0.dist-info}/METADATA +1 -1
  28. {quizgenerator-0.4.2.dist-info → quizgenerator-0.6.0.dist-info}/RECORD +31 -33
  29. QuizGenerator/README.md +0 -5
  30. QuizGenerator/logging.yaml +0 -55
  31. {quizgenerator-0.4.2.dist-info → quizgenerator-0.6.0.dist-info}/WHEEL +0 -0
  32. {quizgenerator-0.4.2.dist-info → quizgenerator-0.6.0.dist-info}/entry_points.txt +0 -0
  33. {quizgenerator-0.4.2.dist-info → quizgenerator-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -3,10 +3,10 @@ import logging
3
3
  import math
4
4
  import keras
5
5
  import numpy as np
6
+ from typing import List, Tuple
6
7
 
7
- from QuizGenerator.misc import MatrixAnswer
8
- from QuizGenerator.question import Question, QuestionRegistry, Answer
9
- from QuizGenerator.contentast import ContentAST
8
+ from QuizGenerator.question import Question, QuestionRegistry
9
+ from QuizGenerator.contentast import ContentAST, AnswerTypes
10
10
  from QuizGenerator.constants import MathRanges
11
11
  from QuizGenerator.mixins import TableQuestionMixin
12
12
 
@@ -60,15 +60,17 @@ class AttentionForwardPass(MatrixQuestion, TableQuestionMixin):
60
60
 
61
61
  ## Answers:
62
62
  # Q, K, V, output, weights
63
-
64
- self.answers["weights"] = MatrixAnswer("weights", self.output)
65
- self.answers["output"] = MatrixAnswer("output", self.output)
63
+
64
+ self.answers["weights"] = AnswerTypes.Matrix(self.weights, label="Weights")
65
+ self.answers["output"] = AnswerTypes.Matrix(self.output, label="Output")
66
66
 
67
67
  return True
68
68
 
69
- def get_body(self, **kwargs) -> ContentAST.Section:
69
+ def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
70
+ """Build question body and collect answers."""
70
71
  body = ContentAST.Section()
71
-
72
+ answers = []
73
+
72
74
  body.add_element(
73
75
  ContentAST.Text("Given the below information about a self attention layer, please calculate the output sequence.")
74
76
  )
@@ -81,19 +83,27 @@ class AttentionForwardPass(MatrixQuestion, TableQuestionMixin):
81
83
  }
82
84
  )
83
85
  )
84
-
86
+
87
+ answers.append(self.answers["weights"])
88
+ answers.append(self.answers["output"])
85
89
  body.add_elements([
86
90
  ContentAST.LineBreak(),
87
- self.answers["weights"].get_ast_element(label=f"Weights"),
91
+ self.answers["weights"],
88
92
  ContentAST.LineBreak(),
89
- self.answers["output"].get_ast_element(label=f"Output"),
93
+ self.answers["output"],
90
94
  ])
91
-
95
+
96
+ return body, answers
97
+
98
+ def get_body(self, **kwargs) -> ContentAST.Section:
99
+ """Build question body (backward compatible interface)."""
100
+ body, _ = self._get_body(**kwargs)
92
101
  return body
93
102
 
94
- def get_explanation(self, **kwargs) -> ContentAST.Section:
103
+ def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
104
+ """Build question explanation."""
95
105
  explanation = ContentAST.Section()
96
- digits = Answer.DEFAULT_ROUNDING_DIGITS
106
+ digits = ContentAST.Answer.DEFAULT_ROUNDING_DIGITS
97
107
 
98
108
  explanation.add_element(
99
109
  ContentAST.Paragraph([
@@ -188,5 +198,10 @@ class AttentionForwardPass(MatrixQuestion, TableQuestionMixin):
188
198
  )
189
199
  explanation.add_element(ContentAST.Matrix(np.round(self.output, digits)))
190
200
 
201
+ return explanation, []
202
+
203
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
204
+ """Build question explanation (backward compatible interface)."""
205
+ explanation, _ = self._get_explanation(**kwargs)
191
206
  return explanation
192
207
 
@@ -3,10 +3,10 @@ import logging
3
3
  import math
4
4
  import keras
5
5
  import numpy as np
6
+ from typing import List, Tuple
6
7
 
7
8
  from QuizGenerator.question import Question, QuestionRegistry
8
- from QuizGenerator.misc import Answer, MatrixAnswer
9
- from QuizGenerator.contentast import ContentAST
9
+ from QuizGenerator.contentast import ContentAST, AnswerTypes
10
10
  from QuizGenerator.constants import MathRanges
11
11
  from .matrices import MatrixQuestion
12
12
 
@@ -65,22 +65,24 @@ class ConvolutionCalculation(MatrixQuestion):
65
65
  self.result = self.conv2d_multi_channel(self.image, self.kernel, stride=self.stride, padding=self.padding)
66
66
 
67
67
  self.answers = {
68
- f"result_{i}" : MatrixAnswer(f"result_{i}", self.result[:,:,i])
68
+ f"result_{i}" : AnswerTypes.Matrix(self.result[:,:,i], label=f"Result of filter {i}")
69
69
  for i in range(self.result.shape[-1])
70
70
  }
71
71
 
72
72
  return True
73
73
 
74
- def get_body(self, **kwargs) -> ContentAST.Section:
74
+ def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
75
+ """Build question body and collect answers."""
75
76
  body = ContentAST.Section()
76
-
77
+ answers = []
78
+
77
79
  body.add_elements(
78
80
  [
79
81
  ContentAST.Text("Given image represented as matrix: "),
80
82
  ContentAST.Matrix(self.image, name="image")
81
83
  ]
82
84
  )
83
-
85
+
84
86
  body.add_elements(
85
87
  [
86
88
  ContentAST.Text("And convolution filters: "),
@@ -89,7 +91,7 @@ class ConvolutionCalculation(MatrixQuestion):
89
91
  for i in range(self.kernel.shape[-1])
90
92
  ]
91
93
  )
92
-
94
+
93
95
  body.add_element(
94
96
  ContentAST.Paragraph(
95
97
  [
@@ -97,24 +99,29 @@ class ConvolutionCalculation(MatrixQuestion):
97
99
  ]
98
100
  )
99
101
  )
100
-
102
+
101
103
  body.add_element(ContentAST.LineBreak())
102
-
103
- body.add_elements([
104
- ContentAST.Container([
105
- self.answers[f"result_{i}"].get_ast_element(label=f"Result of filter {i}"),
106
- ContentAST.LineBreak()
104
+
105
+ for i in range(self.result.shape[-1]):
106
+ answers.append(self.answers[f"result_{i}"])
107
+ body.add_elements([
108
+ ContentAST.Container([
109
+ self.answers[f"result_{i}"],
110
+ ContentAST.LineBreak()
111
+ ])
107
112
  ])
108
- for i in range(self.result.shape[-1])
109
- ])
110
-
111
-
112
-
113
+
114
+ return body, answers
115
+
116
+ def get_body(self, **kwargs) -> ContentAST.Section:
117
+ """Build question body (backward compatible interface)."""
118
+ body, _ = self._get_body(**kwargs)
113
119
  return body
114
120
 
115
- def get_explanation(self, **kwargs) -> ContentAST.Section:
121
+ def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
122
+ """Build question explanation."""
116
123
  explanation = ContentAST.Section()
117
- digits = Answer.DEFAULT_ROUNDING_DIGITS
124
+ digits = ContentAST.Answer.DEFAULT_ROUNDING_DIGITS
118
125
 
119
126
  explanation.add_element(
120
127
  ContentAST.Paragraph([
@@ -183,4 +190,9 @@ class ConvolutionCalculation(MatrixQuestion):
183
190
  ContentAST.Matrix(np.round(self.result[:, :, f_idx], digits))
184
191
  )
185
192
 
193
+ return explanation, []
194
+
195
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
196
+ """Build question explanation (backward compatible interface)."""
197
+ explanation, _ = self._get_explanation(**kwargs)
186
198
  return explanation
@@ -3,10 +3,11 @@ import logging
3
3
  import math
4
4
  import keras
5
5
  import numpy as np
6
+ from typing import List, Tuple
6
7
 
7
8
  from .matrices import MatrixQuestion
8
- from QuizGenerator.question import Question, QuestionRegistry, Answer
9
- from QuizGenerator.contentast import ContentAST
9
+ from QuizGenerator.question import Question, QuestionRegistry
10
+ from QuizGenerator.contentast import ContentAST, AnswerTypes
10
11
  from QuizGenerator.constants import MathRanges
11
12
  from QuizGenerator.mixins import TableQuestionMixin
12
13
 
@@ -63,13 +64,15 @@ class RNNForwardPass(MatrixQuestion, TableQuestionMixin):
63
64
  ## Answers:
64
65
  # x_seq, W_xh, W_hh, b_h, h_0, h_states
65
66
 
66
- self.answers["output_sequence"] = Answer.matrix(key="output_sequence", value=self.h_states)
67
+ self.answers["output_sequence"] = AnswerTypes.Matrix(value=self.h_states, label="Hidden states")
67
68
 
68
69
  return True
69
70
 
70
- def get_body(self, **kwargs) -> ContentAST.Section:
71
+ def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
72
+ """Build question body and collect answers."""
71
73
  body = ContentAST.Section()
72
-
74
+ answers = []
75
+
73
76
  body.add_element(
74
77
  ContentAST.Paragraph([
75
78
  ContentAST.Text("Given the below information about an RNN, please calculate the output sequence."),
@@ -87,22 +90,27 @@ class RNNForwardPass(MatrixQuestion, TableQuestionMixin):
87
90
  }
88
91
  )
89
92
  )
90
-
93
+
91
94
  body.add_element(ContentAST.LineBreak())
92
-
93
- body.add_element(
94
- self.answers["output_sequence"].get_ast_element(label=f"Hidden states")
95
- )
96
-
95
+
96
+ answers.append(self.answers["output_sequence"])
97
+ body.add_element(self.answers["output_sequence"])
98
+
99
+ return body, answers
100
+
101
+ def get_body(self, **kwargs) -> ContentAST.Section:
102
+ """Build question body (backward compatible interface)."""
103
+ body, _ = self._get_body(**kwargs)
97
104
  return body
98
105
 
99
- def get_explanation(self, **kwargs) -> ContentAST.Section:
106
+ def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
107
+ """Build question explanation."""
100
108
  explanation = ContentAST.Section()
101
- digits = Answer.DEFAULT_ROUNDING_DIGITS
109
+ digits = ContentAST.Answer.DEFAULT_ROUNDING_DIGITS
102
110
 
103
111
  explanation.add_element(
104
112
  ContentAST.Paragraph([
105
- "For an RNN forward pass, we compute the hidden state at each time step using:"
113
+ "For an RNN forward pass, we compute the hidden state at each time step using:"
106
114
  ])
107
115
  )
108
116
 
@@ -128,7 +136,7 @@ class RNNForwardPass(MatrixQuestion, TableQuestionMixin):
128
136
  # Show detailed examples for first 2 timesteps (or just 1 if seq_len == 1)
129
137
  seq_len = len(self.x_seq)
130
138
  num_examples = min(2, seq_len)
131
-
139
+
132
140
  explanation.add_element(ContentAST.Paragraph([""]))
133
141
 
134
142
  for t in range(num_examples):
@@ -198,5 +206,10 @@ class RNNForwardPass(MatrixQuestion, TableQuestionMixin):
198
206
  ContentAST.Matrix(np.round(self.h_states, digits))
199
207
  )
200
208
 
209
+ return explanation, []
210
+
211
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
212
+ """Build question explanation (backward compatible interface)."""
213
+ explanation, _ = self._get_explanation(**kwargs)
201
214
  return explanation
202
215
 
@@ -3,11 +3,11 @@ import logging
3
3
  import math
4
4
  import keras
5
5
  import numpy as np
6
+ from typing import List, Tuple
6
7
 
7
- from QuizGenerator.misc import MatrixAnswer
8
8
  from QuizGenerator.premade_questions.cst463.models.matrices import MatrixQuestion
9
- from QuizGenerator.question import Question, QuestionRegistry, Answer
10
- from QuizGenerator.contentast import ContentAST
9
+ from QuizGenerator.question import Question, QuestionRegistry
10
+ from QuizGenerator.contentast import ContentAST, AnswerTypes
11
11
  from QuizGenerator.constants import MathRanges
12
12
  from QuizGenerator.mixins import TableQuestionMixin
13
13
 
@@ -59,17 +59,19 @@ class word2vec__skipgram(MatrixQuestion, TableQuestionMixin):
59
59
 
60
60
  ## Answers:
61
61
  # center_word, center_emb, context_words, context_embs, logits, probs
62
- self.answers["logits"] = Answer.vector_value(key="logits", value=self.logits)
62
+ self.answers["logits"] = AnswerTypes.Vector(self.logits, label="Logits")
63
63
  most_likely_idx = np.argmax(self.probs)
64
64
  most_likely_word = self.context_words[most_likely_idx]
65
- self.answers["center_word"] = Answer.string(key="center_word", value=most_likely_word)
65
+ self.answers["center_word"] = AnswerTypes.String(most_likely_word, label="Most likely context word")
66
66
 
67
67
 
68
68
  return True
69
69
 
70
- def get_body(self, **kwargs) -> ContentAST.Section:
70
+ def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
71
+ """Build question body and collect answers."""
71
72
  body = ContentAST.Section()
72
-
73
+ answers = []
74
+
73
75
  body.add_element(
74
76
  ContentAST.Paragraph([
75
77
  f"Given center word: `{self.center_word}` with embedding {self.center_emb}, compute the skip-gram probabilities for each context word and identify the most likely one."
@@ -78,23 +80,30 @@ class word2vec__skipgram(MatrixQuestion, TableQuestionMixin):
78
80
  body.add_elements([
79
81
  ContentAST.Paragraph([ContentAST.Text(f"`{w}` : "), str(e)]) for w, e in zip(self.context_words, self.context_embs)
80
82
  ])
81
-
83
+
84
+ answers.append(self.answers["logits"])
85
+ answers.append(self.answers["center_word"])
82
86
  body.add_elements([
83
87
  ContentAST.LineBreak(),
84
- self.answers["logits"].get_ast_element("Logits"),
88
+ self.answers["logits"],
85
89
  ContentAST.LineBreak(),
86
- self.answers["center_word"].get_ast_element("Most likely context word")
90
+ self.answers["center_word"]
87
91
  ])
88
-
89
-
92
+
90
93
  log.debug(f"output: {self.logits}")
91
94
  log.debug(f"weights: {self.probs}")
92
-
95
+
96
+ return body, answers
97
+
98
+ def get_body(self, **kwargs) -> ContentAST.Section:
99
+ """Build question body (backward compatible interface)."""
100
+ body, _ = self._get_body(**kwargs)
93
101
  return body
94
102
 
95
- def get_explanation(self, **kwargs) -> ContentAST.Section:
103
+ def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
104
+ """Build question explanation."""
96
105
  explanation = ContentAST.Section()
97
- digits = Answer.DEFAULT_ROUNDING_DIGITS
106
+ digits = ContentAST.Answer.DEFAULT_ROUNDING_DIGITS
98
107
 
99
108
  explanation.add_element(
100
109
  ContentAST.Paragraph([
@@ -199,5 +208,10 @@ class word2vec__skipgram(MatrixQuestion, TableQuestionMixin):
199
208
  ])
200
209
  )
201
210
 
211
+ return explanation, []
212
+
213
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
214
+ """Build question explanation (backward compatible interface)."""
215
+ explanation, _ = self._get_explanation(**kwargs)
202
216
  return explanation
203
217
 
@@ -3,9 +3,10 @@ import logging
3
3
  import math
4
4
  import keras
5
5
  import numpy as np
6
+ from typing import List, Tuple
6
7
 
7
- from QuizGenerator.question import Question, QuestionRegistry, Answer
8
- from QuizGenerator.contentast import ContentAST
8
+ from QuizGenerator.question import Question, QuestionRegistry
9
+ from QuizGenerator.contentast import ContentAST, AnswerTypes
9
10
  from QuizGenerator.constants import MathRanges
10
11
 
11
12
  log = logging.getLogger(__name__)
@@ -84,16 +85,15 @@ class WeightCounting(Question, abc.ABC):
84
85
  continue
85
86
 
86
87
  self.num_parameters = self.model.count_params()
87
- self.answers["num_parameters"] = Answer.integer(
88
- "num_parameters",
89
- self.num_parameters
90
- )
88
+ self.answers["num_parameters"] = AnswerTypes.Int(self.num_parameters, label="Number of Parameters")
91
89
 
92
90
  return True
93
91
 
94
- def get_body(self, **kwargs) -> ContentAST.Section:
92
+ def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
93
+ """Build question body and collect answers."""
95
94
  body = ContentAST.Section()
96
-
95
+ answers = []
96
+
97
97
  body.add_element(
98
98
  ContentAST.Paragraph(
99
99
  [
@@ -101,7 +101,7 @@ class WeightCounting(Question, abc.ABC):
101
101
  ]
102
102
  )
103
103
  )
104
-
104
+
105
105
  body.add_element(
106
106
  ContentAST.Code(
107
107
  self.model_to_python(
@@ -110,18 +110,23 @@ class WeightCounting(Question, abc.ABC):
110
110
  )
111
111
  )
112
112
  )
113
-
113
+
114
114
  body.add_element(ContentAST.LineBreak())
115
-
116
- body.add_element(
117
- ContentAST.Answer(self.answers["num_parameters"], "Number of Parameters")
118
- )
119
-
115
+
116
+ answers.append(self.answers["num_parameters"])
117
+ body.add_element(self.answers["num_parameters"])
118
+
119
+ return body, answers
120
+
121
+ def get_body(self, **kwargs) -> ContentAST.Section:
122
+ """Build question body (backward compatible interface)."""
123
+ body, _ = self._get_body(**kwargs)
120
124
  return body
121
125
 
122
- def get_explanation(self, **kwargs) -> ContentAST.Section:
126
+ def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
127
+ """Build question explanation."""
123
128
  explanation = ContentAST.Section()
124
-
129
+
125
130
  def markdown_summary(model) -> ContentAST.Table:
126
131
  # Ensure the model is built by running build() or calling it once
127
132
  if not model.built:
@@ -129,37 +134,42 @@ class WeightCounting(Question, abc.ABC):
129
134
  model.build(model.input_shape)
130
135
  except:
131
136
  pass # Some subclassed models need real data to build
132
-
137
+
133
138
  data = []
134
-
139
+
135
140
  total_params = 0
136
-
141
+
137
142
  for layer in model.layers:
138
143
  name = layer.name
139
144
  ltype = layer.__class__.__name__
140
-
145
+
141
146
  # Try to extract output shape
142
147
  try:
143
148
  outshape = tuple(layer.output.shape)
144
149
  except:
145
150
  outshape = "?"
146
-
151
+
147
152
  params = layer.count_params()
148
153
  total_params += params
149
-
154
+
150
155
  data.append([name, ltype, outshape, params])
151
-
156
+
152
157
  data.append(["**Total**", "", "", f"**{total_params}**"])
153
158
  return ContentAST.Table(data=data, headers=["Layer", "Type", "Output Shape", "Params"])
154
-
155
-
159
+
160
+
156
161
  summary_lines = []
157
162
  self.model.summary(print_fn=lambda x: summary_lines.append(x))
158
163
  explanation.add_element(
159
164
  # ContentAST.Text('\n'.join(summary_lines))
160
165
  markdown_summary(self.model)
161
166
  )
162
-
167
+
168
+ return explanation, []
169
+
170
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
171
+ """Build question explanation (backward compatible interface)."""
172
+ explanation, _ = self._get_explanation(**kwargs)
163
173
  return explanation
164
174
 
165
175
 
@@ -222,6 +232,4 @@ class WeightCounting_RNN(WeightCounting):
222
232
  return model, ["units", "return_sequences"]
223
233
 
224
234
 
225
- @QuestionRegistry.register()
226
- class ConvolutionCalculation(Question):
227
- pass
235
+ # ConvolutionCalculation is implemented in cnns.py