import logging
from latex2sympy import latex2sympy
from sympy import (Add, Mul, Pow, Symbol, Number, Rational, Expr, Function,
sin, cos, tan, exp, log)
# Configure logging if desired (optional)
if not logging.getLogger().handlers:
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
def parse_math_expression(latex_math_string):
"""
Parses a LaTeX mathematical string into a SymPy expression.
Returns a SymPy expression object or None if parsing fails.
"""
if not isinstance(latex_math_string, str) or not latex_math_string.strip():
logging.warning("Received invalid or empty input for parsing.")
return None
try:
sympy_expr = latex2sympy(latex_math_string)
return sympy_expr
except Exception as e:
logging.error(f"Error during parsing: {e}")
return None
def spell_out_sympy_expression(expr: Expr) -> str:
"""
Recursively converts a SymPy expression into a spelled‑out string.
Handles basic arithmetic operations, powers, functions, symbols, and numbers.
"""
# Numbers and rationals
if isinstance(expr, Number):
return str(expr)
if isinstance(expr, Rational):
return f"{expr.p}/{expr.q}"
# Variables / symbols
if isinstance(expr, Symbol):
return str(expr)
# Addition
if isinstance(expr, Add):
parts = [spell_out_sympy_expression(arg) for arg in expr.args]
return " plus ".join(parts)
# Multiplication
if isinstance(expr, Mul):
# separate negative sign if present
sign = ""
if expr.as_coeff_Mul()[0] == -1:
sign = "negative "
args = expr.as_coeff_Mul()[1].args
else:
args = expr.args
parts = [spell_out_sympy_expression(arg) for arg in args]
return sign + " times ".join(parts)
# Powers
if isinstance(expr, Pow):
base, exponent = expr.as_base_exp()
return f"{spell_out_sympy_expression(base)} to the power of {spell_out_sympy_expression(exponent)}"
# Basic functions (extend as needed)
if isinstance(expr, Function):
if expr.func == sin:
return f"sine of {spell_out_sympy_expression(expr.args[0])}"
if expr.func == cos:
return f"cosine of {spell_out_sympy_expression(expr.args[0])}"
if expr.func == tan:
return f"tangent of {spell_out_sympy_expression(expr.args[0])}"
if expr.func == exp:
return f"exponential of {spell_out_sympy_expression(expr.args[0])}"
if expr.func == log:
return f"logarithm of {spell_out_sympy_expression(expr.args[0])}"
# generic fall‑back
return f"{expr.func.__name__} of {', '.join(spell_out_sympy_expression(a) for a in expr.args)}"
# Fallback for unhandled expression types
return str(expr)
# --- Demonstration ---
examples = [
r"E = mc^2",
r"\int_{0}^{\infty} e^{-x^2}\,dx",
r"\frac{-b \pm \sqrt{b^2-4ac}}{2a}",
r"a^2 + b^2", # simple polynomial
r"-3xy^2", # negative coefficient and product
r"\sin(\theta) + \cos(\theta)" # trigonometric functions
]
for i, latex_input in enumerate(examples, start=1):
sympy_expr = parse_math_expression(latex_input)
if sympy_expr is not None:
print(f"\nExample {i}: {latex_input}")
print("SymPy expression:", sympy_expr)
spelled = spell_out_sympy_expression(sympy_expr)
print("Spelled out:", spelled)
else:
print(f"\nExample {i}: {latex_input}")
print("Parsing failed.")
This script does the following:
- Defines
parse_math_expression– converts a LaTeX string into a SymPy expression usinglatex2sympy. - Defines
spell_out_sympy_expression– traverses the SymPy expression tree and converts it into a plain‑language string, handling numbers, symbols, sums, products, powers, and some common functions (sine, cosine, etc.). - Provides demonstration examples showing how the parser and spelling functions work together.
You can run this script as is to see the output for various LaTeX expressions and how they are spelled out in English. It also highlights how to extend the spell_out_sympy_expression function for additional operations or functions as needed.