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

Question:

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'
Asked By: Elektvocal95

||

Answers:

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
Answered By: trincot