algomath-extract 1.0.0

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 (90) hide show
  1. package/README.md +260 -0
  2. package/bin/algo-extract.js +143 -0
  3. package/bin/algo-generate.js +102 -0
  4. package/bin/algo-help.js +136 -0
  5. package/bin/algo-list.js +56 -0
  6. package/bin/algo-run.js +141 -0
  7. package/bin/algo-status.js +88 -0
  8. package/bin/algo-verify.js +189 -0
  9. package/bin/install.js +349 -0
  10. package/package.json +57 -0
  11. package/requirements.txt +20 -0
  12. package/src/__pycache__/intent.cpython-313.pyc +0 -0
  13. package/src/cli/__pycache__/commands.cpython-313.pyc +0 -0
  14. package/src/cli/cli_entry.py +106 -0
  15. package/src/cli/commands.py +339 -0
  16. package/src/execution/__init__.py +74 -0
  17. package/src/execution/__pycache__/__init__.cpython-313.pyc +0 -0
  18. package/src/execution/__pycache__/display.cpython-313.pyc +0 -0
  19. package/src/execution/__pycache__/errors.cpython-313.pyc +0 -0
  20. package/src/execution/__pycache__/executor.cpython-313.pyc +0 -0
  21. package/src/execution/__pycache__/sandbox.cpython-313.pyc +0 -0
  22. package/src/execution/display.py +261 -0
  23. package/src/execution/errors.py +158 -0
  24. package/src/execution/executor.py +253 -0
  25. package/src/execution/sandbox.py +333 -0
  26. package/src/extraction/__init__.py +102 -0
  27. package/src/extraction/__pycache__/__init__.cpython-313.pyc +0 -0
  28. package/src/extraction/__pycache__/boundaries.cpython-313.pyc +0 -0
  29. package/src/extraction/__pycache__/errors.cpython-313.pyc +0 -0
  30. package/src/extraction/__pycache__/llm_extraction.cpython-313.pyc +0 -0
  31. package/src/extraction/__pycache__/notation.cpython-313.pyc +0 -0
  32. package/src/extraction/__pycache__/parser.cpython-313.pyc +0 -0
  33. package/src/extraction/__pycache__/pdf_processor.cpython-313.pyc +0 -0
  34. package/src/extraction/__pycache__/prompts.cpython-313.pyc +0 -0
  35. package/src/extraction/__pycache__/review.cpython-313.pyc +0 -0
  36. package/src/extraction/__pycache__/schema.cpython-313.pyc +0 -0
  37. package/src/extraction/__pycache__/validation.cpython-313.pyc +0 -0
  38. package/src/extraction/boundaries.py +281 -0
  39. package/src/extraction/errors.py +156 -0
  40. package/src/extraction/llm_extraction.py +225 -0
  41. package/src/extraction/notation.py +240 -0
  42. package/src/extraction/parser.py +402 -0
  43. package/src/extraction/pdf_processor.py +281 -0
  44. package/src/extraction/prompts.py +90 -0
  45. package/src/extraction/review.py +298 -0
  46. package/src/extraction/schema.py +173 -0
  47. package/src/extraction/validation.py +202 -0
  48. package/src/generation/__init__.py +79 -0
  49. package/src/generation/__pycache__/__init__.cpython-313.pyc +0 -0
  50. package/src/generation/__pycache__/code_generator.cpython-313.pyc +0 -0
  51. package/src/generation/__pycache__/errors.cpython-313.pyc +0 -0
  52. package/src/generation/__pycache__/hybrid.cpython-313.pyc +0 -0
  53. package/src/generation/__pycache__/llm_generator.cpython-313.pyc +0 -0
  54. package/src/generation/__pycache__/persistence.cpython-313.pyc +0 -0
  55. package/src/generation/__pycache__/prompts.cpython-313.pyc +0 -0
  56. package/src/generation/__pycache__/review.cpython-313.pyc +0 -0
  57. package/src/generation/__pycache__/templates.cpython-313.pyc +0 -0
  58. package/src/generation/__pycache__/types.cpython-313.pyc +0 -0
  59. package/src/generation/__pycache__/validation.cpython-313.pyc +0 -0
  60. package/src/generation/code_generator.py +375 -0
  61. package/src/generation/errors.py +84 -0
  62. package/src/generation/hybrid.py +210 -0
  63. package/src/generation/llm_generator.py +223 -0
  64. package/src/generation/persistence.py +221 -0
  65. package/src/generation/prompts.py +202 -0
  66. package/src/generation/review.py +254 -0
  67. package/src/generation/templates.py +208 -0
  68. package/src/generation/types.py +196 -0
  69. package/src/generation/validation.py +278 -0
  70. package/src/intent.py +323 -0
  71. package/src/verification/__init__.py +63 -0
  72. package/src/verification/__pycache__/__init__.cpython-313.pyc +0 -0
  73. package/src/verification/__pycache__/checker.cpython-313.pyc +0 -0
  74. package/src/verification/__pycache__/comparison.cpython-313.pyc +0 -0
  75. package/src/verification/__pycache__/explainer.cpython-313.pyc +0 -0
  76. package/src/verification/__pycache__/static_analysis.cpython-313.pyc +0 -0
  77. package/src/verification/checker.py +220 -0
  78. package/src/verification/comparison.py +492 -0
  79. package/src/verification/explainer.py +414 -0
  80. package/src/verification/static_analysis.py +540 -0
  81. package/src/workflows/__init__.py +21 -0
  82. package/src/workflows/__pycache__/__init__.cpython-313.pyc +0 -0
  83. package/src/workflows/__pycache__/extract.cpython-313.pyc +0 -0
  84. package/src/workflows/__pycache__/generate.cpython-313.pyc +0 -0
  85. package/src/workflows/__pycache__/run.cpython-313.pyc +0 -0
  86. package/src/workflows/__pycache__/verify.cpython-313.pyc +0 -0
  87. package/src/workflows/extract.py +181 -0
  88. package/src/workflows/generate.py +155 -0
  89. package/src/workflows/run.py +187 -0
  90. package/src/workflows/verify.py +334 -0
