Search code examples
pythonpython-3.xregexstringregex-group

How to capture within an expression given as a string the resolvable terms using regex and then remove the terms that become 0 after operating on them


import re

#operation string (with parameters)
z_j_func = 'Z = 1 * 0 + 4 * 1/2 + 0 * 0 + 3 / 2 + M * 12 + M * 12 + M * 2 - 4 / 8'

Having a string like this which has parameters in some terms, how do I resolve the parameters without terms?, and then remove the terms that are 0 and join (by adding or subtracting) the terms that can be operated

z_j_func = 'Z = 0 + 2 + 0 + 3 / 2 + M * 12 + M * 12 + M * 2 - 1 / 2'

In the end it should look like this:

z_j_func = 'Z =  3 + M * 12 + M * 12 + M * 2'

Solution

  • In general you would use a symbolic expression parser, like sympy.

    Here is how that would work out, assuming that the string is of the form: <identifier> = <expression>

    import sympy 
    
    def simplify(equation):
        # Identify the operands of the equation
        left, right = equation.replace(" ", "").split("=")
        return f"{left} = {sympy.simplify(right)}"
    
    # Example run
    z_j_func = 'Z = 1 * 0 + 4 * 1/2 + 0 * 0 + 3 / 2 + M * 12 + M * 12 + M * 2 - 4 / 8'   
    print(simplify(z_j_func))  # Z = 26*M + 3
    

    If you prefer not to use a parser, but want to throw your own, then have a look at the next piece of code. It makes several assumptions, including:

    • The input is of the form: <identifier> = <expression>;
    • The expression does not have parentheses;
    • The expression only uses the operators +, -, *, /;
    • Where a / operator is used, it is the rightmost operator in the term;
    • Unknowns (like M) only appear in numerators, not denominators

    The code:

    import re
    from collections import defaultdict
    from math import prod
    from fractions import Fraction
    
    def simplify(equation):
        # Identify the operands of the equation
        left, right = equation.replace(" ", "").split("=")
        
        # Identify the numerators and denominators
        terms = [
            term.split("/") if "/" in term else [term, 1]
            for term in re.split(r"\+|(?=-)", right)
        ]
        
        # Group terms by the unknowns (variable) in their numerators
        d = defaultdict(list)
        for num, denom in terms:
            d["*".join(re.findall(r"(?i)[a-z]+", num))].append(
                (re.sub(r"(?i)\*[a-z]+|[a-z]+\*?", "", num), denom)
            )
    
        # Evaluate the coefficients for each unknown and concatenate
        return left + " = " + " + ".join(
            (key + " * " + str(Fraction(sum(
                prod(map(float, num.split('*'))) / float(denom)
                for num, denom in expr
            )))).lstrip(" *")
            for key, expr in d.items()
        ).replace(" + -", " - ")
    
    z_j_func = 'Z = 1 * 0 + 4 * 1/2 + 0 * 0 + 3 / 2 + M * 12 + M * 12 + M * 2 - 4 / 8'   
    print(simplify(z_j_func))  # Z = M * 26 + 3