QuizGenerator 0.4.3__py3-none-any.whl → 0.5.0__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 +949 -80
  2. QuizGenerator/generate.py +44 -7
  3. QuizGenerator/misc.py +4 -554
  4. QuizGenerator/mixins.py +47 -25
  5. QuizGenerator/premade_questions/cst334/languages.py +139 -125
  6. QuizGenerator/premade_questions/cst334/math_questions.py +78 -66
  7. QuizGenerator/premade_questions/cst334/memory_questions.py +258 -144
  8. QuizGenerator/premade_questions/cst334/persistence_questions.py +71 -33
  9. QuizGenerator/premade_questions/cst334/process.py +51 -20
  10. QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +32 -6
  11. QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +59 -34
  12. QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +27 -8
  13. QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +53 -32
  14. QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +228 -88
  15. QuizGenerator/premade_questions/cst463/models/attention.py +26 -10
  16. QuizGenerator/premade_questions/cst463/models/cnns.py +32 -19
  17. QuizGenerator/premade_questions/cst463/models/rnns.py +25 -12
  18. QuizGenerator/premade_questions/cst463/models/text.py +26 -11
  19. QuizGenerator/premade_questions/cst463/models/weight_counting.py +36 -22
  20. QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +89 -109
  21. QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +126 -53
  22. QuizGenerator/question.py +110 -15
  23. QuizGenerator/quiz.py +74 -23
  24. QuizGenerator/regenerate.py +98 -29
  25. {quizgenerator-0.4.3.dist-info → quizgenerator-0.5.0.dist-info}/METADATA +1 -1
  26. {quizgenerator-0.4.3.dist-info → quizgenerator-0.5.0.dist-info}/RECORD +29 -31
  27. QuizGenerator/README.md +0 -5
  28. QuizGenerator/logging.yaml +0 -55
  29. {quizgenerator-0.4.3.dist-info → quizgenerator-0.5.0.dist-info}/WHEEL +0 -0
  30. {quizgenerator-0.4.3.dist-info → quizgenerator-0.5.0.dist-info}/entry_points.txt +0 -0
  31. {quizgenerator-0.4.3.dist-info → quizgenerator-0.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -566,13 +566,15 @@ class ForwardPassQuestion(SimpleNeuralNetworkBase):
566
566
  # Hidden layer activations
567
567
  for i in range(self.num_hidden):
568
568
  key = f"h{i+1}"
569
- self.answers[key] = Answer.float_value(key, float(self.a1[i]))
569
+ self.answers[key] = Answer.float_value(key, float(self.a1[i]), label=f"h_{i+1}")
570
570
 
571
571
  # Output
572
- self.answers["y_pred"] = Answer.float_value("y_pred", float(self.a2[0]))
572
+ self.answers["y_pred"] = Answer.float_value("y_pred", float(self.a2[0]), label="ŷ")
573
573
 
574
- def get_body(self, **kwargs) -> ContentAST.Section:
574
+ def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
575
+ """Build question body and collect answers."""
575
576
  body = ContentAST.Section()
577
+ answers = []
576
578
 
577
579
  # Question description
578
580
  body.add_element(ContentAST.Paragraph([
@@ -597,28 +599,23 @@ class ForwardPassQuestion(SimpleNeuralNetworkBase):
597
599
  f"**Hidden layer activation:** {self._get_activation_name()}"
598
600
  ]))
599
601
 
600
- # Create answer block
601
- answers = []
602
+ # Collect answers
602
603
  for i in range(self.num_hidden):
603
- answers.append(
604
- ContentAST.Answer(
605
- answer=self.answers[f"h{i+1}"],
606
- label=f"h_{i+1}"
607
- )
608
- )
604
+ answers.append(self.answers[f"h{i+1}"])
609
605
 
610
- answers.append(
611
- ContentAST.Answer(
612
- answer=self.answers["y_pred"],
613
- label="ŷ"
614
- )
615
- )
606
+ answers.append(self.answers["y_pred"])
616
607
 
617
608
  body.add_element(ContentAST.AnswerBlock(answers))
618
609
 
610
+ return body, answers
611
+
612
+ def get_body(self, **kwargs) -> ContentAST.Section:
613
+ """Build question body (backward compatible interface)."""
614
+ body, _ = self._get_body(**kwargs)
619
615
  return body
620
616
 
621
- def get_explanation(self, **kwargs) -> ContentAST.Section:
617
+ def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
618
+ """Build question explanation."""
622
619
  explanation = ContentAST.Section()
623
620
 
624
621
  explanation.add_element(ContentAST.Paragraph([
@@ -695,6 +692,11 @@ class ForwardPassQuestion(SimpleNeuralNetworkBase):
695
692
  "so the output is between 0 and 1, representing the probability of class 1)"
696
693
  ]))
697
694
 
695
+ return explanation, []
696
+
697
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
698
+ """Build question explanation (backward compatible interface)."""
699
+ explanation, _ = self._get_explanation(**kwargs)
698
700
  return explanation
699
701
 
700
702
 
@@ -741,15 +743,17 @@ class BackpropGradientQuestion(SimpleNeuralNetworkBase):
741
743
  # Gradient for W2 (hidden to output)
742
744
  for i in range(self.num_hidden):
743
745
  key = f"dL_dw2_{i}"
744
- self.answers[key] = Answer.auto_float(key, self._compute_gradient_W2(i))
746
+ self.answers[key] = Answer.auto_float(key, self._compute_gradient_W2(i), label=f"∂L/∂w_{i+3}")
745
747
 
746
748
  # Gradient for W1 (input to hidden) - pick first hidden neuron
747
749
  for j in range(self.num_inputs):
748
750
  key = f"dL_dw1_0{j}"
749
- self.answers[key] = Answer.auto_float(key, self._compute_gradient_W1(0, j))
751
+ self.answers[key] = Answer.auto_float(key, self._compute_gradient_W1(0, j), label=f"∂L/∂w_1{j+1}")
750
752
 
751
- def get_body(self, **kwargs) -> ContentAST.Section:
753
+ def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
754
+ """Build question body and collect answers."""
752
755
  body = ContentAST.Section()
756
+ answers = []
753
757
 
754
758
  # Question description
755
759
  body.add_element(ContentAST.Paragraph([
@@ -779,32 +783,25 @@ class BackpropGradientQuestion(SimpleNeuralNetworkBase):
779
783
  "**Calculate the following gradients:**"
780
784
  ]))
781
785
 
782
- # Create answer block
783
- answers = []
784
-
785
- # W2 gradients
786
+ # Collect W2 gradient answers
786
787
  for i in range(self.num_hidden):
787
- answers.append(
788
- ContentAST.Answer(
789
- answer=self.answers[f"dL_dw2_{i}"],
790
- label=f"∂L/∂w_{i+3}"
791
- )
792
- )
788
+ answers.append(self.answers[f"dL_dw2_{i}"])
793
789
 
794
- # W1 gradients (first hidden neuron)
790
+ # Collect W1 gradient answers (first hidden neuron)
795
791
  for j in range(self.num_inputs):
796
- answers.append(
797
- ContentAST.Answer(
798
- answer=self.answers[f"dL_dw1_0{j}"],
799
- label=f"∂L/∂w_1{j+1}"
800
- )
801
- )
792
+ answers.append(self.answers[f"dL_dw1_0{j}"])
802
793
 
803
794
  body.add_element(ContentAST.AnswerBlock(answers))
804
795
 
796
+ return body, answers
797
+
798
+ def get_body(self, **kwargs) -> ContentAST.Section:
799
+ """Build question body (backward compatible interface)."""
800
+ body, _ = self._get_body(**kwargs)
805
801
  return body
806
802
 
807
- def get_explanation(self, **kwargs) -> ContentAST.Section:
803
+ def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
804
+ """Build question explanation."""
808
805
  explanation = ContentAST.Section()
809
806
 
810
807
  explanation.add_element(ContentAST.Paragraph([
@@ -875,6 +872,11 @@ class BackpropGradientQuestion(SimpleNeuralNetworkBase):
875
872
  inline=False
876
873
  ))
877
874
 
875
+ return explanation, []
876
+
877
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
878
+ """Build question explanation (backward compatible interface)."""
879
+ explanation, _ = self._get_explanation(**kwargs)
878
880
  return explanation
879
881
 
880
882
 
@@ -918,14 +920,16 @@ class EnsembleAveragingQuestion(Question):
918
920
 
919
921
  # Mean prediction
920
922
  mean_pred = np.mean(self.predictions)
921
- self.answers["mean"] = Answer.float_value("mean", float(mean_pred))
923
+ self.answers["mean"] = Answer.float_value("mean", float(mean_pred), label="Mean (average)")
922
924
 
923
925
  # Median (optional, but useful)
924
926
  median_pred = np.median(self.predictions)
925
- self.answers["median"] = Answer.float_value("median", float(median_pred))
927
+ self.answers["median"] = Answer.float_value("median", float(median_pred), label="Median")
926
928
 
927
- def get_body(self, **kwargs) -> ContentAST.Section:
929
+ def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
930
+ """Build question body and collect answers."""
928
931
  body = ContentAST.Section()
932
+ answers = []
929
933
 
930
934
  # Question description
931
935
  body.add_element(ContentAST.Paragraph([
@@ -944,26 +948,21 @@ class EnsembleAveragingQuestion(Question):
944
948
  "To create an ensemble, calculate the combined prediction using the following methods:"
945
949
  ]))
946
950
 
947
- # Create answer block
948
- answers = []
949
- answers.append(
950
- ContentAST.Answer(
951
- answer=self.answers["mean"],
952
- label="Mean (average)"
953
- )
954
- )
955
- answers.append(
956
- ContentAST.Answer(
957
- answer=self.answers["median"],
958
- label="Median"
959
- )
960
- )
951
+ # Collect answers
952
+ answers.append(self.answers["mean"])
953
+ answers.append(self.answers["median"])
961
954
 
962
955
  body.add_element(ContentAST.AnswerBlock(answers))
963
956
 
957
+ return body, answers
958
+
959
+ def get_body(self, **kwargs) -> ContentAST.Section:
960
+ """Build question body (backward compatible interface)."""
961
+ body, _ = self._get_body(**kwargs)
964
962
  return body
965
963
 
966
- def get_explanation(self, **kwargs) -> ContentAST.Section:
964
+ def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
965
+ """Build question explanation."""
967
966
  explanation = ContentAST.Section()
968
967
 
969
968
  explanation.add_element(ContentAST.Paragraph([
@@ -1009,6 +1008,11 @@ class EnsembleAveragingQuestion(Question):
1009
1008
  f"({sorted_preds[mid_idx1]:.1f} + {sorted_preds[mid_idx2]:.1f}) / 2 = {median_val:.1f}"
1010
1009
  ]))
1011
1010
 
1011
+ return explanation, []
1012
+
1013
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
1014
+ """Build question explanation (backward compatible interface)."""
1015
+ explanation, _ = self._get_explanation(**kwargs)
1012
1016
  return explanation
1013
1017
 
1014
1018
 
@@ -1079,21 +1083,23 @@ class EndToEndTrainingQuestion(SimpleNeuralNetworkBase):
1079
1083
  self.answers = {}
1080
1084
 
1081
1085
  # Forward pass answers
1082
- self.answers["y_pred"] = Answer.float_value("y_pred", float(self.a2[0]))
1086
+ self.answers["y_pred"] = Answer.float_value("y_pred", float(self.a2[0]), label="1. Forward Pass - Network output ŷ")
1083
1087
 
1084
1088
  # Loss answer
1085
- self.answers["loss"] = Answer.float_value("loss", float(self.loss))
1089
+ self.answers["loss"] = Answer.float_value("loss", float(self.loss), label="2. Loss")
1086
1090
 
1087
1091
  # Gradient answers (for key weights)
1088
- self.answers["grad_w3"] = Answer.auto_float("grad_w3", self._compute_gradient_W2(0))
1089
- self.answers["grad_w11"] = Answer.auto_float("grad_w11", self._compute_gradient_W1(0, 0))
1092
+ self.answers["grad_w3"] = Answer.auto_float("grad_w3", self._compute_gradient_W2(0), label="3. Gradient ∂L/∂w₃")
1093
+ self.answers["grad_w11"] = Answer.auto_float("grad_w11", self._compute_gradient_W1(0, 0), label="4. Gradient ∂L/∂w₁₁")
1090
1094
 
1091
1095
  # Updated weight answers
1092
- self.answers["new_w3"] = Answer.float_value("new_w3", float(self.new_W2[0, 0]))
1093
- self.answers["new_w11"] = Answer.float_value("new_w11", float(self.new_W1[0, 0]))
1096
+ self.answers["new_w3"] = Answer.float_value("new_w3", float(self.new_W2[0, 0]), label="5. Updated w₃:")
1097
+ self.answers["new_w11"] = Answer.float_value("new_w11", float(self.new_W1[0, 0]), label="6. Updated w₁₁:")
1094
1098
 
1095
- def get_body(self, **kwargs) -> ContentAST.Section:
1099
+ def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
1100
+ """Build question body and collect answers."""
1096
1101
  body = ContentAST.Section()
1102
+ answers = []
1097
1103
 
1098
1104
  # Question description
1099
1105
  body.add_element(ContentAST.Paragraph([
@@ -1139,56 +1145,25 @@ class EndToEndTrainingQuestion(SimpleNeuralNetworkBase):
1139
1145
  # Network parameters table
1140
1146
  body.add_element(self._generate_parameter_table(include_activations=False))
1141
1147
 
1142
- # Create answer block
1143
- answers = []
1144
-
1145
- answers.append(
1146
- ContentAST.Answer(
1147
- answer=self.answers["y_pred"],
1148
- label="1. Forward Pass - Network output ŷ"
1149
- )
1150
- )
1151
-
1152
- answers.append(
1153
- ContentAST.Answer(
1154
- answer=self.answers["loss"],
1155
- label="2. Loss"
1156
- )
1157
- )
1158
-
1159
- answers.append(
1160
- ContentAST.Answer(
1161
- answer=self.answers["grad_w3"],
1162
- label="3. Gradient ∂L/∂w₃"
1163
- )
1164
- )
1165
-
1166
- answers.append(
1167
- ContentAST.Answer(
1168
- answer=self.answers["grad_w11"],
1169
- label="4. Gradient ∂L/∂w₁₁"
1170
- )
1171
- )
1172
-
1173
- answers.append(
1174
- ContentAST.Answer(
1175
- answer=self.answers["new_w3"],
1176
- label="5. Updated w₃:"
1177
- )
1178
- )
1179
-
1180
- answers.append(
1181
- ContentAST.Answer(
1182
- answer=self.answers["new_w11"],
1183
- label="6. Updated w₁₁:"
1184
- )
1185
- )
1148
+ # Collect answers
1149
+ answers.append(self.answers["y_pred"])
1150
+ answers.append(self.answers["loss"])
1151
+ answers.append(self.answers["grad_w3"])
1152
+ answers.append(self.answers["grad_w11"])
1153
+ answers.append(self.answers["new_w3"])
1154
+ answers.append(self.answers["new_w11"])
1186
1155
 
1187
1156
  body.add_element(ContentAST.AnswerBlock(answers))
1188
1157
 
1158
+ return body, answers
1159
+
1160
+ def get_body(self, **kwargs) -> ContentAST.Section:
1161
+ """Build question body (backward compatible interface)."""
1162
+ body, _ = self._get_body(**kwargs)
1189
1163
  return body
1190
1164
 
1191
- def get_explanation(self, **kwargs) -> ContentAST.Section:
1165
+ def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[Answer]]:
1166
+ """Build question explanation."""
1192
1167
  explanation = ContentAST.Section()
1193
1168
 
1194
1169
  explanation.add_element(ContentAST.Paragraph([
@@ -1311,4 +1286,9 @@ class EndToEndTrainingQuestion(SimpleNeuralNetworkBase):
1311
1286
  "These updated weights would be used in the next training iteration."
1312
1287
  ]))
1313
1288
 
1289
+ return explanation, []
1290
+
1291
+ def get_explanation(self, **kwargs) -> ContentAST.Section:
1292
+ """Build question explanation (backward compatible interface)."""
1293
+ explanation, _ = self._get_explanation(**kwargs)
1314
1294
  return explanation