SystemSolver / app.py
MasteredUltraInstinct's picture
Update app.py
088b5a9 verified
Raw
History Blame Contribute Delete
6.96 kB
import gradio as gr
from PIL import Image
from pix2tex.cli import LatexOCR
import sympy as sp
from sympy.parsing.latex import parse_latex
import re
model = LatexOCR()
def preprocess_handwritten_image(pil_img):
return pil_img.convert('RGB')
# --- Tab 1 cleaner (UNCHANGED) ---
def clean_latex(latex):
latex = latex.replace('\\ ', '')
latex = latex.replace('\\\\', '\\')
latex = re.sub(r'\\[ \t\n\r\f\v]*', '', latex)
latex = re.sub(r'\\([+\-=])', r'\1', latex)
replacements = {
r'\\chi': 'x', r'chi': 'x',
r'\\xi': 'x', r'xi': 'x',
r'\\alpha': 'x', r'alpha': 'x',
r'\\beta': 'b', r'beta': 'b',
r'\\gamma': 'y', r'gamma': 'y',
r'\\vartheta': '3', r'vartheta': '3',
r'\\mathcalW': 'x', r'mathcalW': 'x',
r'\\pi': 'pi', r'pi': 'pi',
r'\\mathrm': '', r'mathrm': '',
}
for wrong, correct in replacements.items():
latex = re.sub(wrong, correct, latex)
latex = re.sub(r'\\(cal|mathcal)\s*\{?\s*[Xx]\s*\}?', 'x', latex)
latex = re.sub(r'\\(cal|mathcal)\s*\{?\s*[Yy]\s*\}?', 'y', latex)
latex = re.sub(r'\\(cal|mathcal)\s*\{?\s*[Zz]\s*\}?', 'z', latex)
latex = latex.replace('cal x', 'x').replace('cal X', 'x')
latex = latex.replace('mathcal x', 'x').replace('mathcal X', 'x')
latex = latex.replace('{', '').replace('}', '')
latex = latex.strip().rstrip(',.')
latex = re.sub(r'(?<![a-zA-Z0-9])e(?![a-zA-Z0-9])', 'E', latex)
latex = re.sub(r'(\d)([a-zA-Z])', r'\1*\2', latex)
latex = re.sub(r'(\d+)\s*i', r'\1*I', latex)
latex = re.sub(r'(?<![a-zA-Z0-9])i(?![a-zA-Z0-9])', 'I', latex)
latex = re.sub(r'\(([^()]+?)\)\s*([a-zA-Z](\^\d+)?)', r'(\1)*\2', latex)
latex = latex.replace(r'\cdot', '*')
latex = latex.replace('βˆ’', '-')
latex = re.sub(r'[^\w\s^=+*\-().]', '', latex)
if '=' not in latex:
latex += '=0'
latex = latex.replace('pi', '3.1416')
latex = latex.replace('e', '2.7183')
latex = latex.replace('E', '2.7183')
return latex
# --- Tab 2 cleaner ---
def clean_latex2(latex):
latex = latex.replace('\\ ', '')
latex = latex.replace('\\\\', '\\')
latex = re.sub(r'\\[ \t\n\r\f\v]*', '', latex)
latex = latex.replace(r'\times', 'x')
latex = latex.replace(r'\cdot', '*')
latex = latex.replace('βˆ’', '-').replace('–', '-')
latex = re.sub(r'\\(text|mathbf|mathrm|mathit|textbf|mathcal|cal)\s*\{([^{}]+)\}', r'\2', latex)
latex = re.sub(r'\\[a-zA-Z]+', '', latex)
latex = re.sub(r'\{+\}+', '', latex)
latex = re.sub(r'\{([^\{\}]*)\}', r'\1', latex)
latex = re.sub(r'[;,]', '\n', latex)
latex = re.sub(r'[^\w\s=+\-*/().]', '', latex)
latex = re.sub(r'\s+', ' ', latex)
latex = re.sub(r'(?<![=<>])=(?![=<>])', ' = ', latex)
replacements = {
'chi': 'x', 'xi': 'x', 'alpha': 'x', 'beta': 'b',
'gamma': 'y', 'vartheta': '3', 'mathcal': '',
'cal': '', 'mathrm': ''
}
for wrong, right in replacements.items():
latex = re.sub(wrong, right, latex)
return latex.strip()
# --- Polynomial Solver (unchanged) ---
def solve_polynomial(image):
try:
img = preprocess_handwritten_image(image)
latex_result = model(img)
if not latex_result or len(latex_result.strip()) < 2:
return "❌ Could not extract valid LaTeX from image.", "", ""
cleaned_latex = clean_latex(latex_result)
expr = parse_latex(cleaned_latex)
expr = expr.subs(sp.pi, sp.Float(3.1416)).subs(sp.E, sp.Float(2.7183))
if not isinstance(expr, sp.Equality):
raise ValueError("Expression is not an equation.")
lhs = expr.lhs - expr.rhs
if not lhs.is_polynomial():
raise ValueError("Not a polynomial")
output = f"## πŸ“„ Extracted LaTeX\n```latex\n{latex_result}\n```\n"
output += f"---\n## 🧹 Cleaned LaTeX\n```latex\n{cleaned_latex}\n```\n"
output += f"---\n## 🧠 Parsed Expression\n$$ {sp.latex(expr)} $$\n"
factor = sp.factor(lhs)
output += f"---\n## ✏️ Standard Form\n$$ {sp.latex(lhs)} = 0 $$\n"
output += f"---\n## 🧩 Factorized\n$$ {sp.latex(factor)} = 0 $$\n"
x = next(iter(lhs.free_symbols))
roots = sp.solve(lhs, x)
output += "## βœ… Roots\n"
output += "$$\n\\begin{aligned}\n"
for i, r in enumerate(roots, 1):
root_val = sp.N(r, 6)
output += f"\\text{{Root {i}}}:\\quad x &\\approx {sp.latex(root_val)} \\\\\n"
output += "\\end{aligned}\n$$\n"
return output, cleaned_latex, ""
except Exception as e:
return f"❌ Error: {str(e)}", "", ""
# --- NEW Multi-image System Solver ---
def solve_system_multi(img1, img2, img3):
images = [img1, img2, img3]
cleaned_equations = []
raw_outputs = []
for img in images:
if img is None:
continue
try:
img = preprocess_handwritten_image(img)
latex = model(img)
raw_outputs.append(latex)
cleaned = clean_latex2(latex)
parsed = parse_latex(cleaned)
if isinstance(parsed, sp.Equality):
cleaned_equations.append(parsed)
except:
continue
if len(cleaned_equations) < 2:
return f"❌ Could not parse enough valid equations."
symbols = set()
for eq in cleaned_equations:
symbols.update(eq.free_symbols)
sol = sp.solve(cleaned_equations, list(symbols), dict=True)
output = "## πŸ“„ Raw LaTeX:\n"
for latex in raw_outputs:
output += f"- ```latex\n{latex}\n```\n"
output += "---\n## ✏️ Parsed Equations:\n"
for eq in cleaned_equations:
output += f"$$ {sp.latex(eq)} $$\n"
output += "---\n## βœ… Solution:\n"
if sol:
output += "$$" + sp.latex(sol[0]) + "$$"
else:
output += "❌ No solution found or system may be inconsistent."
return output
# === UI ===
with gr.Blocks() as demo:
with gr.Tab("πŸ–ΌοΈ Parse from Image"):
image_input = gr.Image(type="pil", label="πŸ“· Upload Image of Polynomial")
hidden_latex = gr.Textbox(visible=False)
explanation_prompt = gr.Textbox(visible=False)
output_box = gr.Markdown(label="πŸ“‹ Step-by-step Solution")
submit_btn = gr.Button("πŸ” Solve")
submit_btn.click(fn=solve_polynomial, inputs=[image_input], outputs=[output_box, hidden_latex, explanation_prompt])
with gr.Tab("πŸ“ Solve System (Multi-Image)"):
img1 = gr.Image(type="pil", label="πŸ“· Equation 1")
img2 = gr.Image(type="pil", label="πŸ“· Equation 2")
img3 = gr.Image(type="pil", label="πŸ“· Equation 3 (Optional)")
sys_output = gr.Markdown(label="πŸ“‹ Solved System Output")
sys_btn = gr.Button("πŸ“Œ Solve System")
sys_btn.click(fn=solve_system_multi, inputs=[img1, img2, img3], outputs=[sys_output])
if __name__ == "__main__":
demo.launch()