QuizGenerator 0.7.1__py3-none-any.whl → 0.8.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 (30) hide show
  1. QuizGenerator/contentast.py +48 -15
  2. QuizGenerator/generate.py +2 -1
  3. QuizGenerator/mixins.py +14 -100
  4. QuizGenerator/premade_questions/basic.py +24 -29
  5. QuizGenerator/premade_questions/cst334/languages.py +100 -99
  6. QuizGenerator/premade_questions/cst334/math_questions.py +112 -122
  7. QuizGenerator/premade_questions/cst334/memory_questions.py +621 -621
  8. QuizGenerator/premade_questions/cst334/persistence_questions.py +137 -163
  9. QuizGenerator/premade_questions/cst334/process.py +312 -328
  10. QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +34 -35
  11. QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +41 -36
  12. QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +48 -41
  13. QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +285 -521
  14. QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +149 -126
  15. QuizGenerator/premade_questions/cst463/models/attention.py +44 -50
  16. QuizGenerator/premade_questions/cst463/models/cnns.py +43 -47
  17. QuizGenerator/premade_questions/cst463/models/matrices.py +61 -11
  18. QuizGenerator/premade_questions/cst463/models/rnns.py +48 -50
  19. QuizGenerator/premade_questions/cst463/models/text.py +65 -67
  20. QuizGenerator/premade_questions/cst463/models/weight_counting.py +47 -46
  21. QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +100 -156
  22. QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +93 -141
  23. QuizGenerator/question.py +310 -202
  24. QuizGenerator/quiz.py +8 -5
  25. QuizGenerator/regenerate.py +14 -6
  26. {quizgenerator-0.7.1.dist-info → quizgenerator-0.8.1.dist-info}/METADATA +30 -2
  27. {quizgenerator-0.7.1.dist-info → quizgenerator-0.8.1.dist-info}/RECORD +30 -30
  28. {quizgenerator-0.7.1.dist-info → quizgenerator-0.8.1.dist-info}/WHEEL +0 -0
  29. {quizgenerator-0.7.1.dist-info → quizgenerator-0.8.1.dist-info}/entry_points.txt +0 -0
  30. {quizgenerator-0.7.1.dist-info → quizgenerator-0.8.1.dist-info}/licenses/LICENSE +0 -0
@@ -2,6 +2,7 @@
2
2
  import abc
3
3
  import logging
4
4
  import math
5
+ import random
5
6
 
6
7
  from QuizGenerator.question import Question, QuestionRegistry
7
8
  import QuizGenerator.contentast as ca
@@ -22,45 +23,51 @@ class BitsAndBytes(MathQuestion):
22
23
  MIN_BITS = MathRanges.DEFAULT_MIN_MATH_BITS
23
24
  MAX_BITS = MathRanges.DEFAULT_MAX_MATH_BITS
24
25
 
25
- def refresh(self, *args, **kwargs):
26
- super().refresh(*args, **kwargs)
27
-
28
- # Generate the important parts of the problem
29
- self.from_binary = (0 == self.rng.randint(0,1))
30
- self.num_bits = self.rng.randint(self.MIN_BITS, self.MAX_BITS)
31
- self.num_bytes = int(math.pow(2, self.num_bits))
32
-
33
- if self.from_binary:
34
- self.answers = {"answer": ca.AnswerTypes.Int(self.num_bytes,
35
- label="Address space size", unit="Bytes")}
36
- else:
37
- self.answers = {"answer": ca.AnswerTypes.Int(self.num_bits,
38
- label="Number of bits in address", unit="bits")}
39
-
40
- def _get_body(self, **kwargs):
26
+ @classmethod
27
+ def _build_context(cls, *, rng_seed=None, **kwargs):
28
+ rng = random.Random(rng_seed)
29
+ from_binary = (0 == rng.randint(0, 1))
30
+ num_bits = rng.randint(cls.MIN_BITS, cls.MAX_BITS)
31
+ num_bytes = int(math.pow(2, num_bits))
32
+ return {
33
+ "from_binary": from_binary,
34
+ "num_bits": num_bits,
35
+ "num_bytes": num_bytes,
36
+ }
37
+
38
+ @classmethod
39
+ def _build_body(cls, context):
41
40
  """Build question body and collect answers."""
