File size: 16,915 Bytes
ae67c79
 
 
 
 
 
 
fb3d8da
4686307
ae67c79
4686307
 
 
fb3d8da
ae67c79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
import gradio as gr
import torch
import numpy as np
import random
import pandas as pd
import matplotlib.pyplot as plt
import time
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer

model_name = "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B"
model = AutoModelForCausalLM.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)


# Sample problems database (you would expand this)
sample_problems = {
    "algebra": [
        {
            "problem": "Find all positive integers n such that n^2 + 20 is divisible by n + 5.",
            "difficulty": "medium",
            "solution": "Let's denote n^2 + 20 = k(n + 5) for some integer k.\nThis gives us n^2 + 20 = kn + 5k\nn^2 - kn - 5k + 20 = 0\nn^2 - kn = 5k - 20\nWe need to find values of n such that n^2 - kn = 5k - 20 has solutions.\nRearranging, we get n(n - k) = 5k - 20\nFor n > 0 and n + 5 to divide n^2 + 20, we need to check possible values.\nTrying n = 4: 4^2 + 20 = 36, 4 + 5 = 9, and 36 is divisible by 9. So n = 4 works.\nTrying n = 15: 15^2 + 20 = 245, 15 + 5 = 20, and 245 is not divisible by 20.\nAfter checking more values systematically, we find that n = 4 is the only positive integer solution."
        },
        {
            "problem": "Determine all real values of x such that log_(x-1)(x^2 - 5x + 7) = 2.",
            "difficulty": "hard",
            "solution": "For log_(x-1)(x^2 - 5x + 7) = 2 to be defined, we need:\n1) x - 1 > 0, so x > 1\n2) x - 1 ≠ 1, so x ≠ 2\n3) x^2 - 5x + 7 > 0\n\nNow, log_(x-1)(x^2 - 5x + 7) = 2 means (x^2 - 5x + 7) = (x-1)^2\n\nExpanding (x-1)^2 = (x-1)(x-1) = x^2 - 2x + 1\n\nSo we need to solve x^2 - 5x + 7 = x^2 - 2x + 1\n-5x + 7 = -2x + 1\n-3x = -6\nx = 2\n\nBut we already established x ≠ 2, so there are no solutions."
        }
    ],
    "geometry": [
        {
            "problem": "Points A, B, C, and D lie on a circle in that order. If AB = BC = CD and angle BAC = 30°, what is the measure of angle ADC in degrees?",
            "difficulty": "medium",
            "solution": "Since AB = BC = CD, we know that B divides arc AC into two equal parts, and C divides arc BD into two equal parts.\n\nLet's denote the center of the circle as O.\nSince AB = BC, triangles AOB and BOC are isosceles.\nThis means angle AOB = angle BOA and angle BOC = angle COB.\n\nWe know angle BAC = 30°.\nBy the inscribed angle theorem, angle BAC = (1/2) × (arc BC).\nSo arc BC = 60°.\n\nSince AB = BC = CD, arcs AB, BC, and CD all have the same length.\nThis means arc AB = arc BC = arc CD = 60°.\n\nBy the inscribed angle theorem, angle ADC = (1/2) × (arc AC).\nArc AC = arc AB + arc BC = 60° + 60° = 120°.\nTherefore, angle ADC = (1/2) × 120° = 60°."
        }
    ],
    "number_theory": [
        {
            "problem": "Find the sum of all positive integers n such that n^2 + n + 1 is divisible by 7.",
            "difficulty": "hard",
            "solution": "Let's consider n mod 7 and check when n^2 + n + 1 ≡ 0 (mod 7).\n\nFor n ≡ 0 (mod 7): 0^2 + 0 + 1 = 1 ≡ 1 (mod 7) ❌\nFor n ≡ 1 (mod 7): 1^2 + 1 + 1 = 3 ≡ 3 (mod 7) ❌\nFor n ≡ 2 (mod 7): 2^2 + 2 + 1 = 7 ≡ 0 (mod 7) ✓\nFor n ≡ 3 (mod 7): 3^2 + 3 + 1 = 13 ≡ 6 (mod 7) ❌\nFor n ≡ 4 (mod 7): 4^2 + 4 + 1 = 21 ≡ 0 (mod 7) ✓\nFor n ≡ 5 (mod 7): 5^2 + 5 + 1 = 31 ≡ 3 (mod 7) ❌\nFor n ≡ 6 (mod 7): 6^2 + 6 + 1 = 43 ≡ 1 (mod 7) ❌\n\nSo n^2 + n + 1 is divisible by 7 when n ≡ 2 (mod 7) or n ≡ 4 (mod 7).\n\nFor n ≤ 100, the positive integers that satisfy this are:\n2, 4, 9, 11, 16, 18, 23, 25, 30, 32, 37, 39, 44, 46, 51, 53, 58, 60, 65, 67, 72, 74, 79, 81, 86, 88, 93, 95, 100\n\nThe sum of these numbers is 1501."
        }
    ],
    "combinatorics": [
        {
            "problem": "How many different 4-digit numbers can be formed using the digits 1, 2, 3, 4, 5 without repetition?",
            "difficulty": "easy",
            "solution": "We need to create 4-digit numbers using 5 distinct digits without repetition.\n\nFor the first position, we have 5 choices (1, 2, 3, 4, or 5).\nFor the second position, we have 4 remaining choices.\nFor the third position, we have 3 remaining choices.\nFor the fourth position, we have 2 remaining choices.\n\nBy the multiplication principle, the total number of possible 4-digit numbers is:\n5 × 4 × 3 × 2 = 120"
        }
    ]
}

