def add_reduction(reduction, _radius, _theta): ''' Add the given reduction. First check that its left and rights sides are as expected: the left should be the polar form and the right should be the original expression. ''' polar_form = Mult(_radius, Exp(e, Mult(i, _theta))) assert (isinstance(reduction, Judgment) and isinstance(reduction.expr, Equals) and reduction.lhs == polar_form and reduction.rhs == orig_expr), ("Reduction, %s, not a judgement " "for %s = %s" % (reduction, polar_form, orig_expr)) if do_include_unit_length_reduction and _radius == one: # As a unit length complex number, let's include the # reduction from the unit length form in case a unit length # formula is applied (cover the bases). # The 'automation' allowed here is negligible (assuming # we have already proven appropriate set membership by this # point). reductions.add(reduction.inner_expr().lhs.eliminate_one( 0, automation=True)) # But prepare for a multi-stage reduction: # 1 * exp[i * theta] = 1 * orig_expr = orig_expr reductions.add( Mult(one, orig_expr).one_elimination(0, automation=True)) elif reduction.lhs != reduction.rhs: reductions.add(reduction)
def exponent_separation(self, **defaults_config): ''' From self of the form x^{a+b} deduce and return the equality x^{a+b} = x^a x^b. For example, Exp(x, Add(two, c)).split_exponent_sum() (with the apprpriate assumptions) should return: |- (x^{2+c}) = x^2 x^c. ''' # among other things, convert any assumptions=None # to assumptions=() # assumptions = defaults.checkedAssumptions(assumptions) from proveit.numbers import Add, Mult # implement only for the case in which exponent is an Add if not isinstance(self.exponent, Add): raise NotImplementedError( "'Exp.exponent_separation()' implemented only for cases in which " "the exponent appears as a sum (i.e. in the Add class). The " "exponent in this case is {0}.".format(self.exponent)) # list the addends in the exponent, which become exponents the_exponents = self.exponent.operands # list the new exponential factors the_new_factors = [Exp(self.base, new_exp) if new_exp != one else self.base for new_exp in the_exponents] # create the new equivalent product (Mult) mult_equiv = Mult(*the_new_factors) # use the Mult.exponent_combination() to deduce equality to self exp_separated = mult_equiv.exponent_combination() replacements = list(defaults.replacements) if defaults.auto_simplify: with Mult.temporary_simplification_directives() as tmp_directives: # Don't recombine the exponents after separating them. tmp_directives.combine_exponents = False replacements.append(mult_equiv.shallow_simplification()) # reverse the equality relationship and return return exp_separated.derive_reversed(replacements=replacements, auto_simplify=False)
def add_reduction(reduction, _theta): ''' Add the given reduction. First check that its left and rights sides are as expected: the left should be the polar form and the right should be the original expression. ''' polar_form = Exp(e, Mult(i, _theta)) assert (isinstance(reduction, Judgment) and isinstance(reduction.expr, Equals) and reduction.lhs == polar_form and reduction.rhs == orig_expr) if reduction.lhs != reduction.rhs: reductions.add(reduction)
Sum(y_etc, frac(Py_etc, z), domain=S)), domain=Complex))) distributefrac_through_summation distributefrac_through_summation_rev = Forall( [P, S], Implies( Forall(y_etc, InSet(Py_etc, Complex), domain=S), Forall(z, Equals(Sum(y_etc, frac(Py_etc, z), domain=S), frac(Sum(y_etc, Py_etc, domain=S), z)), domain=Complex))) distributefrac_through_summation_rev frac_in_prod = Forall([w_etc, x, y, z_etc], Equals(Mult(w_etc, frac(x, y), z_etc), frac(Mult(w_etc, x, z_etc), y)), domain=Complex) frac_in_prod frac_in_prod_rev = Forall([w_etc, x, y, z_etc], Equals(frac(Mult(w_etc, x, z_etc), y), Mult(w_etc, frac(x, y), z_etc)), domain=Complex) frac_in_prod_rev prod_of_fracs = Forall([x, y, z, w], Equals(Mult(frac(x, z), frac(y, w)), frac(Mult(x, y), Mult(z, w))), domain=Complex) prod_of_fracs
def factorization(self, the_factor, pull="left", group_factors=True, field=None, **defaults_config): ''' Deduce an equality between this VecAdd expression and a version in which either: (1) the scalar factor the_factor has been factored out in front (or possibly out behind) to produce a new ScalarMult; OR (2) the tensor product factor the_factor has been factored out in front (or possible out behind) to produce a new TensorProd. For example, if x = VecAdd(ScalarMult(a, v1), ScalarMult(a, v2)) then x.factorization(a) produces: |- x = ScalarMult(a, VecAdd(v1, v2)). Prove-It will need to know or be able to derive a vector space in which the vectors live. This method only works if the terms of the VecAdd are all ScalarMult objects or all TensorProd objects. In the case of all ScalarMult objects, any nested ScalarMult objects are first flattened if possible. Note: In the case of a VecAdd of all TensorProd objects, the lack of commutativity for tensor products limits any factorable tensor product factors to those occurring on the far left or far right of each tensor product term. Thus, for example, if x = VecAdd(TensorProd(v1, v2, v3), TensorProd(v1, v4, v5)) we can call x.factorization(v1) to obtain |- x = TensorProd(v1, VecAdd(TensorProd(v2, v3), TensorProd(v4, v5))), but we cannot factor v1 our of the expression y = VecAdd(TensorProd(v2, v1, v3), TensorProd(v4, v1, v5)) ''' expr = self eq = TransRelUpdater(expr) replacements = list(defaults.replacements) from proveit.linear_algebra import ScalarMult, TensorProd from proveit.numbers import one, Mult # Case (1) VecAdd(ScalarMult, ScalarMult, ..., ScalarMult) if all(isinstance(op, ScalarMult) for op in self.operands): # look for the_factor in each scalar; # code based on Add.factorization() _b = [] for _i in range(expr.terms.num_entries()): # remove nesting of ScalarMults term = expr.terms[_i].shallow_simplification().rhs expr = eq.update( expr.inner_expr().terms[_i].shallow_simplification()) # simplify the scalar part of the ScalarMult term = term.inner_expr().scalar.shallow_simplification().rhs expr = eq.update(expr.inner_expr().terms[_i].scalar. shallow_simplification()) if hasattr(term.scalar, 'factorization'): term_scalar_factorization = term.scalar.factorization( the_factor, pull, group_factors=group_factors, group_remainder=True, preserve_all=True) if not isinstance(term_scalar_factorization.rhs, Mult): raise ValueError( "Expecting right hand side of each factorization " "to be a product. Instead obtained: {}".format( term_scalar_factorization.rhs)) if pull == 'left': # the grouped remainder on the right _b.append( ScalarMult( term_scalar_factorization.rhs.operands[-1], term.scaled)) else: # the grouped remainder on the left _b.append( ScalarMult( term_scalar_factorization.rhs.operands[0], term.scaled)) # substitute in the factorized term expr = eq.update( term_scalar_factorization.substitution( expr.inner_expr().terms[_i].scalar, preserve_all=True)) else: if term.scalar != the_factor: raise ValueError( "Factor, %s, is not present in the term at " "index %d of %s!" % (the_factor, _i, self)) if pull == 'left': replacements.append( Mult(term.scalar, one).one_elimination(1)) else: replacements.append( Mult(one, term.scalar).one_elimination(0)) _b.append(ScalarMult(one, term.scaled)) if not group_factors and isinstance(the_factor, Mult): factor_sub = the_factor.operands else: factor_sub = ExprTuple(the_factor) # pull left/right not really relevant for the ScalarMult # cases; this simplification step still seems relevant if defaults.auto_simplify: # Simplify the remainder of the factorization if # auto-simplify is enabled. replacements.append(VecAdd(*_b).simplification()) from proveit import K, i, k, V, a # Perhaps here we could search through the operands to find # an appropriate VecSpace? Or maybe it doesn't matter? vec_space_membership = expr.operands[0].deduce_in_vec_space( field=field) _V_sub = vec_space_membership.domain _K_sub = VecSpaces.known_field(_V_sub) _i_sub = expr.operands.num_elements() _k_sub = the_factor _a_sub = ExprTuple(*_b) from proveit.linear_algebra.scalar_multiplication import ( distribution_over_vectors) distribution = distribution_over_vectors.instantiate( { V: _V_sub, K: _K_sub, i: _i_sub, k: _k_sub, a: _a_sub }, replacements=replacements) # need to connect the distributed version back to the # original self, via a shallow_simplification() of # each of the ScalarMult terms resulting in the distribution for _i in range(len(distribution.rhs.operands.entries)): distribution = (distribution.inner_expr().rhs.operands[_i]. shallow_simplify()) eq.update(distribution.derive_reversed()) # Case (2) VecAdd(TensorProd, TensorProd, ..., TensorProd) elif all(isinstance(op, TensorProd) for op in self.operands): # if hasattr(the_factor, 'operands'): # print("the_factor has operands: {}".format(the_factor.operands)) # the_factor_tuple = the_factor.operands.entries # else: # print("the_factor does not have operands: {}".format(the_factor)) # the_factor_tuple = (the_factor,) if isinstance(the_factor, TensorProd): the_factor_tuple = the_factor.operands.entries else: the_factor_tuple = (the_factor, ) # Setting the default_field here because the field # used manually in the association step somehow gets lost VecSpaces.default_field = field # look for the_factor in each TensorProd appearing in # the VecAdd operands, looking at the left vs. right # sides depending on the 'pull' direction specified _b = [] # to hold factors left behind for _i in range(expr.terms.num_entries()): # Notice we're not ready to deal with ExprRange # versions of Add operands here! # We are also implicitly assuming that each TensorProd # has at least two operands term = expr.terms[_i] if hasattr(term, 'operands'): term_tuple = term.operands.entries else: term_tuple = (term, ) if pull == 'left': # look for factor at left-most-side if the_factor_tuple != term_tuple[0:len(the_factor_tuple)]: raise ValueError( "VecAdd.factorization() expecting the_factor " "{0} to appear at the leftmost side of each " "addend, but {0} does not appear at the " "leftmost side of the addend {1}.".format( the_factor, term)) else: # we're OK, so save away the remainder of # factors from the rhs of the term, # and group any multi-term factor on the left if len(term_tuple[len(the_factor_tuple):]) == 1: _b.append(term_tuple[-1]) else: _b.append( TensorProd( *term_tuple[len(the_factor_tuple):])) # then create an associated version of the # expr to match the eventual thm instantiation # ALSO NEED TO DO THIS FOR THE RIGHT CASE expr = eq.update( expr.inner_expr().operands[_i].association( len(the_factor_tuple), len(term_tuple) - len(the_factor_tuple), preserve_all=True)) # perhaps we actually don't need the assoc step? # if len(the_factor_tuple) != 1: # expr = eq.update(expr.inner_expr().operands[_i]. # association(0, len(the_factor_tuple), # preserve_all=True)) elif pull == 'right': # look for factor at right-most-side if the_factor_tuple != term_tuple[-( len(the_factor_tuple)):]: raise ValueError( "VecAdd.factorization() expecting the_factor " "{0} to appear at the rightmost side of each " "addend, but {0} does not appear at the " "rightmost side of the addend {1}.".format( the_factor, term)) else: # we're OK, so save away the remainder of # factors from the lhs of the term, # and group any multi-term factor on the right if len(term_tuple[0:-(len(the_factor_tuple))]) == 1: _b.append(term_tuple[0]) else: _b.append( TensorProd( *term_tuple[0:-(len(the_factor_tuple))])) # then create an associated version of the # expr to match the eventual thm instantiation expr = eq.update( expr.inner_expr().operands[_i].association( 0, len(term_tuple) - len(the_factor_tuple), preserve_all=True)) # perhaps we actually don't need the assoc step? # if len(the_factor_tuple) != 1: # expr = eq.update(expr.inner_expr().operands[_i]. # association( # len(term_tuple)-len(the_factor_tuple), # len(the_factor_tuple), # preserve_all=True)) else: raise ValueError( "VecAdd.factorization() requires 'pull' argument " "to be specified as either 'left' or 'right'.") # now ready to instantiate the TensorProd/VecAdd # theorem: tensor_prod_distribution_over_add # and derive it's reversed result from proveit.linear_algebra.tensors import ( tensor_prod_distribution_over_add) from proveit import a, b, c, i, j, k, K, V from proveit.numbers import zero, one, num # useful to get ahead of time the num of operands # in the_factor and define the replacement # if hasattr(the_factor, 'operands'): # num_factor_entries = num(the_factor.operands.num_entries()) # factor_entries = the_factor.operands.entries # else: # num_factor_entries = one # factor_entries = (the_factor,) # useful to get ahead of time the num of operands # in the_factor and define the replacement if isinstance(the_factor, TensorProd): num_factor_entries = num(the_factor.operands.num_entries()) factor_entries = the_factor.operands.entries else: num_factor_entries = one factor_entries = (the_factor, ) # call deduce_in_vec_space() on the original self # instead of the current expr, otherwise we can run into # compications due to the associated sub-terms vec_space_membership = self.operands[0].deduce_in_vec_space( field=field) _V_sub = vec_space_membership.domain _K_sub = VecSpaces.known_field(_V_sub) if pull == 'left': # num of operands in left the_factor _i_sub = num_factor_entries # num of operands in right factor _k_sub = zero # the actual factor operands _a_sub = factor_entries # the other side is empty _c_sub = () elif pull == 'right': # left side is empty _i_sub = zero # right side has the factor _k_sub = num_factor_entries # left side is empty _a_sub = () # right side has the factor _c_sub = factor_entries _j_sub = num(len(_b)) _b_sub = ExprTuple(*_b) from proveit.linear_algebra.tensors import ( tensor_prod_distribution_over_add) impl = tensor_prod_distribution_over_add.instantiate( { V: _V_sub, K: _K_sub, i: _i_sub, j: _j_sub, k: _k_sub, a: _a_sub, b: _b_sub, c: _c_sub }, preserve_all=True) conseq = impl.derive_consequent() eq.update(conseq.derive_reversed()) else: print("Not yet an identified case. Sorry!") return eq.relation
def eliminate_common_factors(self, **defaults_config): ''' Eliminate all factors in common between the divisor and the dividend. For example, from (k a)|(k b), derive and return a|b. k must be a non-zero complex number. ''' from . import common_factor_elimination from proveit.numbers import Mult, one if self.lhs == self.rhs: # From x | x return 1 | 1. It's vacuous, but whatever. return Divides(one, one).prove() elif (isinstance(self.lhs, Mult) and isinstance(self.rhs, Mult)): # Handle the basic case in which the divisor and # the dividend are each the product of two factors and # the first of these is in common between them. if (self.lhs.operands.is_double() and self.rhs.operands.is_double()): lhs1 = self.lhs.operands[0] lhs2 = self.lhs.operands[1] rhs1 = self.rhs.operands[0] rhs2 = self.rhs.operands[1] deduce_number_set(lhs1) if (lhs1 == rhs1 and InSet(lhs1, Complex).proven() and NotEquals(lhs1, zero).proven()): return common_factor_elimination.instantiate({ a: lhs2, b: rhs2, k: lhs1 }) # Try to convert it to the basic case via factorization # and try again. rhs_factors = set(self.rhs.operands.entries) common_factors = [ factor for factor in self.lhs.factors if factor in rhs_factors ] # Pull the common factors out to the front. if len(common_factors) == 0: return self.prove() # No common factors to eliminate. lhs_factorization = self.lhs.factorization(common_factors, pull='left', group_factors=True, group_remainder=True, preserve_all=True) rhs_factorization = self.rhs.factorization(common_factors, pull='left', group_factors=True, group_remainder=True, preserve_all=True) # Prove this "divides" but the substitute factorized forms. divides_proof = self.prove() if lhs_factorization.lhs != lhs_factorization.rhs: divides_proof = lhs_factorization.sub_right_side_into( divides_proof) if rhs_factorization.lhs != rhs_factorization.rhs: divides_proof = rhs_factorization.sub_right_side_into( divides_proof) lhs1, lhs2 = lhs_factorization.rhs.operands rhs1, rhs2 = rhs_factorization.rhs.operands return common_factor_elimination.instantiate({ a: lhs2, b: rhs2, k: lhs1 }) elif isinstance(self.lhs, Mult) and self.rhs in self.lhs.factors: # From (k z) | k return z | 1. Why not? dividend = Mult(self.rhs, one) divides = Divides(self.lhs, dividend) divides_proof = divides.eliminate_common_factors() return divides_proof.inner_expr().lhs.dividend.one_elimination(1) elif isinstance(self.rhs, Mult) and self.lhs in self.rhs.factors: # From (k z) | k return z | 1. Why not? divisor = Mult(self.lhs, one) divides = Divides(divisor, self.rhs) divides_proof = divides.eliminate_common_factors() return divides_proof.inner_expr().lhs.divisor.one_elimination(1) # There are no common factors. return self.prove()
from proveit import Etcetera from proveit.logic import Forall, InSet, Equals, NotEquals, Implies from proveit.numbers import Mult, Natural, NaturalPos, Integer, Real, RealPos, Complex, Add, Sub, Sum from proveit.common import a, b, x, y, P, S, y_multi, v_etc, w_etc, x_etc, y_etc, z_etc, Py_etc from proveit.numbers.common import zero, one, ComplexSansZero from proveit import begin_theorems, end_theorems begin_theorems(locals()) mult_assoc = Forall([x_etc, y_etc, z_etc], Equals(Mult(x_etc, y_etc, z_etc), Mult(x_etc, Mult(y_etc), z_etc))) mult_assoc mult_nat_closure = Forall((a, b), InSet(Mult(a, b), Natural), domain=Natural) mult_nat_closure mult_nat_pos_closure = Forall((a, b), InSet(Mult(a, b), NaturalPos), domain=NaturalPos) mult_nat_pos_closure mult_int_closure = Forall([x_etc], InSet(Mult(x_etc), Integer), domain=Integer) mult_int_closure mult_real_closure = Forall([x_etc], InSet(Mult(x_etc), Real), domain=Real) mult_real_closure mult_real_pos_closure = Forall([x_etc], InSet(Mult(x_etc), RealPos), domain=RealPos)
def factorization(self, the_factors, pull="left", group_factors=True, group_remainder=True, **defaults_config): ''' Return the proven factorization (equality with the factored form) from pulling the factor(s) from this division to the "left" or "right". If there are multiple occurrences, the first occurrence is used. If group_factors is True, the factors are grouped together as a sub-product. The group_remainder parameter is not relevant here but kept for consistency with other factorization methods. Examples: [(a*b)/(c*d)].factorization((a/c)) proves (a*b)/(c*d) = (a/c)*(b/d) [(a*b)/(c*d)].factorization((1/c)) proves (a*b)/(c*d) = (1/c)*(a*b/d) [(a*b)/(c*d)].factorization(a, pull='right') proves (a*b)/(c*d) = (b/(c*d))*a [a/(c*d)].factorization(a, pull='right') proves a/(c*d) = (1/(c*d))*a [(a*b)/d].factorization((a/d), pull='right') proves (a*b)/d = b*(a/d) ''' from proveit.numbers import one, Mult from . import mult_frac_left, mult_frac_right, prod_of_fracs expr = self eq = TransRelUpdater(expr) if the_factors == self: return eq.relation # self = self if isinstance(the_factors, Div): the_factor_numer = the_factors.numerator the_factor_denom = the_factors.denominator else: the_factor_numer = the_factors the_factor_denom = one replacements = [] # Factor out a fraction. if expr.denominator == the_factor_denom: # Factor (x/z) from (x*y)/z. # x or y may be 1. if the_factor_numer not in (one, expr.numerator): expr = eq.update(expr.inner_expr().numerator.factorization( the_factors.numerator, pull=pull, group_factors=True, group_remainder=True, preserve_all=True)) if pull == 'left': # factor (x*y)/z into (x/z)*y thm = mult_frac_left if the_factor_numer == one: # factor y/z into (1/z)*y _x = one _y = expr.numerator replacements.append( Mult(_x, _y).one_elimination(0, preserve_all=True)) else: # factor (x*y)/z into x*(y/z) thm = mult_frac_right if the_factor_numer == one: # factor x/z into x*(1/z) _x = expr.numerator _y = one replacements.append( Mult(_x, _y).one_elimination(1, preserve_all=True)) if the_factor_numer != one: assert expr.numerator.operands.num_entries() == 2 _x = expr.numerator.operands.entries[0] _y = expr.numerator.operands.entries[1] _z = expr.denominator eq.update( thm.instantiate({ x: _x, y: _y, z: _z }, replacements=replacements)) else: # Factor (x*y)/(z*w) into (x/z)*(y/w). thm = prod_of_fracs if the_factor_denom not in (one, expr.denominator): expr = eq.update(expr.inner_expr().denominator.factorization( the_factor_denom, pull=pull, group_factors=True, preserve_all=True)) assert expr.denominator.operands.num_entries() == 2 _z = expr.denominator.operands.entries[0] _w = expr.denominator.operands.entries[1] elif (pull == 'left') == (the_factor_denom == one): # Factor (x*y)/w into x*(y/w). _z = one _w = expr.denominator replacements.append( Mult(_z, _w).one_elimination(0, preserve_all=True)) else: # Factor (x*y)/z into (x/z)*y. _z = expr.denominator _w = one replacements.append( Mult(_z, _w).one_elimination(1, preserve_all=True)) # Factor the numerator parts unless there is a 1 numerator. if the_factor_numer not in (one, expr.numerator): expr = eq.update(expr.inner_expr().numerator.factorization( the_factor_numer, pull=pull, group_factors=True, group_remainder=True, preserve_all=True)) assert expr.numerator.operands.num_entries() == 2 # Factor (x*y)/(z*w) into (x/z)*(y/w) _x = expr.numerator.operands.entries[0] _y = expr.numerator.operands.entries[1] elif (pull == 'left') == (the_factor_numer == one): # Factor y/(z*w) into (1/z)*(y/w) _x = one _y = expr.numerator replacements.append( Mult(_x, _y).one_elimination(0, preserve_all=True)) else: # Factor x/(y*z) into (x/y)*(1/z) _x = expr.numerator _y = one replacements.append( Mult(_x, _y).one_elimination(1, preserve_all=True)) # create POSSIBLE replacements for inadvertently generated # fractions of the form _x/1 (i.e. _z = 1) # or _y/1 (i.e. _w = 1): if _z == one: replacements.append( frac(_x, _z).divide_by_one_elimination(preserve_all=True)) if _w == one: replacements.append( frac(_y, _w).divide_by_one_elimination(preserve_all=True)) eq.update( thm.instantiate({ x: _x, y: _y, z: _z, w: _w }, replacements=replacements, preserve_expr=expr)) return eq.relation
def cancelation(self, term_to_cancel, **defaults_config): ''' Deduce and return an equality between self and a form in which the given operand has been canceled on the numerator and denominator. For example, [(a*b)/(b*c)].cancelation(b) would return (a*b)/(b*c) = a / c. Assumptions or previous work might be required to establish that the term_to_cancel is non-zero. ''' from proveit.numbers import Mult, one expr = self eq = TransRelUpdater(expr) if self.numerator == self.denominator == term_to_cancel: # x/x = 1 from . import frac_cancel_complete return frac_cancel_complete.instantiate({x: term_to_cancel}) if term_to_cancel != self.numerator: # try to catch Exp objects here as well? # after all, Exp(term_to_cancel, n) has factors! if (not isinstance(self.numerator, Mult) or term_to_cancel not in self.numerator.operands): raise ValueError("%s not in the numerator of %s" % (term_to_cancel, self)) # Factor the term_to_cancel from the numerator to the left. expr = eq.update(expr.inner_expr().numerator.factorization( term_to_cancel, group_factors=True, group_remainder=True, preserve_all=True)) if term_to_cancel != self.denominator: if (not isinstance(self.denominator, Mult) or term_to_cancel not in self.denominator.operands): raise ValueError("%s not in the denominator of %s" % (term_to_cancel, self)) # Factor the term_to_cancel from the denominator to the left. expr = eq.update(expr.inner_expr().denominator.factorization( term_to_cancel, group_factors=True, group_remainder=True, preserve_all=True)) if expr.numerator == expr.denominator == term_to_cancel: # Perhaps it reduced to the trivial x/x = 1 case via # auto-simplification. expr = eq.update(expr.cancelation(term_to_cancel)) return eq.relation else: # (x*y) / (x*z) = y/z with possible automatic reductions # via 1 eliminations. from . import frac_cancel_left replacements = list(defaults.replacements) if expr.numerator == term_to_cancel: numer_prod = Mult(term_to_cancel, one) _y = one replacements.append( numer_prod.one_elimination(1, preserve_expr=term_to_cancel)) else: _y = expr.numerator.operands[1] if expr.denominator == term_to_cancel: denom_prod = Mult(term_to_cancel, one) _z = one replacements.append( denom_prod.one_elimination(1, preserve_expr=term_to_cancel)) else: _z = expr.denominator.operands[1] expr = eq.update( frac_cancel_left.instantiate({ x: term_to_cancel, y: _y, z: _z }, replacements=replacements, preserve_expr=expr)) return eq.relation
def factorization(self, the_factors, pull="left", group_factors=True, group_remainder=None, **defaults_config): ''' Return the proven factorization (equality with the factored form) from pulling the factor(s) from this summation to the "left" or "right". If group_factors is True, the factors will be grouped together as a sub-product. group_remainder is not relevant kept for compatibility with other factor methods. ''' from proveit import ExprTuple, var_range, IndexedVar from proveit.numbers.multiplication import distribute_through_summation from proveit.numbers import Mult, one if not isinstance(the_factors, Expression): # If 'the_factors' is not an Expression, assume it is # an iterable and make it a Mult. the_factors = Mult(*the_factors) if not free_vars(the_factors).isdisjoint(self.instance_params): raise ValueError( 'Cannot factor anything involving summation indices ' 'out of a summation') expr = self # for convenience updating our equation eq = TransRelUpdater(expr) # We may need to factor the summand within the summation summand_assumptions = defaults.assumptions + self.conditions.entries summand_factorization = self.summand.factorization( the_factors, pull, group_factors=group_factors, group_remainder=True, assumptions=summand_assumptions) if summand_factorization.lhs != summand_factorization.rhs: gen_summand_factorization = summand_factorization.generalize( self.instance_params, conditions=self.conditions) expr = eq.update( expr.instance_substitution(gen_summand_factorization, preserve_all=True)) if not group_factors and isinstance(the_factors, Mult): factors = the_factors.factors else: factors = ExprTuple(the_factors) if pull == 'left': _a = factors _c = ExprTuple() summand_remainder = expr.summand.factors[-1] elif pull == 'right': _a = ExprTuple() _c = factors summand_remainder = expr.summand.factors[0] else: raise ValueError("'pull' must be 'left' or 'right', not %s" % pull) _b = self.instance_params _i = _a.num_elements() _j = _b.num_elements() _k = _c.num_elements() _f = Lambda(expr.instance_params, summand_remainder) _Q = Lambda(expr.instance_params, expr.condition) _impl = distribute_through_summation.instantiate( { i: _i, j: _j, k: _k, f: _f, Q: _Q, b: _b }, preserve_all=True) quantified_eq = _impl.derive_consequent(preserve_all=True) eq.update(quantified_eq.instantiate({a: _a, c: _c}, preserve_all=True)) return eq.relation
def complex_polar_coordinates(expr, *, radius_must_be_nonneg=True, nonneg_radius_preferred=True, do_include_unit_length_reduction=True, reductions=None): ''' Given an expression, expr, of the complex number polar form, r * exp(i * theta), or something obviously equivalent to this, where r and theta are Real (and r is preferably RealNonNeg) under the given assumptions, return (r, theta) as a tuple pair. If defaults.automation=False, the r and theta must already be known to be RealNonNeg and Real respectively. If defaults.automation=True, we may attempt to prove these through automation. If radius_must_be_nonneg and nonneg_radius_preferred are False, we won't worry about ensuring that r is non-negative (so the result can be ambiguous). If radius_must_be_nonneg is True, a ValueError will be raised if we can't convert to a form where r is known to be non-negative. If expr is not exactly in this complex number polar form and 'reductions' is provided as a set, add to the 'reductions' set an equation that equates the exact form on the left with the original form on the right. This may be useful to use as 'reductions' in instantiations of theorems that employ the complex number polar form so it may perform proper reductions to the desired form. For example, if expr=5 is provided, the added reduction will be 5 * exp(i * 0) = 5. If do_include_unit_length_reduction is True, we will included reductions so that it will reduce from the unit length form as well. For example, if expr=1 is provided, the added reductions will be exp(i * 0) = 1 1 * 1 = 1. This also works in a way that cascades when reducing from the general polar form: 1 * exp(i * 0) = 1 * 1 = 1 Raise ValueError if the expr is not obviously equivalent to a complex number polar form. Also see unit_length_complex_polar_angle. ''' from . import complex_polar_negation, complex_polar_radius_negation from proveit.logic import InSet, Equals from proveit.numbers import deduce_in_number_set, deduce_number_set from proveit.numbers import zero, one, e, i, pi from proveit.numbers import Real, RealNonPos, RealNonNeg, Complex from proveit.numbers import Add, LessEq, Neg, Mult, Exp orig_expr = expr automation = defaults.automation simplify = defaults.auto_simplify if reductions is None: reductions = set() def add_reduction(reduction, _radius, _theta): ''' Add the given reduction. First check that its left and rights sides are as expected: the left should be the polar form and the right should be the original expression. ''' polar_form = Mult(_radius, Exp(e, Mult(i, _theta))) assert (isinstance(reduction, Judgment) and isinstance(reduction.expr, Equals) and reduction.lhs == polar_form and reduction.rhs == orig_expr), ("Reduction, %s, not a judgement " "for %s = %s" % (reduction, polar_form, orig_expr)) if do_include_unit_length_reduction and _radius == one: # As a unit length complex number, let's include the # reduction from the unit length form in case a unit length # formula is applied (cover the bases). # The 'automation' allowed here is negligible (assuming # we have already proven appropriate set membership by this # point). reductions.add(reduction.inner_expr().lhs.eliminate_one( 0, automation=True)) # But prepare for a multi-stage reduction: # 1 * exp[i * theta] = 1 * orig_expr = orig_expr reductions.add( Mult(one, orig_expr).one_elimination(0, automation=True)) elif reduction.lhs != reduction.rhs: reductions.add(reduction) def raise_not_valid_form(extra_msg=None): if extra_msg is None: extra_msg = "" raise ValueError("%s not in a form that is obviously " "reducible from an r * exp(i*theta) form. %s" % (orig_expr, extra_msg)) if (isinstance(expr, Exp) or (isinstance(expr, Neg) and isinstance(expr.operand, Exp))): # exp(i * theta) reduced from 1 * exp(i * theta). # or exp(i * (theta + pi)) reduced from -exp(i * theta). inner_reductions = set() _theta = unit_length_complex_polar_angle(expr, reductions=inner_reductions) deduce_in_number_set(_theta, Complex) deduce_in_number_set(Mult(i, _theta), Complex) deduce_in_number_set(Exp(e, Mult(i, _theta)), Complex) _r = one expr = Mult(_r, Exp(e, Mult(i, _theta))) # reduction: 1*exp(i * theta) = exp(i * theta) reduction = expr.one_elimination(0, preserve_all=True) # reduction: 1*exp(i * theta) = orig_expr if len(inner_reductions) > 0: reduction = reduction.inner_expr().rhs.substitute( inner_reductions.pop().rhs, preserve_all=True) # Add the reduction and return the coordinates. add_reduction(reduction, _r, _theta) return (_r, _theta) elif isinstance(expr, Neg): # expr = -(r*exp(i*theta0)) = r*exp(i*(theta0 + pi)) inner_reductions = set() # obtain the theta of the negated expression. _r, _theta0 = complex_polar_coordinates( expr.operand, radius_must_be_nonneg=radius_must_be_nonneg, nonneg_radius_preferred=nonneg_radius_preferred, reductions=inner_reductions) # theta = theta0 + pi _theta = Add(_theta0, pi) if defaults.auto_simplify: # simplify theta theta_simplification = _theta.simplification() inner_reductions.add(theta_simplification) _theta = theta_simplification.rhs deduce_in_number_set(_theta, Complex) deduce_in_number_set(Mult(i, _theta), Complex) deduce_in_number_set(Exp(e, Mult(i, _theta)), Complex) # reduction: r*exp(i*theta) = orig_expr [via -(r*exp(i*theta0))] reduction = complex_polar_negation.instantiate( { r: _r, theta: _theta0 }, replacements=inner_reductions, auto_simplify=False) # Add the reduction and return the coordinates. add_reduction(reduction, _r, _theta) return (_r, _theta) # Search for an exponentiation factor with base of 'e' and an # imaginary number in the exponent. complex_exp_factor_idx = None if isinstance(expr, Mult): i_factor_idx = None for idx, factor in enumerate(expr.factors): if isinstance(factor, Exp) and factor.base == e: # exp(x) type factor; check for imaginary number in # exponent. contains_imaginary_factor = False sub_expr = factor.exponent if isinstance(sub_expr, Neg): sub_expr = sub_expr.operand if isinstance(sub_expr, Mult): if i in sub_expr.operands.entries: contains_imaginary_factor = True else: contains_imaginary_factor = (sub_expr == i) if contains_imaginary_factor: # Found imaginary number in an exponent. if ((complex_exp_factor_idx is not None) or (i_factor_idx is not None)): # We already have an imaginary number in # an exponent. We can only have one. raise_not_valid_form() complex_exp_factor_idx = idx deduce_in_number_set(sub_expr, Complex) if complex_exp_factor_idx is None: # No exp(i theta) factor. Let's multiply by exp(i * 0). exp_i0 = Exp(e, Mult(i, zero)) expr = Mult(expr, exp_i0) inner_reductions = set() _r, _theta = complex_polar_coordinates( expr, radius_must_be_nonneg=radius_must_be_nonneg, nonneg_radius_preferred=nonneg_radius_preferred, do_include_unit_length_reduction=False, reductions=inner_reductions) assert _theta == zero deduce_in_number_set(exp_i0, Complex) # reduction: r * exp(i * theta) = orig_expr * exp(i * 0) if len(inner_reductions) > 0: reduction = inner_reductions.pop() else: reduction = Equals(expr, expr).conclude_via_reflexivity() # reduction: r * exp(i * theta) = orig_expr reduction = reduction.inner_expr().rhs.simplify( preserve_expr=orig_expr) add_reduction(reduction, _r, _theta) return (_r, _theta) # expr in ... * exp(... * i * ...) * ... form # Obtain the theta from exp(... * i * ...) = exp[i * theta0]. inner_reductions = set() _theta0 = unit_length_complex_polar_angle( expr.factors[complex_exp_factor_idx], reductions=inner_reductions) expr = Mult(*expr.factors.entries[:complex_exp_factor_idx], Exp(e, Mult(i, _theta0)), *expr.factors.entries[complex_exp_factor_idx + 1:]) # reduction: ... * expr[i * theta0] * ... = orig_expr if len(inner_reductions) > 0: reduction = expr.inner_expr().operands[1].substitution( inner_reductions.pop().rhs, preserve_all=True) else: reduction = Equals(expr, expr).conclude_via_reflexivity() if not expr.operands.is_double() or complex_exp_factor_idx != 1: # Pull the exp(i*theta) type factor to the right. # reduction: r0 * exp(i * theta0) = orig_expr for factor in expr.factors: # Deduce the factors are complex numbers ahead of time # in case automation is disabled. deduce_in_number_set(factor, Complex) reduction = reduction.inner_expr().lhs.factor(complex_exp_factor_idx, pull='right', group_remainder=True, preserve_all=True) expr = reduction.lhs # expr: r0 * exp(i * theta0) assert expr.operands.is_double() and isinstance(expr.operands[1], Exp) # Check that r0 is real and that we know it's relation with zero. _r0 = expr.operands[0] _r0_ns = deduce_number_set(_r0).domain if Real.includes(_r0_ns): InSet(_r0, Real).prove() else: raise_not_valid_form("%s not known to be real." % _r0) is_known_nonneg = RealNonNeg.includes(_r0_ns) is_known_nonpos = RealNonPos.includes(_r0_ns) if radius_must_be_nonneg: # We must know the relationship between r0 and 0 so we # can ensure r is non-negative. if not nonneg_radius_preferred: ValueError("nonneg_radius_preferred must be True if " "radius_must_be_nonneg is True.") if not (is_known_nonneg or is_known_nonpos): raise_not_valid_form("Relation of %s to 0 is unknown and " "radius_must_be_nonneg is True." % _r0) if nonneg_radius_preferred and is_known_nonpos: # r0 <= 0, so we must negate it and add pi to the angle. inner_reductions = {reduction} # theta: theta + pi _theta = Add(_theta0, pi) if simplify: # simplify theta theta_simplification = _theta.simplification() inner_reductions.add(theta_simplification) _theta = theta_simplification.rhs # r: -r0 _r = Neg(_r0) if simplify: # simplify radius radius_simplification = _r.simplification() inner_reductions.add(radius_simplification) _r = radius_simplification.rhs # reduction: r*exp(i*theta) = orig_expr [via r0*exp(i*theta0))] reduction = complex_polar_radius_negation.instantiate( { r: _r0, theta: _theta0 }, replacements=inner_reductions, auto_simplify=False) else: _r, _theta = _r0, _theta0 # Add the reduction and return the coordinates. add_reduction(reduction, _r, _theta) return (_r, _theta)
def unit_length_complex_polar_angle(expr, *, reductions=None): ''' Given an expression, expr, of the complex number polar form, exp(i * theta), or something obviously equivalent to this, where r is RealNonNeg and theta is Real under the given assumptions, return theta. If defaults.automation=False, theta must already be known to be Real. If defaults.automation=True, we may attempt to prove these through automation. If expr is not exactly in this complex number polar form and 'reductions' is provided as a set, add to the 'reductions' set an equation that equates the exact form on the left with the original form on the right. This may be useful to use as 'replacements' in instantiations of theorems that employ the complex number polar form so it may perform proper reductions to the desired form. For example, if expr=1 is provided, the added reduction will be exp(i * 0) = 1 Raise ValueError if the expr is not obviously equivalent to a complex number polar form. Also see complex_polar_coordinates. ''' from proveit import ExprRange from proveit.logic import Equals, InSet from proveit.numbers import deduce_in_number_set, deduce_number_set from proveit.numbers import zero, one, e, i, pi from proveit.numbers import Add, Neg, Mult, Exp, Real, Complex from . import unit_length_complex_polar_negation if reductions is None: reductions = set() orig_expr = expr def raise_not_valid_form(extra_msg=None): if extra_msg is None: extra_msg = "" raise ValueError("%s not in a form that is obviously " "reducible from an exp(i*theta) form. %s" % (orig_expr, extra_msg)) automation = defaults.automation simplify = defaults.auto_simplify def add_reduction(reduction, _theta): ''' Add the given reduction. First check that its left and rights sides are as expected: the left should be the polar form and the right should be the original expression. ''' polar_form = Exp(e, Mult(i, _theta)) assert (isinstance(reduction, Judgment) and isinstance(reduction.expr, Equals) and reduction.lhs == polar_form and reduction.rhs == orig_expr) if reduction.lhs != reduction.rhs: reductions.add(reduction) if expr == one: # expr = 1 = exp(i * 0) _theta = zero expr = Exp(e, Mult(i, _theta)) # reduction: exp(i * 0) = 1 reduction = expr.simplification() # Add the reduction and return theta. add_reduction(reduction, _theta) return _theta if isinstance(expr, Exp) and expr.base == e: if expr.exponent == i: # expr = exp(i) = exp(i * 1) _theta = one expr = Exp(e, Mult(i, one)) # reduction: exp(i * 1) = exp(i) reduction = expr.inner_expr().exponent.one_elimination(1) # Add the reduction and return theta. add_reduction(reduction, _theta) return _theta if hasattr(expr.exponent, 'factorization'): if (isinstance(expr.exponent, Mult) and expr.exponent.operands.is_double() and expr.exponent.operands[0] == i): # Already in the proper form. No reduction needed, # but we do need to check that theta is real. _theta = expr.exponent.factors[1] _theta_ns = deduce_number_set(_theta).domain if not Real.includes(_theta_ns): raise_not_valid_form("%s known to be %s but not Real." % (_theta, _theta_ns)) deduce_in_number_set(_theta, Real) return _theta try: # Factor i in the exponent, pulling to the left to # get into exp(i * theta) form. for operand in expr.exponent.operands: # Deduce the operands are complex numbers ahead of # time in case automation is disabled. deduce_in_number_set(operand, Complex) factorization = expr.inner_expr().exponent.factorization( i, pull='left', group_remainder=True, preserve_all=True) expr = factorization.rhs assert isinstance(expr.exponent, Mult) assert expr.exponent.factors.is_double() assert expr.exponent.factors[0] == i _theta = expr.exponent.factors[1] _theta_ns = deduce_number_set(_theta).domain if not Real.includes(_theta_ns): raise_not_valid_form("%s known to be %s but not Real." % (_theta, _theta_ns)) # reduction: exp(i * theta) = orig_expr reduction = factorization.derive_reversed() # Add the reduction and return theta. add_reduction(reduction, _theta) return _theta except ValueError: raise_not_valid_form() if isinstance(expr, Neg): # expr = -exp(i*theta0) = exp(i*(theta0 + pi)) = exp(i*theta) inner_reductions = set() # obtain the theta of the negated expression. _theta0 = unit_length_complex_polar_angle(expr.operand, reductions=inner_reductions) # theta = theta0 + pi _theta = Add(_theta0, pi) if simplify: # simplify theta theta_simplification = _theta.simplification() inner_reductions.add(theta_simplification) _theta = theta_simplification.rhs # reduction: exp(i*theta) = orig_expr [via -exp(i*theta0)] reduction = unit_length_complex_polar_negation.instantiate( {theta: _theta0}, replacements=inner_reductions, auto_simplify=False) # Add the reduction and return theta. add_reduction(reduction, _theta) return _theta raise_not_valid_form()
def exp_neg_2pi_i_on_two_pow_t(*exp_factors): from proveit.physics.quantum.QPE import _two_pow_t return exp(Neg(frac(Mult(*((two, pi, i) + exp_factors)), _two_pow_t)))
def exp2pi_i(*exp_factors): from proveit.numbers import Mult, pi, i return exp(Mult(*((two, pi, i) + exp_factors)))
Abs(x), y), And( LessThanEquals( Neg(y), x), LessThanEquals( x, y))), domain=Real, conditions=[ GreaterThanEquals( y, zero)]) abs_ineq # transferred by wdc 3/11/2020 triangle_inequality = Forall([a, b], LessThanEquals( Abs(Add(a, b)), Add(Abs(a), Abs(b))), domain=Complex) triangle_inequality # transferred by wdc 3/11/2020 abs_prod = Forall(x_etc, Equals(Abs(Mult(x_etc)), Mult(Etcetera(Abs(x_multi)))), domain=Complex) abs_prod # transferred by wdc 3/11/2020 abs_frac = Forall([a, b], Equals(Abs(frac(a, b)), frac(Abs(a), Abs(b))), domain=Complex) abs_frac # transferred by wdc 3/11/2020 mod_abs_scaled = Forall( (a, b, c), Equals( Mult( a, ModAbs(
Equals(Exp(a, zero), one), domain=Complex, conditions=[NotEquals(a, zero)]) exp_zero_eq_one exponentiated_zero = Forall([x], Equals(Exp(zero, x), zero), domain=Complex, conditions=[NotEquals(x, zero)]) exponentiated_zero exponentiated_one = Forall([x], Equals(Exp(one, x), one), domain=Complex) exponentiated_one sum_in_exp = Forall([a, b, c], Equals(Exp(a, Add(b, c)), Mult(Exp(a, b), Exp(a, c))), domain=Complex, conditions=[NotEquals(a, zero)]) sum_in_exp sum_in_exp_rev = Forall([a, b, c], Equals(Mult(Exp(a, b), Exp(a, c)), Exp(a, Add(b, c))), domain=Complex, conditions=[NotEquals(a, zero)]) sum_in_exp_rev add_one_right_in_exp = Forall([a, b], Equals(Exp(a, Add(b, one)), Mult(Exp(a, b), a)), domain=Complex, conditions=[NotEquals(a, zero)]) add_one_right_in_exp
distribute_neg_through_sum distribute_neg_through_sum_rev = Forall([x_etc], Equals(Add(Etcetera(Neg(x_multi))), Neg(Add(x_etc))), domain=Complex) distribute_neg_through_sum_rev distribute_neg_through_subtract = Forall([x, y], Equals(Neg(Sub(x, y)), Add(Neg(x), y)), domain=Complex) distribute_neg_through_subtract neg_times_pos = Forall([x, y], Equals(Mult(Neg(x), y), Neg(Mult(x, y))), domain=Complex) neg_times_pos neg_times_pos_rev = Forall([x, y], Equals(Neg(Mult(x, y)), Mult(Neg(x), y)), domain=Complex) neg_times_pos_rev pos_times_neg = Forall([x, y], Equals(Mult(x, Neg(y)), Neg(Mult(x, y))), domain=Complex) pos_times_neg pos_times_neg_rev = Forall([x, y], Equals(Neg(Mult(x, y)), Mult(x, Neg(y))),