42
- answers = [self.answers['answer']]
41
+ if context["from_binary"]:
42
+ answer = ca.AnswerTypes.Int(
43
+ context["num_bytes"],
44
+ label="Address space size",
45
+ unit="Bytes"
46
+ )
47
+ else:
48
+ answer = ca.AnswerTypes.Int(
49
+ context["num_bits"],
50
+ label="Number of bits in address",
51
+ unit="bits"
52
+ )
43
53
 
44
54
  body = ca.Section()
45
55
  body.add_element(
46
56
  ca.Paragraph([
47
57
  f"Given that we have "
48
- f"{self.num_bits if self.from_binary else self.num_bytes} {'bits' if self.from_binary else 'bytes'}, "
49
- f"how many {'bits' if not self.from_binary else 'bytes'} "
50
- f"{'do we need to address our memory' if not self.from_binary else 'of memory can be addressed'}?"
58
+ f"{context['num_bits'] if context['from_binary'] else context['num_bytes']} "
59
+ f"{'bits' if context['from_binary'] else 'bytes'}, "
60
+ f"how many {'bits' if not context['from_binary'] else 'bytes'} "
61
+ f"{'do we need to address our memory' if not context['from_binary'] else 'of memory can be addressed'}?"
51
62
  ])
52
63
  )
53
64
 
54
- body.add_element(ca.AnswerBlock(self.answers['answer']))
65
+ body.add_element(ca.AnswerBlock(answer))
55
66
 
56
- return body, answers
57
-
58
- def get_body(self, **kwargs) -> ca.Section:
59
- """Build question body (backward compatible interface)."""
60
- body, _ = self._get_body(**kwargs)
61
67
  return body
62
68
 
63
- def _get_explanation(self, **kwargs):
69
+ @classmethod
70
+ def _build_explanation(cls, context):
64
71
  explanation = ca.Section()
65
72
 
66
73
  explanation.add_element(
@@ -77,20 +84,19 @@ class BitsAndBytes(MathQuestion):
77
84
  ca.Paragraph(["Therefore, we calculate:"])
78
85
  )
79
86
 
80
- if self.from_binary:
87
+ if context["from_binary"]:
81
88
  explanation.add_element(
82
- ca.Equation(f"2 ^ {{{self.num_bits}bits}} = \\textbf{{{self.num_bytes}}}\\text{{bytes}}")
89
+ ca.Equation(
90
+ f"2 ^ {{{context['num_bits']}bits}} = \\textbf{{{context['num_bytes']}}}\\text{{bytes}}"
91
+ )
83
92
  )
84
93
  else:
85
94
  explanation.add_element(
86
- ca.Equation(f"log_{{2}}({self.num_bytes} \\text{{bytes}}) = \\textbf{{{self.num_bits}}}\\text{{bits}}")
95
+ ca.Equation(
96
+ f"log_{{2}}({context['num_bytes']} \\text{{bytes}}) = \\textbf{{{context['num_bits']}}}\\text{{bits}}"
97
+ )
87
98
  )
88
99
 
89
- return explanation, []
90
-
91
- def get_explanation(self, **kwargs) -> ca.Section:
92
- """Build question explanation (backward compatible interface)."""
93
- explanation, _ = self._get_explanation(**kwargs)
94
100
  return explanation
95
101
 
96
102
 
@@ -100,47 +106,46 @@ class HexAndBinary(MathQuestion):
100
106
  MIN_HEXITS = 1
101
107
  MAX_HEXITS = 8
102
108
 