@@ -0,0 +1,414 @@
1
+ """Algorithm explainer module for AlgoMath.
2
+
3
+ Provides natural language explanation of algorithm behavior based on
4
+ extracted algorithm structures and execution traces.
5
+
6
+ Per D-05 through D-08: Brief summaries, detailed walkthroughs, execution values,
7
+ and complexity-based explanation styles.
8
+ """
9
+
10
+ from dataclasses import dataclass, field
11
+ from enum import Enum
12
+ from typing import Any, Dict, List, Optional
13
+ import json
14
+
15
+ from src.extraction.schema import Algorithm, Step, StepType
16
+
17
+
18
+ class ExplanationLevel(Enum):
19
+ """Level of detail for algorithm explanations."""
20
+ BRIEF = "brief"
21
+ DETAILED = "detailed"
22
+
23
+
24
+ @dataclass
25
+ class StepExplanation:
26
+ """Explanation of a single step with execution context.
27
+
28
+ Per D-07: Shows actual values from execution trace.
29
+ Per D-142: Preserves mathematical notation.
30
+ """
31
+ step_id: int
32
+ description: str
33
+ inputs: Dict[str, Any] = field(default_factory=dict)
34
+ outputs: Dict[str, Any] = field(default_factory=dict)
35
+ mathematical_notation: Optional[str] = None
36
+
37
+ def to_dict(self) -> Dict[str, Any]:
38
+ """Convert to dictionary representation."""
39
+ return {
40
+ "step_id": self.step_id,
41
+ "description": self.description,
42
+ "inputs": self.inputs,
43
+ "outputs": self.outputs,
44
+ "mathematical_notation": self.mathematical_notation,
45
+ }
46
+
47
+
48
+ @dataclass
49
+ class ExplanationResult:
50
+ """Result of algorithm explanation.
51
+
52
+ Per D-05: Brief summary (1-2 sentences).
53
+ Per D-06: Detailed step-by-step walkthrough.
54
+ Per D-07: Execution trace integration.
55
+ Per D-08: Complexity score for recommendation.
56
+ """
57
+ summary: str
58
+ detailed_explanation: Optional[str] = None
59
+ step_explanations: List[StepExplanation] = field(default_factory=list)
60
+ complexity_score: float = 0.0
61
+ recommended_level: ExplanationLevel = ExplanationLevel.BRIEF
62
+ execution_trace: Optional[Dict[int, Any]] = None
63
+
64
+ def to_dict(self) -> Dict[str, Any]:
65
+ """Convert to dictionary representation."""
66
+ return {
67
+ "summary": self.summary,
68
+ "detailed_explanation": self.detailed_explanation,
69
+ "step_explanations": [se.to_dict() for se in self.step_explanations],
70
+ "complexity_score": self.complexity_score,
71
+ "recommended_level": self.recommended_level.value,
72
+ "execution_trace": self.execution_trace,
73
+ }
74
+
75
+ def to_json(self) -> str:
76
+ """Convert to JSON string."""
77
+ return json.dumps(self.to_dict(), indent=2)
78
+
79
+
80
+ class AlgorithmExplainer:
81
+ """Explains algorithm behavior in natural language.
82
+
83
+ Per D-05: Provides brief summaries.
84
+ Per D-06: Provides detailed step-by-step walkthroughs.
85
+ Per D-07: Shows actual execution values.
86
+ Per D-08: Adapts explanation style to complexity.
87
+ """
88
+
89
+ def __init__(self, algorithm: Algorithm):
90
+ """Initialize explainer with algorithm.
91
+
92
+ Args:
93
+ algorithm: Algorithm to explain
94
+ """
95
+ self.algorithm = algorithm
96
+
97
+ def explain(
98
+ self,
99
+ level: ExplanationLevel = ExplanationLevel.BRIEF,
100
+ execution_trace: Optional[Dict[int, Any]] = None
101
+ ) -> ExplanationResult:
102
+ """Generate explanation of the algorithm.
103
+
104
+ Args:
105
+ level: Level of detail (brief or detailed)
106
+ execution_trace: Optional execution values per step
107
+
108
+ Returns:
109
+ ExplanationResult with summary and details
110
+ """
111
+ complexity = self._calculate_complexity()
112
+ recommended = self._recommend_level(complexity)
113
+
114
+ # Generate summary (always included)
115
+ summary = self._generate_summary()
116
+
117
+ # Generate detailed explanation if requested or recommended
118
+ detailed = None
119
+ step_explanations = []
120
+ if level == ExplanationLevel.DETAILED or complexity > 0.5:
121
+ detailed = self._generate_detailed(execution_trace)
122
+ step_explanations = self._generate_step_explanations(execution_trace)
123
+
124
+ return ExplanationResult(
125
+ summary=summary,
126
+ detailed_explanation=detailed,
127
+ step_explanations=step_explanations,
128
+ complexity_score=complexity,
129
+ recommended_level=recommended,
130
+ execution_trace=execution_trace
131
+ )
132
+
133
+ def explain_step(self, step_id: int) -> StepExplanation:
134
+ """Explain a specific step.
135
+
136
+ Args:
137
+ step_id: ID of the step to explain
138
+
139
+ Returns:
140
+ StepExplanation with description and details
141
+ """
142
+ step = self._get_step_by_id(step_id)
143
+ if not step:
144
+ return StepExplanation(
145
+ step_id=step_id,
146
+ description=f"Step {step_id} not found"
147
+ )
148
+
149
+ description = self._explain_step_by_type(step)
150
+ notation = self._extract_mathematical_notation(step)
151
+
152
+ return StepExplanation(
153
+ step_id=step_id,
154
+ description=description,
155
+ inputs={name: None for name in step.inputs},
156
+ outputs={name: None for name in step.outputs},
157
+ mathematical_notation=notation
158
+ )
159
+
160
+ def _calculate_complexity(self) -> float:
161
+ """Calculate algorithm complexity score (0.0 to 1.0).
162
+
163
+ Per D-08: Simple (<5 steps, no loops) → low score
164
+ Moderate (5-15 steps, simple loops) → medium
165
+ Complex (>15 steps, nested loops) → high
166
+ """
167
+ score = 0.0
168
+ num_steps = len(self.algorithm.steps)
169
+
170
+ # Step count contribution
171
+ if num_steps < 5:
172
+ score += 0.1
173
+ elif num_steps < 15:
174
+ score += 0.3
175
+ else:
176
+ score += 0.5
177
+
178
+ # Check for loops and conditionals
179
+ has_loops = False
180
+ has_conditionals = False
181
+ has_nested = False
182
+ loop_depths = {}
183
+
184
+ for step in self.algorithm.steps:
185
+ if step.type in (StepType.LOOP_FOR, StepType.LOOP_WHILE):
186
+ has_loops = True
187
+ loop_depths[step.id] = self._get_loop_depth(step)
188
+ if loop_depths[step.id] > 1:
189
+ has_nested = True
190
+ elif step.type == StepType.CONDITIONAL:
191
+ has_conditionals = True
192
+
193
+ if has_loops:
194
+ score += 0.2
195
+ if has_conditionals:
196
+ score += 0.1
197
+ if has_nested:
198
+ score += 0.2
199
+
200
+ return min(score, 1.0)
201
+
202
+ def _get_loop_depth(self, step: Step, depth: int = 1) -> int:
203
+ """Calculate nesting depth of a loop."""
204
+ max_depth = depth
205
+ for body_step_id in step.body:
206
+ body_step = self._get_step_by_id(body_step_id)
207
+ if body_step and body_step.type in (StepType.LOOP_FOR, StepType.LOOP_WHILE):
208
+ nested_depth = self._get_loop_depth(body_step, depth + 1)
209
+ max_depth = max(max_depth, nested_depth)
210
+ return max_depth
211
+
212
+ def _recommend_level(self, complexity: float) -> ExplanationLevel:
213
+ """Recommend explanation level based on complexity."""
214
+ if complexity < 0.3:
215
+ return ExplanationLevel.BRIEF
216
+ return ExplanationLevel.DETAILED
217
+
218
+ def _generate_summary(self) -> str:
219
+ """Generate brief summary (1-2 sentences) per D-05."""
220
+ name = self.algorithm.name
221
+ description = self.algorithm.description
222
+ num_steps = len(self.algorithm.steps)
223
+ num_inputs = len(self.algorithm.inputs)
224
+ num_outputs = len(self.algorithm.outputs)
225
+
226
+ # Build input description
227
+ input_desc = ""
228
+ if num_inputs == 1:
229
+ input_name = self.algorithm.inputs[0].get("name", "input")
230
+ input_desc = f"a single {self.algorithm.inputs[0].get('type', 'value')} input {input_name}"
231
+ elif num_inputs > 1:
232
+ input_desc = f"{num_inputs} inputs"
233
+ else:
234
+ input_desc = "no inputs"
235
+
236
+ # Build output description
237
+ output_desc = ""
238
+ if num_outputs == 1:
239
+ output_name = self.algorithm.outputs[0].get("name", "result")
240
+ output_desc = f"the {output_name}"
241
+ elif num_outputs > 1:
242
+ output_desc = f"{num_outputs} values"
243
+ else:
244
+ output_desc = "a result"
245
+
246
+ # Generate summary sentence
247
+ parts = []
248
+ if description:
249
+ parts.append(f"This {name} algorithm {description.lower()}.")
250
+ else:
251
+ parts.append(f"This is the {name} algorithm.")
252
+
253
+ # Add complexity/structure info
254
+ if num_steps < 5:
255
+ parts.append(f"It takes {input_desc} and returns {output_desc} in a straightforward {num_steps}-step process.")
256
+ elif num_steps < 15:
257
+ parts.append(f"It processes {input_desc} through {num_steps} steps to produce {output_desc}.")
258
+ else:
259
+ parts.append(f"It implements a complex process with {num_steps} steps, taking {input_desc} and returning {output_desc}.")
260
+
261
+ return " ".join(parts)
262
+
263
+ def _generate_detailed(self, execution_trace: Optional[Dict[int, Any]] = None) -> str:
264
+ """Generate detailed step-by-step walkthrough per D-06, D-07."""
265
+ lines = []
266
+ lines.append(f"Detailed walkthrough of {self.algorithm.name}:")
267
+ lines.append("")
268
+
269
+ for step in self.algorithm.steps:
270
+ step_desc = self._explain_step_by_type(step)
271
+ lines.append(f"Step {step.id}: {step_desc}")
272
+
273
+ # Add execution values if available
274
+ if execution_trace and step.id in execution_trace:
275
+ values = execution_trace[step.id]
276
+ if values:
277
+ lines.append(f" At this point: {self._format_values(values)}")
278
+
279
+ lines.append("")
280
+
281
+ return "\n".join(lines)
282
+
283
+ def _generate_step_explanations(
284
+ self,
285
+ execution_trace: Optional[Dict[int, Any]] = None
286
+ ) -> List[StepExplanation]:
287
+ """Generate explanations for all steps."""
288
+ explanations = []
289
+ for step in self.algorithm.steps:
290
+ desc = self._explain_step_by_type(step)
291
+ notation = self._extract_mathematical_notation(step)
292
+
293
+ # Get execution values if available
294
+ inputs = {}
295
+ outputs = {}
296
+ if execution_trace and step.id in execution_trace:
297
+ trace_data = execution_trace[step.id]
298
+ if isinstance(trace_data, dict):
299
+ for var in step.inputs:
300
+ if var in trace_data:
301
+ inputs[var] = trace_data[var]
302
+ for var in step.outputs:
303
+ if var in trace_data:
304
+ outputs[var] = trace_data[var]
305
+
306
+ explanations.append(StepExplanation(
307
+ step_id=step.id,
308
+ description=desc,
309
+ inputs=inputs,
310
+ outputs=outputs,
311
+ mathematical_notation=notation
312
+ ))
313
+
314
+ return explanations
315
+
316
+ def _explain_step_by_type(self, step: Step) -> str:
317
+ """Generate explanation based on step type."""
318
+ if step.type == StepType.ASSIGNMENT:
319
+ return self._explain_assignment(step)
320
+ elif step.type in (StepType.LOOP_FOR, StepType.LOOP_WHILE):
321
+ return self._explain_loop(step)
322
+ elif step.type == StepType.CONDITIONAL:
323
+ return self._explain_conditional(step)
324
+ elif step.type == StepType.RETURN:
325
+ return self._explain_return(step)
326
+ elif step.type == StepType.CALL:
327
+ return self._explain_call(step)
328
+ else:
329
+ return step.description
330
+
331
+ def _explain_assignment(self, step: Step) -> str:
332
+ """Explain an assignment step."""
333
+ if step.expression:
334
+ return f"Assign {step.expression}"
335
+ elif step.outputs:
336
+ return f"Set {', '.join(step.outputs)} to new values"
337
+ return step.description
338
+
339
+ def _explain_loop(self, step: Step) -> str:
340
+ """Explain a loop step."""
341
+ if step.iter_var and step.iter_range:
342
+ return f"Loop with {step.iter_var} in {step.iter_range}, processing {len(step.body)} step(s)"
343
+ elif step.condition:
344
+ return f"While {step.condition}, repeat {len(step.body)} step(s)"
345
+ return f"Iterate through {len(step.body)} step(s)"
346
+
347
+ def _explain_conditional(self, step: Step) -> str:
348
+ """Explain a conditional step."""
349
+ if step.condition:
350
+ has_else = len(step.else_body) > 0
351
+ else_part = " with alternative steps" if has_else else ""
352
+ return f"If {step.condition}, execute {len(step.body)} step(s){else_part}"
353
+ return f"Branch based on condition, executing {len(step.body)} step(s)"
354
+
355
+ def _explain_return(self, step: Step) -> str:
356
+ """Explain a return step."""
357
+ if step.expression:
358
+ return f"Return {step.expression}"
359
+ elif step.inputs:
360
+ return f"Return {', '.join(step.inputs)}"
361
+ return "Return result"
362
+
363
+ def _explain_call(self, step: Step) -> str:
364
+ """Explain a function call step."""
365
+ if step.call_target:
366
+ args = f" with arguments {step.arguments}" if step.arguments else ""
367
+ return f"Call {step.call_target}{args}"
368
+ return step.description
369
+
370
+ def _extract_mathematical_notation(self, step: Step) -> Optional[str]:
371
+ """Extract mathematical notation from step per D-142."""
372
+ if step.expression:
373
+ # Check for subscript notation (x_i, etc.)
374
+ if "_" in step.expression or "^" in step.expression:
375
+ return step.expression
376
+ if step.description and ("_" in step.description or "^" in step.description):
377
+ return step.description
378
+ return None
379
+
380
+ def _format_values(self, values: Dict[str, Any]) -> str:
381
+ """Format execution values for display."""
382
+ parts = []
383
+ for name, value in values.items():
384
+ if isinstance(value, (list, tuple)) and len(value) > 3:
385
+ parts.append(f"{name} = [{value[0]}, ..., {value[-1]}] (len={len(value)})")
386
+ else:
387
+ parts.append(f"{name} = {value}")
388
+ return ", ".join(parts)
389
+
390
+ def _get_step_by_id(self, step_id: int) -> Optional[Step]:
391
+ """Get step by ID."""
392
+ for step in self.algorithm.steps:
393
+ if step.id == step_id:
394
+ return step
395
+ return None
396
+
397
+
398
+ def explain_algorithm(
399
+ algorithm: Algorithm,
400
+ level: ExplanationLevel = ExplanationLevel.BRIEF,
401
+ execution_trace: Optional[Dict[int, Any]] = None
402
+ ) -> ExplanationResult:
403
+ """Convenience function to explain an algorithm.
404
+
405
+ Args:
406
+ algorithm: Algorithm to explain
407
+ level: Level of detail for explanation
408
+ execution_trace: Optional execution values per step
409
+
410
+ Returns:
411
+ ExplanationResult with natural language explanation
412
+ """
413
+ explainer = AlgorithmExplainer(algorithm)
414
+ return explainer.explain(level=level, execution_trace=execution_trace)