Ejemplo n.º 1
0
 def double_scaling_reduction(self, **defaults_config):
     from . import doubly_scaled_as_singly_scaled
     if not isinstance(self.scaled, ScalarMult):
         raise ValueError("'double_scaling_reduction' is only applicable "
                          "for a doubly nested ScalarMult")
     # Reduce doubly-nested ScalarMult
     _x = self.scaled.scaled
     # _V = VecSpaces.known_vec_space(_x)
     # the following is a little klunky, but trying to avoid the
     # use of a default field=Real if we're actually dealing with
     # complex scalars somewhere in the vector
     from proveit import free_vars
     if any([InSet(elem, Complex).proven() for elem in free_vars(self)]):
         _V = VecSpaces.known_vec_space(self, field=Complex)
     else:
         _V = VecSpaces.known_vec_space(self)
     _K = VecSpaces.known_field(_V)
     _alpha = self.scalar
     _beta = self.scaled.scalar
     return doubly_scaled_as_singly_scaled.instantiate({
         K: _K,
         V: _V,
         x: _x,
         alpha: _alpha,
         beta: _beta
     })
Ejemplo n.º 2
0
    def vec_sum_elimination(self, field=None, **defaults_config):
        '''
        For a VecSum in which the summand does not depend on the 
        summation index, return an equality between this VecSum and
        the equivalent expression in which the VecSum is eliminated.
        For example, suppose self = VecSum(i, v, Interval(2, 4)).
        Then self.vec_sum_elimination() would return
        |- self = 3*v
        where the 3*v is actually ScalarMult(3, v).
        The method works only for a VecSum over a single summation
        index, and simply returns self = self if the VecSum elimination
        is not possible due to the summand being dependent on the
        index of summation.
        '''

        expr = self
        summation_index = expr.index
        eq = TransRelUpdater(expr)

        if summation_index not in free_vars(expr.summand):
            vec_space_membership = expr.summand.deduce_in_vec_space(
                field=field,
                assumptions = defaults.assumptions + expr.conditions.entries)
            _V_sub = vec_space_membership.domain
            _K_sub = VecSpaces.known_field(_V_sub)
            _j_sub = expr.condition.domain.lower_bound
            _k_sub = expr.condition.domain.upper_bound
            _v_sub = expr.summand
            from proveit.linear_algebra.addition import vec_sum_of_constant_vec
            eq.update(vec_sum_of_constant_vec.instantiate(
                    {V: _V_sub, K: _K_sub, j: _j_sub, k: _k_sub, v: _v_sub}))

        else:
            print("VecSum cannot be eliminated. The summand {0} appears "
                  "to depend on the index of summation {1}".
                  format(expr.summand, summation_index))

        return eq.relation
Ejemplo n.º 3
0
    def shallow_simplification(self,
                               *,
                               must_evaluate=False,
                               **defaults_config):
        '''
        Returns a proven simplification equation for this Sum
        expression assuming the operands have been simplified.

        For the trivial case of summing over only one item (currently
        implemented just for a Interval where the endpoints are equal),
        derive and return this summation expression equated with the
        simplified form of the single term.
        Assumptions may be necessary to deduce necessary conditions
        for the simplification.
        NEEDS UPDATING
        '''
        from proveit.logic import TRUE, SimplificationError
        from . import sum_single, trivial_sum
        if (isinstance(self.domain, Interval)
                and self.domain.lower_bound == self.domain.upper_bound):
            if hasattr(self, 'index'):
                return sum_single.instantiate({
                    Function(f, self.index): self.summand,
                    a: self.domain.lower_bound
                })
        if (isinstance(self.domain, Interval)
                and self.instance_param not in free_vars(self.summand)
                and self.non_domain_condition() == TRUE):
            # Trivial sum: summand independent of parameter.
            _a = self.domain.lower_bound
            _b = self.domain.upper_bound
            _x = self.summand
            return trivial_sum.instantiate({a: _a, b: _b, x: _x})
        raise SimplificationError(
            "Sum simplification only implemented for a summation over an "
            "integer Interval of one instance variable where the upper "
            "and lower bounds are the same.")
