Source code for QHyper.parser
"""Module for parsing sympy expressions to :py:class:`~QHyper.polynomial.Polynomial`.
This module provides a way to parse sympy expressions and string to
:py:class:`~QHyper.polynomial.Polynomial`.
It is not recommended to use this module for large polynomials as it is very
slow in comparison to creating Polynomial directly from the dict.
.. rubric:: Functions
.. autofunction:: from_str
.. autofunction:: from_sympy
.. autofunction:: to_sympy
"""
import ast
import sympy
from QHyper.polynomial import Polynomial
class ParserException(Exception):
pass
class Parser(ast.NodeVisitor):
def __init__(self) -> None:
self.polynomial: Polynomial | None = None
def visit_Expr(self, node: ast.Expr) -> None:
self.polynomial = self.visit(node.value)
def visit_Constant(self, node: ast.Constant) -> Polynomial:
return Polynomial({tuple(): node.value})
def visit_Name(self, node: ast.Name) -> Polynomial:
return Polynomial({(node.id,): 1})
def visit_BinOp(self, node: ast.BinOp) -> Polynomial:
lhs = self.visit(node.left)
rhs = self.visit(node.right)
if isinstance(node.op, ast.Add):
return lhs + rhs
if isinstance(node.op, ast.Sub):
return lhs - rhs
if isinstance(node.op, ast.Mult):
return lhs * rhs
if isinstance(node.op, ast.Pow):
return lhs ** rhs
raise ParserException(f"Unsupported operation: {lhs} {node.op} {rhs}")
def visit_UnaryOp(self, node: ast.UnaryOp) -> Polynomial:
lhs = self.visit(node.operand)
if isinstance(node.op, ast.USub):
return -lhs
if isinstance(node.op, ast.UAdd):
return lhs
raise ParserException(f"Unsupported operation: {node.op}{lhs}")
[docs]
def to_sympy(poly: Polynomial) -> str:
"""Method to convert a polynomial to a sympy expression.
Might be handy to display the polynomial in a more readable form.
Parameters
----------
poly : Polynomial
The polynomial to be converted.
Returns
-------
str
The sympy expression.
"""
polynomial = ""
for term, const in poly.terms.items():
if const < 0:
polynomial += f"{const}*"
else:
polynomial += f"+{const}*"
polynomial += "*".join(term)
return sympy.parse_expr(polynomial, evaluate=False)
[docs]
def from_str(equation: str) -> Polynomial:
"""Method to parse a string to a polynomial.
Uses ast parser to parse the equation. This method is very slow in
comparison to creating Polynomial directly from the dict. Although, for
smaller polynomials, it is not noticeable.
Parameters
----------
equation : str
The equation to be parsed in form of string.
Returns
-------
Polynomial
The parsed polynomial.
"""
parser = Parser()
ast_tree = ast.parse(equation)
parser.visit(ast_tree)
if parser.polynomial is None:
raise ParserException(f"Failed to parse: {equation}")
return parser.polynomial
[docs]
def from_sympy(equation: sympy.core.Expr) -> Polynomial:
"""Method to convert a sympy expression to a polynomial.
Uses ast parser to parse the equation. This method is very slow in
comparison to creating Polynomial directly from the dict. Although, for
smaller polynomials, it is not noticeable.
Parameters
----------
equation : sympy.core.Expr
The sympy expression to be converted.
Returns
-------
Polynomial
The converted polynomial.
"""
return from_str(str(sympy.expand(equation)))