QuizGenerator 0.6.3__py3-none-any.whl → 0.7.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 +2191 -2193
  2. QuizGenerator/misc.py +1 -1
  3. QuizGenerator/mixins.py +64 -64
  4. QuizGenerator/premade_questions/basic.py +16 -16
  5. QuizGenerator/premade_questions/cst334/languages.py +26 -26
  6. QuizGenerator/premade_questions/cst334/math_questions.py +42 -42
  7. QuizGenerator/premade_questions/cst334/memory_questions.py +124 -124
  8. QuizGenerator/premade_questions/cst334/persistence_questions.py +48 -48
  9. QuizGenerator/premade_questions/cst334/process.py +38 -38
  10. QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +45 -45
  11. QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +34 -34
  12. QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +53 -53
  13. QuizGenerator/premade_questions/cst463/gradient_descent/misc.py +2 -2
  14. QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +65 -65
  15. QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +39 -39
  16. QuizGenerator/premade_questions/cst463/models/attention.py +36 -36
  17. QuizGenerator/premade_questions/cst463/models/cnns.py +26 -26
  18. QuizGenerator/premade_questions/cst463/models/rnns.py +36 -36
  19. QuizGenerator/premade_questions/cst463/models/text.py +32 -32
  20. QuizGenerator/premade_questions/cst463/models/weight_counting.py +15 -15
  21. QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +124 -124
  22. QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +161 -161
  23. QuizGenerator/question.py +41 -41
  24. QuizGenerator/quiz.py +7 -7
  25. QuizGenerator/regenerate.py +114 -13
  26. QuizGenerator/typst_utils.py +2 -2
  27. {quizgenerator-0.6.3.dist-info → quizgenerator-0.7.1.dist-info}/METADATA +1 -1
  28. {quizgenerator-0.6.3.dist-info → quizgenerator-0.7.1.dist-info}/RECORD +31 -31
  29. {quizgenerator-0.6.3.dist-info → quizgenerator-0.7.1.dist-info}/WHEEL +0 -0
  30. {quizgenerator-0.6.3.dist-info → quizgenerator-0.7.1.dist-info}/entry_points.txt +0 -0
  31. {quizgenerator-0.6.3.dist-info → quizgenerator-0.7.1.dist-info}/licenses/LICENSE +0 -0
@@ -6,7 +6,7 @@ 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, AnswerTypes
9
+ import QuizGenerator.contentast as ca
10
10
  from QuizGenerator.question import Question, QuestionRegistry
11
11
  from QuizGenerator.mixins import TableQuestionMixin, BodyTemplatesMixin
12
12
 
@@ -73,18 +73,18 @@ 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}"] = AnswerTypes.Float(self.individual_losses[i], label=f"Sample {i + 1} loss")
76
+ self.answers[f"loss_{i}"] = ca.AnswerTypes.Float(self.individual_losses[i], label=f"Sample {i + 1} loss")
77
77
 
78
78
  # Overall loss answer
79
- self.answers["overall_loss"] = AnswerTypes.Float(self.overall_loss, label="Overall loss")
79
+ self.answers["overall_loss"] = ca.AnswerTypes.Float(self.overall_loss, label="Overall loss")
80
80
 
81
- def _get_body(self, **kwargs) -> Tuple[ContentAST.Element, List[ContentAST.Answer]]:
81
+ def _get_body(self, **kwargs) -> Tuple[ca.Element, List[ca.Answer]]:
82
82
  """Build question body and collect answers."""
83
- body = ContentAST.Section()
83
+ body = ca.Section()
84
84
  answers = []
85
85
 
86
86
  # Question description
