예제 #1
0
 def simplification_of_operands(self, **defaults_config):
     '''
     Prove this Operation equal to a form in which its operands
     have been simplified.
     '''
     from proveit.relation import TransRelUpdater
     from proveit import ExprRange, NamedExprs
     from proveit.logic import is_irreducible_value
     if any(isinstance(operand, ExprRange) for operand in self.operands):
         # If there is any ExprRange in the operands, simplify the
         # operands together as an ExprTuple.
         return self.inner_expr().operands[:].simplification()
     else:
         expr = self
         eq = TransRelUpdater(expr)
         with defaults.temporary() as temp_defaults:
             # No auto-simplification or replacements here;
             # just simplify operands one at a time.
             temp_defaults.preserve_all = True
             operands = self.operands
             if isinstance(operands, NamedExprs):
                 # operands as NamedExprs
                 for key in operands.keys():
                     operand = operands[key]
                     if not is_irreducible_value(operand):
                         inner_operand = getattr(expr.inner_expr(), key)
                         expr = eq.update(inner_operand.simplification())
             else:
                 # operands as ExprTuple
                 for k, operand in enumerate(operands):
                     if not is_irreducible_value(operand):
                         inner_operand = expr.inner_expr().operands[k]
                         expr = eq.update(inner_operand.simplification())
     return eq.relation
예제 #2
0
def pairwise_evaluation(expr, **defaults_config):
    '''
    Evaluation routine applicable to associative operations in which
    operands at the beginning are paired and evaluated sequentially.
    '''
    from proveit import TransRelUpdater
    from proveit.logic import is_irreducible_value
    # successively evaluate and replace the operation performed on
    # the first two operands

    # for convenience while updating our equation:
    eq = TransRelUpdater(expr)

    if is_irreducible_value(expr):
        # The expression is already irreducible, so we are done.
        return eq.relation

    if expr.operands.num_entries() == 2:
        raise ValueError("pairwise_evaluation may only be used when there "
                         "are more than 2 operands.")

    # While there are more than 2 operands, associate the first 2
    # and auto-simplify.
    while expr.operands.num_entries() > 2:
        expr = eq.update(expr.association(0, length=2, auto_simplify=True))
        if is_irreducible_value(expr):
            # The new expression is irreducible, so we are done.
            return eq.relation
        elif not is_irreducible_value(expr.operands[0]):
            # Auto-simplication failed to convert the first operand
            # to an irreducible value, so break out of this loop
            # and generate an appropriate error by trying to
            # evaluate it directly.
            expr = eq.update(expr.inner_expr().operands[0].evaluate())
    return eq.relation
예제 #3
0
    def shallow_simplification(self,
                               *,
                               must_evaluate=False,
                               **defaults_config):
        '''
        Returns a proven simplification equation for this Neg
        expression assuming the operands have been simplified.
        
        Handle negation of 0, double negation, and distribution
        over a sum.
        '''
        from . import negated_zero
        from proveit.numbers import Add, zero

        if must_evaluate and not is_irreducible_value(self.operand):
            # The simplification of the operands may not have
            # worked hard enough.  Let's work harder if we
            # must evaluate.
            self.operand.evaluation()
            return self.evaluation()

        if self.operand == zero:
            return negated_zero

        # Handle double negation:
        if isinstance(self.operand, Neg):
            # simplify double negation
            return self.double_neg_simplification()

        if Neg._simplification_directives_.distribute_over_add:
            # Handle distribution:
            if isinstance(self.operand, Add):
                return self.distribution(auto_simplify=True)

        return Expression.shallow_simplification(self)
예제 #4
0
 def is_irreducible_value(self):
     '''
     An ExprTuple is irreducible if and only if all of its entries
     are irreducible.
     '''
     from proveit.logic import is_irreducible_value
     return all(is_irreducible_value(entry) for entry in self.entries)
예제 #5
0
 def operands_are_irreducible(self):
     '''
     Return True iff all of the operands of this Operation are
     irreducible.
     '''
     from proveit.logic import is_irreducible_value
     return all(
         is_irreducible_value(operand) for operand in self.operands.entries)
예제 #6
0
 def __init__(self, *digits, styles=None):
     NumeralSequence.__init__(self,
                              DecimalSequence._operator_,
                              *digits,
                              styles=styles)
     for digit in self.digits:
         if is_irreducible_value(digit) and digit not in DIGITS:
             raise Exception(
                 'A DecimalSequence may only be composed of 0-9 digits')
