def test_quantity_simplify(): from sympy.physics.units.util import quantity_simplify from sympy.physics.units import kilo, foot from sympy.core.symbol import symbols x, y = symbols('x y') assert quantity_simplify(x*(8*kilo*newton*meter + y)) == x*(8000*meter*newton + y) assert quantity_simplify(foot*inch*(foot + inch)) == foot**2*(foot + foot/12)/12 assert quantity_simplify(foot*inch*(foot*foot + inch*(foot + inch))) == foot**2*(foot**2 + foot/12*(foot + foot/12))/12 assert quantity_simplify(2**(foot/inch*kilo/1000)*inch) == 4096*foot/12 assert quantity_simplify(foot**2*inch + inch**2*foot) == 13*foot**3/144
def test_quantity_simplify(): from sympy.physics.units.util import quantity_simplify from sympy.physics.units import kilo, foot from sympy.core.symbol import symbols x, y = symbols('x y') assert quantity_simplify(x*(8*kilo*newton*meter + y)) == x*(8000*meter*newton + y) assert quantity_simplify(foot*inch*(foot + inch)) == foot**2*(foot + foot/12)/12 assert quantity_simplify(foot*inch*(foot*foot + inch*(foot + inch))) == foot**2*(foot**2 + foot/12*(foot + foot/12))/12 assert quantity_simplify(2**(foot/inch*kilo/1000)*inch) == 4096*foot/12 assert quantity_simplify(foot**2*inch + inch**2*foot) == 13*foot**3/144
def parseUnits(text, dimension=None): """Parse a string with units possibly included. See parseExpr for pre-processing steps. Raises ExpressionError same as parseExpr. Raises UnitMisMatchError if the expression has incompatible units. :param text: str :return: sympy.Expr or None """ logger.log(logging.DEBUG - 1, f'parseUnits({text})') if text == '': return None text = text.replace('^', '**') err = _notSafeError(text) or _keywordError(text) try: expr = parse_expr(text, evaluate=False, local_dict=unitSubs) str(expr) # catch some problems except AttributeError as e: raise ExpressionError("Unknown function call") except (TokenError, SyntaxError, TypeError) as e: raise ExpressionError("Syntax error") except Exception as e: raise ExpressionError((type(e).__name__, repr(e))) if isinstance(expr, Function): raise ExpressionError('Function is not a valid expression') try: expr = quantity_simplify(expr) except TypeError: pass try: unitsAreConsistent(expr, dimension) except UnitMisMatchError as e: raise e return expr
def getDimension(expr): """Get the units.Dimension expression of `expr`. If the dimension cannot be determined, returns None. :param expr: sympy.Expr :return: units.Dimension or None """ logger.log(logging.DEBUG - 1, f'getDimension({expr})') if expr is not None: try: result = units.systems.SI._collect_factor_and_dimension(expr)[1] except ValueError as e: logger.log(logging.DEBUG - 1, f'getDimension() -> {repr(e)}') result = None except AttributeError as e: logger.log(logging.DEBUG - 1, f'getDimension() -> {repr(e)}') ev = quantity_simplify(expr).evalf() result = units.systems.SI._collect_factor_and_dimension(ev)[1] else: result = None logger.log(logging.DEBUG - 1, f'getDimension({expr}) -> {result}') return result
def __post_init__(self): e_o, e_i, e_m, R, l_o, l_i, l_m, d, a_1, a_2, a_3, b_1, b_2, b_3 = symbols('e_o e_i e_m R l_o l_i l_m d a_1 a_2 a_3 b_1 b_2 b_3') e_o = self.extracellular_permittivity * eps0 # S/m e_i = self.intracellular_permittivity * eps0 #S/m e_m = self.membrane_permittivity * eps0 #S/m R = self.cell_diameter / 2 self.R = R l_o = self.extracellular_conductivity*Spm # S/m l_i = self.intracellular_conductivity*Spm #S/m l_m = self.membrane_conductivity*Spm #S/m d = self.membrane_thickness # epsilon_0 sub1, sub2 = symbols('sub1 sub2') sub1 = (3 * (R**2) - 3 * d * R + d**2) sub2 = (3 * d * R - d**2) a_1 = 3 * d * l_o * ((l_i * (sub1)) + l_m*(sub2)) #eq.9a a_2 = 3 * d * ((l_i * e_o + l_o * e_i) * sub1 + (l_m * e_o + l_o * e_m) * sub2) a_3 = 3 * d * e_o * (e_i * (sub1) + e_m * sub2) b_1 = 2 * R**3 * (l_m + 2*l_o) * (l_m + (1/2) * l_i) + 2 * (R-d)**3 * (l_m - l_o) * (l_i - l_m) b_2 = 2 * R**3 * (l_i * ((1/2) * e_m + e_o) + l_m * ((1/2)*e_i + 2*e_m + 2*e_o) + l_o * (e_i + 2 * e_m)) + (2 * (R - d)**3\ * (l_i * (e_m - e_o) + l_m * (e_i - 2*e_m + e_o) - l_o * (e_i - e_m))) # is this truly a multiply, or a cross? b_3 = 2 * R**3 * (e_m + 2*e_o) * (e_m + (1/2) * e_i) + 2 * (R-d)**3 * (e_m - e_o) * (e_i - e_m) sympy.pprint(powsimp(util.quantity_simplify(refine(a_1.simplify()))).as_coeff_Mul()[1]) print() sympy.pprint(powsimp(util.quantity_simplify(refine(a_2.simplify()))).as_coeff_Mul()[1]) print() sympy.pprint(powsimp(util.quantity_simplify(refine(a_3.simplify()))).as_coeff_Mul()[1]) print() sympy.pprint(powsimp(util.quantity_simplify(refine(b_1.simplify()))).as_coeff_Mul()[1]) print() sympy.pprint(powsimp(util.quantity_simplify(refine(b_2.simplify()))).as_coeff_Mul()[1]) print() sympy.pprint(powsimp(util.quantity_simplify(refine(b_3.simplify()))).as_coeff_Mul()[1]) print() print()
def errorCheck(self): """Checks if self.text() makes is a valid sympy.Expr. If expression contains units, checks they are compatible with self.dimension. Sets self._expr to None or the sympy.Expr version of self.text(). Returns applicable error status. :return: ExpressionError when relevant UnitMisMatchError when relevant False if no error None if resulting expression is None """ self.logger.log(logging.DEBUG - 1, f'errorCheck()') try: expr = parseUnits(self.text(), self._dimension) except (ExpressionError, UnitMisMatchError) as e: self.logger.log(logging.DEBUG - 1, f'errorCheck() -> {repr(e)}') self._expr = None self.exprChanged[object].emit(None) self.valueChanged[object].emit(None) self.displayValue.emit('- - -') return e if expr is None: self.logger.log(logging.DEBUG - 1, 'errorCheck() -> None') self._expr = None self.exprChanged[object].emit(None) self.valueChanged[object].emit(None) self.displayValue.emit('- - -') return None # else: # no problems self.logger.log(logging.DEBUG - 1, 'errorCheck() -> False') self._expr = quantity_simplify(expr) self.exprChanged[object].emit(self._expr) self.valueChanged[object].emit(self._expr.simplify().evalf()) self.displayValue.emit(str(self._expr.evalf(4))) return False
def simplify(expr, ratio=1.7, measure=count_ops, rational=False, inverse=False): """Simplifies the given expression. Simplification is not a well defined term and the exact strategies this function tries can change in the future versions of SymPy. If your algorithm relies on "simplification" (whatever it is), try to determine what you need exactly - is it powsimp()?, radsimp()?, together()?, logcombine()?, or something else? And use this particular function directly, because those are well defined and thus your algorithm will be robust. Nonetheless, especially for interactive use, or when you don't know anything about the structure of the expression, simplify() tries to apply intelligent heuristics to make the input expression "simpler". For example: >>> from sympy import simplify, cos, sin >>> from sympy.abc import x, y >>> a = (x + x**2)/(x*sin(y)**2 + x*cos(y)**2) >>> a (x**2 + x)/(x*sin(y)**2 + x*cos(y)**2) >>> simplify(a) x + 1 Note that we could have obtained the same result by using specific simplification functions: >>> from sympy import trigsimp, cancel >>> trigsimp(a) (x**2 + x)/x >>> cancel(_) x + 1 In some cases, applying :func:`simplify` may actually result in some more complicated expression. The default ``ratio=1.7`` prevents more extreme cases: if (result length)/(input length) > ratio, then input is returned unmodified. The ``measure`` parameter lets you specify the function used to determine how complex an expression is. The function should take a single argument as an expression and return a number such that if expression ``a`` is more complex than expression ``b``, then ``measure(a) > measure(b)``. The default measure function is :func:`count_ops`, which returns the total number of operations in the expression. For example, if ``ratio=1``, ``simplify`` output can't be longer than input. :: >>> from sympy import sqrt, simplify, count_ops, oo >>> root = 1/(sqrt(2)+3) Since ``simplify(root)`` would result in a slightly longer expression, root is returned unchanged instead:: >>> simplify(root, ratio=1) == root True If ``ratio=oo``, simplify will be applied anyway:: >>> count_ops(simplify(root, ratio=oo)) > count_ops(root) True Note that the shortest expression is not necessary the simplest, so setting ``ratio`` to 1 may not be a good idea. Heuristically, the default value ``ratio=1.7`` seems like a reasonable choice. You can easily define your own measure function based on what you feel should represent the "size" or "complexity" of the input expression. Note that some choices, such as ``lambda expr: len(str(expr))`` may appear to be good metrics, but have other problems (in this case, the measure function may slow down simplify too much for very large expressions). If you don't know what a good metric would be, the default, ``count_ops``, is a good one. For example: >>> from sympy import symbols, log >>> a, b = symbols('a b', positive=True) >>> g = log(a) + log(b) + log(a)*log(1/b) >>> h = simplify(g) >>> h log(a*b**(-log(a) + 1)) >>> count_ops(g) 8 >>> count_ops(h) 5 So you can see that ``h`` is simpler than ``g`` using the count_ops metric. However, we may not like how ``simplify`` (in this case, using ``logcombine``) has created the ``b**(log(1/a) + 1)`` term. A simple way to reduce this would be to give more weight to powers as operations in ``count_ops``. We can do this by using the ``visual=True`` option: >>> print(count_ops(g, visual=True)) 2*ADD + DIV + 4*LOG + MUL >>> print(count_ops(h, visual=True)) 2*LOG + MUL + POW + SUB >>> from sympy import Symbol, S >>> def my_measure(expr): ... POW = Symbol('POW') ... # Discourage powers by giving POW a weight of 10 ... count = count_ops(expr, visual=True).subs(POW, 10) ... # Every other operation gets a weight of 1 (the default) ... count = count.replace(Symbol, type(S.One)) ... return count >>> my_measure(g) 8 >>> my_measure(h) 14 >>> 15./8 > 1.7 # 1.7 is the default ratio True >>> simplify(g, measure=my_measure) -log(a)*log(b) + log(a) + log(b) Note that because ``simplify()`` internally tries many different simplification strategies and then compares them using the measure function, we get a completely different result that is still different from the input expression by doing this. If rational=True, Floats will be recast as Rationals before simplification. If rational=None, Floats will be recast as Rationals but the result will be recast as Floats. If rational=False(default) then nothing will be done to the Floats. If inverse=True, it will be assumed that a composition of inverse functions, such as sin and asin, can be cancelled in any order. For example, ``asin(sin(x))`` will yield ``x`` without checking whether x belongs to the set where this relation is true. The default is False. """ expr = sympify(expr) try: return expr._eval_simplify(ratio=ratio, measure=measure) except AttributeError: pass original_expr = expr = signsimp(expr) from sympy.simplify.hyperexpand import hyperexpand from sympy.functions.special.bessel import BesselBase from sympy import Sum, Product if not isinstance(expr, Basic) or not expr.args: # XXX: temporary hack return expr if inverse and expr.has(Function): expr = inversecombine(expr) if not expr.args: # simplified to atomic return expr if not isinstance(expr, (Add, Mul, Pow, ExpBase)): return expr.func(*[simplify(x, ratio=ratio, measure=measure, rational=rational) for x in expr.args]) # TODO: Apply different strategies, considering expression pattern: # is it a purely rational function? Is there any trigonometric function?... # See also https://github.com/sympy/sympy/pull/185. def shorter(*choices): '''Return the choice that has the fewest ops. In case of a tie, the expression listed first is selected.''' if not has_variety(choices): return choices[0] return min(choices, key=measure) # rationalize Floats floats = False if rational is not False and expr.has(Float): floats = True expr = nsimplify(expr, rational=True) expr = bottom_up(expr, lambda w: w.normal()) expr = Mul(*powsimp(expr).as_content_primitive()) _e = cancel(expr) expr1 = shorter(_e, _mexpand(_e).cancel()) # issue 6829 expr2 = shorter(together(expr, deep=True), together(expr1, deep=True)) if ratio is S.Infinity: expr = expr2 else: expr = shorter(expr2, expr1, expr) if not isinstance(expr, Basic): # XXX: temporary hack return expr expr = factor_terms(expr, sign=False) # hyperexpand automatically only works on hypergeometric terms expr = hyperexpand(expr) expr = piecewise_fold(expr) if expr.has(BesselBase): expr = besselsimp(expr) if expr.has(TrigonometricFunction, HyperbolicFunction): expr = trigsimp(expr, deep=True) if expr.has(log): expr = shorter(expand_log(expr, deep=True), logcombine(expr)) if expr.has(CombinatorialFunction, gamma): # expression with gamma functions or non-integer arguments is # automatically passed to gammasimp expr = combsimp(expr) if expr.has(Sum): expr = sum_simplify(expr) if expr.has(Product): expr = product_simplify(expr) from sympy.physics.units import Quantity from sympy.physics.units.util import quantity_simplify if expr.has(Quantity): expr = quantity_simplify(expr) short = shorter(powsimp(expr, combine='exp', deep=True), powsimp(expr), expr) short = shorter(short, cancel(short)) short = shorter(short, factor_terms(short), expand_power_exp(expand_mul(short))) if short.has(TrigonometricFunction, HyperbolicFunction, ExpBase): short = exptrigsimp(short) # get rid of hollow 2-arg Mul factorization hollow_mul = Transform( lambda x: Mul(*x.args), lambda x: x.is_Mul and len(x.args) == 2 and x.args[0].is_Number and x.args[1].is_Add and x.is_commutative) expr = short.xreplace(hollow_mul) numer, denom = expr.as_numer_denom() if denom.is_Add: n, d = fraction(radsimp(1/denom, symbolic=False, max_terms=1)) if n is not S.One: expr = (numer*n).expand()/d if expr.could_extract_minus_sign(): n, d = fraction(expr) if d != 0: expr = signsimp(-n/(-d)) if measure(expr) > ratio*measure(original_expr): expr = original_expr # restore floats if floats and rational is None: expr = nfloat(expr, exponent=False) return expr
def test_quantity_simplify_across_dimensions(): from sympy.physics.units.util import quantity_simplify from sympy.physics.units import ampere, ohm, volt, joule, pascal, farad, second, watt, siemens, henry, tesla, weber, hour, newton assert quantity_simplify(ampere * ohm, across_dimensions=True, unit_system="SI") == volt assert quantity_simplify(6 * ampere * ohm, across_dimensions=True, unit_system="SI") == 6 * volt assert quantity_simplify(volt / ampere, across_dimensions=True, unit_system="SI") == ohm assert quantity_simplify(volt / ohm, across_dimensions=True, unit_system="SI") == ampere assert quantity_simplify(joule / meter**3, across_dimensions=True, unit_system="SI") == pascal assert quantity_simplify(farad * ohm, across_dimensions=True, unit_system="SI") == second assert quantity_simplify(joule / second, across_dimensions=True, unit_system="SI") == watt assert quantity_simplify(meter**3 / second, across_dimensions=True, unit_system="SI") == meter**3 / second assert quantity_simplify(joule / second, across_dimensions=True, unit_system="SI") == watt assert quantity_simplify(joule / coulomb, across_dimensions=True, unit_system="SI") == volt assert quantity_simplify(volt / ampere, across_dimensions=True, unit_system="SI") == ohm assert quantity_simplify(ampere / volt, across_dimensions=True, unit_system="SI") == siemens assert quantity_simplify(coulomb / volt, across_dimensions=True, unit_system="SI") == farad assert quantity_simplify(volt * second / ampere, across_dimensions=True, unit_system="SI") == henry assert quantity_simplify(volt * second / meter**2, across_dimensions=True, unit_system="SI") == tesla assert quantity_simplify(joule / ampere, across_dimensions=True, unit_system="SI") == weber assert quantity_simplify(5 * kilometer / hour, across_dimensions=True, unit_system="SI") == 25 * meter / (18 * second) assert quantity_simplify(5 * kilogram * meter / second**2, across_dimensions=True, unit_system="SI") == 5 * newton
def simplify(expr, ratio=1.7, measure=count_ops, rational=False): # type: (object, object, object, object) -> object """ Simplifies the given expression. Simplification is not a well defined term and the exact strategies this function tries can change in the future versions of SymPy. If your algorithm relies on "simplification" (whatever it is), try to determine what you need exactly - is it powsimp()?, radsimp()?, together()?, logcombine()?, or something else? And use this particular function directly, because those are well defined and thus your algorithm will be robust. Nonetheless, especially for interactive use, or when you don't know anything about the structure of the expression, simplify() tries to apply intelligent heuristics to make the input expression "simpler". For example: >>> from sympy import simplify, cos, sin >>> from sympy.abc import x, y >>> a = (x + x**2)/(x*sin(y)**2 + x*cos(y)**2) >>> a (x**2 + x)/(x*sin(y)**2 + x*cos(y)**2) >>> simplify(a) x + 1 Note that we could have obtained the same result by using specific simplification functions: >>> from sympy import trigsimp, cancel >>> trigsimp(a) (x**2 + x)/x >>> cancel(_) x + 1 In some cases, applying :func:`simplify` may actually result in some more complicated expression. The default ``ratio=1.7`` prevents more extreme cases: if (result length)/(input length) > ratio, then input is returned unmodified. The ``measure`` parameter lets you specify the function used to determine how complex an expression is. The function should take a single argument as an expression and return a number such that if expression ``a`` is more complex than expression ``b``, then ``measure(a) > measure(b)``. The default measure function is :func:`count_ops`, which returns the total number of operations in the expression. For example, if ``ratio=1``, ``simplify`` output can't be longer than input. :: >>> from sympy import sqrt, simplify, count_ops, oo >>> root = 1/(sqrt(2)+3) Since ``simplify(root)`` would result in a slightly longer expression, root is returned unchanged instead:: >>> simplify(root, ratio=1) == root True If ``ratio=oo``, simplify will be applied anyway:: >>> count_ops(simplify(root, ratio=oo)) > count_ops(root) True Note that the shortest expression is not necessary the simplest, so setting ``ratio`` to 1 may not be a good idea. Heuristically, the default value ``ratio=1.7`` seems like a reasonable choice. You can easily define your own measure function based on what you feel should represent the "size" or "complexity" of the input expression. Note that some choices, such as ``lambda expr: len(str(expr))`` may appear to be good metrics, but have other problems (in this case, the measure function may slow down simplify too much for very large expressions). If you don't know what a good metric would be, the default, ``count_ops``, is a good one. For example: >>> from sympy import symbols, log >>> a, b = symbols('a b', positive=True) >>> g = log(a) + log(b) + log(a)*log(1/b) >>> h = simplify(g) >>> h log(a*b**(-log(a) + 1)) >>> count_ops(g) 8 >>> count_ops(h) 5 So you can see that ``h`` is simpler than ``g`` using the count_ops metric. However, we may not like how ``simplify`` (in this case, using ``logcombine``) has created the ``b**(log(1/a) + 1)`` term. A simple way to reduce this would be to give more weight to powers as operations in ``count_ops``. We can do this by using the ``visual=True`` option: >>> print(count_ops(g, visual=True)) 2*ADD + DIV + 4*LOG + MUL >>> print(count_ops(h, visual=True)) 2*LOG + MUL + POW + SUB >>> from sympy import Symbol, S >>> def my_measure(expr): ... POW = Symbol('POW') ... # Discourage powers by giving POW a weight of 10 ... count = count_ops(expr, visual=True).subs(POW, 10) ... # Every other operation gets a weight of 1 (the default) ... count = count.replace(Symbol, type(S.One)) ... return count >>> my_measure(g) 8 >>> my_measure(h) 14 >>> 15./8 > 1.7 # 1.7 is the default ratio True >>> simplify(g, measure=my_measure) -log(a)*log(b) + log(a) + log(b) Note that because ``simplify()`` internally tries many different simplification strategies and then compares them using the measure function, we get a completely different result that is still different from the input expression by doing this. If rational=True, Floats will be recast as Rationals before simplification. If rational=None, Floats will be recast as Rationals but the result will be recast as Floats. If rational=False(default) then nothing will be done to the Floats. """ expr = sympify(expr) try: return expr._eval_simplify(ratio=ratio, measure=measure) except AttributeError: pass original_expr = expr = signsimp(expr) from sympy.simplify.hyperexpand import hyperexpand from sympy.functions.special.bessel import BesselBase from sympy import Sum, Product if not isinstance(expr, Basic) or not expr.args: # XXX: temporary hack return expr if not isinstance(expr, (Add, Mul, Pow, ExpBase)): if isinstance(expr, Function) and hasattr(expr, "inverse"): if len(expr.args) == 1 and len(expr.args[0].args) == 1 and \ isinstance(expr.args[0], expr.inverse(argindex=1)): return simplify(expr.args[0].args[0], ratio=ratio, measure=measure, rational=rational) return expr.func(*[simplify(x, ratio=ratio, measure=measure, rational=rational) for x in expr.args]) # TODO: Apply different strategies, considering expression pattern: # is it a purely rational function? Is there any trigonometric function?... # See also https://github.com/sympy/sympy/pull/185. def shorter(*choices): '''Return the choice that has the fewest ops. In case of a tie, the expression listed first is selected.''' if not has_variety(choices): return choices[0] return min(choices, key=measure) # rationalize Floats floats = False if rational is not False and expr.has(Float): floats = True expr = nsimplify(expr, rational=True) expr = bottom_up(expr, lambda w: w.normal()) expr = Mul(*powsimp(expr).as_content_primitive()) _e = cancel(expr) expr1 = shorter(_e, _mexpand(_e).cancel()) # issue 6829 expr2 = shorter(together(expr, deep=True), together(expr1, deep=True)) if ratio is S.Infinity: expr = expr2 else: expr = shorter(expr2, expr1, expr) if not isinstance(expr, Basic): # XXX: temporary hack return expr expr = factor_terms(expr, sign=False) # hyperexpand automatically only works on hypergeometric terms expr = hyperexpand(expr) expr = piecewise_fold(expr) if expr.has(BesselBase): expr = besselsimp(expr) if expr.has(TrigonometricFunction, HyperbolicFunction): expr = trigsimp(expr, deep=True) if expr.has(log): expr = shorter(expand_log(expr, deep=True), logcombine(expr)) if expr.has(CombinatorialFunction, gamma): # expression with gamma functions or non-integer arguments is # automatically passed to gammasimp expr = combsimp(expr) if expr.has(Sum): expr = sum_simplify(expr) if expr.has(Product): expr = product_simplify(expr) from sympy.physics.units import Quantity from sympy.physics.units.util import quantity_simplify if expr.has(Quantity): expr = quantity_simplify(expr) short = shorter(powsimp(expr, combine='exp', deep=True), powsimp(expr), expr) short = shorter(short, cancel(short)) short = shorter(short, factor_terms(short), expand_power_exp(expand_mul(short))) if short.has(TrigonometricFunction, HyperbolicFunction, ExpBase): short = exptrigsimp(short) # get rid of hollow 2-arg Mul factorization hollow_mul = Transform( lambda x: Mul(*x.args), lambda x: x.is_Mul and len(x.args) == 2 and x.args[0].is_Number and x.args[1].is_Add and x.is_commutative) expr = short.xreplace(hollow_mul) numer, denom = expr.as_numer_denom() if denom.is_Add: n, d = fraction(radsimp(1/denom, symbolic=False, max_terms=1)) if n is not S.One: expr = (numer*n).expand()/d if expr.could_extract_minus_sign(): n, d = fraction(expr) if d != 0: expr = signsimp(-n/(-d)) if measure(expr) > ratio*measure(original_expr): expr = original_expr # restore floats if floats and rational is None: expr = nfloat(expr, exponent=False) return expr
def errorCheck(self): """Checks if self.text() makes is a valid sympy.Expr. If expression contains units, checks their compatibility, and their convertibility to comboBox's selected units. Sets self._expr to None or the sympy.Expr version of self.text(). Calculates and stores widget's evaluated value. Returns applicable error status. :return: ExpressionError when relevant UnitMisMatchError when relevant False if no error None if resulting expression is None """ self.logger.log(logging.DEBUG - 1, f'errorCheck() all') self._value = None self.lineEdit._expr = None def emit_error(err): self.logger.log(logging.DEBUG - 1, f'errorCheck() -> {repr(err)}') self.exprChanged[object].emit(None) self.valueChanged[object].emit(None) self.displayValue.emit('- - -') return err def emit_none(reason): self.logger.log(logging.DEBUG - 1, f'errorCheck() -> {repr(reason)}') self.exprChanged[object].emit(None) self.valueChanged[object].emit(None) self.displayValue.emit('- - -') return None text = self.lineEdit.text() if text == '': return emit_none('empty text') text = text.replace('^', '**') try: err = _notSafeError(text) or _keywordError(text) except ExpressionError as e: return emit_error(e) try: expr = parse_expr(text, evaluate=False, local_dict=unitSubs) str(expr) # catch some problems except AttributeError as e: err = ExpressionError("Unknown function call") except (TokenError, SyntaxError) as e: err = ExpressionError("Syntax error") except Exception as e: err = ExpressionError((type(e).__name__, repr(e))) if err is False and isinstance(expr, Function): err = ExpressionError('Function is not a valid expression') if err: return emit_error(err) try: expr = quantity_simplify(expr) except TypeError: pass u = getDimension(self.getUnits()) try: unitsAreConsistent(expr, u) except UnitMisMatchError as e: return emit_error(e) self.logger.log(logging.DEBUG - 1, 'errorCheck() -> False') expr = convertTo(expr, self.getUnits()) self.lineEdit._expr = expr self._value = expr.evalf() self.exprChanged[object].emit(expr) self.valueChanged[object].emit(self._value) self.displayValue.emit(str(expr.evalf(4))) # finish errorCheck, all good return False