QuizGenerator 0.4.4__py3-none-any.whl → 0.5.1__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 (31) hide show
  1. QuizGenerator/contentast.py +952 -82
  2. QuizGenerator/generate.py +45 -9
  3. QuizGenerator/misc.py +4 -554
  4. QuizGenerator/mixins.py +47 -25
  5. QuizGenerator/premade_questions/cst334/languages.py +139 -125
  6. QuizGenerator/premade_questions/cst334/math_questions.py +78 -66
  7. QuizGenerator/premade_questions/cst334/memory_questions.py +258 -144
  8. QuizGenerator/premade_questions/cst334/persistence_questions.py +71 -33
  9. QuizGenerator/premade_questions/cst334/process.py +554 -64
  10. QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +32 -6
  11. QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +59 -34
  12. QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +27 -8
  13. QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +53 -32
  14. QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +228 -88
  15. QuizGenerator/premade_questions/cst463/models/attention.py +26 -10
  16. QuizGenerator/premade_questions/cst463/models/cnns.py +32 -19
  17. QuizGenerator/premade_questions/cst463/models/rnns.py +25 -12
  18. QuizGenerator/premade_questions/cst463/models/text.py +26 -11
  19. QuizGenerator/premade_questions/cst463/models/weight_counting.py +36 -22
  20. QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +89 -109
  21. QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +117 -51
  22. QuizGenerator/question.py +110 -15
  23. QuizGenerator/quiz.py +81 -24
  24. QuizGenerator/regenerate.py +98 -29
  25. {quizgenerator-0.4.4.dist-info → quizgenerator-0.5.1.dist-info}/METADATA +1 -1
  26. {quizgenerator-0.4.4.dist-info → quizgenerator-0.5.1.dist-info}/RECORD +29 -31
  27. QuizGenerator/README.md +0 -5
  28. QuizGenerator/logging.yaml +0 -55
  29. {quizgenerator-0.4.4.dist-info → quizgenerator-0.5.1.dist-info}/WHEEL +0 -0
  30. {quizgenerator-0.4.4.dist-info → quizgenerator-0.5.1.dist-info}/entry_points.txt +0 -0
  31. {quizgenerator-0.4.4.dist-info → quizgenerator-0.5.1.dist-info}/licenses/LICENSE +0 -0
@@ -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
8
- from QuizGenerator.misc import Answer, MatrixAnswer
8
+ from QuizGenerator.question import Question, QuestionRegistry, Answer
9
+ from QuizGenerator.misc import MatrixAnswer
9
10
  from QuizGenerator.contentast import ContentAST
10
11
  from QuizGenerator.constants import MathRanges
11
12
  from .matrices import MatrixQuestion
@@ -65,22 +66,24 @@ class ConvolutionCalculation(MatrixQuestion):
65
66
  self.result = self.conv2d_multi_channel(self.image, self.kernel, stride=self.stride, padding=self.padding)
66
67
 
67
68
  self.answers = {
68
- f"result_{i}" : MatrixAnswer(f"result_{i}", self.result[:,:,i])
69
+ f"result_{i}" : MatrixAnswer(f"result_{i}", self.result[:,:,i], label=f"Result of filter {i}")
69
70
  for i in range(self.result.shape[-1])
70
71
  }
71
72
 
72
73
  return True
73
74
 
74
- def get_body(self, **kwargs) -> ContentAST.Section:
75
+ def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
76
+ """Build question body and collect answers."""
75
77
  body = ContentAST.Section()
76
-
78
+ answers = []
79
+
77
80
  body.add_elements(
78
81
  [
79
82
  ContentAST.Text("Given image represented as matrix: "),
80
83
  ContentAST.Matrix(self.image, name="image")
81
84
  ]
82
85
  )
83
-
86
+
84
87
  body.add_elements(
85
88
  [
86
89
  ContentAST.Text("And convolution filters: "),
@@ -89,7 +92,7 @@ class ConvolutionCalculation(MatrixQuestion):
89
92
  for i in range(self.kernel.shape[-1])
90
93
  ]
91
94
  )
92
-
95
+
93
96
  body.add_element(
94
97
  ContentAST.Paragraph(
95
98
  [
@@ -97,22 +100,27 @@ class ConvolutionCalculation(MatrixQuestion):
97
100
  ]
98
101
  )
99
102
  )
