def evalf_expression(expression, n=15, evaluate=None):
    """
    Customized version of evalf.
    Attempts to truncate expressions to n digits in a way to get 
    identical results for two expressions that agree to n digits.
    To accomplish, the method performs the following steps
    1.  Traverses expression tree and attempts to call evalf with max(30,n+5)
        precision on every element.
        The first pass puts expression in a consistent form.
    2.  Traverses expression tree, converting each number to a float
        with the precision specified by n.
        For consistency, converts expression to string and back to float
        so that expression loses memory of original value and two
        expression will be the same if their conversion to string is the same.
    """

    expression = sympify(expression)
    initial_n_digits = max(30, n+5)

    # first convert every number to a float with initial_n_digits
    # for consistency
    if evaluate is not False:
        expression =  bottom_up(
            expression, lambda w: _initial_evalf(w,initial_n_digits), 
            atoms=True, evaluate=evaluate)

    expression =  bottom_up(
        expression,
        lambda w: w if not w.is_Number else Float(str(w.evalf(n))),
        atoms=True, evaluate=evaluate)

    return expression
    def test_substitute_list_tuple(self):
        x=Symbol('x')
        y=Symbol('y')
        z=Symbol('z')
        expr = bottom_up((x,y,z), lambda w: w if not w==x else Tuple(x,y), atoms=True)
        self.assertEqual(expr, ((x,y),y,z))

        expr = bottom_up((x,y,z), lambda w: w if not w==x else [x,y], atoms=True)
        self.assertEqual(expr, ([x,y],y,z))

        self.assertRaises(AttributeError, 
                          bottom_up, x**2, 
                          lambda w: w if not w==x else Tuple(x,y),
                          atoms=True)
def round_expression(expression, n=0, initial_n_digits=100, evaluate=None):
    """
    Customized version of round
    Attempts to round expressions to n decimal places in a way to get 
    identical results for two expressions that agree to n decimal places.
    To accomplish, the method performs the following steps
    1.  Traverses expression tree and attempts to call evalf with 
        initial_n_digits precision on every element.
        The first pass puts expression in a consistent form.
    2.  Traverses expression tree, converting each number to a float
        with the number of decimal places specified by n n.
        For consistency, converts expression to string and back to float
        so that expression loses memory of original value and two
        expression will be the same if their conversion to string is the same.
        If n is 0 or smaller, then converts to integer rather than float.

    """
    
    expression = sympify(expression)


    # first convert every number to a float with initial_n_digits
    # for consistency
    if evaluate is not False:
        expression =  bottom_up(
            expression, lambda w: _initial_evalf(w,initial_n_digits), 
            atoms=True, evaluate=evaluate)

    # next, round numbers
    if n <= 0:
        from sympy import Integer
        expression =  bottom_up(
            expression,
            lambda w: w if not w.is_Number else Integer(modified_round(w,n)),
            atoms=True, evaluate=evaluate)
    else:
        expression =  bottom_up(
            expression,
            lambda w: w if not w.is_Number else Float(str(modified_round(w,n))),
            atoms=True, evaluate=evaluate)
        
    return expression
def normalize_floats(expression, n_digits=14, evaluate=None):
    """
    To ensure consistency of expression with floats in presence of
    machine precision errors, round all floats to n_digits,
    converting expression to string and back to lose memory
    of original value and ensure two expression will be the same 
    if their conversion to string is the same
    """
    expression = sympify(expression)
    expression =  bottom_up(
        expression,
        lambda w: w if not w.is_Float else Float(str(w.evalf(n_digits))),
        atoms=True, evaluate=evaluate)
        
    return expression
 def test_rounds_numbers_to_integers(self):
     expr = bottom_up(sympify("2.0*x"), lambda w: w if not w.is_Number else Integer(w.round()), atoms=True)
     self.assertEqual(str(expr), "2*x")
 def test_evalf_accepted_each_atom(self):
     expr = bottom_up(sympify("sin(x/3)"), lambda w: w if not w.is_Number else w.evalf(4), atoms=True)
     self.assertEqual(str(expr), "sin(0.3333*x)")