예제 #7
0
 def __init__(self, *bits, styles=None):
     NumeralSequence.__init__(self,
                              BinarySequence._operator_,
                              *bits,
                              styles=styles)
     self.bits = bits
     for bit in self.bits:
         if is_irreducible_value(bit) and bit not in BITS:
             raise Exception(
                 'A BinarySequence may only be composed of bits')
예제 #8
0
 def is_irreducible_value(self):
     '''
     This needs work, but we know that 1/x is irreducible if
     x is irreducible, not a negation, not 0 and not 1.
     '''
     from proveit.logic import is_irreducible_value
     from proveit.numbers import zero, one, Neg
     if (self.numerator == one and self.denominator not in (zero, one)
             and not isinstance(self.denominator, Neg)
             and is_irreducible_value(self.denominator)):
         return True
     return False  # TODO: handle any proper fraction, etc.
예제 #9
0
 def shallow_simplification(self, *, must_evaluate=False,
                            **defaults_config):
     '''
     Proves that this ExprTuple is equal to an ExprTuple
     with ExprRanges reduced unless these are "preserved"
     expressions.
     '''
     from proveit.relation import TransRelUpdater
     from proveit import ExprRange
     from proveit.logic import is_irreducible_value, EvaluationError
     expr = self
     eq = TransRelUpdater(expr)
     if defaults.preserve_all:
         # Preserve all sub-expressions -- don't simplify.
         return eq.relation
     _k = 0
     for entry in self.entries:
         if isinstance(entry, ExprRange):
             if entry in defaults.preserved_exprs:
                 if must_evaluate:
                     # An ExprRange is not irreducible.
                     raise EvaluationError(self)
                 # Preserve this entry -- don't simplify it.
                 _k += ExprTuple(entry).num_entries()
                 continue
             entry_simp = entry.reduction()
             if must_evaluate and not is_irreducible_value(entry_simp.rhs):
                 raise EvaluationError(self)
             if entry_simp.lhs != entry_simp.rhs:
                 substitution = expr.substitution(
                         entry_simp, start_idx=_k, preserve_all=True)
                 expr = eq.update(substitution)
             _k += entry_simp.rhs.num_entries()
         else:
             if must_evaluate and not is_irreducible_value(entry):
                 raise EvaluationError(self)
             _k += 1
     return eq.relation
예제 #10
0
 def do_reduced_evaluation(self, assumptions=USE_DEFAULTS, **kwargs):
     '''
     Only handles -0 = 0 or double negation.
     '''
     from proveit.logic import EvaluationError
     from . import negated_zero
     from proveit.numbers import zero
     if self.operand == zero:
         return negated_zero
     if isinstance(
             self.operand,
             Neg) and is_irreducible_value(
             self.operand.operand):
         return self.double_neg_simplification(assumptions)
     raise EvaluationError(self, assumptions)
예제 #11
0
 def shallow_simplification(self, *, must_evaluate=False,
                            **defaults_config):
     '''
     Simplify via inner product linearity:
     <a x, y> = a <x, y>
     <x, y> = <x, a y>
     <x + y, z> = <x, z> + <y, z>
     <x, y + z> = <x, y> + <x, z>
     '''
     from proveit.linear_algebra import VecSpaces, ScalarMult, VecAdd
     from proveit.linear_algebra.inner_products import (
             inner_prod_scalar_mult_left, inner_prod_scalar_mult_right,
             inner_prod_vec_add_left, inner_prod_vec_add_right)
     _u, _v = self.operands
     try:
         vec_space = VecSpaces.common_known_vec_space((_u, _v))
     except UnsatisfiedPrerequisites:
         # No known common vectors space for the operands, so
         # we have no specific shallow_simplication we can do here.
         return Operation.shallow_simplification(
                 self, must_evaluate=must_evaluate)
     field = VecSpaces.known_field(vec_space)
     simp = None
     if isinstance(_u, ScalarMult):
         simp = inner_prod_scalar_mult_left.instantiate(
                 {K:field, H:vec_space, a:_u.scalar, x:_u.scaled, y:_v})
     if isinstance(_v, ScalarMult):
         simp = inner_prod_scalar_mult_right.instantiate(
                 {K:field, H:vec_space, a:_v.scalar, x:_u, y:_v.scaled})
     if isinstance(_u, VecAdd):
         simp = inner_prod_vec_add_left.instantiate(
                 {K:field, H:vec_space, x:_u.terms[0], y:_u.terms[1], z:_v})
     if isinstance(_v, VecAdd):
         simp = inner_prod_vec_add_right.instantiate(
                 {K:field, H:vec_space, x:_u, y:_v.terms[0], z:_v.terms[1]})
     if simp is None:
         return Operation.shallow_simplification(
                 self, must_evaluate=must_evaluate)
     if must_evaluate and not is_irreducible_value(simp.rhs):
         return simp.inner_expr().rhs.evaluate()
     return simp