Ejemplo n.º 4
0
 def factor(self,
            theFactor,
            pull="left",
            groupFactor=False,
            groupRemainder=None,
            assumptions=frozenset()):
     '''
     Pull out a common factor from a summation, pulling it either to the "left" or "right".
     If groupFactor is True and theFactor is a product, it will be grouped together as a 
     sub-product.  groupRemainder is not relevant kept for compatibility with other factor
     methods.  Returns the equality that equates self to this new version.
     Give any assumptions necessary to prove that the operands are in Complexes so that
     the associative and commutation theorems are applicable.
     '''
     from proveit.number.multiplication.theorems import distributeThroughSummationRev
     from proveit.number import Mult
     if not free_vars(theFactor).isdisjoint(self.indices):
         raise Exception(
             'Cannot factor anything involving summation indices out of a summation'
         )
     # We may need to factor the summand within the summation
     summand_assumptions = assumptions | {
         InSet(index, self.domain)
         for index in self.indices
     }
     summandFactorEq = self.summand.factor(theFactor,
                                           pull,
                                           groupFactor=False,
                                           groupRemainder=True,
                                           assumptions=summand_assumptions)
     summandInstanceEquivalence = summandFactorEq.generalize(
         self.indices, domain=self.domain).checked(assumptions)
     eq = Equation(
         self.instanceSubstitution(summandInstanceEquivalence).checked(
             assumptions))
     factorOperands = theFactor.operands if isinstance(theFactor,
                                                       Mult) else theFactor
     xDummy, zDummy = self.safeDummyVars(2)
     # Now do the actual factoring by reversing distribution
     if pull == 'left':
         Pop, Pop_sub = Operation(
             P, self.indices), summandFactorEq.rhs.operands[-1]
         xSub = factorOperands
         zSub = []
     elif pull == 'right':
         Pop, Pop_sub = Operation(
             P, self.indices), summandFactorEq.rhs.operands[0]
         xSub = []
         zSub = factorOperands
     # We need to deduce that theFactor is in Complexes and that all instances of Pop_sup are in Complexes.
     deduceInComplexes(factorOperands, assumptions=assumptions)
     deduceInComplexes(Pop_sub,
                       assumptions=assumptions
                       | {InSet(idx, self.domain)
                          for idx in self.indices}).generalize(
                              self.indices,
                              domain=self.domain).checked(assumptions)
     # Now we specialize distributThroughSummationRev
     spec1 = distributeThroughSummationRev.specialize({
         Pop:
         Pop_sub,
         S:
         self.domain,
         yEtc:
         self.indices,
         xEtc:
         Etcetera(MultiVariable(xDummy)),
         zEtc:
         Etcetera(MultiVariable(zDummy))
     }).checked()
     eq.update(spec1.deriveConclusion().specialize({
         Etcetera(MultiVariable(xDummy)):
         xSub,
         Etcetera(MultiVariable(zDummy)):
         zSub
     }))
     if groupFactor and len(factorOperands) > 1:
         eq.update(
             eq.eqExpr.rhs.group(endIdx=len(factorOperands),
                                 assumptions=assumptions))
     return eq.eqExpr  #.checked(assumptions)
