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()