예제 #12
0
    def shallow_simplification(self,
                               *,
                               must_evaluate=False,
                               **defaults_config):
        '''
        Returns a proven simplification equation for this Abs
        expression assuming the operand has been simplified.
        
        Handles a number of absolute value simplifications:
            1. ||x|| = |x| given x is complex
            2. |x| = x given x ≥ 0
            3. |x| = -x given x ≤ 0
                             (may try to prove this if easy to do)
            4. |-x| = |x|
            5. |x_1 * ... * x_n| = |x_1| * ... * |x_n|
            6. |a / b| = |a| / |b|
            7. |exp(i a)| = 1 given a in Real
            8. |r exp(i a) - r exp(i b)| = 2 r sin(|a - b|/2)
                given a and b in Real.
            9. |x_1 + ... + x_n| = +/-(x_1 + ... + x_n) if
               the terms are known to be all non-negative
               or all non-positive.
        '''
        from proveit.logic import Equals
        from proveit.numbers import e, Add, Neg, LessEq, Mult, Div, Exp
        from proveit.numbers import zero, RealNonNeg, RealNonPos
        from proveit.logic import EvaluationError, is_irreducible_value

        if is_irreducible_value(self.operand):
            if isinstance(self.operand, Neg):
                # |-x| where 'x' is a literal.
                return self.distribution()
            else:
                # If the operand is irreducible, we can just use
                # abs_elimination.
                return self.abs_elimination()
        elif must_evaluate:
            # The simplification of the operands may not have
            # worked hard enough.  Let's work harder if we
            # must evaluate.
            self.operand.evaluation()
            return self.evaluation()

        # among other things, convert any assumptions=None
        # to assumptions=() (thus averting len(None) errors)

        # Check if we have an established relationship between
        # self.operand and zero.
        try:
            deduce_number_set(self.operand)
        except UnsatisfiedPrerequisites:
            pass
        if (LessEq(zero, self.operand).proven()
                or LessEq(self.operand, zero).proven()):
            # Either |x| = x or |x| = -x depending upon the sign
            # of x (comparison with zero).
            return self.abs_elimination()

        if isinstance(self.operand, Abs):
            # Double absolute-value.  We can remove one of them.
            return self.double_abs_elimination()

        # Distribute over a product or division.
        if (isinstance(self.operand, Mult) or isinstance(self.operand, Div)
                or isinstance(self.operand, Neg)):
            # Let's distribute the absolute values over the product
            # or division (the absolute value of the factors/components
            # will be simplified seperately if auto_simplify is True).
            return self.distribution()

        # |exp(i a)| = 1
        if isinstance(self.operand, Exp) and self.operand.base == e:
            try:
                return self.unit_length_simplification()
            except ValueError:
                # Not in a complex polar form.
                pass

        # |r exp(i a) - r exp(i b)| = 2 r sin(|a - b|/2)
        if (isinstance(self.operand, Add)
                and self.operand.operands.is_double()):
            try:
                return self.chord_length_simplification()
            except (ProofFailure, ValueError):
                # Not in a complex polar form.
                pass

        # |x_1 + ... + x_n| = +/-(x_1 + ... + x_n)
        # if the terms are known to be all non-negative or all
        # non-positive.
        if isinstance(self.operand, Add):
            all_nonneg = True
            all_nonpos = True
            for term in self.operand.terms:
                # Note that "not proven" is not the same as "disproven".
                # Not proven means there is something we do not know.
                # Disproven means that we do know the converse.
                if all_nonneg and not LessEq(zero, term).proven():
                    all_nonneg = False
                if all_nonpos and not LessEq(term, zero).proven():
                    all_nonpos = False
            if all_nonpos:
                InSet(self.operand, RealNonPos).prove()
            elif all_nonneg:
                InSet(self.operand, RealNonNeg).prove()
            if all_nonpos or all_nonneg:
                # Do another pass now that we know the sign of
                # the operand.
                return self.shallow_simplification()

        # Default is no simplification.
        return Equals(self, self).prove()
