QuizGenerator 0.4.2__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 (52) hide show
  1. QuizGenerator/README.md +5 -0
  2. QuizGenerator/__init__.py +27 -0
  3. QuizGenerator/__main__.py +7 -0
  4. QuizGenerator/canvas/__init__.py +13 -0
  5. QuizGenerator/canvas/canvas_interface.py +627 -0
  6. QuizGenerator/canvas/classes.py +235 -0
  7. QuizGenerator/constants.py +149 -0
  8. QuizGenerator/contentast.py +1955 -0
  9. QuizGenerator/generate.py +253 -0
  10. QuizGenerator/logging.yaml +55 -0
  11. QuizGenerator/misc.py +579 -0
  12. QuizGenerator/mixins.py +548 -0
  13. QuizGenerator/performance.py +202 -0
  14. QuizGenerator/premade_questions/__init__.py +0 -0
  15. QuizGenerator/premade_questions/basic.py +103 -0
  16. QuizGenerator/premade_questions/cst334/__init__.py +1 -0
  17. QuizGenerator/premade_questions/cst334/languages.py +391 -0
  18. QuizGenerator/premade_questions/cst334/math_questions.py +297 -0
  19. QuizGenerator/premade_questions/cst334/memory_questions.py +1400 -0
  20. QuizGenerator/premade_questions/cst334/ostep13_vsfs.py +572 -0
  21. QuizGenerator/premade_questions/cst334/persistence_questions.py +451 -0
  22. QuizGenerator/premade_questions/cst334/process.py +648 -0
  23. QuizGenerator/premade_questions/cst463/__init__.py +0 -0
  24. QuizGenerator/premade_questions/cst463/gradient_descent/__init__.py +3 -0
  25. QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +369 -0
  26. QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +305 -0
  27. QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +650 -0
  28. QuizGenerator/premade_questions/cst463/gradient_descent/misc.py +73 -0
  29. QuizGenerator/premade_questions/cst463/math_and_data/__init__.py +2 -0
  30. QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +631 -0
  31. QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +534 -0
  32. QuizGenerator/premade_questions/cst463/models/__init__.py +0 -0
  33. QuizGenerator/premade_questions/cst463/models/attention.py +192 -0
  34. QuizGenerator/premade_questions/cst463/models/cnns.py +186 -0
  35. QuizGenerator/premade_questions/cst463/models/matrices.py +24 -0
  36. QuizGenerator/premade_questions/cst463/models/rnns.py +202 -0
  37. QuizGenerator/premade_questions/cst463/models/text.py +203 -0
  38. QuizGenerator/premade_questions/cst463/models/weight_counting.py +227 -0
  39. QuizGenerator/premade_questions/cst463/neural-network-basics/__init__.py +6 -0
  40. QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +1314 -0
  41. QuizGenerator/premade_questions/cst463/tensorflow-intro/__init__.py +6 -0
  42. QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +936 -0
  43. QuizGenerator/qrcode_generator.py +293 -0
  44. QuizGenerator/question.py +715 -0
  45. QuizGenerator/quiz.py +467 -0
  46. QuizGenerator/regenerate.py +472 -0
  47. QuizGenerator/typst_utils.py +113 -0
  48. quizgenerator-0.4.2.dist-info/METADATA +265 -0
  49. quizgenerator-0.4.2.dist-info/RECORD +52 -0
  50. quizgenerator-0.4.2.dist-info/WHEEL +4 -0
  51. quizgenerator-0.4.2.dist-info/entry_points.txt +3 -0
  52. quizgenerator-0.4.2.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,297 @@