100
-
103
+
101
104
  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()
105
+
106
+ for i in range(self.result.shape[-1]):
107
+ answers.append(self.answers[f"result_{i}"])
108
+ body.add_elements([
109
+ ContentAST.Container([
110
+ self.answers[f"result_{i}"],
111
+ ContentAST.LineBreak()
112
+ ])
107
113
  ])
108
- for i in range(self.result.shape[-1])
109
- ])
110
-
111
-
112
-
114
+
115
+ return body, answers
116
+
117
+ def get_body(self, **kwargs) -> ContentAST.Section:
118
+ """Build question body (backward compatible interface)."""
119
+ body, _ = self._get_body(**kwargs)
113
120
  return body
114
121
 
115
- def get_explanation(self, **kwargs) -> ContentAST.Section:
122
+ def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
123
+ """Build question explanation."""
116
124
  explanation = ContentAST.Section()
117
125
  digits = Answer.DEFAULT_ROUNDING_DIGITS
118
126
 
@@ -183,4 +191,9 @@ class ConvolutionCalculation(MatrixQuestion):
183
191
  ContentAST.Matrix(np.round(self.result[:, :, f_idx], digits))
184
192
  )
185
193
 
194
+ return explanation, []
195
+
196
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
197
+ """Build question explanation (backward compatible interface)."""
198
+ explanation, _ = self._get_explanation(**kwargs)
186
199
  return explanation
@@ -3,6 +3,7 @@ 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
9
  from QuizGenerator.question import Question, QuestionRegistry, Answer
@@ -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"] = Answer.matrix(key="output_sequence", 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[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[Answer]]:
107
+ """Build question explanation."""
100
108
  explanation = ContentAST.Section()
101
109
  digits = 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,6 +3,7 @@ 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.misc import MatrixAnswer
8
9
  from QuizGenerator.premade_questions.cst463.models.matrices import MatrixQuestion
@@ -59,17 +60,19 @@ class word2vec__skipgram(MatrixQuestion, TableQuestionMixin):
59
60
 
60
61
  ## Answers:
61
62
  # center_word, center_emb, context_words, context_embs, logits, probs
62
- self.answers["logits"] = Answer.vector_value(key="logits", value=self.logits)
63
+ self.answers["logits"] = Answer.vector_value(key="logits", value=self.logits, label="Logits")
63
64
  most_likely_idx = np.argmax(self.probs)
64
65
  most_likely_word = self.context_words[most_likely_idx]
65
- self.answers["center_word"] = Answer.string(key="center_word", value=most_likely_word)
66
+ self.answers["center_word"] = Answer.string(key="center_word", value=most_likely_word, label="Most likely context word")
66
67
 
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[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
  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,21 +81,28 @@ class word2vec__skipgram(MatrixQuestion, TableQuestionMixin):
78
81
  body.add_elements([
79
82
  ContentAST.Paragraph([ContentAST.Text(f"`{w}` : "), str(e)]) for w, e in zip(self.context_words, self.context_embs)
80
83
  ])
81
-
84
+
85
+ answers.append(self.answers["logits"])
86
+ answers.append(self.answers["center_word"])
82
87
  body.add_elements([
83
88
  ContentAST.LineBreak(),
84
- self.answers["logits"].get_ast_element("Logits"),
89
+ self.answers["logits"],
85
90
  ContentAST.LineBreak(),
86
- self.answers["center_word"].get_ast_element("Most likely context word")
91
+ self.answers["center_word"]
87
92
  ])
88
-
89
-
93
+
90
94
  log.debug(f"output: {self.logits}")
91
95
  log.debug(f"weights: {self.probs}")
92
-
96
+
97
+ return body, answers
98
+
99
+ def get_body(self, **kwargs) -> ContentAST.Section:
100
+ """Build question body (backward compatible interface)."""
101
+ body, _ = self._get_body(**kwargs)
93
102
  return body
94
103
 
95
- def get_explanation(self, **kwargs) -> ContentAST.Section:
104
+ def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
105
+ """Build question explanation."""
96
106
  explanation = ContentAST.Section()
97
107
  digits = Answer.DEFAULT_ROUNDING_DIGITS
98
108
 
@@ -199,5 +209,10 @@ class word2vec__skipgram(MatrixQuestion, TableQuestionMixin):
199
209
  ])