예제 #13
0
    def shallow_simplification(self,
                               *,
                               must_evaluate=False,
                               **defaults_config):
        '''
        Attempt to determine whether this conjunction, with
        simplified operands, evaluates to TRUE or FALSE under the given
        assumptions.  If all operands have simplified to TRUE,
        the conjunction is TRUE.  If any of the operands have
        simplified to FALSE, the conjunction may be FALSE (if the
        other operands are provably Boolean).
        If it can't be evaluated, and must_evaluate is False,
        ungroup nested conjunctions if that is an active
        simplification direction.  Also, if applicable, perform
        a unary reduction: And(A) = A.
        '''
        from proveit import ExprRange
        from proveit.logic import (Equals, FALSE, TRUE, EvaluationError,
                                   is_irreducible_value)
        # load in truth-table evaluations
        from . import (and_t_t, and_t_f, and_f_t, and_f_f,
                       conjunction_eq_quantification)

        if self.operands.num_entries() == 0:
            from proveit.logic.booleans.conjunction import \
                empty_conjunction_eval
            # And() = TRUE
            return empty_conjunction_eval

        # Check whether or not all of the operands are TRUE
        # or any are FALSE.
        all_are_true = True
        for operand in self.operands:
            if operand != TRUE:
                all_are_true = False
            if operand == FALSE:
                # If any simplified operand is FALSE, the conjunction
                # may only evaluate to FALSE if it can be evaluated.
                # Only use automation here if 'must_evaluate' is True.
                self.conclude_negation(automation=must_evaluate)
                return Equals(self, FALSE).prove()

        # If all of the operands are TRUE, we can prove that the
        # conjunction is equal to TRUE.
        if all_are_true:
            self.conclude()
            return Equals(self, TRUE).prove()

        if must_evaluate:
            if self.operands.contains_range():
                if self.operands.num_entries() == 1:
                    # Conjunction of a single ExprRange.  Convert
                    # to a universal quantification and evaluate that.
                    expr_range = self.operands[0]
                    _i = expr_range.true_start_index
                    _j = expr_range.true_end_index
                    _P = expr_range.lambda_map
                    conj_eq_quant = (conjunction_eq_quantification.instantiate(
                        {
                            i: _i,
                            j: _j,
                            P: _P
                        }, preserve_all=True))
                    return conj_eq_quant.apply_transitivity(
                        conj_eq_quant.rhs.evaluation())
                return prove_via_grouping_ranges(
                    self, lambda expr, **kwargs: expr.evaluation(**kwargs))
            if not all(
                    is_irreducible_value(operand)
                    for operand in self.operands):
                # The simplification of the operands may not have
                # worked hard enough.  Let's work harder if we
                # must evaluate.
                for operand in self.operands:
                    if not is_irreducible_value(operand):
                        operand.evaluation()
                return self.evaluation()
            # Can't evaluate the conjunction if no operand was
            # FALSE but they aren't all TRUE.
            raise EvaluationError(self)

        if self.operands.is_single():
            # And(A) = A
            return self.unary_reduction()

        expr = self
        # for convenience updating our equation
        eq = TransRelUpdater(expr)

        if And._simplification_directives_.ungroup:
            # ungroup the expression (disassociate nested conjunctions).
            _n = 0
            length = expr.operands.num_entries() - 1
            # loop through all operands
            while _n < length:
                operand = expr.operands[_n]
                if isinstance(operand, And):
                    # if it is grouped, ungroup it
                    expr = eq.update(
                        expr.disassociation(_n, auto_simplify=False))
                length = expr.operands.num_entries()
                _n += 1

        return Expression.shallow_simplification(self)
