QuizGenerator 0.6.2__py3-none-any.whl → 0.7.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 (30) hide show
  1. QuizGenerator/contentast.py +2198 -2213
  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/typst_utils.py +2 -2
  26. {quizgenerator-0.6.2.dist-info → quizgenerator-0.7.0.dist-info}/METADATA +1 -1
  27. {quizgenerator-0.6.2.dist-info → quizgenerator-0.7.0.dist-info}/RECORD +30 -30
  28. {quizgenerator-0.6.2.dist-info → quizgenerator-0.7.0.dist-info}/WHEEL +0 -0
  29. {quizgenerator-0.6.2.dist-info → quizgenerator-0.7.0.dist-info}/entry_points.txt +0 -0
  30. {quizgenerator-0.6.2.dist-info → quizgenerator-0.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -5,7 +5,7 @@ import logging
5
5
  from typing import List, Tuple
6
6
  import sympy as sp
7
7
 
8
- from QuizGenerator.contentast import ContentAST, AnswerTypes
8
+ import QuizGenerator.contentast as ca
9
9
  from QuizGenerator.question import Question, QuestionRegistry
10
10
  from .misc import generate_function, format_vector
11
11
 
@@ -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] = AnswerTypes.Float(gradient_value, label=label)
60
+ self.answers[answer_key] = ca.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,20 +75,20 @@ 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"] = AnswerTypes.String(vector_str, pdf_only=True)
78
+ self.answers["gradient_vector"] = ca.AnswerTypes.String(vector_str, pdf_only=True)
79
79
 
80
- def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
80
+ def _get_body(self, **kwargs) -> Tuple[ca.Section, List[ca.Answer]]:
81
81
  """Build question body and collect answers."""
82
- body = ContentAST.Section()
82
+ body = ca.Section()
83
83
  answers = []
84
84
 
85
85
  # Display the function