1
+ #!env python
2
+ import abc
3
+ import logging
4
+ import math
5
+
6
+ from QuizGenerator.question import Question, QuestionRegistry, Answer
7
+ from QuizGenerator.contentast import ContentAST
8
+ from QuizGenerator.constants import MathRanges
9
+
10
+ log = logging.getLogger(__name__)
11
+
12
+
13
+ class MathQuestion(Question, abc.ABC):
14
+ def __init__(self, *args, **kwargs):
15
+ kwargs["topic"] = kwargs.get("topic", Question.Topic.MATH)
16
+ super().__init__(*args, **kwargs)
17
+
18
+
19
+ @QuestionRegistry.register()
20
+ class BitsAndBytes(MathQuestion):
21
+
22
+ MIN_BITS = MathRanges.DEFAULT_MIN_MATH_BITS
23
+ MAX_BITS = MathRanges.DEFAULT_MAX_MATH_BITS
24
+
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" : Answer.integer("num_bytes", self.num_bytes)}
35
+ else:
36
+ self.answers = {"answer" : Answer.integer("num_bits", self.num_bits)}
37
+
38
+ def get_body(self, **kwargs) -> ContentAST.Section:
39
+ body = ContentAST.Section()
40
+ body.add_element(
41
+ ContentAST.Paragraph([
42
+ f"Given that we have "
43
+ f"{self.num_bits if self.from_binary else self.num_bytes} {'bits' if self.from_binary else 'bytes'}, "
44
+ f"how many {'bits' if not self.from_binary else 'bytes'} "
45
+ f"{'do we need to address our memory' if not self.from_binary else 'of memory can be addressed'}?"
46
+ ])
47
+ )
48
+
49
+ if self.from_binary:
50
+ body.add_element(
51
+ ContentAST.AnswerBlock(
52
+ ContentAST.Answer(
53
+ answer=self.answers['answer'],
54
+ label="Address space size",
55
+ unit="Bytes"
56
+ ),
57
+ )
58
+ )
59
+ else:
60
+ body.add_element(
61
+ ContentAST.AnswerBlock(
62
+ ContentAST.Answer(
63
+ answer=self.answers['answer'],
64
+ label="Number of bits in address",
65
+ unit="bits"
66
+ ),
67
+ )
68
+ )
69
+
70
+ return body
71
+
72
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
73
+ explanation = ContentAST.Section()
74
+
75
+ explanation.add_element(
76
+ ContentAST.Paragraph([
77
+ "Remember that for these problems we use one of these two equations (which are equivalent)"
78
+ ])
79
+ )
80
+ explanation.add_elements([
81
+ ContentAST.Equation(r"log_{2}(\text{#bytes}) = \text{#bits}"),
82
+ ContentAST.Equation(r"2^{(\text{#bits})} = \text{#bytes}")
83
+ ])
84
+
85
+ explanation.add_element(
86
+ ContentAST.Paragraph(["Therefore, we calculate:"])
87
+ )
88
+
89
+ if self.from_binary:
90
+ explanation.add_element(
91
+ ContentAST.Equation(f"2 ^ {{{self.num_bits}bits}} = \\textbf{{{self.num_bytes}}}\\text{{bytes}}")
92
+ )
93
+ else:
94
+ explanation.add_element(
95
+ ContentAST.Equation(f"log_{{2}}({self.num_bytes} \\text{{bytes}}) = \\textbf{{{self.num_bits}}}\\text{{bits}}")
96
+ )
97
+
98
+ return explanation
99
+
100
+
101
+ @QuestionRegistry.register()
102
+ class HexAndBinary(MathQuestion):
103
+
104
+ MIN_HEXITS = 1
105
+ MAX_HEXITS = 8
106
+
107
+ def refresh(self, **kwargs):
108
+ super().refresh(**kwargs)
109
+
110
+ self.from_binary = self.rng.choice([True, False])
111
+ self.number_of_hexits = self.rng.randint(1, 8)
112
+ self.value = self.rng.randint(1, 16**self.number_of_hexits)
113
+
114
+ self.hex_val = f"0x{self.value:0{self.number_of_hexits}X}"
115
+ self.binary_val = f"0b{self.value:0{4*self.number_of_hexits}b}"
116
+
117
+ if self.from_binary:
118
+ self.answers['answer'] = Answer.string("hex_val", self.hex_val)
119
+ else:
120
+ self.answers['answer'] = Answer.string("binary_val", self.binary_val)
121
+
122
+ def get_body(self, **kwargs) -> ContentAST.Section:
123
+ body = ContentAST.Section()
124
+
125
+ body.add_element(
126
+ ContentAST.Paragraph([
127
+ f"Given the number {self.hex_val if not self.from_binary else self.binary_val} "
128
+ f"please convert it to {'hex' if self.from_binary else 'binary'}.",
129
+ "Please include base indicator all padding zeros as appropriate (e.g. 0x01 should be 0b00000001)",
130
+ ])
131
+ )
132
+
133
+ body.add_element(
134
+ ContentAST.AnswerBlock([
135
+ ContentAST.Answer(
136
+ answer = self.answers['answer'],
137
+ label=f"Value in {'hex' if self.from_binary else 'binary'}: ",
138
+ )
139
+ ])
140
+ )
141
+
142
+ return body
143
+
144
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
145
+ explanation = ContentAST.Section()
146
+
147
+ paragraph = ContentAST.Paragraph([
148
+ "The core idea for converting between binary and hex is to divide and conquer. "
149
+ "Specifically, each hexit (hexadecimal digit) is equivalent to 4 bits. "
150
+ ])
151
+
152
+ if self.from_binary:
153
+ paragraph.add_line(
154
+ "Therefore, we need to consider each group of 4 bits together and convert them to the appropriate hexit."
155
+ )
156
+ else:
157
+ paragraph.add_line(
158
+ "Therefore, we need to consider each hexit and convert it to the appropriate 4 bits."
159
+ )
160
+
161
+ explanation.add_element(paragraph)
162
+
163
+ # Generate translation table
164
+ binary_str = f"{self.value:0{4*self.number_of_hexits}b}"
165
+ hex_str = f"{self.value:0{self.number_of_hexits}X}"
166
+
167
+ explanation.add_element(
168
+ ContentAST.Table(
169
+ data=[
170
+ ["0b"] + [binary_str[i:i+4] for i in range(0, len(binary_str), 4)],
171
+ ["0x"] + list(hex_str)
172
+ ],
173
+ # alignments='center', #['center' for _ in range(0, 1+len(hex_str))],
174
+ padding=False
175
+
176
+ )
177
+ )
178
+
179
+ if self.from_binary:
180
+ explanation.add_element(
181
+ ContentAST.Paragraph([
182
+ f"Which gives us our hex value of: 0x{hex_str}"
183
+ ])
184
+ )
185
+ else:
186
+ explanation.add_element(
187
+ ContentAST.Paragraph([
188
+ f"Which gives us our binary value of: 0b{binary_str}"
189
+ ])
190
+ )
191
+
192
+ return explanation
193
+
194
+
195
+ @QuestionRegistry.register()
196
+ class AverageMemoryAccessTime(MathQuestion):
197
+
198
+ CHANCE_OF_99TH_PERCENTILE = 0.75
199
+
200
+ def refresh(self, rng_seed=None, *args, **kwargs):
201
+ super().refresh(rng_seed=rng_seed, *args, **kwargs)
202
+
203
+ # Figure out how many orders of magnitude different we are
204
+ orders_of_magnitude_different = self.rng.randint(1,4)
205
+ self.hit_latency = self.rng.randint(1,9)
206
+ self.miss_latency = int(self.rng.randint(1, 9) * math.pow(10, orders_of_magnitude_different))
207
+
208
+ # Add in a complication of making it sometimes very, very close
209
+ if self.rng.random() < self.CHANCE_OF_99TH_PERCENTILE:
210
+ # Then let's make it very close to 99%
211
+ self.hit_rate = (99 + self.rng.random()) / 100
212
+ else:
213
+ self.hit_rate = self.rng.random()
214
+
215
+ # Calculate the hit rate
216
+ self.hit_rate = round(self.hit_rate, 4)
217
+
218
+ # Calculate the AverageMemoryAccessTime (which is the answer itself)
219
+ self.amat = self.hit_rate * self.hit_latency + (1 - self.hit_rate) * self.miss_latency
220
+
221
+ self.answers = {
222
+ "amat": Answer.float_value("answer__amat", self.amat)
223
+ }
224
+
225
+ # Finally, do the self.rngizing of the question, to avoid these being non-deterministic
226
+ self.show_miss_rate = self.rng.random() > 0.5
227
+
228
+ # At this point, everything in the question should be set.
229
+ pass
230
+
231
+ def get_body(self, **kwargs) -> ContentAST.Section:
232
+ body = ContentAST.Section()
233
+
234
+ # Add in background information
235
+ body.add_element(
236
+ ContentAST.Paragraph([
237
+ ContentAST.Text("Please calculate the Average Memory Access Time given the below information. "),
238
+ ContentAST.Text(
239
+ f"Please round your answer to {Answer.DEFAULT_ROUNDING_DIGITS} decimal points. ",
240
+ hide_from_latex=True
241
+ )
242
+ ])
243
+ )
244
+ table_data = [
245
+ ["Hit Latency", f"{self.hit_latency} cycles"],
246
+ ["Miss Latency", f"{self.miss_latency} cycles"]
247
+ ]
248
+
249
+ # Add in either miss rate or hit rate -- we only need one of them
250
+ if self.show_miss_rate:
251
+ table_data.append(["Miss Rate", f"{100 * (1 - self.hit_rate): 0.2f}%"])
252
+ else:
253
+ table_data.append(["Hit Rate", f"{100 * self.hit_rate: 0.2f}%"])
254
+
255
+ body.add_element(
256
+ ContentAST.Table(
257
+ data=table_data
258
+ )
259
+ )
260
+
261
+ body.add_element(
262
+ ContentAST.AnswerBlock([
263
+ ContentAST.Answer(
264
+ answer=self.answers["amat"],
265
+ label="Average Memory Access Time",
266
+ unit="cycles"
267
+ )
268
+ ])
269
+ )
270
+
271
+ return body
272
+
273
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
274
+ explanation = ContentAST.Section()
275
+
276
+ # Add in General explanation
277
+ explanation.add_element(
278
+ ContentAST.Paragraph([
279
+ "Remember that to calculate the Average Memory Access Time "
280
+ "we weight both the hit and miss times by their relative likelihood.",
281
+ "That is, we calculate:"
282
+ ])
283
+ )
284
+
285
+ # Add in equations
286
+ explanation.add_element(
287
+ ContentAST.Equation.make_block_equation__multiline_equals(
288
+ lhs="AMAT",
289
+ rhs=[
290
+ r"(hit\_rate)*(hit\_cost) + (1 - hit\_rate)*(miss\_cost)",
291
+ f"({self.hit_rate: 0.{Answer.DEFAULT_ROUNDING_DIGITS}f})*({self.hit_latency}) + ({1 - self.hit_rate: 0.{Answer.DEFAULT_ROUNDING_DIGITS}f})*({self.miss_latency}) = {self.amat: 0.{Answer.DEFAULT_ROUNDING_DIGITS}f}\\text{{cycles}}"
292
+ ]
293
+ )
294
+ )
295
+
296
+ return explanation
297
+