103
- def refresh(self, **kwargs):
104
- super().refresh(**kwargs)
105
-
106
- self.from_binary = self.rng.choice([True, False])
107
- self.number_of_hexits = self.rng.randint(1, 8)
108
- self.value = self.rng.randint(1, 16**self.number_of_hexits)
109
-
110
- self.hex_val = f"0x{self.value:0{self.number_of_hexits}X}"
111
- self.binary_val = f"0b{self.value:0{4*self.number_of_hexits}b}"
112
-
113
- if self.from_binary:
114
- self.answers['answer'] = ca.AnswerTypes.String(self.hex_val,
115
- label="Value in hex")
116
- else:
117
- self.answers['answer'] = ca.AnswerTypes.String(self.binary_val,
118
- label="Value in binary")
119
-
120
- def _get_body(self, **kwargs):
109
+ @classmethod
110
+ def _build_context(cls, *, rng_seed=None, **kwargs):
111
+ rng = random.Random(rng_seed)
112
+ from_binary = rng.choice([True, False])
113
+ number_of_hexits = rng.randint(1, 8)
114
+ value = rng.randint(1, 16**number_of_hexits)
115
+ hex_val = f"0x{value:0{number_of_hexits}X}"
116
+ binary_val = f"0b{value:0{4*number_of_hexits}b}"
117
+ return {
118
+ "from_binary": from_binary,
119
+ "number_of_hexits": number_of_hexits,
120
+ "value": value,
121
+ "hex_val": hex_val,
122
+ "binary_val": binary_val,
123
+ }
124
+
125
+ @classmethod
126
+ def _build_body(cls, context):
121
127
  """Build question body and collect answers."""
122
- answers = [self.answers['answer']]
128
+ if context["from_binary"]:
129
+ answer = ca.AnswerTypes.String(context["hex_val"], label="Value in hex")
130
+ else:
131
+ answer = ca.AnswerTypes.String(context["binary_val"], label="Value in binary")
123
132
 
124
133
  body = ca.Section()
125
134
 
126
135
  body.add_element(
127
136
  ca.Paragraph([
128
- f"Given the number {self.hex_val if not self.from_binary else self.binary_val} "
129
- f"please convert it to {'hex' if self.from_binary else 'binary'}.",
137
+ f"Given the number {context['hex_val'] if not context['from_binary'] else context['binary_val']} "
138
+ f"please convert it to {'hex' if context['from_binary'] else 'binary'}.",
130
139
  "Please include base indicator all padding zeros as appropriate (e.g. 0x01 should be 0b00000001)",
131
140
  ])
132
141
  )
133
142
 
134
- body.add_element(ca.AnswerBlock(self.answers['answer']))
135
-
136
- return body, answers
143
+ body.add_element(ca.AnswerBlock(answer))
137
144
 
138
- def get_body(self, **kwargs) -> ca.Section:
139
- """Build question body (backward compatible interface)."""
140
- body, _ = self._get_body(**kwargs)
141
145
  return body
142
146
 
143
- def _get_explanation(self, **kwargs):
147
+ @classmethod
148
+ def _build_explanation(cls, context):
144
149
  explanation = ca.Section()
145
150
 
146
151
  paragraph = ca.Paragraph([
@@ -148,7 +153,7 @@ class HexAndBinary(MathQuestion):
148
153
  "Specifically, each hexit (hexadecimal digit) is equivalent to 4 bits. "
149
154
  ])
150
155
 
151
- if self.from_binary:
156
+ if context["from_binary"]:
152
157
  paragraph.add_line(
153
158
  "Therefore, we need to consider each group of 4 bits together and convert them to the appropriate hexit."
154
159
  )
@@ -160,8 +165,8 @@ class HexAndBinary(MathQuestion):
160
165
  explanation.add_element(paragraph)
161
166
 
162
167
  # Generate translation table