Ejemplo n.º 5
0
    def tensor_prod_factoring(self, idx=None, idx_beg=None, idx_end=None,
                              field=None, **defaults_config):
        '''
        For a VecSum with a TensorProd summand or ScalarMult summand
        with a scaled attribute being a TensorProd, factor out from
        the VecSum the TensorProd vectors other than the ones indicated
        by the (0-based) idx, or idx_beg and idx_end pair and return
        an equality between the original VecSum and the new TensorProd.
        For example, we could take the VecSum defined by
        vec_sum = VecSum(TensorProd(x, f(i), y, z))
        and call vec_sum.tensor_prod_factoring(idx_beg=1, idx_end=2)
        to obtain:

            |- VecSum(TensorProd(x, f(i), y, z)) = 
               TensorProd(x, VecSum(TensorProd(f(i), y)), z)

        This method should work even if the summand is a nested
        ScalarMult. Note that any vectors inside the TensorProd that
        depend on the index of summation cannot be pulled out of the
        VecSum and thus will cause the method to fail if not chosen
        to remain inside the VecSum. If all idx args are 'None',
        method will factor out all possible vector factors, including
        the case where all factors could be removed and the VecSum
        eliminated entirely.
        Note that this method only works when self has a single
        index of summation.
        '''
        expr = self
        the_summand = self.summand

        eq = TransRelUpdater(expr)

        # Check that 
        #    (1) the_summand is a TensorProd
        # or (2) the_summand is a ScalarMult;
        # otherwise, this method does not apply
        from proveit.linear_algebra import ScalarMult, TensorProd
        if isinstance(the_summand, ScalarMult):
            # try shallow simplification first to remove nested
            # ScalarMults and multiplicative identities
            expr = eq.update(expr.inner_expr().summand.shallow_simplification())
            the_summand = expr.summand
        if isinstance(the_summand, TensorProd):
            tensor_prod_expr = the_summand
            tensor_prod_summand = True
            tensor_prod_factors_list = list(
                    the_summand.operands.entries)
        elif (isinstance(the_summand, ScalarMult)
              and isinstance(the_summand.scaled, TensorProd)):
            tensor_prod_expr = the_summand.scaled
            tensor_prod_summand = False
            tensor_prod_factors_list = list(
                    the_summand.scaled.operands.entries)
        else:
            raise ValueError(
                "tensor_prod_factoring() requires the VecSum summand "
                "to be a TensorProd or a ScalarMult (with its 'scaled' "
                "attribute a TensorProd); instead the "
                "summand is {}".format(self.instance_expr))

        if idx is None and idx_beg is None and idx_end is None:
            # prepare to take out all possible factors, including
            # the complete elimination of the VecSum if possible
            if expr.index not in free_vars(expr.summand):
                # summand does not depend on index of summation
                # so we can eliminate the VecSum entirely
                return expr.vec_sum_elimination(field=field)
            if expr.index in free_vars(tensor_prod_expr):
                # identify the extractable vs. non-extractable
                # TensorProd factors (and there must be at least
                # one such non-extractable factor)
                
                idx_beg = -1
                idx_end = -1
                for i in range(len(expr.summand.operands.entries)):
                    if expr.index in free_vars(tensor_prod_expr.operands[i]):
                        if idx_beg == -1:
                            idx_beg = i
                            idx_end = idx_beg
                        else:
                            idx_end = i
            else:
                # The alternative is that the summand is
                # a ScalarMult with the scalar (but not the scaled)
                # being dependent on the index of summation. It's not
                # obvious what's best to do in this case, but we set
                # things up to factor out all but the last of the
                # TensorProd factors (so we'll factor out at least
                # 1 factor)
                idx_beg = len(tensor_prod_expr.operands.entries) - 1
                idx_end = idx_beg


        # Check that the provided idxs are within bounds
        # (it should refer to an actual TensorProd operand)

        num_vec_factors = len(tensor_prod_factors_list)
        if idx is not None and idx >= num_vec_factors:
            raise ValueError(
                    "idx value {0} provided for tensor_prod_factoring() "
                    "method is out-of-bounds; the TensorProd summand has "
                    "{1} factors: {2}, and thus possibly indices 0-{3}".
                    format(idx, len(tensor_prod_factors_list),
                           tensor_prod_factors_list,
                           len(tensor_prod_factors_list)-1))
        if idx_beg is not None and idx_end is not None:
            if (idx_end < idx_beg or idx_beg >= num_vec_factors or
                idx_end >= num_vec_factors):
                raise ValueError(
                    "idx_beg value {0} or idx_end value {1} (or both) "
                    "provided for tensor_prod_factoring() "
                    "method is/are out-of-bounds; the TensorProd summand "
                    "has {2} factors: {3}, and thus possibly indices 0-{3}".
                    format(idx_beg, idx_end, num_vec_factors,
                           tensor_prod_factors_list,num_vec_factors-1))
        if idx is not None:
            # take single idx as the default
            idx_beg = idx
            idx_end = idx

        # Check that the TensorProd factors to be factored out do not
        # rely on the VecSum index of summation
        summation_index = expr.index
        for i in range(num_vec_factors):
            if i < idx_beg or i > idx_end:
                the_factor = tensor_prod_factors_list[i]
                if summation_index in free_vars(the_factor):
                    raise ValueError(
                            "TensorProd factor {0} cannot be factored "
                            "out of the given VecSum summation because "
                            "it is a function of the summation index {1}.".
                            format(the_factor, summation_index))
        
        # Everything checks out as best we can tell, so prepare to
        # import and instantiate the appropriate theorem,
        # depending on whether:
        # (1) the_summand is a TensorProd, or
        # (2) the_summand is a ScalarMult (with a TensorProd 'scaled')
        if tensor_prod_summand:
            from proveit.linear_algebra.tensors import (
                tensor_prod_distribution_over_summation)
        else:
            from proveit.linear_algebra.tensors import (
                tensor_prod_distribution_over_summation_with_scalar_mult)
        if idx_beg != idx_end:
            # need to associate the elements and change idx value
            # but process is slightly different in the two cases
            if tensor_prod_summand:
                expr = eq.update(expr.inner_expr().summand.association(
                        idx_beg, idx_end-idx_beg+1))
                tensor_prod_expr = expr.summand
            else:
                expr = eq.update(expr.inner_expr().summand.scaled.association(
                        idx_beg, idx_end-idx_beg+1))
                tensor_prod_expr = expr.summand.scaled
        idx = idx_beg

        from proveit import K, f, Q, i, j, k, V, a, b, c, s
        # actually, maybe it doesn't matter and we can deduce the 
        # vector space regardless: (Adding this temp 12/26/21)
        vec_space_membership = expr.summand.deduce_in_vec_space(
            field=field,
            assumptions = defaults.assumptions + expr.conditions.entries)
        _V_sub = vec_space_membership.domain
        # Substitutions regardless of Case
        _K_sub = VecSpaces.known_field(_V_sub)
        _b_sub = expr.indices
        _j_sub = _b_sub.num_elements()
        _Q_sub = Lambda(expr.indices, expr.condition)
        # Case-specific substitutions, using updated tensor_prod_expr:
        _a_sub = tensor_prod_expr.operands[:idx]
        _c_sub = tensor_prod_expr.operands[idx+1:]
        _f_sub = Lambda(expr.indices, tensor_prod_expr.operands[idx])
        if not tensor_prod_summand:
            _s_sub = Lambda(expr.indices, expr.summand.scalar)
        # Case-dependent substitutions:
        _i_sub = _a_sub.num_elements()
        _k_sub = _c_sub.num_elements()

        if tensor_prod_summand:
            impl = tensor_prod_distribution_over_summation.instantiate(
                    {K:_K_sub, f:_f_sub, Q:_Q_sub, i:_i_sub, j:_j_sub,
                     k:_k_sub, V:_V_sub, a:_a_sub, b:_b_sub, c:_c_sub},
                     preserve_expr=expr)
        else:
            impl = (tensor_prod_distribution_over_summation_with_scalar_mult.
                   instantiate(
                    {K:_K_sub, f:_f_sub, Q:_Q_sub, i:_i_sub, j:_j_sub,
                     k:_k_sub, V:_V_sub, a:_a_sub, b:_b_sub, c:_c_sub,
                     s: _s_sub}, preserve_expr=expr))

        expr = eq.update(impl.derive_consequent(
                assumptions = defaults.assumptions + expr.conditions.entries).
                derive_reversed())

        return eq.relation