예제 #14
0
파일: or_op.py 프로젝트: PyProveIt/Prove-It
    def shallow_simplification(self,
                               *,
                               must_evaluate=False,
                               **defaults_config):
        '''
        Attempt to determine whether this disjunction, with
        simplified operands, evaluates to TRUE or FALSE under the given
        assumptions.  If all operands have simplified to FALSE,
        the disjunction is FALSE.  If any of the operands have
        simplified to TRUE, the disjunction may be TRUE (if the
        other operands are provably Boolean).
        If it can't be evaluated, and must_evaluate is False,
        ungroup nested disjunctions if that is an active
        simplification direction.  Also, if applicable, perform
        a unary reduction: Or(A) = A.
        '''
        from proveit.logic import (Equals, FALSE, TRUE, EvaluationError,
                                   is_irreducible_value)
        # load in truth-table evaluations
        from . import or_t_t, or_t_f, or_f_t, or_f_f
        if self.operands.num_entries() == 0:
            from proveit.logic.booleans.disjunction import \
                empty_disjunction_eval
            # Or() = FALSE
            return empty_disjunction_eval

        # Check whether or not all of the operands are FALSE
        # or any are TRUE.
        all_are_false = True
        for operand in self.operands:
            if operand != FALSE:
                all_are_false = False
            if operand == TRUE:
                # If any simplified operand is TRUE, the disjunction
                # may only evaluate to TRUE if it can be evaluated.
                # Only use automation here if 'must_evaluate' is True.
                self.conclude(automation=must_evaluate)
                return Equals(self, TRUE).prove()

        # If all of the operands are FALSE, we can prove that the
        # conjunction is equal to FALSE.
        if all_are_false:
            self.conclude_negation()
            return Equals(self, FALSE).prove()

        if must_evaluate:
            if not all(
                    is_irreducible_value(operand)
                    for operand in self.operands):
                # The simplification of the operands may not have
                # worked hard enough.  Let's work harder if we
                # must evaluate.
                for operand in self.operands:
                    if not is_irreducible_value(operand):
                        operand.evaluation()
                return self.evaluation()
            # Can't evaluate the conjunction if no operand was
            # FALSE but they aren't all TRUE.
            raise EvaluationError(self)

        if self.operands.is_single():
            # Or(A) = A
            return self.unary_reduction()

        expr = self
        # for convenience updating our equation
        eq = TransRelUpdater(expr)

        if Or._simplification_directives_.ungroup:
            # ungroup the expression (disassociate nested disjunctions).
            _n = 0
            length = expr.operands.num_entries() - 1
            # loop through all operands
            while _n < length:
                operand = expr.operands[_n]
                if isinstance(operand, Or):
                    # if it is grouped, ungroup it
                    expr = eq.update(
                        expr.disassociation(_n, auto_simplify=False))
                length = expr.operands.num_entries()
                _n += 1

        return Expression.shallow_simplification(self)
예제 #15
0
 def irreducible_value(self):
     from proveit.numbers import zero
     return is_irreducible_value(self.operand) and self.operand != zero
예제 #16
0
 def is_irreducible_value(self):
     from proveit.numbers import zero
     if isinstance(self.operand, Neg):
         return False  # double negation is reducible
     return is_irreducible_value(self.operand) and self.operand != zero