86
86
  body.add_element(
87
- ContentAST.Paragraph([
87
+ ca.Paragraph([
88
88
  "Given the function ",
89
- ContentAST.Equation(sp.latex(self.equation), inline=True),
89
+ ca.Equation(sp.latex(self.equation), inline=True),
90
90
  ", calculate the gradient at the point ",
91
- ContentAST.Equation(format_vector(self.evaluation_point), inline=True),
91
+ ca.Equation(format_vector(self.evaluation_point), inline=True),
92
92
  "."
93
93
  ])
94
94
  )
@@ -98,9 +98,9 @@ class DerivativeQuestion(Question, abc.ABC):
98
98
 
99
99
  # For PDF: Use OnlyLatex to show gradient vector format (no answer blank)
100
100
  body.add_element(
101
- ContentAST.OnlyLatex([
102
- ContentAST.Paragraph([
103
- ContentAST.Equation(
101
+ ca.OnlyLatex([
102
+ ca.Paragraph([
103
+ ca.Equation(
104
104
  f"\\left. \\nabla f \\right|_{{{eval_point_str}}} = ",
105
105
  inline=True
106
106
  )
@@ -113,9 +113,9 @@ class DerivativeQuestion(Question, abc.ABC):
113
113
  answer = self.answers[f"partial_derivative_{i}"]
114
114
  answers.append(answer)
115
115
  body.add_element(
116
- ContentAST.OnlyHtml([
117
- ContentAST.Paragraph([
118
- ContentAST.Equation(
116
+ ca.OnlyHtml([
117
+ ca.Paragraph([
118
+ ca.Equation(
119
119
  f"\\left. {self._format_partial_derivative(i)} \\right|_{{{eval_point_str}}} = ",
120
120
  inline=True
121
121
  ),
@@ -126,32 +126,32 @@ class DerivativeQuestion(Question, abc.ABC):
126
126
 
127
127
  return body, answers
128
128
 
129
- def get_body(self, **kwargs) -> ContentAST.Section:
129
+ def get_body(self, **kwargs) -> ca.Section:
130
130
  """Build question body (backward compatible interface)."""
131
131
  body, _ = self._get_body(**kwargs)
132
132
  return body
133
133
 
134
- def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
134
+ def _get_explanation(self, **kwargs) -> Tuple[ca.Section, List[ca.Answer]]:
135
135
  """Build question explanation."""
136
- explanation = ContentAST.Section()
136
+ explanation = ca.Section()
137
137
 
138
138
  # Show the function and its gradient
139
139
  explanation.add_element(
140
- ContentAST.Paragraph([
140
+ ca.Paragraph([
141
141
  "To find the gradient, we calculate the partial derivatives of ",
142
- ContentAST.Equation(sp.latex(self.equation), inline=True),
142
+ ca.Equation(sp.latex(self.equation), inline=True),
143
143
  ":"
144
144
  ])
145
145
  )
146
146
 
147
147
  # Show analytical gradient
148
148
  explanation.add_element(
149
- ContentAST.Equation(f"\\nabla f = {sp.latex(self.gradient_function)}", inline=False)
149
+ ca.Equation(f"\\nabla f = {sp.latex(self.gradient_function)}", inline=False)
150
150
  )
151
151
 
152
152
  # Show evaluation at the specific point
153
153
  explanation.add_element(
154
- ContentAST.Paragraph([
154
+ ca.Paragraph([
155
155
  f"Evaluating at the point {format_vector(self.evaluation_point)}:"
156
156
  ])
157
157
  )
@@ -162,18 +162,18 @@ 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 ContentAST.Answer.accepted_strings for clean numerical formatting
165
+ # Use ca.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(ContentAST.Answer.accepted_strings(numerical_value), key=lambda s: len(s))[0]
172
+ clean_value = sorted(ca.Answer.accepted_strings(numerical_value), key=lambda s: len(s))[0]
173
173
 
174
174
  explanation.add_element(
175
- ContentAST.Paragraph([
176
- ContentAST.Equation(
175
+ ca.Paragraph([
176
+ ca.Equation(
177
177
  f"{self._format_partial_derivative(i)} = {sp.latex(partial_expr)} = {clean_value}",
178
178
  inline=False
179
179
  )
@@ -182,7 +182,7 @@ class DerivativeQuestion(Question, abc.ABC):
182
182
 
183
183
  return explanation, []
184
184
 
185
- def get_explanation(self, **kwargs) -> ContentAST.Section:
185
+ def get_explanation(self, **kwargs) -> ca.Section:
186
186
  """Build question explanation (backward compatible interface)."""
187
187
  explanation, _ = self._get_explanation(**kwargs)
188
188
  return explanation
@@ -284,26 +284,26 @@ 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[ContentAST.Answer]]:
287
+ def _get_explanation(self, **kwargs) -> Tuple[ca.Section, List[ca.Answer]]:
288
288
  """Build question explanation."""
289
- explanation = ContentAST.Section()
289
+ explanation = ca.Section()
290
290
 
291
291
  # Show the composed function structure
292
292
  explanation.add_element(
293
- ContentAST.Paragraph([
293
+ ca.Paragraph([
294
294
  "This is a composition of functions requiring the chain rule. The function ",
295
- ContentAST.Equation(sp.latex(self.equation), inline=True),
295
+ ca.Equation(sp.latex(self.equation), inline=True),
296
296
  " can be written as ",
297
- ContentAST.Equation(f"f(g(x)) \\text{{ where }} g(x) = {sp.latex(self.inner_function)}", inline=True),
297
+ ca.Equation(f"f(g(x)) \\text{{ where }} g(x) = {sp.latex(self.inner_function)}", inline=True),
298
298
  "."
299
299
  ])
300
300
  )
301
301
 
302
302
  # Explain chain rule with Leibniz notation
303
303
  explanation.add_element(
304
- ContentAST.Paragraph([
304
+ ca.Paragraph([
305
305
  "The chain rule states that for a composite function ",
306
- ContentAST.Equation("f(g(x))", inline=True),
306
+ ca.Equation("f(g(x))", inline=True),
307
307
  ", the derivative with respect to each variable is found by multiplying the derivative of the outer function with respect to the inner function by the derivative of the inner function with respect to the variable:"
308
308
  ])
309
309
  )
@@ -312,14 +312,14 @@ class DerivativeChain(DerivativeQuestion):
312
312
  for i in range(self.num_variables):
313
313
  var_name = f"x_{i}"
314
314
  explanation.add_element(
315
- ContentAST.Equation(
315
+ ca.Equation(
316
316
  f"\\frac{{\\partial f}}{{\\partial {var_name}}} = \\frac{{\\partial f}}{{\\partial g}} \\cdot \\frac{{\\partial g}}{{\\partial {var_name}}}",
317
317
  inline=False
318
318
  )
319
319
  )
320
320
 
321
321
  explanation.add_element(
322
- ContentAST.Paragraph([
322
+ ca.Paragraph([
323
323
  "Applying this to our specific function:"
324
324
  ])
325
325
  )
@@ -333,13 +333,13 @@ class DerivativeChain(DerivativeQuestion):
333
333
  inner_deriv = self.inner_function.diff(self.variables[i])
334
334
 
335
335
  explanation.add_element(
336
- ContentAST.Paragraph([
336
+ ca.Paragraph([
337
337
  f"For {var_name}:"
338
338
  ])
339
339
  )
340
340
 
341
341
  explanation.add_element(
342
- ContentAST.Equation(
342
+ ca.Equation(
343
343
  f"\\frac{{\\partial f}}{{\\partial {var_name}}} = \\left({sp.latex(outer_deriv)}\\right) \\cdot \\left({sp.latex(inner_deriv)}\\right)",
344
344
  inline=False
345
345
  )
@@ -347,18 +347,18 @@ class DerivativeChain(DerivativeQuestion):
347
347
 
348
348
  # Show analytical gradient
349
349
  explanation.add_element(
350
- ContentAST.Paragraph([
350
+ ca.Paragraph([
351
351
  "This gives us the complete gradient:"
352
352
  ])
353
353
  )
354
354
 
355
355
  explanation.add_element(
356
- ContentAST.Equation(f"\\nabla f = {sp.latex(self.gradient_function)}", inline=False)
356
+ ca.Equation(f"\\nabla f = {sp.latex(self.gradient_function)}", inline=False)
357
357
  )
358
358
 
359
359
  # Show evaluation at the specific point
360
360
  explanation.add_element(
361
- ContentAST.Paragraph([
361
+ ca.Paragraph([
362
362
  f"Evaluating at the point {format_vector(self.evaluation_point)}:"
363
363
  ])
364
364
  )
@@ -369,18 +369,18 @@ class DerivativeChain(DerivativeQuestion):
369
369
  partial_expr = self.gradient_function[i]
370
370
  partial_value = partial_expr.subs(subs_map)
371
371
 
372
- # Use ContentAST.Answer.accepted_strings for clean numerical formatting
372
+ # Use ca.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(ContentAST.Answer.accepted_strings(numerical_value), key=lambda s: len(s))[0]
379
+ clean_value = sorted(ca.Answer.accepted_strings(numerical_value), key=lambda s: len(s))[0]
380
380
 
381
381
  explanation.add_element(
382
- ContentAST.Paragraph([
383
- ContentAST.Equation(
382
+ ca.Paragraph([
383
+ ca.Equation(
384
384
  f"{self._format_partial_derivative(i)} = {sp.latex(partial_expr)} = {clean_value}",
385
385
  inline=False
386
386
  )
@@ -389,7 +389,7 @@ class DerivativeChain(DerivativeQuestion):
389
389
 
390
390
  return explanation, []
391
391
 
392
- def get_explanation(self, **kwargs) -> ContentAST.Section:
392
+ def get_explanation(self, **kwargs) -> ca.Section:
393
393
  """Build question explanation (backward compatible interface)."""
394
394
  explanation, _ = self._get_explanation(**kwargs)
395
395
  return explanation
@@ -6,7 +6,7 @@ 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, 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
 
@@ -15,7 +15,7 @@ from .misc import generate_function, format_vector
15
15
  log = logging.getLogger(__name__)
16
16
 
17
17
 
18
- # Note: This file does not use ContentAST.Answer wrappers - it uses TableQuestionMixin
18
+ # Note: This file does not use ca.Answer wrappers - it uses TableQuestionMixin
19
19
  # which handles answer display through create_answer_table(). The answers are created
20
20
  # with labels embedded at creation time in refresh().
21
21
 
@@ -98,19 +98,19 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
98
98
 
99
99
  # Location answer
100
100
  location_key = f"answer__location_{step}"
101
- self.answers[location_key] = AnswerTypes.Vector(list(result['location']), label=f"Location at step {step}")
101
+ self.answers[location_key] = ca.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] = AnswerTypes.Vector(list(result['gradient']), label=f"Gradient at step {step}")
105
+ self.answers[gradient_key] = ca.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] = AnswerTypes.Vector(list(result['update']), label=f"Update at step {step}")
109
+ self.answers[update_key] = ca.AnswerTypes.Vector(list(result['update']), label=f"Update at step {step}")
110
110
 
111
- def _get_body(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
111
+ def _get_body(self, **kwargs) -> Tuple[ca.Section, List[ca.Answer]]:
112
112
  """Build question body and collect answers."""
113
- body = ContentAST.Section()
113
+ body = ca.Section()
114
114
  answers = []
115
115
 
116
116
  # Introduction
@@ -118,24 +118,24 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
118
118
  sign = "-" if self.minimize else "+"
119
119
 
120
120
  body.add_element(
121
- ContentAST.Paragraph(
121
+ ca.Paragraph(
122
122
  [
123
123
  f"Use gradient descent to {objective} the function ",
124
- ContentAST.Equation(sp.latex(self.function), inline=True),
124
+ ca.Equation(sp.latex(self.function), inline=True),
125
125
  " with learning rate ",
126
- ContentAST.Equation(f"\\alpha = {self.learning_rate}", inline=True),
126
+ ca.Equation(f"\\alpha = {self.learning_rate}", inline=True),
127
127
  f" and starting point {self.starting_point[0] if self.num_variables == 1 else tuple(self.starting_point)}. "
128
128
  "Fill in the table below with your calculations."
129
129
  ]
130
130
  )
131
131
  )
132
132
 
133
- # Create table data - use ContentAST.Equation for proper LaTeX rendering in headers
133
+ # Create table data - use ca.Equation for proper LaTeX rendering in headers
134
134
  headers = [
135
135
  "n",
136
136
  "location",
137
- ContentAST.Equation("\\nabla f", inline=True),
138
- ContentAST.Equation("\\alpha \\cdot \\nabla f", inline=True)
137
+ ca.Equation("\\nabla f", inline=True),
138
+ ca.Equation("\\alpha \\cdot \\nabla f", inline=True)
139
139
  ]
140
140
  table_rows = []
141
141
 
@@ -174,17 +174,17 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
174
174
 
175
175
  return body, answers
176
176
 
177
- def get_body(self, **kwargs) -> ContentAST.Section:
177
+ def get_body(self, **kwargs) -> ca.Section:
178
178
  """Build question body (backward compatible interface)."""
179
179
  body, _ = self._get_body(**kwargs)
180
180
  return body
181
181
 
182
- def _get_explanation(self, **kwargs) -> Tuple[ContentAST.Section, List[ContentAST.Answer]]:
182
+ def _get_explanation(self, **kwargs) -> Tuple[ca.Section, List[ca.Answer]]:
183
183
  """Build question explanation."""
184
- explanation = ContentAST.Section()
184
+ explanation = ca.Section()
185
185
 
186
186
  explanation.add_element(
187
- ContentAST.Paragraph(
187
+ ca.Paragraph(
188
188
  [
189
189
  "Gradient descent is an optimization algorithm that iteratively moves towards "
190
190
  "the minimum of a function by taking steps proportional to the negative of the gradient."
@@ -196,10 +196,10 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
196
196
  sign = "-" if self.minimize else "+"
197
197
 
198
198
  explanation.add_element(
199
- ContentAST.Paragraph(
199
+ ca.Paragraph(
200
200
  [
201
201
  f"We want to {objective} the function ",
202
- ContentAST.Equation(sp.latex(self.function), inline=True),
202
+ ca.Equation(sp.latex(self.function), inline=True),
203
203
  ". First, we calculate the analytical gradient:"
204
204
  ]
205
205
  )
@@ -207,16 +207,16 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
207
207
 
208
208
  # Add analytical gradient calculation as a display equation (vertical vector)
209
209
  explanation.add_element(
210
- ContentAST.Equation(f"\\nabla f = {sp.latex(self.gradient_function)}", inline=False)
210
+ ca.Equation(f"\\nabla f = {sp.latex(self.gradient_function)}", inline=False)
211
211
  )
212
212
 
213
213
  explanation.add_element(
214
- ContentAST.Paragraph(
214
+ ca.Paragraph(
215
215
  [
216
216
  f"Since we want to {objective}, we use the update rule: ",
217
- ContentAST.Equation(f"x_{{new}} = x_{{old}} {sign} \\alpha \\nabla f", inline=True),
217
+ ca.Equation(f"x_{{new}} = x_{{old}} {sign} \\alpha \\nabla f", inline=True),
218
218
  f". We start at {tuple(self.starting_point)} with learning rate ",
219
- ContentAST.Equation(f"\\alpha = {self.learning_rate}", inline=True),
219
+ ca.Equation(f"\\alpha = {self.learning_rate}", inline=True),
220
220
  "."
221
221
  ]
222
222
  )
@@ -224,7 +224,7 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
224
224
 
225
225
  # Add completed table showing all solutions
226
226
  explanation.add_element(
227
- ContentAST.Paragraph(
227
+ ca.Paragraph(
228
228
  [
229
229
  "**Solution Table:**"
230
230
  ]
@@ -235,8 +235,8 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
235
235
  solution_headers = [
236
236
  "n",
237
237
  "location",
238
- ContentAST.Equation("\\nabla f", inline=True),
239
- ContentAST.Equation("\\alpha \\cdot \\nabla f", inline=True)
238
+ ca.Equation("\\nabla f", inline=True),
239
+ ca.Equation("\\alpha \\cdot \\nabla f", inline=True)
240
240
  ]
241
241
 
242
242
  solution_rows = []
@@ -264,7 +264,7 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
264
264
  step = result['step']
265
265
 
266
266
  explanation.add_element(
267
- ContentAST.Paragraph(
267
+ ca.Paragraph(
268
268
  [
269
269
  f"**Step {step}:**"
270
270
  ]
@@ -272,7 +272,7 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
272
272
  )
273
273
 
274
274
  explanation.add_element(
275
- ContentAST.Paragraph(
275
+ ca.Paragraph(
276
276
  [
277
277
  f"Location: {format_vector(result['location'])}"
278
278
  ]
@@ -280,7 +280,7 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
280
280
  )
281
281
 
282
282
  explanation.add_element(
283
- ContentAST.Paragraph(
283
+ ca.Paragraph(
284
284
  [
285
285
  f"Gradient: {format_vector(result['gradient'])}"
286
286
  ]
@@ -288,10 +288,10 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
288
288
  )
289
289
 
290
290
  explanation.add_element(
291
- ContentAST.Paragraph(
291
+ ca.Paragraph(
292
292
  [
293
293
  "Update: ",
294
- ContentAST.Equation(
294
+ ca.Equation(
295
295
  f"\\alpha \\cdot \\nabla f = {self.learning_rate} \\cdot {format_vector(result['gradient'])} = {format_vector(result['update'])}",
296
296
  inline=True
297
297
  )
@@ -306,7 +306,7 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
306
306
  next_loc = [current_loc[j] - update[j] for j in range(len(current_loc))]
307
307
 
308
308
  explanation.add_element(
309
- ContentAST.Paragraph(
309
+ ca.Paragraph(
310
310
  [
311
311
  f"Next location: {format_vector(current_loc)} - {format_vector(result['update'])} = {format_vector(next_loc)}"
312
312
  ]
@@ -315,7 +315,7 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
315
315
 
316
316
  function_values = [r['function_value'] for r in self.gradient_descent_results]
317
317
  explanation.add_element(
318
- ContentAST.Paragraph(
318
+ ca.Paragraph(
319
319
  [
320
320
  f"Function values: {[f'{v:.4f}' for v in function_values]}"
321
321
  ]
@@ -324,7 +324,7 @@ class GradientDescentWalkthrough(GradientDescentQuestion, TableQuestionMixin, Bo
324
324
 
325
325
  return explanation, []
326
326
 
327
- def get_explanation(self, **kwargs) -> ContentAST.Section:
327
+ def get_explanation(self, **kwargs) -> ca.Section:
328
328
  """Build question explanation (backward compatible interface)."""
329
329
  explanation, _ = self._get_explanation(**kwargs)
330
330
  return explanation