Example #7
0
def try_normalize_expr(expr):
    """
    Attempt to normalize expression.
    If relational, subtract rhs from both sides.
    Convert any subclass of ImmutableMatrix to ImmutableMatrix.
    Convert any subclass of Derivative to Derivative
    Convert customized log commands to sympy log
    Use doit, expand, then ratsimp to simplify rationals, then expand again
    """

    def _remove_one_coefficient(expr):
        # remove a coefficent of a Mul that is just 1.0
        from sympy import Mul

        if expr.is_Mul and expr.args[0] == 1:
            if len(expr.args[1:]) == 1:
                return expr.args[1]
            else:
                return Mul(*expr.args[1:])
        else:
            return expr

    from .user_commands import log, ln
    from sympy import log as sympy_log

    def replace_logs_to_sympy(w):
        if w.func == ln or w.func == log:
            return sympy_log(*w.args)
        else:
            return w

    def normalize_transformations(w):
        # same as
        #    lambda w: w.doit().expand().ratsimp().expand()
        # except catch Polynomial error that could be triggered by ratsimp()
        # and catch attribute error for objects like Interval
        from sympy import PolynomialError

        w = w.doit()
        try:
            w = w.expand()
        except (AttributeError, TypeError):
            pass
        if w.has(sympy_log):
            from sympy import logcombine

            try:
                w = logcombine(w)
            except TypeError:
                pass
        try:
            w = w.ratsimp().expand()
        except (AttributeError, PolynomialError, UnicodeEncodeError, TypeError):
            pass
        return w

    expr = bottom_up(
        expr, lambda w: w if not isinstance(w, ImmutableMatrix) else ImmutableMatrix(*w.args), nonbasic=True
    )

    from sympy import Derivative

    expr = bottom_up(expr, lambda w: w if not isinstance(w, Derivative) else Derivative(*w.args))

    try:
        if expr.is_Relational:
            from sympy import StrictLessThan, LessThan, Equality, Unequality

            # in attempt to normalize relational
            # 1. subtract sides so rhs is zero (lhs for less than inequalities)
            # 2. try to find coefficient of first term and divide by it

            lmr = (expr.lhs - expr.rhs).expand()

            if lmr.is_Add:
                term = lmr.args[0]
            else:
                term = lmr
            coeff = term.as_coeff_Mul()[0]
            if not (isinstance(expr, Equality) or isinstance(expr, Unequality)):
                coeff = Abs(coeff)
            if coeff:
                lmr = lmr / coeff

            if isinstance(expr, StrictLessThan) or isinstance(expr, LessThan):
                expr = expr.func(0, -lmr)
            else:
                expr = expr.func(lmr, 0)
    except AttributeError:
        pass

    # replace logs with sympy log
    expr = bottom_up(expr, replace_logs_to_sympy)
    # transformations to try to normalize
    expr = bottom_up(expr, normalize_transformations)
    # remove any cofficients of 1.0
    # expr=bottom_up(expr, _remove_one_coefficient)

    return expr
Example #8
0
    def compare_with_expression_sub(self, new_expr, additional_rounding=None):
        """
        Compare expression of object with new_expression.
        Returns:
        1  if expressions are considered equal.
           If normalize_on_compare is set, then expressions are considered
           equal if their normalized expressions to the same.
           Otherwise, expressions themselves must be the same.
        -1 if normalize_on compare is not set, the expressions themselves
           are not the same, but their normalized expressions are the same.
        0  if the expressions not the same and the normalized expressions
           are not the same
        p  number p, 0 < p < 1, if expressions are partially equal,
           where p indicates the fraction of expressions that are equal
        -p number p, 0 < p < 1, if normalize_on compare is not set,
           the expressions themselves are not the same,
           but their normalized expressions are partially equal, then
           p indicates the fraction of normalized expressions that are equal
        In all determinations of equality, expressions are rounded to
        the precision determined by round_on_compare and round_absolute, if set,
        minus any additional rounding specified.
        """

        from mitesting.sympy_customized import EVALUATE_NONE

        expression = self._expression

        evaluate_level = self.return_evaluate_level()

        if evaluate_level != EVALUATE_NONE:
            comparison_evaluate = None
        else:
            comparison_evaluate = False

        # replace Symbol('lamda') with Symbol('lambda')
        def replace_lamda(w):
            if w == Symbol("lamda"):
                return Symbol("lambda")
            elif w == Symbol("lamda", real=True):
                return Symbol("lambda", real=True)
            else:
                return w

        expression = bottom_up(expression, replace_lamda, evaluate=comparison_evaluate, atoms=True)
        new_expr = bottom_up(new_expr, replace_lamda, evaluate=comparison_evaluate, atoms=True)

        # As long as evaluate is not False
        # convert customized ln command to customized log command
        # also convert sympy log to customized log command
        if evaluate_level != EVALUATE_NONE:
            from .user_commands import log, ln
            from sympy import log as sympy_log

            def replace_logs(w):
                if w.func == ln or w.func == sympy_log:
                    return log(*w.args)
                else:
                    return w

            expression = bottom_up(expression, replace_logs)
            new_expr = bottom_up(new_expr, replace_logs)

        # Calculate the normalized expressions for both expressions,
        # rounded to precision as specified by
        # round_on_compare and round_absolute (with additional rounding)
        new_expr_normalize = self.eval_to_comparison_precision(
            try_normalize_expr(new_expr), additional_rounding=additional_rounding
        )
        expression_normalize = self.eval_to_comparison_precision(
            try_normalize_expr(expression), additional_rounding=additional_rounding
        )

        # As long as evaluate is not False
        # evaluate both expressions to precision as specified by
        # round_on_compare and round_absolute (with additional rounding)
        new_expr = self.eval_to_comparison_precision(
            new_expr, additional_rounding=additional_rounding, evaluate=comparison_evaluate
        )
        expression = self.eval_to_comparison_precision(
            expression, additional_rounding=additional_rounding, evaluate=comparison_evaluate
        )

        tuple_is_unordered = self._parameters.get("tuple_is_unordered", False)
        match_partial_on_compare = self._parameters.get("match_partial_on_compare", False)
        expressions_equal = 0
        equal_if_normalize = 0
        if self._parameters.get("normalize_on_compare"):
            expressions_equal = check_equality(
                expression_normalize,
                new_expr_normalize,
                tuple_is_unordered=tuple_is_unordered,
                partial_matches=match_partial_on_compare,
            )
        else:
            expressions_equal = check_equality(
                expression, new_expr, tuple_is_unordered=tuple_is_unordered, partial_matches=match_partial_on_compare
            )
            if expressions_equal == 0:
                equal_if_normalize = check_equality(
                    expression_normalize,
                    new_expr_normalize,
                    tuple_is_unordered=tuple_is_unordered,
                    partial_matches=match_partial_on_compare,
                )

        if expressions_equal:
            return expressions_equal
        if equal_if_normalize:
            return -1 * equal_if_normalize
        return 0