# Function to generate problem based on filters
def generate_problem(topic, difficulty):
    filtered_problems = [p for p in sample_problems.get(topic, []) if p["difficulty"] == difficulty]
    if filtered_problems:
        return random.choice(filtered_problems)["problem"]
    return "No problem found matching the criteria. Try a different combination."

# Function to solve problem using AI model
def solve_problem(problem_text):
    if not problem_text.strip():
        return "Please enter a problem first."
    
    prompt = f"Solve this math olympiad problem step by step:\n\n{problem_text}\n\nSolution:"
    
    # Add a small delay to simulate AI thinking (remove in production)
    time.sleep(2)
    
    inputs = tokenizer(prompt, return_tensors="pt")
    
    # In a real system, you would use your AI model here
    # outputs = model.generate(inputs["input_ids"], max_length=1024, temperature=0.7)
    # solution = tokenizer.decode(outputs[0], skip_special_tokens=True).split("Solution:")[1].strip()
    
    # For demo purposes, we'll provide a placeholder solution
    for topic in sample_problems:
        for problem in sample_problems[topic]:
            if problem["problem"] == problem_text:
                return problem["solution"]
    
    return "I'll solve this step-by-step:\n\n1. First, let's understand what the problem is asking...\n\n(This is a placeholder. In the actual implementation, the AI model would generate a detailed solution.)"

# Function to analyze student solution
def analyze_solution(problem, student_solution, ai_solution):
    if not student_solution.strip():
        return "Please enter your solution first."
    
    # In a real system, you would compare the solutions more intelligently
    # For demo purposes, we'll provide a placeholder analysis
    
    feedback = "Solution Analysis:\n\n"
    
    # Simple keyword checking (very basic, would be much more sophisticated in reality)
    ai_keywords = set([word.lower() for word in ai_solution.split() if len(word) > 4])
    student_keywords = set([word.lower() for word in student_solution.split() if len(word) > 4])
    
    common_keywords = ai_keywords.intersection(student_keywords)
    
    if len(common_keywords) / max(1, len(ai_keywords)) > 0.4:
        feedback += "✓ Your approach seems correct and contains many of the key concepts needed.\n\n"
    else:
        feedback += "⚠ Your approach may be missing some key concepts or taking a different direction.\n\n"
    
    # Check for solution steps
    if student_solution.count("\n") < 3:
        feedback += "⚠ Your solution could benefit from showing more steps and reasoning.\n\n"
    else:
        feedback += "✓ Good job showing your work step by step!\n\n"
    
    # Give general encouragement
    feedback += "Areas to focus on:\n"
    feedback += "- Consider whether you've addressed all constraints in the problem\n"
    feedback += "- Check if your solution is logically complete\n"
    feedback += "- Verify any algebraic manipulations\n\n"
    
    return feedback

