# -*- coding: utf-8 -*- """ Several methods to simplify expressions involving unit objects. """ from __future__ import division from sympy import Add, Mul, Pow from sympy.core.compatibility import reduce from sympy.physics.unitsystems import Dimension, Unit, Quantity def dim_simplify(expr): """ Simplify expression by recursively evaluating the dimension arguments. This function proceeds to a very rough dimensional analysis. It tries to simplify expression with dimensions, and it deletes all what multiplies a dimension without being a dimension. This is necessary to avoid strange behavior when Add(L, L) be transformed into Mul(2, L). """ if isinstance(expr, Dimension): return expr args = [] for arg in expr.args: if isinstance(arg, (Mul, Pow, Add)): arg = dim_simplify(arg) args.append(arg) if all([arg.is_number or (isinstance(arg, Dimension) and arg.is_dimensionless) for arg in args]): return Dimension({}) if isinstance(expr, Pow): if isinstance(args[0], Dimension): return args[0].pow(args[1]) else: raise ValueError("Basis of Pow is not a Dimension: %s" % args[0]) elif isinstance(expr, Add): if (all(isinstance(arg, Dimension) for arg in args) or all(arg.is_dimensionless for arg in args if isinstance(arg, Dimension))): return reduce(lambda x, y: x.add(y), args) else: raise ValueError("Dimensions cannot be added: %s" % expr) elif isinstance(expr, Mul): args = [arg for arg in args if isinstance(arg, Dimension)] return reduce(lambda x, y: x.mul(y), args) raise ValueError("Cannot be simplifed: %s", expr) def qsimplify(expr): """ Simplify expression by recursively evaluating the quantity arguments. If units are encountered, as it can be when using Constant, they are converted to quantity. """ def redmul(x, y): """ Function used to combine args in multiplications. This is necessary because the previous computation was not commutative, and Mul(3, u) was not simplified; but Mul(u, 3) was. """ if isinstance(x, Quantity): return x.mul(y) elif isinstance(y, Quantity): return y.mul(x) else: return x*y args = [] for arg in expr.args: arg = arg.evalf() if isinstance(arg, (Mul, Pow, Add)): arg = qsimplify(arg) args.append(arg) q_args, o_args = [], [] for arg in args: if isinstance(arg, Quantity): q_args.append(arg) elif isinstance(arg, Unit): # replace unit by a quantity to make the simplification q_args.append(arg.as_quantity) else: o_args.append(arg) if isinstance(expr, Pow): return args[0].pow(args[1]) elif isinstance(expr, Add): if q_args != []: quantities = reduce(lambda x, y: x.add(y), q_args) else: quantities = [] return reduce(lambda x, y: x+y, o_args, quantities) elif isinstance(expr, Mul): if q_args != []: quantities = reduce(lambda x, y: x.mul(y), q_args) else: quantities = [] return reduce(redmul, o_args, quantities) else: return expr