QuizGenerator 0.5.1__py3-none-any.whl → 0.6.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.
- QuizGenerator/contentast.py +1056 -1231
- QuizGenerator/generate.py +174 -2
- QuizGenerator/misc.py +0 -6
- QuizGenerator/mixins.py +7 -8
- QuizGenerator/premade_questions/basic.py +3 -3
- QuizGenerator/premade_questions/cst334/languages.py +45 -51
- QuizGenerator/premade_questions/cst334/math_questions.py +9 -10
- QuizGenerator/premade_questions/cst334/memory_questions.py +39 -56
- QuizGenerator/premade_questions/cst334/persistence_questions.py +12 -27
- QuizGenerator/premade_questions/cst334/process.py +11 -22
- QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +11 -11
- QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +7 -7
- QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +6 -6
- QuizGenerator/premade_questions/cst463/gradient_descent/misc.py +2 -2
- QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +15 -19
- QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +149 -442
- QuizGenerator/premade_questions/cst463/models/attention.py +7 -8
- QuizGenerator/premade_questions/cst463/models/cnns.py +6 -7
- QuizGenerator/premade_questions/cst463/models/rnns.py +6 -6
- QuizGenerator/premade_questions/cst463/models/text.py +7 -8
- QuizGenerator/premade_questions/cst463/models/weight_counting.py +5 -9
- QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +22 -22
- QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +25 -25
- QuizGenerator/question.py +13 -14
- {quizgenerator-0.5.1.dist-info → quizgenerator-0.6.1.dist-info}/METADATA +1 -1
- {quizgenerator-0.5.1.dist-info → quizgenerator-0.6.1.dist-info}/RECORD +29 -29
- {quizgenerator-0.5.1.dist-info → quizgenerator-0.6.1.dist-info}/WHEEL +0 -0
- {quizgenerator-0.5.1.dist-info → quizgenerator-0.6.1.dist-info}/entry_points.txt +0 -0
- {quizgenerator-0.5.1.dist-info → quizgenerator-0.6.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -9,8 +9,8 @@ import logging
|
|
|
9
9
|
import math
|
|
10
10
|
from typing import List, Optional
|
|
11
11
|
|
|
12
|
-
from QuizGenerator.contentast import ContentAST
|
|
13
|
-
from QuizGenerator.question import Question,
|
|
12
|
+
from QuizGenerator.contentast import ContentAST, AnswerTypes
|
|
13
|
+
from QuizGenerator.question import Question, QuestionRegistry, RegenerableChoiceMixin
|
|
14
14
|
from QuizGenerator.mixins import TableQuestionMixin, BodyTemplatesMixin
|
|
15
15
|
|
|
16
16
|
log = logging.getLogger(__name__)
|
|
@@ -40,9 +40,9 @@ class VirtualAddressParts(MemoryQuestion, TableQuestionMixin):
|
|
|
40
40
|
self.num_bits_vpn = self.num_bits_va - self.num_bits_offset
|
|
41
41
|
|
|
42
42
|
self.possible_answers = {
|
|
43
|
-
self.Target.VA_BITS:
|
|
44
|
-
self.Target.OFFSET_BITS:
|
|
45
|
-
self.Target.VPN_BITS:
|
|
43
|
+
self.Target.VA_BITS: AnswerTypes.Int(self.num_bits_va, unit="bits"),
|
|
44
|
+
self.Target.OFFSET_BITS: AnswerTypes.Int(self.num_bits_offset, unit="bits"),
|
|
45
|
+
self.Target.VPN_BITS: AnswerTypes.Int(self.num_bits_vpn, unit="bits")
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
# Select what kind of question we are going to be
|
|
@@ -233,6 +233,7 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
233
233
|
number_of_hits = 0
|
|
234
234
|
for (request_number, request) in enumerate(self.requests):
|
|
235
235
|
was_hit, evicted, cache_state = self.cache.query_cache(request, request_number)
|
|
236
|
+
log.debug(f"cache_state: \"{cache_state}\"")
|
|
236
237
|
if was_hit:
|
|
237
238
|
number_of_hits += 1
|
|
238
239
|
self.request_results[request_number] = {
|
|
@@ -244,24 +245,17 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
244
245
|
|
|
245
246
|
self.answers.update(
|
|
246
247
|
{
|
|
247
|
-
f"answer__hit-{request_number}":
|
|
248
|
-
|
|
249
|
-
),
|
|
250
|
-
f"answer__evicted-{request_number}": Answer.string(
|
|
251
|
-
f"answer__evicted-{request_number}", ('-' if evicted is None else f"{evicted}")
|
|
252
|
-
),
|
|
253
|
-
f"answer__cache_state-{request_number}": Answer.list_value(
|
|
254
|
-
f"answer__cache_state-{request_number}", copy.copy(cache_state)
|
|
255
|
-
),
|
|
248
|
+
f"answer__hit-{request_number}": AnswerTypes.String(('hit' if was_hit else 'miss')),
|
|
249
|
+
f"answer__evicted-{request_number}": AnswerTypes.String(('-' if evicted is None else f"{evicted}")),
|
|
250
|
+
f"answer__cache_state-{request_number}": AnswerTypes.List(value=copy.copy(cache_state), order_matters=True),
|
|
256
251
|
}
|
|
257
252
|
)
|
|
258
253
|
|
|
259
254
|
self.hit_rate = 100 * number_of_hits / (self.num_requests)
|
|
260
255
|
self.answers.update(
|
|
261
256
|
{
|
|
262
|
-
"answer__hit_rate":
|
|
263
|
-
"
|
|
264
|
-
label=f"Hit rate, excluding non-capacity misses. Round to {Answer.DEFAULT_ROUNDING_DIGITS} decimal digits if appropriate.",
|
|
257
|
+
"answer__hit_rate": AnswerTypes.Float(self.hit_rate,
|
|
258
|
+
label=f"Hit rate, excluding non-capacity misses",
|
|
265
259
|
unit="%"
|
|
266
260
|
)
|
|
267
261
|
}
|
|
@@ -399,13 +393,12 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
399
393
|
self.virtual_address = self.rng.randint(1, int(self.bounds / self.PROBABILITY_OF_VALID))
|
|
400
394
|
|
|
401
395
|
if self.virtual_address < self.bounds:
|
|
402
|
-
self.answers["answer"] =
|
|
403
|
-
"answer__physical_address",
|
|
396
|
+
self.answers["answer"] = AnswerTypes.Hex(
|
|
404
397
|
self.base + self.virtual_address,
|
|
405
398
|
length=math.ceil(math.log2(self.base + self.virtual_address))
|
|
406
399
|
)
|
|
407
400
|
else:
|
|
408
|
-
self.answers["answer"] =
|
|
401
|
+
self.answers["answer"] = AnswerTypes.String("INVALID")
|
|
409
402
|
|
|
410
403
|
def _get_body(self):
|
|
411
404
|
"""Build question body and collect answers."""
|
|
@@ -586,21 +579,15 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
586
579
|
|
|
587
580
|
# Set answers based on whether it's in bounds or not
|
|
588
581
|
if self.__within_bounds(self.segment, self.offset, self.bounds[self.segment]):
|
|
589
|
-
self.answers["answer__physical_address"] =
|
|
590
|
-
"answer__physical_address",
|
|
582
|
+
self.answers["answer__physical_address"] = AnswerTypes.Binary(
|
|
591
583
|
self.physical_address,
|
|
592
584
|
length=self.physical_bits,
|
|
593
585
|
label="Physical Address"
|
|
594
586
|
)
|
|
595
587
|
else:
|
|
596
|
-
self.answers["answer__physical_address"] =
|
|
597
|
-
"answer__physical_address",
|
|
598
|
-
"INVALID",
|
|
599
|
-
label="Physical Address"
|
|
600
|
-
)
|
|
588
|
+
self.answers["answer__physical_address"] = AnswerTypes.String("INVALID", label="Physical Address")
|
|
601
589
|
|
|
602
|
-
self.answers["answer__segment"] =
|
|
603
|
-
label="Segment name")
|
|
590
|
+
self.answers["answer__segment"] = AnswerTypes.String(self.segment, label="Segment name")
|
|
604
591
|
|
|
605
592
|
def _get_body(self):
|
|
606
593
|
"""Build question body and collect answers."""
|
|
@@ -825,29 +812,27 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
825
812
|
|
|
826
813
|
self.answers.update(
|
|
827
814
|
{
|
|
828
|
-
"answer__vpn":
|
|
829
|
-
"answer__offset":
|
|
830
|
-
"answer__pte":
|
|
815
|
+
"answer__vpn": AnswerTypes.Binary(self.vpn, length=self.num_bits_vpn, label="VPN"),
|
|
816
|
+
"answer__offset": AnswerTypes.Binary(self.offset, length=self.num_bits_offset, label="Offset"),
|
|
817
|
+
"answer__pte": AnswerTypes.Binary(self.pte, length=(self.num_bits_pfn + 1), label="PTE"),
|
|
831
818
|
}
|
|
832
819
|
)
|
|
833
820
|
|
|
834
821
|
if self.is_valid:
|
|
835
822
|
self.answers.update(
|
|
836
823
|
{
|
|
837
|
-
"answer__is_valid":
|
|
838
|
-
"answer__pfn":
|
|
839
|
-
"answer__physical_address":
|
|
840
|
-
"answer__physical_address", self.physical_address,
|
|
841
|
-
length=(self.num_bits_pfn + self.num_bits_offset), label="Physical Address"
|
|
824
|
+
"answer__is_valid": AnswerTypes.String("VALID", label="VALID or INVALID?"),
|
|
825
|
+
"answer__pfn": AnswerTypes.Binary(self.pfn, length=self.num_bits_pfn, label="PFN"),
|
|
826
|
+
"answer__physical_address": AnswerTypes.Binary(self.physical_address, length=(self.num_bits_pfn + self.num_bits_offset), label="Physical Address"
|
|
842
827
|
),
|
|
843
828
|
}
|
|
844
829
|
)
|
|
845
830
|
else:
|
|
846
831
|
self.answers.update(
|
|
847
832
|
{
|
|
848
|
-
"answer__is_valid":
|
|
849
|
-
"answer__pfn":
|
|
850
|
-
"answer__physical_address":
|
|
833
|
+
"answer__is_valid": AnswerTypes.String("INVALID", label="VALID or INVALID?"),
|
|
834
|
+
"answer__pfn": AnswerTypes.String("INVALID", label="PFN"),
|
|
835
|
+
"answer__physical_address": AnswerTypes.String("INVALID", label="Physical Address"),
|
|
851
836
|
}
|
|
852
837
|
)
|
|
853
838
|
|
|
@@ -1187,19 +1172,19 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1187
1172
|
|
|
1188
1173
|
# Set up answers
|
|
1189
1174
|
self.answers.update({
|
|
1190
|
-
"answer__pdi":
|
|
1175
|
+
"answer__pdi": AnswerTypes.Binary(self.pdi, length=self.num_bits_pdi,
|
|
1191
1176
|
label="PDI (Page Directory Index)"),
|
|
1192
|
-
"answer__pti":
|
|
1177
|
+
"answer__pti": AnswerTypes.Binary(self.pti, length=self.num_bits_pti,
|
|
1193
1178
|
label="PTI (Page Table Index)"),
|
|
1194
|
-
"answer__offset":
|
|
1179
|
+
"answer__offset": AnswerTypes.Binary(self.offset, length=self.num_bits_offset,
|
|
1195
1180
|
label="Offset"),
|
|
1196
|
-
"answer__pd_entry":
|
|
1181
|
+
"answer__pd_entry": AnswerTypes.Binary(self.pd_entry, length=(self.num_bits_pfn + 1),
|
|
1197
1182
|
label="PD Entry (from Page Directory)"),
|
|
1198
1183
|
"answer__pt_number": (
|
|
1199
|
-
|
|
1184
|
+
AnswerTypes.Binary(self.page_table_number, length=self.num_bits_pfn,
|
|
1200
1185
|
label="Page Table Number")
|
|
1201
1186
|
if self.pd_valid
|
|
1202
|
-
else
|
|
1187
|
+
else AnswerTypes.String("INVALID", label="Page Table Number")
|
|
1203
1188
|
),
|
|
1204
1189
|
})
|
|
1205
1190
|
|
|
@@ -1207,31 +1192,29 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1207
1192
|
# (regardless of whether that PTE is valid or invalid)
|
|
1208
1193
|
if self.pd_valid:
|
|
1209
1194
|
self.answers.update({
|
|
1210
|
-
"answer__pte":
|
|
1195
|
+
"answer__pte": AnswerTypes.Binary(self.pte, length=(self.num_bits_pfn + 1),
|
|
1211
1196
|
label="PTE (from Page Table)"),
|
|
1212
1197
|
})
|
|
1213
1198
|
else:
|
|
1214
1199
|
# If PD is invalid, student can't look up the page table
|
|
1215
1200
|
# Accept both "INVALID" (for consistency) and "N/A" (for accuracy)
|
|
1216
1201
|
self.answers.update({
|
|
1217
|
-
"answer__pte":
|
|
1202
|
+
"answer__pte": AnswerTypes.String(["INVALID", "N/A"], label="PTE (from Page Table)"),
|
|
1218
1203
|
})
|
|
1219
1204
|
|
|
1220
1205
|
# Validity, PFN, and Physical Address depend on BOTH levels being valid
|
|
1221
1206
|
if self.pd_valid and self.pt_valid:
|
|
1222
1207
|
self.answers.update({
|
|
1223
|
-
"answer__is_valid":
|
|
1224
|
-
"answer__pfn":
|
|
1225
|
-
"answer__physical_address":
|
|
1226
|
-
"answer__physical_address", self.physical_address,
|
|
1227
|
-
length=(self.num_bits_pfn + self.num_bits_offset), label="Physical Address"
|
|
1208
|
+
"answer__is_valid": AnswerTypes.String("VALID", label="VALID or INVALID?"),
|
|
1209
|
+
"answer__pfn": AnswerTypes.Binary(self.pfn, length=self.num_bits_pfn, label="PFN"),
|
|
1210
|
+
"answer__physical_address": AnswerTypes.Binary(self.physical_address, length=(self.num_bits_pfn + self.num_bits_offset), label="Physical Address"
|
|
1228
1211
|
),
|
|
1229
1212
|
})
|
|
1230
1213
|
else:
|
|
1231
1214
|
self.answers.update({
|
|
1232
|
-
"answer__is_valid":
|
|
1233
|
-
"answer__pfn":
|
|
1234
|
-
"answer__physical_address":
|
|
1215
|
+
"answer__is_valid": AnswerTypes.String("INVALID", label="VALID or INVALID?"),
|
|
1216
|
+
"answer__pfn": AnswerTypes.String("INVALID", label="PFN"),
|
|
1217
|
+
"answer__physical_address": AnswerTypes.String("INVALID", label="Physical Address"),
|
|
1235
1218
|
})
|
|
1236
1219
|
|
|
1237
1220
|
def _get_body(self, *args, **kwargs):
|
|
@@ -5,8 +5,8 @@ import abc
|
|
|
5
5
|
import difflib
|
|
6
6
|
import logging
|
|
7
7
|
|
|
8
|
-
from QuizGenerator.question import Question,
|
|
9
|
-
from QuizGenerator.contentast import ContentAST
|
|
8
|
+
from QuizGenerator.question import Question, QuestionRegistry
|
|
9
|
+
from QuizGenerator.contentast import ContentAST, AnswerTypes
|
|
10
10
|
from QuizGenerator.mixins import TableQuestionMixin, BodyTemplatesMixin
|
|
11
11
|
|
|
12
12
|
log = logging.getLogger(__name__)
|
|
@@ -36,22 +36,10 @@ class HardDriveAccessTime(IOQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
36
36
|
self.disk_access_delay = self.access_delay * self.number_of_reads + self.transfer_delay
|
|
37
37
|
|
|
38
38
|
self.answers.update({
|
|
39
|
-
"answer__rotational_delay":
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
),
|
|
43
|
-
"answer__access_delay": Answer.float_value(
|
|
44
|
-
"answer__access_delay",
|
|
45
|
-
self.access_delay
|
|
46
|
-
),
|
|
47
|
-
"answer__transfer_delay": Answer.float_value(
|
|
48
|
-
"answer__transfer_delay",
|
|
49
|
-
self.transfer_delay
|
|
50
|
-
),
|
|
51
|
-
"answer__disk_access_delay": Answer.float_value(
|
|
52
|
-
"answer__disk_access_delay",
|
|
53
|
-
self.disk_access_delay
|
|
54
|
-
),
|
|
39
|
+
"answer__rotational_delay" : AnswerTypes.Float(self.rotational_delay),
|
|
40
|
+
"answer__access_delay" : AnswerTypes.Float(self.access_delay),
|
|
41
|
+
"answer__transfer_delay" : AnswerTypes.Float(self.transfer_delay),
|
|
42
|
+
"answer__disk_access_delay" : AnswerTypes.Float(self.disk_access_delay),
|
|
55
43
|
})
|
|
56
44
|
|
|
57
45
|
def _get_body(self, *args, **kwargs):
|
|
@@ -190,10 +178,10 @@ class INodeAccesses(IOQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
190
178
|
self.inode_index_in_block = int(self.inode_address_in_block / self.inode_size)
|
|
191
179
|
|
|
192
180
|
self.answers.update({
|
|
193
|
-
"answer__inode_address":
|
|
194
|
-
"answer__inode_block":
|
|
195
|
-
"answer__inode_address_in_block":
|
|
196
|
-
"answer__inode_index_in_block":
|
|
181
|
+
"answer__inode_address": AnswerTypes.Int(self.inode_address),
|
|
182
|
+
"answer__inode_block": AnswerTypes.Int(self.inode_block),
|
|
183
|
+
"answer__inode_address_in_block": AnswerTypes.Int(self.inode_address_in_block),
|
|
184
|
+
"answer__inode_index_in_block": AnswerTypes.Int(self.inode_index_in_block),
|
|
197
185
|
})
|
|
198
186
|
|
|
199
187
|
def _get_body(self):
|
|
@@ -334,7 +322,7 @@ class VSFS_states(IOQuestion):
|
|
|
334
322
|
|
|
335
323
|
def __init__(self, *args, **kwargs):
|
|
336
324
|
super().__init__(*args, **kwargs)
|
|
337
|
-
self.answer_kind = Answer.
|
|
325
|
+
self.answer_kind = ContentAST.Answer.CanvasAnswerKind.MULTIPLE_DROPDOWN
|
|
338
326
|
|
|
339
327
|
self.num_steps = kwargs.get("num_steps", 10)
|
|
340
328
|
|
|
@@ -356,11 +344,8 @@ class VSFS_states(IOQuestion):
|
|
|
356
344
|
))
|
|
357
345
|
self.rng.shuffle(wrong_answers)
|
|
358
346
|
|
|
359
|
-
self.answers["answer__cmd"] = Answer(
|
|
360
|
-
"answer__cmd",
|
|
347
|
+
self.answers["answer__cmd"] = ContentAST.Answer.dropdown(
|
|
361
348
|
f"{operations[-1]['cmd']}",
|
|
362
|
-
kind=Answer.AnswerKind.MULTIPLE_DROPDOWN,
|
|
363
|
-
correct=True,
|
|
364
349
|
baffles=list(set([op['cmd'] for op in operations[:-1] if op != operations[-1]['cmd']])),
|
|
365
350
|
label="Command"
|
|
366
351
|
)
|
|
@@ -13,8 +13,8 @@ from typing import List
|
|
|
13
13
|
|
|
14
14
|
import matplotlib.pyplot as plt
|
|
15
15
|
|
|
16
|
-
from QuizGenerator.contentast import ContentAST
|
|
17
|
-
from QuizGenerator.question import Question,
|
|
16
|
+
from QuizGenerator.contentast import ContentAST, AnswerTypes
|
|
17
|
+
from QuizGenerator.question import Question, QuestionRegistry, RegenerableChoiceMixin
|
|
18
18
|
from QuizGenerator.mixins import TableQuestionMixin, BodyTemplatesMixin
|
|
19
19
|
|
|
20
20
|
log = logging.getLogger(__name__)
|
|
@@ -356,23 +356,15 @@ class SchedulingQuestion(ProcessQuestion, RegenerableChoiceMixin, TableQuestionM
|
|
|
356
356
|
|
|
357
357
|
for job_id in sorted(self.job_stats.keys()):
|
|
358
358
|
self.answers.update({
|
|
359
|
-
f"answer__response_time_job{job_id}":
|
|
360
|
-
|
|
361
|
-
self.job_stats[job_id]["Response"]
|
|
362
|
-
),
|
|
363
|
-
f"answer__turnaround_time_job{job_id}": Answer.auto_float(
|
|
364
|
-
f"answer__turnaround_time_job{job_id}",
|
|
365
|
-
self.job_stats[job_id]["TAT"]
|
|
366
|
-
),
|
|
359
|
+
f"answer__response_time_job{job_id}": AnswerTypes.Float(self.job_stats[job_id]["Response"]),
|
|
360
|
+
f"answer__turnaround_time_job{job_id}": AnswerTypes.Float(self.job_stats[job_id]["TAT"]),
|
|
367
361
|
})
|
|
368
362
|
self.answers.update({
|
|
369
|
-
"answer__average_response_time":
|
|
370
|
-
"answer__average_response_time",
|
|
363
|
+
"answer__average_response_time": AnswerTypes.Float(
|
|
371
364
|
sum([job.response_time for job in jobs]) / len(jobs),
|
|
372
365
|
label="Overall average response time"
|
|
373
366
|
),
|
|
374
|
-
"answer__average_turnaround_time":
|
|
375
|
-
"answer__average_turnaround_time",
|
|
367
|
+
"answer__average_turnaround_time": AnswerTypes.Float(
|
|
376
368
|
sum([job.turnaround_time for job in jobs]) / len(jobs),
|
|
377
369
|
label="Overall average TAT"
|
|
378
370
|
)
|
|
@@ -388,7 +380,7 @@ class SchedulingQuestion(ProcessQuestion, RegenerableChoiceMixin, TableQuestionM
|
|
|
388
380
|
Tuple of (body_ast, answers_list)
|
|
389
381
|
"""
|
|
390
382
|
from typing import List
|
|
391
|
-
answers: List[Answer] = []
|
|
383
|
+
answers: List[ContentAST.Answer] = []
|
|
392
384
|
|
|
393
385
|
# Create table data for scheduling results
|
|
394
386
|
table_rows = []
|
|
@@ -427,7 +419,7 @@ class SchedulingQuestion(ProcessQuestion, RegenerableChoiceMixin, TableQuestionM
|
|
|
427
419
|
)
|
|
428
420
|
|
|
429
421
|
instructions = ContentAST.OnlyHtml([ContentAST.Paragraph([
|
|
430
|
-
f"Please format answer as fractions, mixed numbers, or numbers rounded to a maximum of {Answer.DEFAULT_ROUNDING_DIGITS} digits after the decimal. "
|
|
422
|
+
f"Please format answer as fractions, mixed numbers, or numbers rounded to a maximum of {ContentAST.Answer.DEFAULT_ROUNDING_DIGITS} digits after the decimal. "
|
|
431
423
|
"Examples of appropriately formatted answers would be `0`, `3/2`, `1 1/3`, `1.6667`, and `1.25`. "
|
|
432
424
|
"Note that answers that can be rounded to whole numbers should be, rather than being left in fractional form."
|
|
433
425
|
])])
|
|
@@ -915,16 +907,13 @@ class MLFQQuestion(ProcessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
915
907
|
|
|
916
908
|
for job_id in sorted(self.job_stats.keys()):
|
|
917
909
|
self.answers.update({
|
|
918
|
-
f"answer__turnaround_time_job{job_id}":
|
|
919
|
-
f"answer__turnaround_time_job{job_id}",
|
|
920
|
-
self.job_stats[job_id]["TAT"]
|
|
921
|
-
)
|
|
910
|
+
f"answer__turnaround_time_job{job_id}": AnswerTypes.Float(self.job_stats[job_id]["TAT"])
|
|
922
911
|
})
|
|
923
912
|
|
|
924
913
|
return self.is_interesting()
|
|
925
914
|
|
|
926
915
|
def _get_body(self, *args, **kwargs):
|
|
927
|
-
answers: List[Answer] = []
|
|
916
|
+
answers: List[ContentAST.Answer] = []
|
|
928
917
|
|
|
929
918
|
queue_rows = []
|
|
930
919
|
for i in reversed(range(self.num_queues)):
|
|
@@ -964,7 +953,7 @@ class MLFQQuestion(ProcessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
964
953
|
|
|
965
954
|
instructions = (
|
|
966
955
|
f"Compute the turnaround time (TAT) for each job. "
|
|
967
|
-
f"Round to at most {Answer.DEFAULT_ROUNDING_DIGITS} digits after the decimal."
|
|
956
|
+
f"Round to at most {ContentAST.Answer.DEFAULT_ROUNDING_DIGITS} digits after the decimal."
|
|
968
957
|
)
|
|
969
958
|
|
|
970
959
|
body = ContentAST.Section()
|
|
@@ -5,8 +5,8 @@ import logging
|
|
|
5
5
|
from typing import List, Tuple
|
|
6
6
|
import sympy as sp
|
|
7
7
|
|
|
8
|
-
from QuizGenerator.contentast import ContentAST
|
|
9
|
-
from QuizGenerator.question import Question,
|
|
8
|
+
from QuizGenerator.contentast import ContentAST, AnswerTypes
|
|
9
|
+
from QuizGenerator.question import Question, QuestionRegistry
|
|
10
10
|
from .misc import generate_function, format_vector
|
|
11
11
|
|
|
12
12
|
log = logging.getLogger(__name__)
|
|
@@ -57,7 +57,7 @@ class DerivativeQuestion(Question, abc.ABC):
|
|
|
57
57
|
# Use auto_float for Canvas compatibility with integers and decimals
|
|
58
58
|
# Label includes the partial derivative notation
|
|
59
59
|
label = f"∂f/∂x_{i} at ({eval_point_str})"
|
|
60
|
-
self.answers[answer_key] =
|
|
60
|
+
self.answers[answer_key] = AnswerTypes.Float(gradient_value, label=label)
|
|
61
61
|
|
|
62
62
|
def _create_gradient_vector_answer(self) -> None:
|
|
63
63
|
"""Create a single gradient vector answer for PDF format."""
|
|
@@ -75,9 +75,9 @@ class DerivativeQuestion(Question, abc.ABC):
|
|
|
75
75
|
|
|
76
76
|
# Format as vector for display using consistent formatting
|
|
77
77
|
vector_str = format_vector(gradient_values)
|
|
78
|
-
self.answers["gradient_vector"] =
|
|
78
|
+
self.answers["gradient_vector"] = AnswerTypes.String(vector_str, pdf_only=True)
|
|
79
79
|
|
|
80
|
-
def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
|
|
80
|
+
def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
|
|
81
81
|
"""Build question body and collect answers."""
|
|
82
82
|
body = ContentAST.Section()
|
|
83
83
|
answers = []
|
|
@@ -131,7 +131,7 @@ class DerivativeQuestion(Question, abc.ABC):
|
|
|
131
131
|
body, _ = self._get_body(**kwargs)
|
|
132
132
|
return body
|
|
133
133
|
|
|
134
|
-
def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
|
|
134
|
+
def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
|
|
135
135
|
"""Build question explanation."""
|
|
136
136
|
explanation = ContentAST.Section()
|
|
137
137
|
|
|
@@ -162,14 +162,14 @@ class DerivativeQuestion(Question, abc.ABC):
|
|
|
162
162
|
partial_expr = self.gradient_function[i]
|
|
163
163
|
partial_value = partial_expr.subs(subs_map)
|
|
164
164
|
|
|
165
|
-
# Use Answer.accepted_strings for clean numerical formatting
|
|
165
|
+
# Use ContentAST.Answer.accepted_strings for clean numerical formatting
|
|
166
166
|
try:
|
|
167
167
|
numerical_value = float(partial_value)
|
|
168
168
|
except (TypeError, ValueError):
|
|
169
169
|
numerical_value = float(partial_value.evalf())
|
|
170
170
|
|
|
171
171
|
# Get clean string representation
|
|
172
|
-
clean_value = sorted(Answer.accepted_strings(numerical_value), key=lambda s: len(s))[0]
|
|
172
|
+
clean_value = sorted(ContentAST.Answer.accepted_strings(numerical_value), key=lambda s: len(s))[0]
|
|
173
173
|
|
|
174
174
|
explanation.add_element(
|
|
175
175
|
ContentAST.Paragraph([
|
|
@@ -284,7 +284,7 @@ class DerivativeChain(DerivativeQuestion):
|
|
|
284
284
|
f = sp.Function('f')
|
|
285
285
|
self.equation = sp.Eq(f(*self.variables), self.function)
|
|
286
286
|
|
|
287
|
-
def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
|
|
287
|
+
def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
|
|
288
288
|
"""Build question explanation."""
|
|
289
289
|
explanation = ContentAST.Section()
|
|
290
290
|
|
|
@@ -369,14 +369,14 @@ class DerivativeChain(DerivativeQuestion):
|
|
|
369
369
|
partial_expr = self.gradient_function[i]
|
|
370
370
|
partial_value = partial_expr.subs(subs_map)
|
|
371
371
|
|
|
372
|
-
# Use Answer.accepted_strings for clean numerical formatting
|
|
372
|
+
# Use ContentAST.Answer.accepted_strings for clean numerical formatting
|
|
373
373
|
try:
|
|
374
374
|
numerical_value = float(partial_value)
|
|
375
375
|
except (TypeError, ValueError):
|
|
376
376
|
numerical_value = float(partial_value.evalf())
|
|
377
377
|
|
|
378
378
|
# Get clean string representation
|
|
379
|
-
clean_value = sorted(Answer.accepted_strings(numerical_value), key=lambda s: len(s))[0]
|
|
379
|
+
clean_value = sorted(ContentAST.Answer.accepted_strings(numerical_value), key=lambda s: len(s))[0]
|
|
380
380
|
|
|
381
381
|
explanation.add_element(
|
|
382
382
|
ContentAST.Paragraph([
|
|
@@ -6,8 +6,8 @@ import math
|
|
|
6
6
|
from typing import List, Tuple, Callable, Union, Any
|
|
7
7
|
import sympy as sp
|
|
8
8
|
|
|
9
|
-
from QuizGenerator.contentast import ContentAST
|
|
10
|
-
from QuizGenerator.question import Question,
|
|
9
|
+
from QuizGenerator.contentast import ContentAST, AnswerTypes
|
|
10
|
+
from QuizGenerator.question import Question, QuestionRegistry
|
|
11
11
|
from QuizGenerator.mixins import TableQuestionMixin, BodyTemplatesMixin
|
|
12
12
|
|
|
13
13
|
from .misc import generate_function, format_vector
|
|
@@ -98,17 +98,17 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
|
|
|
98
98
|
|
|
99
99
|
# Location answer
|
|
100
100
|
location_key = f"answer__location_{step}"
|
|
101
|
-
self.answers[location_key] =
|
|
101
|
+
self.answers[location_key] = AnswerTypes.Vector(list(result['location']), label=f"Location at step {step}")
|
|
102
102
|
|
|
103
103
|
# Gradient answer
|
|
104
104
|
gradient_key = f"answer__gradient_{step}"
|
|
105
|
-
self.answers[gradient_key] =
|
|
105
|
+
self.answers[gradient_key] = AnswerTypes.Vector(list(result['gradient']), label=f"Gradient at step {step}")
|
|
106
106
|
|
|
107
107
|
# Update answer
|
|
108
108
|
update_key = f"answer__update_{step}"
|
|
109
|
-
self.answers[update_key] =
|
|
109
|
+
self.answers[update_key] = AnswerTypes.Vector(list(result['update']), label=f"Update at step {step}")
|
|
110
110
|
|
|
111
|
-
def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
|
|
111
|
+
def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
|
|
112
112
|
"""Build question body and collect answers."""
|
|
113
113
|
body = ContentAST.Section()
|
|
114
114
|
answers = []
|
|
@@ -179,7 +179,7 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
|
|
|
179
179
|
body, _ = self._get_body(**kwargs)
|
|
180
180
|
return body
|
|
181
181
|
|
|
182
|
-
def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
|
|
182
|
+
def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
|
|
183
183
|
"""Build question explanation."""
|
|
184
184
|
explanation = ContentAST.Section()
|
|
185
185
|
|
|
@@ -6,8 +6,8 @@ import math
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
from typing import List, Tuple, Dict, Any
|
|
8
8
|
|
|
9
|
-
from QuizGenerator.contentast import ContentAST
|
|
10
|
-
from QuizGenerator.question import Question,
|
|
9
|
+
from QuizGenerator.contentast import ContentAST, AnswerTypes
|
|
10
|
+
from QuizGenerator.question import Question, QuestionRegistry
|
|
11
11
|
from QuizGenerator.mixins import TableQuestionMixin, BodyTemplatesMixin
|
|
12
12
|
|
|
13
13
|
log = logging.getLogger(__name__)
|
|
@@ -73,12 +73,12 @@ class LossQuestion(Question, TableQuestionMixin, BodyTemplatesMixin, abc.ABC):
|
|
|
73
73
|
|
|
74
74
|
# Individual loss answers
|
|
75
75
|
for i in range(self.num_samples):
|
|
76
|
-
self.answers[f"loss_{i}"] =
|
|
76
|
+
self.answers[f"loss_{i}"] = AnswerTypes.Float(self.individual_losses[i], label=f"Sample {i + 1} loss")
|
|
77
77
|
|
|
78
78
|
# Overall loss answer
|
|
79
|
-
self.answers["overall_loss"] =
|
|
79
|
+
self.answers["overall_loss"] = AnswerTypes.Float(self.overall_loss, label="Overall loss")
|
|
80
80
|
|
|
81
|
-
def _get_body(self, **kwargs) -> Tuple[ContentAST.Element, List[Answer]]:
|
|
81
|
+
def _get_body(self, **kwargs) -> Tuple[ContentAST.Element, List[ContentAST.Answer]]:
|
|
82
82
|
"""Build question body and collect answers."""
|
|
83
83
|
body = ContentAST.Section()
|
|
84
84
|
answers = []
|
|
@@ -115,7 +115,7 @@ class LossQuestion(Question, TableQuestionMixin, BodyTemplatesMixin, abc.ABC):
|
|
|
115
115
|
"""Create the data table with answer fields."""
|
|
116
116
|
pass
|
|
117
117
|
|
|
118
|
-
def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Element, List[Answer]]:
|
|
118
|
+
def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Element, List[ContentAST.Answer]]:
|
|
119
119
|
"""Build question explanation."""
|
|
120
120
|
explanation = ContentAST.Section()
|
|
121
121
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
from typing import List, Tuple, Callable, Union, Any
|
|
3
3
|
import sympy as sp
|
|
4
4
|
|
|
5
|
-
from QuizGenerator.
|
|
5
|
+
from QuizGenerator.contentast import ContentAST
|
|
6
6
|
|
|
7
7
|
def generate_function(rng, num_variables: int, max_degree: int, use_quadratic: bool = True) -> tuple[Any, sp.Expr, sp.MutableDenseMatrix, sp.Equality]:
|
|
8
8
|
"""
|
|
@@ -61,7 +61,7 @@ def format_vector(vec: List[float]) -> str:
|
|
|
61
61
|
|
|
62
62
|
vector_string = ', '.join(
|
|
63
63
|
[
|
|
64
|
-
sorted(Answer.accepted_strings(v), key=lambda s: len(s))[0]
|
|
64
|
+
sorted(ContentAST.Answer.accepted_strings(v), key=lambda s: len(s))[0]
|
|
65
65
|
for v in vec
|
|
66
66
|
]
|
|
67
67
|
)
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import abc
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
|
-
from QuizGenerator.question import Question, QuestionRegistry
|
|
6
|
-
from QuizGenerator.contentast import ContentAST
|
|
5
|
+
from QuizGenerator.question import Question, QuestionRegistry
|
|
6
|
+
from QuizGenerator.contentast import ContentAST, AnswerTypes
|
|
7
7
|
from QuizGenerator.mixins import MathOperationQuestion
|
|
8
8
|
|
|
9
9
|
log = logging.getLogger(__name__)
|
|
@@ -21,7 +21,7 @@ class MatrixMathQuestion(MathOperationQuestion, Question):
|
|
|
21
21
|
- ContentAST.Matrix for mathematical matrices
|
|
22
22
|
- ContentAST.Equation.make_block_equation__multiline_equals for step-by-step solutions
|
|
23
23
|
- ContentAST.OnlyHtml for Canvas-specific content
|
|
24
|
-
- Answer.integer for numerical answers
|
|
24
|
+
- ContentAST.Answer.integer for numerical answers
|
|
25
25
|
"""
|
|
26
26
|
def __init__(self, *args, **kwargs):
|
|
27
27
|
kwargs["topic"] = kwargs.get("topic", Question.Topic.MATH)
|
|
@@ -169,7 +169,7 @@ class MatrixAddition(MatrixMathQuestion):
|
|
|
169
169
|
for i in range(rows):
|
|
170
170
|
for j in range(cols):
|
|
171
171
|
answer_key = f"answer_{i}_{j}"
|
|
172
|
-
self.answers[answer_key] =
|
|
172
|
+
self.answers[answer_key] = AnswerTypes.Int(result[i][j])
|
|
173
173
|
else:
|
|
174
174
|
# For multipart questions, use subpart letter format
|
|
175
175
|
letter = chr(ord('a') + subpart_index)
|
|
@@ -178,7 +178,7 @@ class MatrixAddition(MatrixMathQuestion):
|
|
|
178
178
|
for i in range(rows):
|
|
179
179
|
for j in range(cols):
|
|
180
180
|
answer_key = f"subpart_{letter}_{i}_{j}"
|
|
181
|
-
self.answers[answer_key] =
|
|
181
|
+
self.answers[answer_key] = AnswerTypes.Int(result[i][j])
|
|
182
182
|
|
|
183
183
|
def refresh(self, *args, **kwargs):
|
|
184
184
|
"""Override refresh to set rows/cols for compatibility."""
|
|
@@ -299,7 +299,7 @@ class MatrixScalarMultiplication(MatrixMathQuestion):
|
|
|
299
299
|
for i in range(rows):
|
|
300
300
|
for j in range(cols):
|
|
301
301
|
answer_key = f"answer_{i}_{j}"
|
|
302
|
-
self.answers[answer_key] =
|
|
302
|
+
self.answers[answer_key] = AnswerTypes.Int(result[i][j])
|
|
303
303
|
else:
|
|
304
304
|
# For multipart questions, use subpart letter format
|
|
305
305
|
letter = chr(ord('a') + subpart_index)
|
|
@@ -308,7 +308,7 @@ class MatrixScalarMultiplication(MatrixMathQuestion):
|
|
|
308
308
|
for i in range(rows):
|
|
309
309
|
for j in range(cols):
|
|
310
310
|
answer_key = f"subpart_{letter}_{i}_{j}"
|
|
311
|
-
self.answers[answer_key] =
|
|
311
|
+
self.answers[answer_key] = AnswerTypes.Int(result[i][j])
|
|
312
312
|
|
|
313
313
|
def refresh(self, *args, **kwargs):
|
|
314
314
|
"""Override refresh to handle different scalars per subpart."""
|
|
@@ -501,31 +501,27 @@ class MatrixMultiplication(MatrixMathQuestion):
|
|
|
501
501
|
# For single questions, use the old answer format
|
|
502
502
|
# Dimension answers
|
|
503
503
|
if result is not None:
|
|
504
|
-
self.answers["result_rows"] =
|
|
505
|
-
|
|
506
|
-
self.answers["result_cols"] = Answer.integer("result_cols", self.result_cols,
|
|
507
|
-
label="Number of columns in result")
|
|
504
|
+
self.answers["result_rows"] = AnswerTypes.Int(self.result_rows, label="Number of rows in result")
|
|
505
|
+
self.answers["result_cols"] = AnswerTypes.Int(self.result_cols, label="Number of columns in result")
|
|
508
506
|
|
|
509
507
|
# Matrix element answers
|
|
510
508
|
for i in range(self.max_dim):
|
|
511
509
|
for j in range(self.max_dim):
|
|
512
510
|
answer_key = f"answer_{i}_{j}"
|
|
513
511
|
if i < self.result_rows and j < self.result_cols:
|
|
514
|
-
self.answers[answer_key] =
|
|
512
|
+
self.answers[answer_key] = AnswerTypes.Int(result[i][j])
|
|
515
513
|
else:
|
|
516
|
-
self.answers[answer_key] =
|
|
514
|
+
self.answers[answer_key] = AnswerTypes.String("-")
|
|
517
515
|
else:
|
|
518
516
|
# Multiplication not possible
|
|
519
|
-
self.answers["result_rows"] =
|
|
520
|
-
|
|
521
|
-
self.answers["result_cols"] = Answer.string("result_cols", "-",
|
|
522
|
-
label="Number of columns in result")
|
|
517
|
+
self.answers["result_rows"] = AnswerTypes.String("-", label="Number of rows in result")
|
|
518
|
+
self.answers["result_cols"] = AnswerTypes.String("-", label="Number of columns in result")
|
|
523
519
|
|
|
524
520
|
# All matrix elements are "-"
|
|
525
521
|
for i in range(self.max_dim):
|
|
526
522
|
for j in range(self.max_dim):
|
|
527
523
|
answer_key = f"answer_{i}_{j}"
|
|
528
|
-
self.answers[answer_key] =
|
|
524
|
+
self.answers[answer_key] = AnswerTypes.String("-")
|
|
529
525
|
else:
|
|
530
526
|
# For multipart questions, use subpart letter format
|
|
531
527
|
letter = chr(ord('a') + subpart_index)
|
|
@@ -537,7 +533,7 @@ class MatrixMultiplication(MatrixMathQuestion):
|
|
|
537
533
|
for i in range(rows):
|
|
538
534
|
for j in range(cols):
|
|
539
535
|
answer_key = f"subpart_{letter}_{i}_{j}"
|
|
540
|
-
self.answers[answer_key] =
|
|
536
|
+
self.answers[answer_key] = AnswerTypes.Int(result[i][j])
|
|
541
537
|
|
|
542
538
|
def _add_single_question_answers(self, body):
|
|
543
539
|
"""Add Canvas-only answer fields for MatrixMultiplication with dash instruction.
|