MathSolver / solver.py
Taizun's picture
Update solver.py
765211f verified
raw
history blame
10.1 kB
import sympy as sp
from sympy.parsing.sympy_parser import parse_expr, standard_transformations, implicit_multiplication_application
from sympy.solvers import solve
from sympy import integrate, diff, latex,simplify, expand, log, exp, sin, cos, tan, asin, acos, atan, Symbol, factorial, laplace_transform
import re
def format_expression(expr):
latex_expr = latex(expr)
replacements = {
'**': '^', # Power notation
'*x': 'x', # Remove unnecessary multiplication signs
'*(': '(', # Remove multiplication before parentheses
'exp': 'e^', # Exponential notation
'sqrt': '√', # Square root
'factorial': '!', # Factorial symbol
'gamma': 'Γ', # Gamma function
'Gamma': 'Γ', # Sometimes SymPy capitalizes it
'fresnels': 'S', # Fresnel S integral
'fresnelc': 'C', # Fresnel C integral
'hyper': '₁F₂', # Generalized hypergeometric function
'log': 'ln', # Natural logarithm
'oo': '∞', # Infinity symbol
'pi': 'π', # Pi symbol
'E': 'ℯ', # Euler's constant
'I': '𝒊', # Imaginary unit
'Abs': '|', # Absolute value
'Integral': '∫', # Integral symbol
'Derivative': 'd/dx', # Differentiation
'Sum': 'Σ', # Summation symbol
'Product': '∏', # Product symbol
'sin': 'sin', 'cos': 'cos', 'tan': 'tan', # Trig functions (unchanged)
'asin': 'sin⁻¹', 'acos': 'cos⁻¹', 'atan': 'tan⁻¹', # Inverse trig
'sinh': 'sinh', 'cosh': 'cosh', 'tanh': 'tanh', # Hyperbolic trig
'asinh': 'sinh⁻¹', 'acosh': 'cosh⁻¹', 'atanh': 'tanh⁻¹', # Inverse hyperbolic trig
'diff': 'd/dx', # Derivative notation
'integrate': '∫', # Integral notation
'Limit': 'lim', # Limit notation
'floor': '⌊', # Floor function
'ceiling': '⌈', # Ceiling function
'mod': 'mod', # Modulus (unchanged)
'Re': 'ℜ', # Real part
'Im': 'ℑ' # Imaginary part
}
for old, new in replacements.items():
latex_expr = latex_expr.replace(old, new)
return f"$$ {latex_expr} $$"
def preprocess_equation(equation_str):
"""Convert user-friendly equation format to SymPy format."""
try:
# Replace common mathematical notations
replacements = {
'^': '**',
'sin⁻¹': 'asin',
'cos⁻¹': 'acos',
'tan⁻¹': 'atan',
'e^': 'exp',
'ln': 'log', # Convert ln to log (SymPy default)
'√': 'sqrt', # Convert square root symbol to sqrt()
'!': '.factorial()', # Convert factorial to function call
}
for old, new in replacements.items():
equation_str = equation_str.replace(old, new)
equation_str = re.sub(r'(\d+)!', r'factorial(\1)', equation_str)
# Handle exponential expressions
if 'exp' in equation_str:
parts = equation_str.split('exp')
for i in range(1, len(parts)):
if parts[i] and parts[i][0] != '(':
parts[i] = '(' + parts[i]
if '=' in parts[i]:
exp_part, rest = parts[i].split('=', 1)
parts[i] = exp_part + ')=' + rest
else:
parts[i] = parts[i] + ')'
equation_str = 'exp'.join(parts)
# Add multiplication symbol where needed
processed = ''
i = 0
while i < len(equation_str):
if i + 1 < len(equation_str):
if equation_str[i].isdigit() and equation_str[i+1] == 'x':
processed += equation_str[i] + '*'
i += 1
continue
processed += equation_str[i]
i += 1
return processed
except Exception as e:
raise Exception(f"Error in equation format: {str(e)}")
def process_expression(expr_str):
"""Process mathematical expressions without equations."""
try:
processed_expr = preprocess_equation(expr_str)
x = Symbol('x')
if expr_str.startswith('∫'): # Integration
expr_to_integrate = processed_expr[1:].strip()
expr = parse_expr(expr_to_integrate, transformations=(standard_transformations + (implicit_multiplication_application,)))
result = integrate(expr, x)
return f"∫{format_expression(expr)} = {format_expression(result)}"
elif expr_str.startswith('d/dx'): # Differentiation
expr_to_diff = processed_expr[4:].strip()
if expr_to_diff.startswith('(') and expr_to_diff.endswith(')'):
expr_to_diff = expr_to_diff[1:-1]
expr = parse_expr(expr_to_diff, transformations=(standard_transformations + (implicit_multiplication_application,)))
result = diff(expr, x)
return f"d/dx({format_expression(expr)}) = {format_expression(result)}"
elif 'factorial' in processed_expr: # Factorial case
expr = parse_expr(processed_expr, transformations=(standard_transformations + (implicit_multiplication_application,)))
result = expr.doit() # Compute the factorial correctly
return f"{format_expression(expr)} = {result}"
elif '/' in expr_str: # Handle fractions and return decimal
expr = parse_expr(processed_expr, transformations=(standard_transformations + (implicit_multiplication_application,)))
simplified = simplify(expr)
decimal_value = float(simplified)
return f"Simplified: {format_expression(simplified)}\nDecimal: {decimal_value}"
else: # Regular expression simplification
expr = parse_expr(processed_expr, transformations=(standard_transformations + (implicit_multiplication_application,)))
simplified = simplify(expr)
expanded = expand(simplified)
return f"Simplified: {format_expression(simplified)}\nExpanded: {format_expression(expanded)}"
except Exception as e:
raise Exception(f"Error processing expression: {str(e)}")
except Exception as e:
raise Exception(f"Error processing expression: {str(e)}")
def to_latex(expr):
"""Converts a SymPy expression into LaTeX format."""
return f"$$ {sp.latex(expr)} $$"
def solve_equation(equation_str):
"""Solves an equation and returns a detailed LaTeX-formatted step-by-step solution."""
try:
if '=' not in equation_str:
return process_expression(equation_str)
left_side, right_side = [side.strip() for side in equation_str.split('=')]
transformations = standard_transformations + (implicit_multiplication_application,)
left_expr = sp.parse_expr(left_side, transformations=transformations)
right_expr = sp.parse_expr(right_side, transformations=transformations)
equation = left_expr - right_expr
x = Symbol('x')
solutions = solve(equation, x)
steps = []
steps.append(f"**Step 1:** Original equation: \n{to_latex(left_expr)} = {to_latex(right_expr)}")
steps.append(f"**Step 2:** Move all terms to one side: \n{to_latex(equation)} = 0")
factored = sp.factor(equation)
if factored != equation:
steps.append(f"**Step 3:** Factorizing the equation: \n{to_latex(factored)} = 0")
steps.append(f"**Step 4:** Solving for x:")
for sol in solutions:
steps.append(f" x = {to_latex(sol)}")
steps.append("**Step 5:** Verification:")
for sol in solutions:
verification = equation.subs(x, sol)
steps.append(f" When x = {to_latex(sol)}, the equation evaluates to {to_latex(verification)}")
return "\n".join(steps)
except Exception as e:
return f"Error: {str(e)}"
def integrate_expression(expression_str):
"""Computes the integral of a given expression and provides detailed LaTeX-formatted steps."""
try:
x = Symbol('x')
expr = sp.parse_expr(expression_str, transformations=standard_transformations + (implicit_multiplication_application,))
steps = []
steps.append(f"**Step 1:** Original integral: \n$$ \\int {sp.latex(expr)} \\,dx $$")
if '^' in expression_str:
steps.append("**Step 2:** Checking if substitution is needed.")
result = integrate(expr, x)
steps.append(f"**Step 3:** Applying integration formula(s):")
steps.append(f"$$ \\int f(x) \\,dx = F(x) + C $$")
steps.append(f"**Step 4:** Solution: \n$$ {sp.latex(result)} + C $$")
return "\n".join(steps)
except Exception as e:
return f"Error: {str(e)}"
def laplace_transform_expression(expression_str):
"""Computes the Laplace transform of a given expression with LaTeX-formatted steps."""
try:
t, s = sp.symbols('t s')
expr = sp.parse_expr(expression_str, transformations=standard_transformations + (implicit_multiplication_application,))
steps = []
steps.append(f"**Step 1:** Original function: \n$$ \\mathcal{L}\\{{ {sp.latex(expr)} \\}}(t) $$")
L_transform = laplace_transform(expr, t, s, noconds=True)
steps.append(f"**Step 2:** Applying Laplace Transform:")
steps.append("$$ \\mathcal{L}\\{ f(t) \\} = \\int_{0}^{\\infty} e^{-st} f(t) \\,dt $$")
steps.append(f"**Step 3:** Solution: \n$$ {sp.latex(L_transform)} $$")
return "\n".join(steps)
except Exception as e:
return f"Error: {str(e)}"
def process_input(equation):
"""Determine whether the input is an equation, an integral, or a Laplace transform."""
try:
if "laplace" in equation.lower():
solution = laplace_transform_expression(equation)
elif "integrate" in equation.lower():
solution = integrate_expression(equation)
else:
solution = solve_equation(equation)
return solution
except Exception as e:
return f"Error processing input: {str(e)}"