87
- body.add_element(ContentAST.Paragraph([
87
+ body.add_element(ca.Paragraph([
88
88
  f"Given the dataset below, calculate the {self._get_loss_function_short_name()} for each sample "
89
89
  f"and the overall {self._get_loss_function_short_name()}."
90
90
  ]))
@@ -97,7 +97,7 @@ class LossQuestion(Question, TableQuestionMixin, BodyTemplatesMixin, abc.ABC):
97
97
  answers.append(self.answers[f"loss_{i}"])
98
98
 
99
99
  # Overall loss question
100
- body.add_element(ContentAST.Paragraph([
100
+ body.add_element(ca.Paragraph([
101
101
  f"Overall {self._get_loss_function_short_name()}: "
102
102
  ]))
103
103
  answers.append(self.answers["overall_loss"])
@@ -105,31 +105,31 @@ class LossQuestion(Question, TableQuestionMixin, BodyTemplatesMixin, abc.ABC):
105
105
 
106
106
  return body, answers
107
107
 
108
- def get_body(self, **kwargs) -> ContentAST.Element:
108
+ def get_body(self, **kwargs) -> ca.Element:
109
109
  """Build question body (backward compatible interface)."""
110
110
  body, _ = self._get_body(**kwargs)
111
111
  return body
112
112
 
113
113
  @abc.abstractmethod
114
- def _create_data_table(self) -> ContentAST.Element:
114
+ def _create_data_table(self) -> ca.Element:
115
115
  """Create the data table with answer fields."""
116
116
  pass
117
117
 
118
- def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Element, List[ContentAST.Answer]]:
118
+ def _get_explanation(self, **kwargs) -> Tuple[ca.Element, List[ca.Answer]]:
119
119
  """Build question explanation."""
120
- explanation = ContentAST.Section()
120
+ explanation = ca.Section()
121
121
 
122
- explanation.add_element(ContentAST.Paragraph([
122
+ explanation.add_element(ca.Paragraph([
123
123
  f"To calculate the {self._get_loss_function_name()}, we apply the formula to each sample:"
124
124
  ]))
125
125
 
126
- explanation.add_element(ContentAST.Equation(self._get_loss_function_formula(), inline=False))
126
+ explanation.add_element(ca.Equation(self._get_loss_function_formula(), inline=False))
127
127
 
128
128
  # Step-by-step calculations
129
129
  explanation.add_element(self._create_calculation_steps())
130
130
 
131
131
  # Completed table
132
- explanation.add_element(ContentAST.Paragraph(["Completed table:"]))
132
+ explanation.add_element(ca.Paragraph(["Completed table:"]))
133
133
  explanation.add_element(self._create_completed_table())
134
134
 
135
135
  # Overall loss calculation
@@ -137,23 +137,23 @@ class LossQuestion(Question, TableQuestionMixin, BodyTemplatesMixin, abc.ABC):
137
137
 
138
138
  return explanation, []
139
139
 
140
- def get_explanation(self, **kwargs) -> ContentAST.Element:
140
+ def get_explanation(self, **kwargs) -> ca.Element:
141
141
  """Build question explanation (backward compatible interface)."""
142
142
  explanation, _ = self._get_explanation(**kwargs)
143
143
  return explanation
144
144
 
145
145
  @abc.abstractmethod
146
- def _create_calculation_steps(self) -> ContentAST.Element:
146
+ def _create_calculation_steps(self) -> ca.Element:
147
147
  """Create step-by-step calculation explanations."""
148
148
  pass
149
149
 
150
150
  @abc.abstractmethod
151
- def _create_completed_table(self) -> ContentAST.Element:
151
+ def _create_completed_table(self) -> ca.Element:
152
152
  """Create the completed table with all values filled in."""
153
153
  pass
154
154
 
155
155
  @abc.abstractmethod
156
- def _create_overall_loss_explanation(self) -> ContentAST.Element:
156
+ def _create_overall_loss_explanation(self) -> ca.Element:
157
157
  """Create explanation for overall loss calculation."""
158
158
  pass
159
159
 
@@ -225,7 +225,7 @@ class LossQuestion_Linear(LossQuestion):
225
225
  else:
226
226
  return r"L(\mathbf{y}, \mathbf{p}) = \sum_{i=1}^{k} (y_i - p_i)^2"
227
227
 
228
- def _create_data_table(self) -> ContentAST.Element:
228
+ def _create_data_table(self) -> ca.Element:
229
229
  """Create table with input features, true values, predictions, and loss fields."""
230
230
  headers = ["x"]
231
231
 
@@ -268,12 +268,12 @@ class LossQuestion_Linear(LossQuestion):
268
268
 
269
269
  return self.create_answer_table(headers, rows, answer_columns=["loss"])
270
270
 
271
- def _create_calculation_steps(self) -> ContentAST.Element:
271
+ def _create_calculation_steps(self) -> ca.Element:
272
272
  """Show step-by-step MSE calculations."""
273
- steps = ContentAST.Section()
273
+ steps = ca.Section()
274
274
 
275
275
  for i, sample in enumerate(self.data):
276
- steps.add_element(ContentAST.Paragraph([f"Sample {i+1}:"]))
276
+ steps.add_element(ca.Paragraph([f"Sample {i+1}:"]))
277
277
 
278
278
  if self.num_output_vars == 1:
279
279
  y = sample['true_values']
@@ -286,7 +286,7 @@ class LossQuestion_Linear(LossQuestion):
286
286
  calculation = f"L = ({y:.2f} - {p:.2f})^2 = ({diff:.2f})^2 = {loss:.4f}"
287
287
  else:
288
288
  calculation = f"L = ({y:.2f} - ({p:.2f}))^2 = ({diff:.2f})^2 = {loss:.4f}"
289
- steps.add_element(ContentAST.Equation(calculation, inline=False))
289
+ steps.add_element(ca.Equation(calculation, inline=False))
290
290
  else:
291
291
  # Multi-output calculation
292
292
  y_vals = sample['true_values']
@@ -302,11 +302,11 @@ class LossQuestion_Linear(LossQuestion):
302
302
  terms.append(f"({y:.2f} - ({p:.2f}))^2")
303
303
 
304
304
  calculation = f"L = {' + '.join(terms)} = {loss:.4f}"
305
- steps.add_element(ContentAST.Equation(calculation, inline=False))
305
+ steps.add_element(ca.Equation(calculation, inline=False))
306
306
 
307
307
  return steps
308
308
 
309
- def _create_completed_table(self) -> ContentAST.Element:
309
+ def _create_completed_table(self) -> ca.Element:
310
310
  """Create table with all values including calculated losses."""
311
311
  headers = ["x_0", "x_1"]
312
312
 
@@ -346,20 +346,20 @@ class LossQuestion_Linear(LossQuestion):
346
346
 
347
347
  rows.append(row)
348
348
 
349
- return ContentAST.Table(headers=headers, data=rows)
349
+ return ca.Table(headers=headers, data=rows)
350
350
 
351
- def _create_overall_loss_explanation(self) -> ContentAST.Element:
351
+ def _create_overall_loss_explanation(self) -> ca.Element:
352
352
  """Explain overall MSE calculation."""
353
- explanation = ContentAST.Section()
353
+ explanation = ca.Section()
354
354
 
355
- explanation.add_element(ContentAST.Paragraph([
355
+ explanation.add_element(ca.Paragraph([
356
356
  "The overall MSE is the average of individual losses:"
357
357
  ]))
358
358
 
359
359
  losses_str = " + ".join([f"{loss:.4f}" for loss in self.individual_losses])
360
360
  calculation = f"MSE = \\frac{{{losses_str}}}{{{self.num_samples}}} = {self.overall_loss:.4f}"
361
361
 
362
- explanation.add_element(ContentAST.Equation(calculation, inline=False))
362
+ explanation.add_element(ca.Equation(calculation, inline=False))
363
363
 
364
364
  return explanation
365
365
 
@@ -416,7 +416,7 @@ class LossQuestion_Logistic(LossQuestion):
416
416
  def _get_loss_function_formula(self) -> str:
417
417
  return r"L(y, p) = -[y \ln(p) + (1-y) \ln(1-p)]"
418
418
 
419
- def _create_data_table(self) -> ContentAST.Element:
419
+ def _create_data_table(self) -> ca.Element:
420
420
  """Create table with features, true labels, predicted probabilities, and loss fields."""
421
421
  headers = ["x", "y", "p", "loss"]
422
422
 
@@ -441,27 +441,27 @@ class LossQuestion_Logistic(LossQuestion):
441
441
 
442
442
  return self.create_answer_table(headers, rows, answer_columns=["loss"])
443
443
 
444
- def _create_calculation_steps(self) -> ContentAST.Element:
444
+ def _create_calculation_steps(self) -> ca.Element:
445
445
  """Show step-by-step log-loss calculations."""
446
- steps = ContentAST.Section()
446
+ steps = ca.Section()
447
447
 
448
448
  for i, sample in enumerate(self.data):
449
449
  y = sample['true_values']
450
450
  p = sample['predictions']
451
451
  loss = self.individual_losses[i]
452
452
 
453
- steps.add_element(ContentAST.Paragraph([f"Sample {i+1}:"]))
453
+ steps.add_element(ca.Paragraph([f"Sample {i+1}:"]))
454
454
 
455
455
  if y == 1:
456
456
  calculation = f"L = -[1 \\cdot \\ln({p:.3f}) + 0 \\cdot \\ln(1-{p:.3f})] = -\\ln({p:.3f}) = {loss:.4f}"
457
457
  else:
458
458
  calculation = f"L = -[0 \\cdot \\ln({p:.3f}) + 1 \\cdot \\ln(1-{p:.3f})] = -\\ln({1-p:.3f}) = {loss:.4f}"
459
459
 
460
- steps.add_element(ContentAST.Equation(calculation, inline=False))
460
+ steps.add_element(ca.Equation(calculation, inline=False))
461
461
 
462
462
  return steps
463
463
 
464
- def _create_completed_table(self) -> ContentAST.Element:
464
+ def _create_completed_table(self) -> ca.Element:
465
465
  """Create table with all values including calculated losses."""
466
466
  headers = ["x_0", "x_1", "y", "p", "loss"]
467
467
 
@@ -484,20 +484,20 @@ class LossQuestion_Logistic(LossQuestion):
484
484
 
485
485
  rows.append(row)
486
486
 
487
- return ContentAST.Table(headers=headers, data=rows)
487
+ return ca.Table(headers=headers, data=rows)
488
488
 
489
- def _create_overall_loss_explanation(self) -> ContentAST.Element:
489
+ def _create_overall_loss_explanation(self) -> ca.Element:
490
490
  """Explain overall log-loss calculation."""
491
- explanation = ContentAST.Section()
491
+ explanation = ca.Section()
492
492
 
493
- explanation.add_element(ContentAST.Paragraph([
493
+ explanation.add_element(ca.Paragraph([
494
494
  "The overall log-loss is the average of individual losses:"
495
495
  ]))
496
496
 
497
497
  losses_str = " + ".join([f"{loss:.4f}" for loss in self.individual_losses])
498
498
  calculation = f"\\text{{Log-Loss}} = \\frac{{{losses_str}}}{{{self.num_samples}}} = {self.overall_loss:.4f}"
499
499
 
500
- explanation.add_element(ContentAST.Equation(calculation, inline=False))
500
+ explanation.add_element(ca.Equation(calculation, inline=False))
501
501
 
502
502
  return explanation
503
503
 
@@ -560,7 +560,7 @@ class LossQuestion_MulticlassLogistic(LossQuestion):
560
560
  def _get_loss_function_formula(self) -> str:
561
561
  return r"L(\mathbf{y}, \mathbf{p}) = -\sum_{i=1}^{K} y_i \ln(p_i)"
562
562
 
563
- def _create_data_table(self) -> ContentAST.Element:
563
+ def _create_data_table(self) -> ca.Element:
564
564
  """Create table with features, true class vectors, predicted probabilities, and loss fields."""
565
565
  headers = ["x", "y", "p", "loss"]
566
566
 
@@ -587,22 +587,22 @@ class LossQuestion_MulticlassLogistic(LossQuestion):
587
587
 
588
588
  return self.create_answer_table(headers, rows, answer_columns=["loss"])
589
589
 
590
- def _create_calculation_steps(self) -> ContentAST.Element:
590
+ def _create_calculation_steps(self) -> ca.Element:
591
591
  """Show step-by-step cross-entropy calculations."""
592
- steps = ContentAST.Section()
592
+ steps = ca.Section()
593
593
 
594
594
  for i, sample in enumerate(self.data):
595
595
  y_vec = sample['true_values']
596
596
  p_vec = sample['predictions']
597
597
  loss = self.individual_losses[i]
598
598
 
599
- steps.add_element(ContentAST.Paragraph([f"Sample {i+1}:"]))
599
+ steps.add_element(ca.Paragraph([f"Sample {i+1}:"]))
600
600
 
601
601
  # Show vector dot product calculation
602
602
  y_str = "[" + ", ".join([str(y) for y in y_vec]) + "]"
603
603
  p_str = "[" + ", ".join([f"{p:.3f}" for p in p_vec]) + "]"
604
604
 
605
- steps.add_element(ContentAST.Paragraph([f"\\mathbf{{y}} = {y_str}, \\mathbf{{p}} = {p_str}"]))
605
+ steps.add_element(ca.Paragraph([f"\\mathbf{{y}} = {y_str}, \\mathbf{{p}} = {p_str}"]))
606
606
 
607
607
  # Find the true class (where y_i = 1)
608
608
  try:
@@ -622,11 +622,11 @@ class LossQuestion_MulticlassLogistic(LossQuestion):
622
622
  # Fallback in case no class is set to 1 (shouldn't happen, but safety check)
623
623
  calculation = f"L = -\\mathbf{{y}} \\cdot \\ln(\\mathbf{{p}}) = {loss:.4f}"
624
624
 
625
- steps.add_element(ContentAST.Equation(calculation, inline=False))
625
+ steps.add_element(ca.Equation(calculation, inline=False))
626
626
 
627
627
  return steps
628
628
 
629
- def _create_completed_table(self) -> ContentAST.Element:
629
+ def _create_completed_table(self) -> ca.Element:
630
630
  """Create table with all values including calculated losses."""
631
631
  headers = ["x_0", "x_1", "y", "p", "loss"]
632
632
 
@@ -651,19 +651,19 @@ class LossQuestion_MulticlassLogistic(LossQuestion):
651
651
 
652
652
  rows.append(row)
653
653
 
654
- return ContentAST.Table(headers=headers, data=rows)
654
+ return ca.Table(headers=headers, data=rows)
655
655
 
656
- def _create_overall_loss_explanation(self) -> ContentAST.Element:
656
+ def _create_overall_loss_explanation(self) -> ca.Element:
657
657
  """Explain overall cross-entropy loss calculation."""
658
- explanation = ContentAST.Section()
658
+ explanation = ca.Section()
659
659
 
660
- explanation.add_element(ContentAST.Paragraph([
660
+ explanation.add_element(ca.Paragraph([
661
661
  "The overall cross-entropy loss is the average of individual losses:"
662
662
  ]))
663
663
 
664
664
  losses_str = " + ".join([f"{loss:.4f}" for loss in self.individual_losses])
665
665
  calculation = f"\\text{{Cross-Entropy}} = \\frac{{{losses_str}}}{{{self.num_samples}}} = {self.overall_loss:.4f}"
666
666
 
667
- explanation.add_element(ContentAST.Equation(calculation, inline=False))
667
+ explanation.add_element(ca.Equation(calculation, inline=False))
668
668
 
669
669
  return explanation
@@ -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.contentast import ContentAST
5
+ import QuizGenerator.contentast as ca
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(ContentAST.Answer.accepted_strings(v), key=lambda s: len(s))[0]
64
+ sorted(ca.Answer.accepted_strings(v), key=lambda s: len(s))[0]
65
65
  for v in vec
66
66
  ]
67
67
  )