예제 #17
0
        def wrapper(*args, **kwargs):
            '''
            The wrapper for the equality_prover decorator.
            '''
            from proveit._core_.expression.expr import Expression
            from proveit.logic import Equals, TRUE, EvaluationError
            # Obtain the original Expression to be on the left side
            # of the resulting equality Judgment.
            _self = args[0]
            if isinstance(_self, Expression):
                expr = _self
            elif hasattr(_self, 'expr'):
                expr = _self.expr
            else:
                raise TypeError("@equality_prover, %s, expected to be a "
                                "method for an Expression type or it must "
                                "have an 'expr' attribute." % func)
            proven_truth = None
            if is_simplification_method or is_evaluation_method:
                from proveit.logic import is_irreducible_value
                if is_irreducible_value(expr):
                    # Already irreducible.  Done.
                    proven_truth = (Equals(expr,
                                           expr).conclude_via_reflexivity())

            # If _no_eval_check is set to True, don't bother
            # checking for an existing evaluation.  Used internally
            # in Operation.simplification, Operation.evaluation,
            # Conditional.simplification, and Judgment.simplify.
            _no_eval_check = kwargs.pop('_no_eval_check', False)
            if (not _no_eval_check
                    and (is_evaluation_method or
                         (defaults.simplify_with_known_evaluations
                          and is_simplification_method))):
                from proveit.logic import evaluate_truth
                if expr.proven():
                    # The expression is proven so it equals true.
                    proven_truth = Equals(expr,
                                          TRUE).conclude_boolean_equality()
                else:
                    # See if there is a known evaluation (or if one may
                    # be derived via known equalities if
                    # defaults.automation is enabled).
                    if 'assumptions' in kwargs:
                        # Use new assumptions temporarily.
                        with defaults.temporary() as tmp_defaults:
                            tmp_defaults.assumptions = kwargs.get(
                                'assumptions')
                            if expr.proven():
                                # expr is proven, so it evaluates
                                # to TRUE.
                                proven_truth = evaluate_truth(expr)
                            else:
                                proven_truth = Equals.get_known_evaluation(
                                    expr)
                    else:
                        proven_truth = Equals.get_known_evaluation(expr)
                # For an 'evaluation' or 'simplification', we should
                # force auto_simplify on and preserve_all off to
                # simplify as much as possible.
                kwargs['auto_simplify'] = True
                kwargs['preserve_all'] = False
            if proven_truth is None:
                proven_truth = decorated_relation_prover(*args, **kwargs)
            proven_expr = proven_truth.expr
            if not isinstance(proven_expr, Equals):
                raise TypeError("@equality_prover, %s, expected to prove an "
                                "Equals expression, not %s of type %s." %
                                (func, proven_expr, proven_expr.__class__))
            if is_evaluation_method or (is_shallow_simplification_method
                                        and kwargs.get('must_evaluate',
                                                       False) == True):
                # The right side of an evaluation must be irreducible.
                from proveit.logic import is_irreducible_value
                if not is_irreducible_value(proven_expr.rhs):
                    raise EvaluationError(_self)
            return proven_truth
예제 #18
0
    def shallow_simplification(self,
                               *,
                               must_evaluate=False,
                               **defaults_config):
        '''
        Returns a proven simplification equation for this Divide
        expression assuming the operands have been simplified.

        Specifically, cancels common factors and eliminates ones.
        '''
        from proveit.logic import is_irreducible_value
        from proveit.numbers import one, Neg
        expr = self
        # for convenience updating our equation
        eq = TransRelUpdater(expr)

        # perform cancelations where possible
        expr = eq.update(expr.cancelations(preserve_all=True))
        if not isinstance(expr, Div):
            # complete cancelation.
            return eq.relation

        if self.is_irreducible_value():
            # already irreducible
            return Equals(self, self).conclude_via_reflexivity()

        if must_evaluate and not all(
                is_irreducible_value(operand) for operand in self.operands):
            for operand in self.operands:
                if not is_irreducible_value(operand):
                    # The simplification of the operands may not have
                    # worked hard enough.  Let's work harder if we
                    # must evaluate.
                    operand.evaluation()
            return self.evaluation()

        if expr.denominator == one:
            # eliminate division by one
            expr = eq.update(expr.divide_by_one_elimination(preserve_all=True))

        if (isinstance(expr, Div)
                and Div._simplification_directives_.factor_negation
                and isinstance(expr.numerator, Neg)):
            # we have something like (-a)/b but want -(a/b)
            expr = eq.update(expr.neg_extraction())
            # return eq.relation  # no more division simplifications.

        if (Div._simplification_directives_.reduce_zero_numerator
                and isinstance(expr, Div)):
            if ((expr.numerator == zero
                 or Equals(expr.numerator, zero).proven())
                    and NotEquals(expr.denominator, zero).proven()):
                # we have something like 0/x so reduce to 0
                expr = eq.update(expr.zero_numerator_reduction())

        # finally, check if we have something like (x/(y/z))
        # but! remember to check here if we still even have a Div expr
        # b/c some of the work above might have changed it to
        # something else!
        if (isinstance(expr, Div) and isinstance(expr.denominator, Div)
                and NotEquals(expr.denominator.numerator, zero).proven()
                and NotEquals(expr.denominator.denominator, zero).prove()):
            expr = eq.update(expr.div_in_denominator_reduction())

        return eq.relation