200
210
  )
201
211
 
212
+ return explanation, []
213
+
214
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
215
+ """Build question explanation (backward compatible interface)."""
216
+ explanation, _ = self._get_explanation(**kwargs)
202
217
  return explanation
203
218
 
@@ -3,6 +3,7 @@ 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, Answer
8
9
  from QuizGenerator.contentast import ContentAST
@@ -86,14 +87,17 @@ class WeightCounting(Question, abc.ABC):
86
87
  self.num_parameters = self.model.count_params()
87
88
  self.answers["num_parameters"] = Answer.integer(
88
89
  "num_parameters",
89
- self.num_parameters
90
+ self.num_parameters,
91
+ label="Number of Parameters"
90
92
  )
91
93
 
92
94
  return True
93
95
 
94
- def get_body(self, **kwargs) -> ContentAST.Section:
96
+ def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
97
+ """Build question body and collect answers."""
95
98
  body = ContentAST.Section()
96
-
99
+ answers = []
100
+
97
101
  body.add_element(
98
102
  ContentAST.Paragraph(
99
103
  [
@@ -101,7 +105,7 @@ class WeightCounting(Question, abc.ABC):
101
105
  ]
102
106
  )
103
107
  )
104
-
108
+
105
109
  body.add_element(
106
110
  ContentAST.Code(
107
111
  self.model_to_python(
@@ -110,18 +114,23 @@ class WeightCounting(Question, abc.ABC):
110
114
  )
111
115
  )
112
116
  )
113
-
117
+
114
118
  body.add_element(ContentAST.LineBreak())
115
-
116
- body.add_element(
117
- ContentAST.Answer(self.answers["num_parameters"], "Number of Parameters")
118
- )
119
-
119
+
120
+ answers.append(self.answers["num_parameters"])
121
+ body.add_element(self.answers["num_parameters"])
122
+
123
+ return body, answers
124
+
125
+ def get_body(self, **kwargs) -> ContentAST.Section:
126
+ """Build question body (backward compatible interface)."""
127
+ body, _ = self._get_body(**kwargs)
120
128
  return body
121
129
 
122
- def get_explanation(self, **kwargs) -> ContentAST.Section:
130
+ def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
131
+ """Build question explanation."""
123
132
  explanation = ContentAST.Section()
124
-
133
+
125
134
  def markdown_summary(model) -> ContentAST.Table:
126
135
  # Ensure the model is built by running build() or calling it once
127
136
  if not model.built:
@@ -129,37 +138,42 @@ class WeightCounting(Question, abc.ABC):
129
138
  model.build(model.input_shape)
130
139
  except:
131
140
  pass # Some subclassed models need real data to build
132
-
141
+
133
142
  data = []
134
-
143
+
135
144
  total_params = 0
136
-
145
+
137
146
  for layer in model.layers:
138
147
  name = layer.name
139
148
  ltype = layer.__class__.__name__
140
-
149
+
141
150
  # Try to extract output shape
142
151
  try:
143
152
  outshape = tuple(layer.output.shape)
144
153
  except:
145
154
  outshape = "?"
146
-
155
+
147
156
  params = layer.count_params()
148
157
  total_params += params
149
-
158
+
150
159
  data.append([name, ltype, outshape, params])
151
-
160
+
152
161
  data.append(["**Total**", "", "", f"**{total_params}**"])
153
162
  return ContentAST.Table(data=data, headers=["Layer", "Type", "Output Shape", "Params"])
154
-
155
-
163
+
164
+
156
165
  summary_lines = []
157
166
  self.model.summary(print_fn=lambda x: summary_lines.append(x))
158
167
  explanation.add_element(
159
168
  # ContentAST.Text('\n'.join(summary_lines))
160
169
  markdown_summary(self.model)
161
170
  )
162
-
171
+
172
+ return explanation, []
173
+
174
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
175
+ """Build question explanation (backward compatible interface)."""
176
+ explanation, _ = self._get_explanation(**kwargs)
163
177
  return explanation
164
178
 
165
179