Ejemplo n.º 6
0
    def factors_extraction(self, field=None, **defaults_config):
        '''
        Derive an equality between this VecSum and the result
        when all possible leading scalar factors have been extracted
        and moved to the front of the VecSum (for example, in the
        case where the summand of the VecSum is a ScalarMult) and
        all possible tensor product factors have been moved outside
        the VecSum (in front if possible, or afterward if necessary).
        For example, we could take the VecSum
            vec_sum = VecSum(ScalarMult(a, TensorProd(x, f(i), y))),
        where the index of summation is i, and call
            vec_sum.factor_extraction() to obtain:
            |- vec_sum = 
               ScalarMult(a, TensorProd(x, VecSum(f(i)), y))
        Note that any factors inside the summand that depend on the
        index of summation cannot be pulled out from inside the VecSum,
        and thus pose limitations on the result.
        Note that this method only works when self has a single
        index of summation, and only when self has a summand that is
        a ScalarMult or TensorProd.
        Later versions of this method should provide mechanisms to
        specify factors to extract from, and/or leave behind in, the
        VecSum.
        '''
        expr = self
        summation_index = expr.index
        assumptions = defaults.assumptions + expr.conditions.entries
        assumptions_with_conditions = (
                defaults.assumptions + expr.conditions.entries)

        # for convenience in updating our equation:
        # this begins with eq.relation as expr = expr
        eq = TransRelUpdater(expr)

        # If the summand is a ScalarMult, perform a
        # shallow_simplification(), which will remove nested
        # ScalarMults and multiplicative identities. This is
        # intended to simplify without changing too much the
        # intent of the user. This might even transform the
        # ScalarMult object into something else.
        from proveit.linear_algebra import ScalarMult, TensorProd
        if isinstance(expr.summand, ScalarMult):
            expr = eq.update(
                    expr.inner_expr().summand.shallow_simplification())
        if isinstance(expr.summand, ScalarMult):
            # had to re-check, b/c the shallow_simplification might
            # have transformed the ScalarMult into the scaled object
            tensor_prod_summand = False # not clearly useful; review please
            the_scalar = expr.summand.scalar
            
        elif isinstance(expr.summand, TensorProd):
            tensor_prod_summand = True # not clearly useful; review please

        if isinstance(expr.summand, ScalarMult):
            if summation_index not in free_vars(expr.summand.scalar):
                # it doesn't matter what the scalar is; the whole thing
                # can be pulled out in front of the VecSum
                from proveit.linear_algebra.scalar_multiplication import (
                    distribution_over_vec_sum)
                summand_in_vec_space = expr.summand.deduce_in_vec_space(
                        field=field, assumptions=assumptions_with_conditions)
                _V_sub = summand_in_vec_space.domain
                _K_sub = VecSpaces.known_field(_V_sub)
                _b_sub = expr.indices
                _j_sub = _b_sub.num_elements()
                _f_sub = Lambda(expr.indices, expr.summand.scaled)
                _Q_sub = Lambda(expr.indices, expr.condition)
                _k_sub = expr.summand.scalar
                imp = distribution_over_vec_sum.instantiate(
                        {V: _V_sub, K: _K_sub, b: _b_sub, j: _j_sub,
                         f: _f_sub, Q: _Q_sub, k: _k_sub},
                         assumptions=assumptions_with_conditions)
                expr = eq.update(imp.derive_consequent(
                    assumptions=assumptions_with_conditions).derive_reversed())
            else:
                # The scalar portion is dependent on summation index.
                # If the scalar itself is a Mult of things, go through
                # and pull to the front of the Mult all individual
                # factors that are not dependent on the summation index.
                if isinstance(expr.summand.scalar, Mult):
                    # Repeatedly pull index-independent factors #
                    # to the front of the Mult factors          #

                    # prepare to count the extractable and
                    # unextractable factors
                    _num_factored = 0
                    _num_unfactored = len(expr.summand.scalar.operands.entries)

                    # go through factors from back to front
                    for the_factor in reversed(
                            expr.summand.scalar.operands.entries):

                        if summation_index not in free_vars(the_factor):
                            expr = eq.update(
                                expr.inner_expr().summand.scalar.factorization(
                                    the_factor,
                                    assumptions=assumptions_with_conditions,
                                    preserve_all=True))
                            _num_factored += 1
                            _num_unfactored -= 1

                    # group the factorable factors
                    if _num_factored > 0:
                        expr = eq.update(
                            expr.inner_expr().summand.scalar.association(
                                0, _num_factored,
                                assumptions=assumptions_with_conditions,
                                preserve_all=True))
                    # group the unfactorable factors
                    if _num_unfactored > 1:
                        expr = eq.update(
                            expr.inner_expr().summand.scalar.association(
                                1, _num_unfactored,
                                assumptions=assumptions_with_conditions,
                                preserve_all=True))

                    # finally, extract any factorable scalar factors
                    if _num_factored > 0:
                        from proveit.linear_algebra.scalar_multiplication import (
                                distribution_over_vec_sum_with_scalar_mult)
                        # Mult._simplification_directives_.ungroup = False
                        # _V_sub = VecSpaces.known_vec_space(expr, field=field)
                        summand_in_vec_space = (
                                expr.summand.deduce_in_vec_space(
                                        field = field,
                                        assumptions =
                                        assumptions_with_conditions))
                        _V_sub = summand_in_vec_space.domain
                        _K_sub = VecSpaces.known_field(_V_sub)
                        _b_sub = expr.indices
                        _j_sub = _b_sub.num_elements()
                        _f_sub = Lambda(expr.indices, expr.summand.scaled)
                        _Q_sub = Lambda(expr.indices, expr.condition)
                        _c_sub = Lambda(expr.indices,
                                        expr.summand.scalar.operands[1])
                        _k_sub = expr.summand.scalar.operands[0]
                        # when instantiating, we set preserve_expr=expr;
                        # otherwise auto_simplification disassociates inside
                        # the Mult.
                        impl = distribution_over_vec_sum_with_scalar_mult.instantiate(
                                {V:_V_sub, K:_K_sub, b: _b_sub, j: _j_sub,
                                 f: _f_sub, Q: _Q_sub, c:_c_sub, k: _k_sub},
                                 preserve_expr=expr,
                                assumptions=assumptions_with_conditions)
                        expr = eq.update(impl.derive_consequent(
                                assumptions=assumptions_with_conditions).
                                derive_reversed())

                else:
                    # The scalar component is dependent on summation
                    # index but is not a Mult.
                    # Revert everything and return self = self.
                    print("Found summation index {0} in the scalar {1} "
                          "and the scalar is not a Mult object.".
                      format(summation_index, expr.summand.scalar))
                    eq = TransRelUpdater(self)

        # ============================================================ #
        # VECTOR FACTORS                                               #
        # ============================================================ #
        # After the scalar factors (if any) have been dealt with,
        # proceed with the vector factors in any remaining TensorProd
        # in the summand.
        # Notice that we are not guaranteed at this point that we even
        # have a TensorProd to factor, and if we do have a TensorProd
        # we have not identified the non-index-dependent factors to 
        # extract.
        # After processing above for scalar factors, we might now have
        # (1) expr = VecSum (we didn't find scalar factors to extract),
        # inside of which we might have a ScalarMult or a TensorProd;
        # or (2) expr = ScalarMult (we found some scalar factors to
        # extract), with a VecSum as the scaled component.

        if isinstance(expr, VecSum):
            expr = eq.update(expr.tensor_prod_factoring())
        elif isinstance(expr, ScalarMult) and isinstance(expr.scaled, VecSum):
            expr = eq.update(expr.inner_expr().scaled.tensor_prod_factoring())

        return eq.relation