예제 #19
0
    def shallow_simplification(self, *, must_evaluate=False,
                               **defaults_config):
        '''
        Returns a proven simplification equation for this Exp
        expression assuming the operands have been simplified.

        Handles the following evaluations:
            a^0 = 1 for any complex a
            0^x = 0 for any positive x
            1^x = 1 for any complex x
            a^(Log(a, x)) = x for RealPos a and x, a != 1.
            x^n = x*x*...*x = ? for a natural n and irreducible x.

        Handles a zero or one exponent or zero or one base as
        simplifications.
        '''
        from proveit.relation import TransRelUpdater
        from proveit.logic import EvaluationError, is_irreducible_value
        from proveit.logic import InSet
        from proveit.numbers import (zero, one, two, is_literal_int,
                                     is_literal_rational,
                                     Log, Rational, Abs)
        from . import (exp_zero_eq_one, exponentiated_zero,
                       exponentiated_one, exp_nat_pos_expansion)

        if self.is_irreducible_value():
            # already irreducible
            return Equals(self, self).conclude_via_reflexivity()

        if must_evaluate:
            if not all(is_irreducible_value(operand) for
                       operand in self.operands):
                for operand in self.operands:
                    if not is_irreducible_value(operand):
                        # The simplification of the operands may not have
                        # worked hard enough.  Let's work harder if we
                        # must evaluate.
                        operand.evaluation()
                return self.evaluation()

        if self.exponent == zero:
            return exp_zero_eq_one.instantiate({a: self.base})  # =1
        elif self.base == zero:
            # Will fail if the exponent is not positive, but this
            # is the only sensible thing to try.
            return exponentiated_zero.instantiate({x: self.exponent})  # =0
        elif self.exponent == one:
            return self.power_of_one_reduction()
        elif self.base == one:
            return exponentiated_one.instantiate({x: self.exponent})  # =1
        elif (isinstance(self.base, Exp) and
            isinstance(self.base.exponent, Div) and
            self.base.exponent.numerator == one and
                self.base.exponent.denominator == self.exponent):
            from . import nth_power_of_nth_root
            _n, _x = nth_power_of_nth_root.instance_params
            return nth_power_of_nth_root.instantiate(
                {_n: self.exponent, _x: self.base.base})
        elif (isinstance(self.base, Exp) and
            isinstance(self.exponent, Div) and
            self.exponent.numerator == one and
                self.exponent.denominator == self.base.exponent):
            from . import nth_root_of_nth_power, sqrt_of_square
            _n = self.base.exponent
            _x =  self.base.base
            if _n == two:
                return sqrt_of_square.instantiate({x: _x})
            return nth_root_of_nth_power.instantiate({n: _n, x: _x})
        elif (is_literal_rational(self.base) and
                  is_literal_int(self.exponent) and
                  self.exponent.as_int() > 1):
            expr = self
            eq = TransRelUpdater(expr)
            expr = eq.update(exp_nat_pos_expansion.instantiate(
                    {x:self.base, n:self.exponent}, preserve_all=True))
            # We should come up with a better way of reducing
            # ExprRanges representing repetitions:
            _n = self.exponent.as_int()
            if _n <= 0 or _n > 9:
                raise NotImplementedError("Currently only implemented for 1-9")
            repetition_thm = proveit.numbers.numerals.decimals \
                .__getattr__('reduce_%s_repeats' % _n)
            rep_reduction = repetition_thm.instantiate({x: self.base})
            expr = eq.update(expr.inner_expr().operands.substitution(
                    rep_reduction.rhs, preserve_all=True))
            expr = eq.update(expr.evaluation())
            return eq.relation
        elif (isinstance(self.exponent, Log)
            and self.base == self.exponent.base):
            # base_ns  = self.base.deduce_number_set()
            # antilog_ns = self.exponent.antilog.deduce_number_set()
            if (InSet(self.base, RealPos).proven()
                and InSet(self.exponent.antilog, RealPos).proven()
                and NotEquals(self.base, one).proven()):
                return self.power_of_log_reduction()
        expr = self
        # for convenience updating our equation:
        eq = TransRelUpdater(expr)
        if self.exponent == two and isinstance(self.base, Abs):
            from . import (square_abs_rational_simp,
                                     square_abs_real_simp)
            # |a|^2 = a if a is real
            try:
                deduce_number_set(self.base)
            except UnsatisfiedPrerequisites:
                pass
            rational_base = InSet(self.base, Rational).proven()
            real_base = InSet(self.base, Real).proven()
            thm = None
            if rational_base:
                thm = square_abs_rational_simp
            elif real_base:
                thm = square_abs_real_simp
            if thm is not None:
                simp = thm.instantiate({a: self.base.operand})
                expr = eq.update(simp)
                # A further simplification may be possible after
                # eliminating the absolute value.
                expr = eq.update(expr.simplification())

        return eq.relation