# Function to generate practice schedule
def generate_schedule(topics, difficulty_level, hours_per_week, weeks):
    if not topics or not difficulty_level or not hours_per_week or not weeks:
        return "Please fill in all fields."
    
    # Create a DataFrame for the schedule
    schedule = []
    days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
    
    # Distribute topics across the schedule
    topics_cycle = topics.copy()
    random.shuffle(topics_cycle)
    
    # Calculate hours per day (simple distribution)
    hours_per_day = [hours_per_week // 5 if d in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] else 
                     hours_per_week // 10 for d in days]
    
    # Ensure the total equals hours per week
    while sum(hours_per_day) < hours_per_week:
        idx = random.randint(0, len(days)-1)
        hours_per_day[idx] += 1
    
    for week in range(1, weeks+1):
        for day_idx, day in enumerate(days):
            if hours_per_day[day_idx] > 0:
                topic = topics_cycle[week % len(topics_cycle)]
                schedule.append({
                    'Week': week,
                    'Day': day,
                    'Topic': topic,
                    'Hours': hours_per_day[day_idx],
                    'Difficulty': difficulty_level
                })
    
    df = pd.DataFrame(schedule)
    
    # Create a visualization
    fig, ax = plt.subplots(figsize=(10, 6))
    topic_hours = df.groupby('Topic')['Hours'].sum().reset_index()
    ax.bar(topic_hours['Topic'], topic_hours['Hours'])
    ax.set_title('Hours by Topic in Training Schedule')
    ax.set_xlabel('Topic')
    ax.set_ylabel('Total Hours')
    plt.xticks(rotation=45)
    plt.tight_layout()
    
    # Convert to HTML for display
    schedule_html = df.to_html(index=False)
    
    return fig, schedule_html

# Function to track progress
def update_progress(topic, correct, incorrect):
    # This would connect to a database in a real implementation
    # For now, we just return a visualization
    topics = ['Algebra', 'Geometry', 'Number Theory', 'Combinatorics', 'Calculus']
    correct_counts = [0, 0, 0, 0, 0]
    incorrect_counts = [0, 0, 0, 0, 0]
    
    # Update the counts based on input
    try:
        topic_idx = topics.index(topic)
        correct_counts[topic_idx] = int(correct)
        incorrect_counts[topic_idx] = int(incorrect)
    except:
        pass
    
    # Create the progress chart
    fig, ax = plt.subplots(figsize=(10, 6))
    
    x = np.arange(len(topics))
    width = 0.35
    
    ax.bar(x - width/2, correct_counts, width, label='Correct')
    ax.bar(x + width/2, incorrect_counts, width, label='Incorrect')
    
    # Add labels and legend
    ax.set_ylabel('Number of Problems')
    ax.set_title('Progress by Topic')
    ax.set_xticks(x)
    ax.set_xticklabels(topics)
    ax.legend()
    
    plt.tight_layout()
    
    # Calculate accuracy
    total = sum(correct_counts) + sum(incorrect_counts)
    accuracy = sum(correct_counts) / max(1, total) * 100
    
    return fig, f"Overall Accuracy: {accuracy:.1f}%"

# Function for the competition simulator
def simulate_competition(num_problems, difficulty, time_limit):
    if not num_problems or not difficulty or not time_limit:
        return "Please fill in all fields."
    
    # Generate a set of problems for the competition
    competition_problems = []
    for topic in sample_problems:
        filtered = [p for p in sample_problems[topic] if p["difficulty"] == difficulty]
        if filtered:
            competition_problems.extend(filtered[:min(2, len(filtered))])
    
    if len(competition_problems) > num_problems:
        competition_problems = random.sample(competition_problems, num_problems)
    
    # Format the problems
    formatted_problems = ""
    for i, p in enumerate(competition_problems, 1):
        formatted_problems += f"Problem {i}: {p['problem']}\n\n"
    
    # Calculate expected time per problem
    time_per_problem = time_limit / max(1, len(competition_problems))
    
    return f"Competition Simulation\n\nDifficulty: {difficulty}\nTime Limit: {time_limit} minutes\nRecommended time per problem: {time_per_problem:.1f} minutes\n\n{formatted_problems}"

# Create the Gradio interface
with gr.Blocks(title="AI Math Olympiad Trainer") as demo:
    gr.Markdown("# AI Math Olympiad Trainer System")
    
    with gr.Tab("Problem Generator"):
        gr.Markdown("### Generate and Solve Math Olympiad Problems")
        
        with gr.Row():
            with gr.Column():
                topic_dropdown = gr.Dropdown(
                    choices=["algebra", "geometry", "number_theory", "combinatorics"], 
                    label="Topic"
                )
                difficulty_dropdown = gr.Dropdown(
                    choices=["easy", "medium", "hard"], 
                    label="Difficulty"
                )
                generate_btn = gr.Button("Generate Problem")
            
            with gr.Column():
                problem_output = gr.Textbox(label="Problem", lines=5)
        
        with gr.Row():
            with gr.Column():
                solution_input = gr.Textbox(label="Your Solution", lines=10)
                analyze_btn = gr.Button("Analyze My Solution")
            
            with gr.Column():
                ai_solution_btn = gr.Button("Get AI Solution")
                ai_solution_output = gr.Textbox(label="AI Solution", lines=10)
                analysis_output = gr.Textbox(label="Analysis", lines=8)
    
    with gr.Tab("Training Schedule"):
        gr.Markdown("### Create a Personalized Training Schedule")
        
        with gr.Row():
            with gr.Column():
                topics_multiselect = gr.CheckboxGroup(
                    choices=["Algebra", "Geometry", "Number Theory", "Combinatorics", "Calculus"],
                    label="Select Topics"
                )
                difficulty_radio = gr.Radio(
                    choices=["easy", "medium", "hard", "mixed"],
                    label="Difficulty Level"
                )
                hours_slider = gr.Slider(
                    minimum=1, maximum=30, value=10, step=1,
                    label="Hours per Week"
                )
                weeks_slider = gr.Slider(
                    minimum=1, maximum=12, value=4, step=1,
                    label="Number of Weeks"
                )
                schedule_btn = gr.Button("Generate Schedule")
            
            with gr.Column():
                schedule_plot = gr.Plot(label="Hours Distribution")
                schedule_output = gr.HTML(label="Your Schedule")
    
    with gr.Tab("Progress Tracker"):
        gr.Markdown("### Track Your Progress")
        
        with gr.Row():
            with gr.Column():
                progress_topic = gr.Dropdown(
                    choices=["Algebra", "Geometry", "Number Theory", "Combinatorics", "Calculus"],
                    label="Topic"
                )
                correct_slider = gr.Slider(
                    minimum=0, maximum=50, value=0, step=1,
                    label="Correct Solutions"
                )
                incorrect_slider = gr.Slider(
                    minimum=0, maximum=50, value=0, step=1,
                    label="Incorrect Solutions"
                )
                update_btn = gr.Button("Update Progress")
            
            with gr.Column():
                progress_plot = gr.Plot(label="Progress Chart")
                accuracy_output = gr.Textbox(label="Accuracy")
    
    with gr.Tab("Competition Simulator"):
        gr.Markdown("### Simulate a Math Competition")
        
        with gr.Row():
            with gr.Column():
                problems_slider = gr.Slider(
                    minimum=1, maximum=10, value=3, step=1,
                    label="Number of Problems"
                )
                comp_difficulty = gr.Radio(
                    choices=["easy", "medium", "hard"],
                    label="Difficulty"
                )
                time_slider = gr.Slider(
                    minimum=15, maximum=180, value=60, step=15,
                    label="Time Limit (minutes)"
                )
                simulate_btn = gr.Button("Start Simulation")
            
            with gr.Column():
                simulation_output = gr.Textbox(label="Competition Problems", lines=15)
    
    # Connect the functions
    generate_btn.click(
        generate_problem, 
        inputs=[topic_dropdown, difficulty_dropdown], 
        outputs=problem_output
    )
    
    ai_solution_btn.click(
        solve_problem,
        inputs=[problem_output],
        outputs=ai_solution_output
    )
    
    analyze_btn.click(
        analyze_solution,
        inputs=[problem_output, solution_input, ai_solution_output],
        outputs=analysis_output
    )
    
    schedule_btn.click(
        generate_schedule,
        inputs=[topics_multiselect, difficulty_radio, hours_slider, weeks_slider],
        outputs=[schedule_plot, schedule_output]
    )
    
    update_btn.click(
        update_progress,
        inputs=[progress_topic, correct_slider, incorrect_slider],
        outputs=[progress_plot, accuracy_output]
    )
    
    simulate_btn.click(
        simulate_competition,
        inputs=[problems_slider, comp_difficulty, time_slider],
        outputs=simulation_output
    )

# Launch the app
demo.launch()