File size: 3,519 Bytes
7813169
a8211b4
 
 
 
 
7813169
 
 
 
 
 
 
 
a8211b4
 
7813169
 
 
a8211b4
7813169
 
 
 
a8211b4
7813169
 
 
 
 
a8211b4
7813169
 
 
 
a8211b4
7813169
a8211b4
7813169
a8211b4
 
7813169
 
a8211b4
7813169
 
 
 
 
 
 
 
 
a8211b4
 
7813169
a8211b4
7813169
 
 
 
a8211b4
7813169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import sympy as sp
import random
from typing import Dict, Any, Tuple

class TaskGenerationEngine:
    def __init__(self):
        self.x = sp.Symbol('x')
        # Components for generating random functions F(x)
        self.basic_functions = [
            lambda x, c: x**c,
            lambda x, c: sp.sin(c*x),
            lambda x, c: sp.cos(c*x),
            lambda x, c: sp.exp(c*x),
            lambda x, c: sp.ln(sp.Abs(c*x))
        ]

    def _score_difficulty(self, components: int, nesting: int) -> float:
        """D = num_components + degree_of_nesting * 2"""
        return float(components + nesting * 2.0)

    def generate_random_function(self, complexity: int) -> Tuple[Any, float]:
        """Generates a random F(x)."""
        num_components = max(1, int(complexity / 2))
        nesting = max(0, int(complexity / 4))
        
        f_expr = 0
        for _ in range(num_components):
            comp_func = random.choice(self.basic_functions)
            coeff = random.randint(1, 5)
            term = comp_func(self.x, coeff)
            
            # Apply nesting
            for _ in range(nesting):
                outer = random.choice(self.basic_functions)
                term = outer(term, 1)
            
            f_expr += random.randint(1, 10) * term
            
        return f_expr, self._score_difficulty(num_components, nesting)

    def generate_task(self, target_difficulty_band: float) -> Dict[str, Any]:
        """Provides an indefinite integral task."""
        complexity = max(1, int(target_difficulty_band))
        
        # 1. Generate F(x)
        F_expr, diff = self.generate_random_function(complexity)
        
        # 2. Differentiate to get the problem f(x)
        f_expr = sp.diff(F_expr, self.x)
        
        # 3. Format strings
        problem_text = f"Find the indefinite integral: \int ({sp.pretty(f_expr)}) dx"
        solution_text = f"{sp.simplify(F_expr)} + C"
        
        return {
            "problem": problem_text,
            "difficulty": diff,
            "solution": solution_text,
            "type": "integration",
            "sympy_F": F_expr,
            "sympy_f": f_expr
        }

    def generate_variants(self, task: Dict[str, Any], count: int = 2) -> list[Dict[str, Any]]:
        """
        LADDER Component: Recursive Decomposition for Integration.
        Breaks down sums or simplifies coefficients.
        """
        variants = []
        F_expr = task.get("sympy_F")
        
        if F_expr is None:
             # Fallback if task was not generated by us
             return [self.generate_task(max(1, task.get("difficulty", 2) - 2))]

        # Recursive Rule 1: Linearity (split sums)
        if isinstance(F_expr, sp.Add):
            args = F_expr.args
            for arg in args[:count]:
                sub_F = arg
                sub_f = sp.diff(sub_F, self.x)
                variants.append({
                    "problem": f"Integrate step-variant: \int ({sp.pretty(sub_f)}) dx",
                    "solution": f"{sub_F} + C",
                    "difficulty": task["difficulty"] - 1.0,
                    "type": "integration",
                    "sympy_F": sub_F,
                    "sympy_f": sub_f
                })

        # Recursive Rule 2: Constant simplification
        if not variants:
            # Just return a simpler integral by reducing difficulty
            variants.append(self.generate_task(max(1.0, task["difficulty"] - 2.0)))

        return variants[:count]