163
- binary_str = f"{self.value:0{4*self.number_of_hexits}b}"
164
- hex_str = f"{self.value:0{self.number_of_hexits}X}"
168
+ binary_str = f"{context['value']:0{4*context['number_of_hexits']}b}"
169
+ hex_str = f"{context['value']:0{context['number_of_hexits']}X}"
165
170
 
166
171
  explanation.add_element(
167
172
  ca.Table(
@@ -175,7 +180,7 @@ class HexAndBinary(MathQuestion):
175
180
  )
176
181
  )
177
182
 
178
- if self.from_binary:
183
+ if context["from_binary"]:
179
184
  explanation.add_element(
180
185
  ca.Paragraph([
181
186
  f"Which gives us our hex value of: 0x{hex_str}"
@@ -188,11 +193,6 @@ class HexAndBinary(MathQuestion):
188
193
  ])
189
194
  )
190
195
 
191
- return explanation, []
192
-
193
- def get_explanation(self, **kwargs) -> ca.Section:
194
- """Build question explanation (backward compatible interface)."""
195
- explanation, _ = self._get_explanation(**kwargs)
196
196
  return explanation
197
197
 
198
198
 
@@ -201,40 +201,38 @@ class AverageMemoryAccessTime(MathQuestion):
201
201
 
202
202
  CHANCE_OF_99TH_PERCENTILE = 0.75
203
203
 
204
- def refresh(self, rng_seed=None, *args, **kwargs):
205
- super().refresh(rng_seed=rng_seed, *args, **kwargs)
206
-
207
- # Figure out how many orders of magnitude different we are
208
- orders_of_magnitude_different = self.rng.randint(1,4)
209
- self.hit_latency = self.rng.randint(1,9)
210
- self.miss_latency = int(self.rng.randint(1, 9) * math.pow(10, orders_of_magnitude_different))
211
-
212
- # Add in a complication of making it sometimes very, very close
213
- if self.rng.random() < self.CHANCE_OF_99TH_PERCENTILE:
214
- # Then let's make it very close to 99%
215
- self.hit_rate = (99 + self.rng.random()) / 100
204
+ @classmethod
205
+ def _build_context(cls, *, rng_seed=None, **kwargs):
206
+ rng = random.Random(rng_seed)
207
+ orders_of_magnitude_different = rng.randint(1, 4)
208
+ hit_latency = rng.randint(1, 9)
209
+ miss_latency = int(rng.randint(1, 9) * math.pow(10, orders_of_magnitude_different))
210
+
211
+ if rng.random() < cls.CHANCE_OF_99TH_PERCENTILE:
212
+ hit_rate = (99 + rng.random()) / 100
216
213
  else:
217
- self.hit_rate = self.rng.random()
218
-
219
- # Calculate the hit rate
220
- self.hit_rate = round(self.hit_rate, 4)
221
-
222
- # Calculate the AverageMemoryAccessTime (which is the answer itself)
223
- self.amat = self.hit_rate * self.hit_latency + (1 - self.hit_rate) * self.miss_latency
224
-
225
- self.answers = {
226
- "amat": ca.AnswerTypes.Float(self.amat, label="Average Memory Access Time", unit="cycles")
214
+ hit_rate = rng.random()
215
+
216
+ hit_rate = round(hit_rate, 4)
217
+ amat = hit_rate * hit_latency + (1 - hit_rate) * miss_latency
218
+ show_miss_rate = rng.random() > 0.5
219
+
220
+ return {
221
+ "hit_latency": hit_latency,
222
+ "miss_latency": miss_latency,
223
+ "hit_rate": hit_rate,
224
+ "amat": amat,
225
+ "show_miss_rate": show_miss_rate,
227
226
  }
228
-
229
- # Finally, do the self.rngizing of the question, to avoid these being non-deterministic
230
- self.show_miss_rate = self.rng.random() > 0.5
231
-
232
- # At this point, everything in the question should be set.
233
- pass
234
-
235
- def _get_body(self, **kwargs):
227
+
228
+ @classmethod
229
+ def _build_body(cls, context):
236
230
  """Build question body and collect answers."""
237
- answers = [self.answers["amat"]]
231
+ answer = ca.AnswerTypes.Float(
232
+ context["amat"],
233
+ label="Average Memory Access Time",
234
+ unit="cycles"
235
+ )
238
236
 
239
237
  body = ca.Section()
240
238
 
@@ -249,15 +247,15 @@ class AverageMemoryAccessTime(MathQuestion):
249
247
  ])
