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.
- QuizGenerator/README.md +5 -0
- QuizGenerator/__init__.py +27 -0
- QuizGenerator/__main__.py +7 -0
- QuizGenerator/canvas/__init__.py +13 -0
- QuizGenerator/canvas/canvas_interface.py +627 -0
- QuizGenerator/canvas/classes.py +235 -0
- QuizGenerator/constants.py +149 -0
- QuizGenerator/contentast.py +1955 -0
- QuizGenerator/generate.py +253 -0
- QuizGenerator/logging.yaml +55 -0
- QuizGenerator/misc.py +579 -0
- QuizGenerator/mixins.py +548 -0
- QuizGenerator/performance.py +202 -0
- QuizGenerator/premade_questions/__init__.py +0 -0
- QuizGenerator/premade_questions/basic.py +103 -0
- QuizGenerator/premade_questions/cst334/__init__.py +1 -0
- QuizGenerator/premade_questions/cst334/languages.py +391 -0
- QuizGenerator/premade_questions/cst334/math_questions.py +297 -0
- QuizGenerator/premade_questions/cst334/memory_questions.py +1400 -0
- QuizGenerator/premade_questions/cst334/ostep13_vsfs.py +572 -0
- QuizGenerator/premade_questions/cst334/persistence_questions.py +451 -0
- QuizGenerator/premade_questions/cst334/process.py +648 -0
- QuizGenerator/premade_questions/cst463/__init__.py +0 -0
- QuizGenerator/premade_questions/cst463/gradient_descent/__init__.py +3 -0
- QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +369 -0
- QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +305 -0
- QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +650 -0
- QuizGenerator/premade_questions/cst463/gradient_descent/misc.py +73 -0
- QuizGenerator/premade_questions/cst463/math_and_data/__init__.py +2 -0
- QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +631 -0
- QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +534 -0
- QuizGenerator/premade_questions/cst463/models/__init__.py +0 -0
- QuizGenerator/premade_questions/cst463/models/attention.py +192 -0
- QuizGenerator/premade_questions/cst463/models/cnns.py +186 -0
- QuizGenerator/premade_questions/cst463/models/matrices.py +24 -0
- QuizGenerator/premade_questions/cst463/models/rnns.py +202 -0
- QuizGenerator/premade_questions/cst463/models/text.py +203 -0
- QuizGenerator/premade_questions/cst463/models/weight_counting.py +227 -0
- QuizGenerator/premade_questions/cst463/neural-network-basics/__init__.py +6 -0
- QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +1314 -0
- QuizGenerator/premade_questions/cst463/tensorflow-intro/__init__.py +6 -0
- QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +936 -0
- QuizGenerator/qrcode_generator.py +293 -0
- QuizGenerator/question.py +715 -0
- QuizGenerator/quiz.py +467 -0
- QuizGenerator/regenerate.py +472 -0
- QuizGenerator/typst_utils.py +113 -0
- quizgenerator-0.4.2.dist-info/METADATA +265 -0
- quizgenerator-0.4.2.dist-info/RECORD +52 -0
- quizgenerator-0.4.2.dist-info/WHEEL +4 -0
- quizgenerator-0.4.2.dist-info/entry_points.txt +3 -0
- 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
|
+
|