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
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
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)
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)
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)
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')
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')
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.
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
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)
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
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()
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)
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)
def irreducible_value(self): from proveit.numbers import zero return is_irreducible_value(self.operand) and self.operand != zero
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
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
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
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