MasteredUltraInstinct commited on
Commit
ed6e039
·
verified ·
1 Parent(s): 918ee22

Upload 3 files

Browse files
Files changed (3) hide show
  1. image.py +478 -0
  2. linear.py +134 -0
  3. polynomial.py +158 -0
image.py ADDED
@@ -0,0 +1,478 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import sympy as sp
3
+ from pix2text import Pix2Text
4
+ from PIL import Image
5
+ import numpy as np
6
+ import matplotlib.pyplot as plt
7
+ import re
8
+ import io
9
+ import logging
10
+
11
+ # Configure logging for debugging
12
+ logging.basicConfig(level=logging.INFO)
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # Define symbolic variables
16
+ x, y = sp.symbols('x y')
17
+
18
+ # Initialize Pix2Text model globally
19
+ try:
20
+ p2t_model = Pix2Text.from_config()
21
+ logger.info("Pix2Text model loaded successfully")
22
+ except Exception as e:
23
+ logger.error(f"Failed to load Pix2Text model: {e}")
24
+ p2t_model = None
25
+
26
+ def clean_latex_expression(latex_str):
27
+ """Clean and normalize LaTeX expression for SymPy parsing"""
28
+ if not latex_str:
29
+ return ""
30
+
31
+ latex_str = latex_str.strip()
32
+ latex_str = re.sub(r'^\$\$|\$\$$', '', latex_str) # Remove $$ delimiters
33
+ latex_str = re.sub(r'\\[a-zA-Z]+\{([^}]*)\}', r'\1', latex_str) # Remove LaTeX commands
34
+ latex_str = re.sub(r'\\{2,}', r'\\', latex_str) # Fix multiple backslashes
35
+ latex_str = re.sub(r'\s+', ' ', latex_str) # Normalize whitespace
36
+ latex_str = re.sub(r'\^{([^}]+)}', r'**\1', latex_str) # Convert x^{n} to x**n
37
+ latex_str = re.sub(r'(\d*\.?\d+)\s*([xy])', r'\1*\2', latex_str) # Add multiplication: 1.0x -> 1.0*x
38
+ latex_str = re.sub(r'\s*([+\-*/=])\s*', r'\1', latex_str) # Remove spaces around operators
39
+ if '=' in latex_str:
40
+ left, right = latex_str.split('=')
41
+ latex_str = f"{left} - ({right})" # Move right-hand side to left
42
+ return latex_str.strip()
43
+
44
+ def parse_equation_type(latex_str):
45
+ """Determine if the equation is polynomial (single-variable) or linear system (two-variable)"""
46
+ try:
47
+ cleaned = clean_latex_expression(latex_str)
48
+ if not cleaned:
49
+ return 'polynomial'
50
+
51
+ # Check for two-variable system
52
+ if 'y' in cleaned and 'x' in cleaned:
53
+ if '\\\\' in latex_str or '\n' in latex_str or len(re.split(r'\\\\|\n|;', latex_str)) >= 2:
54
+ return 'linear_system'
55
+ return 'linear' # Single equation with x and y
56
+
57
+ # Check for single-variable polynomial
58
+ try:
59
+ expr = sp.sympify(cleaned.split('-')[0] if '-' in cleaned else cleaned)
60
+ if x in expr.free_symbols and y not in expr.free_symbols:
61
+ degree = sp.degree(expr, x)
62
+ return 'polynomial' if degree > 0 else 'linear'
63
+ elif x not in expr.free_symbols and y in expr.free_symbols:
64
+ return 'polynomial' # Treat as polynomial in y if x is absent
65
+ else:
66
+ return 'polynomial' # Default to polynomial if no clear variables
67
+ except:
68
+ if 'x**' in cleaned or '^' in latex_str:
69
+ return 'polynomial'
70
+ return 'polynomial' # Fallback to polynomial
71
+ except Exception as e:
72
+ logger.error(f"Error determining equation type: {e}")
73
+ return 'polynomial'
74
+
75
+ def extract_polynomial_coefficients(latex_str):
76
+ """Extract polynomial coefficients from LaTeX string"""
77
+ try:
78
+ cleaned = clean_latex_expression(latex_str)
79
+ if '-' in cleaned:
80
+ cleaned = cleaned.split('-')[0].strip() # Use left side for polynomial
81
+
82
+ expr = sp.sympify(cleaned, evaluate=False)
83
+ if x not in expr.free_symbols and y not in expr.free_symbols:
84
+ raise ValueError("No variable (x or y) found in expression")
85
+
86
+ variable = x if x in expr.free_symbols else y
87
+ degree = sp.degree(expr, variable)
88
+ if degree < 1 or degree > 8:
89
+ raise ValueError(f"Polynomial degree {degree} is out of supported range (1-8)")
90
+
91
+ poly = sp.Poly(expr, variable)
92
+ coeffs = [float(poly.coeff_monomial(variable**i)) for i in range(degree, -1, -1)]
93
+
94
+ return {
95
+ "type": "polynomial",
96
+ "degree": degree,
97
+ "coeffs": " ".join(map(str, coeffs)),
98
+ "latex": latex_str,
99
+ "success": True,
100
+ "variable": str(variable)
101
+ }
102
+ except Exception as e:
103
+ logger.error(f"Error extracting polynomial coefficients: {e}")
104
+ return {
105
+ "type": "polynomial",
106
+ "degree": 2,
107
+ "coeffs": "1 0 0",
108
+ "latex": latex_str,
109
+ "success": False,
110
+ "error": str(e),
111
+ "variable": "x"
112
+ }
113
+
114
+ def extract_linear_system_coefficients(latex_str):
115
+ """Extract linear system coefficients from LaTeX string"""
116
+ try:
117
+ cleaned = clean_latex_expression(latex_str)
118
+ equations = re.split(r'\\\\|\n|;', latex_str)
119
+ if len(equations) < 2:
120
+ equations = re.split(r'(?<=[0-9])\s*(?=[+-]?\s*[0-9]*[xy])', cleaned)
121
+
122
+ if len(equations) < 2 or 'y' not in cleaned or 'x' not in cleaned:
123
+ raise ValueError("Could not find two equations or two variables (x, y) in system")
124
+
125
+ eq1_str = equations[0].strip()
126
+ eq2_str = equations[1].strip()
127
+
128
+ def parse_linear_eq(eq_str):
129
+ if '-' not in eq_str:
130
+ raise ValueError("No equals sign (converted to '-') found")
131
+ left, right = eq_str.split('-')
132
+ expr = sp.sympify(left) - sp.sympify(right or '0')
133
+ a = float(expr.coeff(x, 1)) if expr.coeff(x, 1) else 0
134
+ b = float(expr.coeff(y, 1)) if expr.coeff(y, 1) else 0
135
+ c = float(-expr.as_coefficients_dict()[1]) if 1 in expr.as_coefficients_dict() else 0
136
+ return f"{a} {b} {c}"
137
+
138
+ eq1_coeffs = parse_linear_eq(eq1_str)
139
+ eq2_coeffs = parse_linear_eq(eq2_str)
140
+
141
+ return {
142
+ "type": "linear",
143
+ "eq1_coeffs": eq1_coeffs,
144
+ "eq2_coeffs": eq2_coeffs,
145
+ "latex": latex_str,
146
+ "success": True
147
+ }
148
+ except Exception as e:
149
+ logger.error(f"Error extracting linear system coefficients: {e}")
150
+ return {
151
+ "type": "linear",
152
+ "eq1_coeffs": "1 1 3",
153
+ "eq2_coeffs": "1 -1 1",
154
+ "latex": latex_str,
155
+ "success": False,
156
+ "error": str(e)
157
+ }
158
+
159
+ def extract_equation_from_image(image_file):
160
+ """Extract equation from image using Pix2Text"""
161
+ try:
162
+ if p2t_model is None:
163
+ return {
164
+ "type": "error",
165
+ "latex": "Pix2Text model not loaded. Please check installation.",
166
+ "success": False
167
+ }
168
+
169
+ if image_file is None:
170
+ return {
171
+ "type": "error",
172
+ "latex": "No image file provided.",
173
+ "success": False
174
+ }
175
+
176
+ if isinstance(image_file, str):
177
+ image = Image.open(image_file)
178
+ else:
179
+ image = Image.open(image_file.name)
180
+
181
+ if image.mode != 'RGB':
182
+ image = image.convert('RGB')
183
+
184
+ logger.info(f"Processing image of size: {image.size}")
185
+
186
+ result = p2t_model.recognize_text_formula(image)
187
+ if not result or result.strip() == "":
188
+ return {
189
+ "type": "error",
190
+ "latex": "No text or formulas detected in the image.",
191
+ "success": False
192
+ }
193
+
194
+ logger.info(f"Extracted text: {result}")
195
+
196
+ eq_type = parse_equation_type(result)
197
+ if eq_type == 'polynomial':
198
+ return extract_polynomial_coefficients(result)
199
+ elif eq_type == 'linear_system':
200
+ return extract_linear_system_coefficients(result)
201
+ else:
202
+ return {
203
+ "type": "error",
204
+ "latex": f"Unsupported equation type detected: {eq_type}",
205
+ "success": False
206
+ }
207
+ except Exception as e:
208
+ logger.error(f"Error processing image: {e}")
209
+ return {
210
+ "type": "error",
211
+ "latex": f"Error processing image: {str(e)}",
212
+ "success": False
213
+ }
214
+
215
+ def solve_polynomial(degree, coeff_string, real_only):
216
+ """Solve polynomial equation"""
217
+ try:
218
+ coeffs = list(map(float, coeff_string.strip().split()))
219
+ if len(coeffs) != degree + 1:
220
+ return f"⚠️ Please enter exactly {degree + 1} coefficients.", None, None
221
+
222
+ poly = sum([coeffs[i] * x**(degree - i) for i in range(degree + 1)])
223
+ simplified = sp.simplify(poly)
224
+ factored = sp.factor(simplified)
225
+ roots = sp.solve(sp.Eq(simplified, 0), x)
226
+
227
+ if real_only:
228
+ roots = [r for r in roots if sp.im(r) == 0]
229
+
230
+ roots_output = "$$\n" + "\\ ".join(
231
+ [f"r_{{{i}}} = {sp.latex(sp.nsimplify(r, rational=True))}" for i, r in enumerate(roots, 1)]
232
+ ) + "\n$$"
233
+
234
+ steps_output = f"""
235
+ ### Polynomial Expression
236
+ $$ {sp.latex(poly)} = 0 $$
237
+ ### Simplified
238
+ $$ {sp.latex(simplified)} = 0 $$
239
+ ### Factored
240
+ $$ {sp.latex(factored)} = 0 $$
241
+ ### Roots {'(Only Real)' if real_only else '(All Roots)'}
242
+ {roots_output}
243
+ """
244
+
245
+ x_vals = np.linspace(-10, 10, 400)
246
+ y_vals = np.polyval(coeffs, x_vals)
247
+
248
+ fig, ax = plt.subplots(figsize=(6, 4))
249
+ ax.plot(x_vals, y_vals, label="Polynomial", color="blue")
250
+ ax.axhline(0, color='black', linewidth=0.5)
251
+ ax.axvline(0, color='black', linewidth=0.5)
252
+ ax.grid(True)
253
+ ax.set_title("Graph of the Polynomial")
254
+ ax.set_xlabel("x")
255
+ ax.set_ylabel("f(x)")
256
+ ax.legend()
257
+
258
+ return steps_output, fig, ""
259
+ except Exception as e:
260
+ return f"❌ Error: {e}", None, ""
261
+
262
+ def solve_linear_system_from_coeffs(eq1_str, eq2_str):
263
+ """Solve linear system"""
264
+ try:
265
+ coeffs1 = list(map(float, eq1_str.strip().split()))
266
+ coeffs2 = list(map(float, eq2_str.strip().split()))
267
+
268
+ if len(coeffs1) != 3 or len(coeffs2) != 3:
269
+ return "⚠️ Please enter exactly 3 coefficients for each equation.", None, None, None
270
+
271
+ a1, b1, c1 = coeffs1
272
+ a2, b2, c2 = coeffs2
273
+
274
+ eq1 = sp.Eq(a1 * x + b1 * y, c1)
275
+ eq2 = sp.Eq(a2 * x + b2 * y, c2)
276
+
277
+ sol = sp.solve([eq1, eq2], (x, y), dict=True)
278
+ if not sol:
279
+ return "❌ No unique solution.", None, None, None
280
+
281
+ solution = sol[0]
282
+ eq_latex = f"$$ {sp.latex(eq1)} \\ {sp.latex(eq2)} $$"
283
+
284
+ steps = rf"""
285
+ ### Step-by-step Solution
286
+ 1. **Original Equations:**
287
+ $$ {sp.latex(eq1)} $$
288
+ $$ {sp.latex(eq2)} $$
289
+ 2. **Standard Form:** Already provided.
290
+ 3. **Solve using SymPy `solve`:** Internally applies substitution/elimination.
291
+ 4. **Solve for `x` and `y`:**
292
+ $$ x = {sp.latex(solution[x])}, \quad y = {sp.latex(solution[y])} $$
293
+ 5. **Verification:** Substitute back into both equations."""
294
+
295
+ x_vals = np.linspace(-10, 10, 400)
296
+ f1 = sp.solve(eq1, y)
297
+ f2 = sp.solve(eq2, y)
298
+
299
+ fig, ax = plt.subplots()
300
+ if f1:
301
+ f1_func = sp.lambdify(x, f1[0], modules='numpy')
302
+ ax.plot(x_vals, f1_func(x_vals), label=sp.latex(eq1))
303
+ if f2:
304
+ f2_func = sp.lambdify(x, f2[0], modules='numpy')
305
+ ax.plot(x_vals, f2_func(x_vals), label=sp.latex(eq2))
306
+
307
+ ax.plot(solution[x], solution[y], 'ro', label=f"Solution ({solution[x]}, {solution[y]})")
308
+ ax.axhline(0, color='black', linewidth=0.5)
309
+ ax.axvline(0, color='black', linewidth=0.5)
310
+ ax.legend()
311
+ ax.set_title("Graph of the Linear System")
312
+ ax.grid(True)
313
+
314
+ return eq_latex, steps, fig, ""
315
+ except Exception as e:
316
+ return f"❌ Error: {e}", None, None, None
317
+
318
+ def solve_extracted_equation(eq_data, real_only):
319
+ """Route to appropriate solver based on equation type"""
320
+ if eq_data["type"] == "polynomial":
321
+ return solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)
322
+ elif eq_data["type"] == "linear":
323
+ return "❌ Single linear equation not supported. Please upload a system of equations.", None, ""
324
+ elif eq_data["type"] == "linear_system":
325
+ return solve_linear_system_from_coeffs(eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])
326
+ else:
327
+ return "❌ Unknown equation type", None, ""
328
+
329
+ def image_tab():
330
+ """Create the Image Upload Solver tab"""
331
+ with gr.Tab("Image Upload Solver"):
332
+ gr.Markdown("## Solve Equations from Image")
333
+
334
+ with gr.Row():
335
+ image_input = gr.File(
336
+ label="Upload Question Image",
337
+ file_types=[".pdf", ".png", ".jpg", ".jpeg"],
338
+ file_count="single"
339
+ )
340
+ image_upload_btn = gr.Button("Process Image")
341
+
342
+ gr.Markdown("**Supported Formats:** .pdf, .png, .jpg, .jpeg")
343
+
344
+ with gr.Row():
345
+ real_image_checkbox = gr.Checkbox(label="Show Only Real Roots (for Polynomials)", value=False)
346
+ preview_image_btn = gr.Button("Preview Equation")
347
+
348
+ image_equation_display = gr.Markdown()
349
+
350
+ with gr.Row():
351
+ confirm_image_btn = gr.Button("Display Solution", visible=False)
352
+ edit_image_btn = gr.Button("Make Changes Manually", visible=False)
353
+
354
+ edit_latex_input = gr.Textbox(label="Edit LaTeX Equation", visible=False, lines=3)
355
+ save_edit_btn = gr.Button("Save Changes", visible=False)
356
+
357
+ image_steps_md = gr.Markdown()
358
+ image_plot_output = gr.Plot()
359
+ extracted_eq_state = gr.State()
360
+
361
+ def handle_image_upload(image_file):
362
+ """Handle image upload and initial processing"""
363
+ if image_file is None:
364
+ return "", None, "", None, None
365
+
366
+ try:
367
+ eq_data = extract_equation_from_image(image_file)
368
+ if eq_data["success"]:
369
+ return "", eq_data, "", None, None
370
+ else:
371
+ return "", eq_data, "", None, None
372
+ except Exception as e:
373
+ return "", None, "", None, None
374
+
375
+ image_upload_btn.click(
376
+ fn=handle_image_upload,
377
+ inputs=[image_input],
378
+ outputs=[image_equation_display, extracted_eq_state, image_steps_md,
379
+ image_plot_output, edit_latex_input]
380
+ )
381
+
382
+ def preview_image_equation(eq_data, real_only):
383
+ """Preview the extracted equation"""
384
+ if eq_data is None:
385
+ return ("⚠️ No equation data available. Please upload and process an image first.",
386
+ gr.update(visible=False), gr.update(visible=False), "", None)
387
+
388
+ if eq_data["type"] == "error":
389
+ return (eq_data["latex"], gr.update(visible=False), gr.update(visible=False), "", None)
390
+
391
+ if eq_data["type"] == "polynomial":
392
+ eq_type_display = "Polynomial Equation"
393
+ elif eq_data["type"] == "linear_system":
394
+ eq_type_display = "Linear System"
395
+ else:
396
+ eq_type_display = "Unknown Equation Type"
397
+
398
+ preview_text = f"""
399
+ ### ✅ Confirm {eq_type_display}
400
+ **Extracted LaTeX:** {eq_data['latex']}
401
+ """
402
+
403
+ return (preview_text, gr.update(visible=True), gr.update(visible=True), "", None)
404
+
405
+ preview_image_btn.click(
406
+ fn=preview_image_equation,
407
+ inputs=[extracted_eq_state, real_image_checkbox],
408
+ outputs=[image_equation_display, confirm_image_btn, edit_image_btn,
409
+ image_steps_md, image_plot_output]
410
+ )
411
+
412
+ def confirm_image_solution(eq_data, real_only):
413
+ """Confirm and solve the extracted equation"""
414
+ if eq_data is None or eq_data["type"] == "error":
415
+ return "⚠️ No valid equation to solve.", None, ""
416
+
417
+ try:
418
+ steps, plot, error = solve_extracted_equation(eq_data, real_only)
419
+ return steps, plot, ""
420
+ except Exception as e:
421
+ return f"❌ Error solving equation: {str(e)}", None, ""
422
+
423
+ confirm_image_btn.click(
424
+ fn=confirm_image_solution,
425
+ inputs=[extracted_eq_state, real_image_checkbox],
426
+ outputs=[image_steps_md, image_plot_output, image_equation_display]
427
+ )
428
+
429
+ def enable_manual_edit(eq_data):
430
+ """Enable manual editing of the equation"""
431
+ if eq_data is None:
432
+ latex_value = "No equation to edit. Please upload an image first."
433
+ elif eq_data["type"] == "error":
434
+ latex_value = "Error in extraction. Please enter your equation manually."
435
+ else:
436
+ latex_value = eq_data.get("latex", "")
437
+
438
+ return (gr.update(visible=True, value=latex_value),
439
+ gr.update(visible=True),
440
+ gr.update(visible=False),
441
+ gr.update(visible=False))
442
+
443
+ edit_image_btn.click(
444
+ fn=enable_manual_edit,
445
+ inputs=[extracted_eq_state],
446
+ outputs=[edit_latex_input, save_edit_btn, confirm_image_btn, edit_image_btn]
447
+ )
448
+
449
+ def save_manual_changes(latex_input, real_only):
450
+ """Save manual changes and solve"""
451
+ try:
452
+ if not latex_input or latex_input.strip() == "":
453
+ return "⚠️ Please enter a valid equation.", None, ""
454
+
455
+ eq_type = parse_equation_type(latex_input)
456
+ if eq_type == 'polynomial':
457
+ eq_data = extract_polynomial_coefficients(latex_input)
458
+ steps, plot, error = solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)
459
+ elif eq_type == 'linear_system':
460
+ eq_data = extract_linear_system_coefficients(latex_input)
461
+ eq_latex, steps, plot, error = solve_linear_system_from_coeffs(
462
+ eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])
463
+ else:
464
+ return "❌ Unsupported equation type", None, ""
465
+
466
+ return steps, plot, ""
467
+ except Exception as e:
468
+ return f"❌ Error parsing manual input: {str(e)}", None, ""
469
+
470
+ save_edit_btn.click(
471
+ fn=save_manual_changes,
472
+ inputs=[edit_latex_input, real_image_checkbox],
473
+ outputs=[image_steps_md, image_plot_output, image_equation_display]
474
+ )
475
+
476
+ return (image_input, image_upload_btn, real_image_checkbox, preview_image_btn,
477
+ image_equation_display, confirm_image_btn, edit_image_btn, edit_latex_input,
478
+ save_edit_btn, image_steps_md, image_plot_output, extracted_eq_state)
linear.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import sympy as sp
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+ import random
6
+
7
+ x, y = sp.symbols('x y')
8
+
9
+ def generate_linear_template():
10
+ return "$$ a_1x + b_1y = c_1 \\ a_2x + b_2y = c_2 $$"
11
+
12
+ def load_linear_example():
13
+ examples = [
14
+ ("1 -4 -2", "5 1 9"),
15
+ ("2 1 8", "1 -1 2"),
16
+ ("3 2 12", "1 1 5"),
17
+ ("4 -1 3", "2 3 6"),
18
+ ("1 2 10", "3 -1 5")
19
+ ]
20
+ return random.choice(examples)
21
+
22
+ def solve_linear_system_from_coeffs(eq1_str, eq2_str):
23
+ try:
24
+ coeffs1 = list(map(float, eq1_str.strip().split()))
25
+ coeffs2 = list(map(float, eq2_str.strip().split()))
26
+
27
+ if len(coeffs1) != 3 or len(coeffs2) != 3:
28
+ return "⚠️ Please enter exactly 3 coefficients for each equation.", None, None, None
29
+
30
+ a1, b1, c1 = coeffs1
31
+ a2, b2, c2 = coeffs2
32
+
33
+ eq1 = sp.Eq(a1 * x + b1 * y, c1)
34
+ eq2 = sp.Eq(a2 * x + b2 * y, c2)
35
+
36
+ sol = sp.solve([eq1, eq2], (x, y), dict=True)
37
+ if not sol:
38
+ return "❌ No unique solution.", None, None, None
39
+
40
+ solution = sol[0]
41
+ eq_latex = f"$$ {sp.latex(eq1)} \\ {sp.latex(eq2)} $$"
42
+
43
+ steps = rf"""
44
+ ### Step-by-step Solution
45
+ 1. **Original Equations:**
46
+ $$ {sp.latex(eq1)} $$
47
+ $$ {sp.latex(eq2)} $$
48
+ 2. **Standard Form:** Already provided.
49
+ 3. **Solve using SymPy `solve`:** Internally applies substitution/elimination.
50
+ 4. **Solve for `x` and `y`:**
51
+ $$ x = {sp.latex(solution[x])}, \quad y = {sp.latex(solution[y])} $$
52
+ 5. **Verification:** Substitute back into both equations."""
53
+
54
+ x_vals = np.linspace(-10, 10, 400)
55
+ f1 = sp.solve(eq1, y)
56
+ f2 = sp.solve(eq2, y)
57
+
58
+ fig, ax = plt.subplots()
59
+ if f1:
60
+ f1_func = sp.lambdify(x, f1[0], modules='numpy')
61
+ ax.plot(x_vals, f1_func(x_vals), label=sp.latex(eq1))
62
+ if f2:
63
+ f2_func = sp.lambdify(x, f2[0], modules='numpy')
64
+ ax.plot(x_vals, f2_func(x_vals), label=sp.latex(eq2))
65
+
66
+ ax.plot(solution[x], solution[y], 'ro', label=f"Solution ({solution[x]}, {solution[y]})")
67
+ ax.axhline(0, color='black', linewidth=0.5)
68
+ ax.axvline(0, color='black', linewidth=0.5)
69
+ ax.legend()
70
+ ax.set_title("Graph of the Linear System")
71
+ ax.grid(True)
72
+
73
+ return eq_latex, steps, fig, ""
74
+ except Exception as e:
75
+ return f"❌ Error: {e}", None, None, None
76
+
77
+ def linear_tab():
78
+ with gr.Tab("Linear System Solver"):
79
+ gr.Markdown("## Solve 2x2 Linear System")
80
+ linear_template = gr.Markdown(value=generate_linear_template())
81
+
82
+ with gr.Row():
83
+ linear_eq1_input = gr.Textbox(label="Equation 1 Coefficients (a1 b1 c1)", placeholder="e.g. 2 1 8")
84
+ linear_eq2_input = gr.Textbox(label="Equation 2 Coefficients (a2 b2 c2)", placeholder="e.g. 1 -1 2")
85
+
86
+ linear_example_btn = gr.Button("Load Example")
87
+ preview_button = gr.Button("Preview Equations")
88
+
89
+ linear_equation_display = gr.Markdown()
90
+ with gr.Row():
91
+ confirm_btn = gr.Button("Display Solution", visible=False)
92
+ cancel_btn = gr.Button("Make Changes in Equation", visible=False)
93
+
94
+ linear_steps_md = gr.Markdown()
95
+ linear_plot = gr.Plot()
96
+ linear_error = gr.Textbox(visible=False)
97
+
98
+ def update_example():
99
+ eq1, eq2 = load_linear_example()
100
+ return eq1, eq2
101
+
102
+ linear_example_btn.click(fn=update_example, inputs=[], outputs=[linear_eq1_input, linear_eq2_input])
103
+
104
+ def preview_equations(eq1_str, eq2_str):
105
+ try:
106
+ coeffs1 = list(map(float, eq1_str.strip().split()))
107
+ coeffs2 = list(map(float, eq2_str.strip().split()))
108
+ if len(coeffs1) != 3 or len(coeffs2) != 3:
109
+ return "⚠️ Please enter exactly 3 coefficients for each equation.", gr.update(visible=False), gr.update(visible=False)
110
+ a1, b1, c1 = coeffs1
111
+ a2, b2, c2 = coeffs2
112
+ eq1 = sp.Eq(a1 * x + b1 * y, c1)
113
+ eq2 = sp.Eq(a2 * x + b2 * y, c2)
114
+ eq_latex = f"### ✅ Confirm Equations\n\n$$ {sp.latex(eq1)} \\\\ {sp.latex(eq2)} $$"
115
+ return eq_latex, gr.update(visible=True), gr.update(visible=True)
116
+ except Exception as e:
117
+ return f"❌ Error parsing equations: {e}", gr.update(visible=False), gr.update(visible=False)
118
+
119
+ preview_button.click(
120
+ fn=preview_equations,
121
+ inputs=[linear_eq1_input, linear_eq2_input],
122
+ outputs=[linear_equation_display, confirm_btn, cancel_btn]
123
+ )
124
+
125
+ cancel_btn.click(
126
+ fn=lambda: (gr.update(visible=False), gr.update(visible=False), "", None, None),
127
+ outputs=[confirm_btn, cancel_btn, linear_equation_display, linear_steps_md, linear_plot]
128
+ )
129
+
130
+ confirm_btn.click(
131
+ fn=solve_linear_system_from_coeffs,
132
+ inputs=[linear_eq1_input, linear_eq2_input],
133
+ outputs=[linear_equation_display, linear_steps_md, linear_plot, linear_error]
134
+ )
polynomial.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import sympy as sp
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+
6
+ # Define symbolic variable for polynomial operations
7
+ x = sp.symbols('x')
8
+
9
+ # Function to generate a LaTeX template for a polynomial based on its degree
10
+ def generate_polynomial_template(degree):
11
+ terms = [f"a_{{{i}}}x^{degree - i}" for i in range(degree)]
12
+ terms.append(f"a_{{{degree}}}")
13
+ return "$$" + " + ".join(terms) + " = 0$$"
14
+
15
+ # Function to load example coefficients for a given polynomial degree
16
+ def load_poly_example(degree):
17
+ examples = {
18
+ 1: "3 9",
19
+ 2: "1 -3 2",
20
+ 3: "1 -6 11 -6",
21
+ 4: "1 0 -5 0 4",
22
+ 5: "1 -9 3 8 1 8",
23
+ 6: "1 -9 3 8 1 8 3",
24
+ 7: "1 -9 3 8 1 8 6 2",
25
+ 8: "1 -9 3 8 1 8 2 3 7"
26
+ }
27
+ return examples.get(degree, "")
28
+
29
+ # Function to solve the polynomial equation and generate a graph
30
+ def solve_polynomial(degree, coeff_string, real_only):
31
+ try:
32
+ coeffs = list(map(float, coeff_string.strip().split()))
33
+ if len(coeffs) != degree + 1:
34
+ return f"⚠️ Please enter exactly {degree + 1} coefficients.", None, None
35
+
36
+ poly = sum([coeffs[i] * x**(degree - i) for i in range(degree + 1)])
37
+ simplified = sp.simplify(poly)
38
+ factored = sp.factor(simplified)
39
+ roots = sp.solve(sp.Eq(simplified, 0), x)
40
+
41
+ if real_only:
42
+ roots = [r for r in roots if sp.im(r) == 0]
43
+
44
+ roots_output = "$$\n" + "\\ ".join(
45
+ [f"r_{{{i}}} = {sp.latex(sp.nsimplify(r, rational=True))}" for i, r in enumerate(roots, 1)]
46
+ ) + "\n$$"
47
+
48
+ steps_output = f"""
49
+ ### Polynomial Expression
50
+ $$ {sp.latex(poly)} = 0 $$
51
+ ### Simplified
52
+ $$ {sp.latex(simplified)} = 0 $$
53
+ ### Factored
54
+ $$ {sp.latex(factored)} = 0 $$
55
+ ### Roots {'(Only Real)' if real_only else '(All Roots)'}
56
+ {roots_output}
57
+ """
58
+
59
+ x_vals = np.linspace(-10, 10, 400)
60
+ y_vals = np.polyval(coeffs, x_vals)
61
+
62
+ fig, ax = plt.subplots(figsize=(6, 4))
63
+ ax.plot(x_vals, y_vals, label="Polynomial", color="blue")
64
+ ax.axhline(0, color='black', linewidth=0.5)
65
+ ax.axvline(0, color='black', linewidth=0.5)
66
+ ax.grid(True)
67
+ ax.set_title("Graph of the Polynomial")
68
+ ax.set_xlabel("x")
69
+ ax.set_ylabel("f(x)")
70
+ ax.legend()
71
+
72
+ return steps_output, fig, ""
73
+ except Exception as e:
74
+ return f"❌ Error: {e}", None, ""
75
+
76
+ # Function to create the Polynomial Solver tab with Gradio components
77
+ def polynomial_tab():
78
+ with gr.Tab("Polynomial Solver"):
79
+ # Row for displaying the equation template and real roots checkbox
80
+ with gr.Row():
81
+ template_display = gr.Markdown(value=generate_polynomial_template(2))
82
+ real_checkbox = gr.Checkbox(label="Show Only Real Roots", value=False)
83
+
84
+ # Row for selecting the polynomial degree and entering coefficients
85
+ with gr.Row():
86
+ degree_slider = gr.Slider(1, 8, value=2, step=1, label="Select Degree of Polynomial Equation")
87
+ coeff_input = gr.Textbox(label="Enter Coefficients (space-separated)", placeholder="e.g. 1 -3 2")
88
+
89
+ # Row for example and preview buttons
90
+ with gr.Row():
91
+ example_btn = gr.Button("Load Example")
92
+ preview_poly_button = gr.Button("Preview Equation")
93
+
94
+ # Row for displaying the confirmed equation
95
+ with gr.Row():
96
+ poly_equation_display = gr.Markdown()
97
+
98
+ # Row for confirm and cancel buttons
99
+ with gr.Row():
100
+ confirm_poly_btn = gr.Button("Display Solution", visible=False)
101
+ cancel_poly_btn = gr.Button("Make Changes in Equation", visible=False)
102
+
103
+ # Markdown component to display step-by-step solution
104
+ steps_md = gr.Markdown()
105
+
106
+ # Plot component to display the polynomial graph
107
+ plot_output = gr.Plot()
108
+
109
+ # Textbox to display errors (initially hidden)
110
+ error_box = gr.Textbox(visible=False)
111
+
112
+ # Function to preview the polynomial equation based on user input
113
+ def preview_polynomial(degree, coeff_string, real_only):
114
+ try:
115
+ coeffs = list(map(float, coeff_string.strip().split()))
116
+ if len(coeffs) != degree + 1:
117
+ return f"⚠️ Please enter exactly {degree + 1} coefficients.", gr.update(visible=False), gr.update(visible=False), "", None
118
+ poly = sum([coeffs[i] * x**(degree - i) for i in range(degree + 1)])
119
+ eq_latex = f"### ✅ Confirm Polynomial\n\n$$ {sp.latex(poly)} = 0 $$"
120
+ return eq_latex, gr.update(visible=True), gr.update(visible=True), "", None
121
+ except Exception as e:
122
+ return f"❌ Error parsing coefficients: {e}", gr.update(visible=False), gr.update(visible=False), "", None
123
+
124
+ # Event handler for preview button click
125
+ preview_poly_button.click(
126
+ fn=preview_polynomial,
127
+ inputs=[degree_slider, coeff_input, real_checkbox],
128
+ outputs=[poly_equation_display, confirm_poly_btn, cancel_poly_btn, steps_md, plot_output]
129
+ )
130
+
131
+ # Function to handle cancellation of the preview
132
+ def cancel_poly():
133
+ return gr.update(visible=False), gr.update(visible=False), "", "", None
134
+
135
+ # Event handler for cancel button click
136
+ cancel_poly_btn.click(
137
+ fn=cancel_poly,
138
+ inputs=[],
139
+ outputs=[confirm_poly_btn, cancel_poly_btn, poly_equation_display, steps_md, plot_output]
140
+ )
141
+
142
+ # Event handler for confirm button click to solve and display results
143
+ confirm_poly_btn.click(
144
+ fn=solve_polynomial,
145
+ inputs=[degree_slider, coeff_input, real_checkbox],
146
+ outputs=[steps_md, plot_output, error_box]
147
+ )
148
+
149
+ # Event handler to update the template when the degree slider changes
150
+ degree_slider.change(fn=generate_polynomial_template, inputs=degree_slider, outputs=template_display)
151
+
152
+ # Event handler to load an example when the example button is clicked
153
+ example_btn.click(fn=load_poly_example, inputs=degree_slider, outputs=coeff_input)
154
+
155
+ # Initialize the template display with the default degree (2) on load
156
+ template_display.value = generate_polynomial_template(2)
157
+
158
+ return template_display, real_checkbox, degree_slider, coeff_input, example_btn, preview_poly_button, poly_equation_display, confirm_poly_btn, cancel_poly_btn, steps_md, plot_output, error_box