250
248
  )
251
249
  table_data = [
252
- ["Hit Latency", f"{self.hit_latency} cycles"],
253
- ["Miss Latency", f"{self.miss_latency} cycles"]
250
+ ["Hit Latency", f"{context['hit_latency']} cycles"],
251
+ ["Miss Latency", f"{context['miss_latency']} cycles"]
254
252
  ]
255
253
 
256
254
  # Add in either miss rate or hit rate -- we only need one of them
257
- if self.show_miss_rate:
258
- table_data.append(["Miss Rate", f"{100 * (1 - self.hit_rate): 0.2f}%"])
255
+ if context["show_miss_rate"]:
256
+ table_data.append(["Miss Rate", f"{100 * (1 - context['hit_rate']): 0.2f}%"])
259
257
  else:
260
- table_data.append(["Hit Rate", f"{100 * self.hit_rate: 0.2f}%"])
258
+ table_data.append(["Hit Rate", f"{100 * context['hit_rate']: 0.2f}%"])
261
259
 
262
260
  body.add_element(
263
261
  ca.Table(
@@ -267,16 +265,12 @@ class AverageMemoryAccessTime(MathQuestion):
267
265
 
268
266
  body.add_element(ca.LineBreak())
269
267
 
270
- body.add_element(ca.AnswerBlock(self.answers["amat"]))
268
+ body.add_element(ca.AnswerBlock(answer))
271
269
 
272
- return body, answers
273
-
274
- def get_body(self, **kwargs) -> ca.Section:
275
- """Build question body (backward compatible interface)."""
276
- body, _ = self._get_body(**kwargs)
277
270
  return body
278
271
 
279
- def _get_explanation(self, **kwargs):
272
+ @classmethod
273
+ def _build_explanation(cls, context):
280
274
  explanation = ca.Section()
281
275
 
282
276
  # Add in General explanation
@@ -294,15 +288,11 @@ class AverageMemoryAccessTime(MathQuestion):
294
288
  lhs="AMAT",
295
289
  rhs=[
296
290
  r"(hit\_rate)*(hit\_cost) + (1 - hit\_rate)*(miss\_cost)",
297
- f"({self.hit_rate: 0.{ca.Answer.DEFAULT_ROUNDING_DIGITS}f})*({self.hit_latency}) + ({1 - self.hit_rate: 0.{ca.Answer.DEFAULT_ROUNDING_DIGITS}f})*({self.miss_latency}) = {self.amat: 0.{ca.Answer.DEFAULT_ROUNDING_DIGITS}f}\\text{{cycles}}"
291
+ f"({context['hit_rate']: 0.{ca.Answer.DEFAULT_ROUNDING_DIGITS}f})*({context['hit_latency']}) + "
292
+ f"({1 - context['hit_rate']: 0.{ca.Answer.DEFAULT_ROUNDING_DIGITS}f})*({context['miss_latency']}) = "
293
+ f"{context['amat']: 0.{ca.Answer.DEFAULT_ROUNDING_DIGITS}f}\\text{{cycles}}"
298
294
  ]
299
295
  )
300
296
  )
301
297
 
302
- return explanation, []
303
-
304
- def get_explanation(self, **kwargs) -> ca.Section:
305
- """Build question explanation (backward compatible interface)."""
306
- explanation, _ = self._get_explanation(**kwargs)
307
298
  return explanation
308
-