Ejemplo n.º 7
0
    def factorization(self,
                      the_factor,
                      pull="left",
                      group_factor=True,
                      group_remainder=None,
                      assumptions=USE_DEFAULTS):
        '''
        If group_factor is True and the_factor is a product, it will be grouped together as a
        sub-product.  group_remainder is not relevant kept for compatibility with other factor
        methods.  Returns the equality that equates self to this new version.
        Give any assumptions necessary to prove that the operands are in Complex so that
        the associative and commutation theorems are applicable.
        '''
        from proveit.numbers.multiplication import distribute_through_summation
        from proveit.numbers import Mult
        if not free_vars(the_factor).isdisjoint(self.indices):
            raise Exception(
                'Cannot factor anything involving summation indices out of a summation'
            )
        expr = self
        # for convenience updating our equation
        eq = TransRelUpdater(expr, assumptions)

        assumptions = defaults.checked_assumptions(assumptions)
        # We may need to factor the summand within the summation
        summand_assumptions = assumptions + self.condition
        summand_factorization = self.summand.factorization(
            the_factor,
            pull,
            group_factor=False,
            group_remainder=True,
            assumptions=summand_assumptions)

        summand_instance_equivalence = summand_factor_eq.generalize(
            self.indices, domain=self.domain).checked(assumptions)

        eq = Equation(
            self.instance_substitution(summand_instance_equivalence).checked(
                assumptions))
        factor_operands = the_factor.operands if isinstance(
            the_factor, Mult) else the_factor
        x_dummy, z_dummy = self.safe_dummy_vars(2)
        # Now do the actual factoring by reversing distribution
        if pull == 'left':
            Pop, Pop_sub = Operation(
                P, self.indices), summand_factor_eq.rhs.operands[-1]
            x_sub = factor_operands
            z_sub = []
        elif pull == 'right':
            Pop, Pop_sub = Operation(
                P, self.indices), summand_factor_eq.rhs.operands[0]
            x_sub = []
            z_sub = factor_operands
        # We need to deduce that the_factor is in Complex and that all
        # instances of Pop_sup are in Complex.
        deduce_in_complex(factor_operands, assumptions=assumptions)
        deduce_in_complex(Pop_sub,
                          assumptions=assumptions
                          | {InSet(idx, self.domain)
                             for idx in self.indices}).generalize(
                                 self.indices,
                                 domain=self.domain).checked(assumptions)
        # Now we instantiate distribut_through_summation_rev
        spec1 = distribute_through_summation_rev.instantiate({
            Pop:
            Pop_sub,
            S:
            self.domain,
            y_etc:
            self.indices,
            x_etc:
            Etcetera(Multi_variable(x_dummy)),
            z_etc:
            Etcetera(Multi_variable(z_dummy))
        }).checked()
        eq.update(spec1.derive_conclusion().instantiate({
            Etcetera(Multi_variable(x_dummy)):
            x_sub,
            Etcetera(Multi_variable(z_dummy)):
            z_sub
        }))
        if group_factor and factor_operands.num_entries() > 1:
            eq.update(
                eq.eq_expr.rhs.group(end_idx=factor_operands.num_entries(),
                                     assumptions=assumptions))
        return eq.eq_expr  # .checked(assumptions)
Ejemplo n.º 8
0
    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