Ejemplo n.º 1
0
 def _formatted(self, formatType, fence=False, **kwargs):
     outStr = ''
     explicit_conditions = ExprTuple(*self.explicitConditions())
     inner_fence = (len(explicit_conditions) > 0)
     formatted_instance_var = self.instanceVar.formatted(formatType)
     formatted_instance_element = self.instanceElement.formatted(
         formatType, fence=inner_fence)
     formatted_domain = self.domain.formatted(formatType, fence=True)
     if formatType == 'latex': outStr += r"\left\{"
     else: outStr += "{"
     outStr += formatted_instance_element
     if len(explicit_conditions) > 0:
         formatted_conditions = explicit_conditions.formatted(formatType,
                                                              fence=False)
         if formatType == 'latex': outStr += r'~|~'
         else: outStr += ' s.t. '  # such that
         outStr += formatted_conditions
     if formatType == 'latex': outStr += r"\right\}"
     else: outStr += "}"
     outStr += '_{' + formatted_instance_var
     if self.domain is not None:
         if formatType == 'latex': outStr += r' \in '
         else: outStr += ' in '
         outStr += formatted_domain
     outStr += '}'
     return outStr
Ejemplo n.º 2
0
def apply_association_thm(expr,
                          startIdx,
                          length,
                          thm,
                          assumptions=USE_DEFAULTS):
    from proveit import ExprTuple
    from proveit.logic import Equals
    beg = startIdx
    if beg < 0: beg = len(expr.operands) + beg  # use wrap-around indexing
    end = beg + length
    if end > len(expr.operands):
        raise IndexError("'startIdx+length' out of bounds: %d > %d." %
                         (end, len(expr.operands)))
    if beg == 0 and end == len(expr.operands):
        # association over the entire range is trivial:
        return Equals(expr, expr).prove()  # simply the self equality
    i, j, k, A, B, C = thm.allInstanceVars()
    _A = ExprTuple(*expr.operands[:beg])
    _B = ExprTuple(*expr.operands[beg:end])
    _C = ExprTuple(*expr.operands[end:])
    _i = _A.length(assumptions)
    _j = _B.length(assumptions)
    _k = _C.length(assumptions)
    return thm.specialize({
        i: _i,
        j: _j,
        k: _k,
        A: _A,
        B: _B,
        C: _C
    },
                          assumptions=assumptions)
Ejemplo n.º 3
0
 def derive_cart_exp_membership(self, **defaults_config):
     '''
     If the domain is a tensor product Cartesian exponentials on the
     same field, prove that the element is also a membero of
     the Cartesion exponential of the sum of the exponents.
     Thst is, if the domain is of the form
         K^{n_1} ⊗ K^{n_2} ⊗ ... ⊗ K^{n_m}
     derive that the element is also contained in
         K^{n_1 · n_2 · ... · n_m}
     '''
     from . import tensor_prod_of_cart_exps_within_cart_exp
     _K, _ns = self._get_cart_exps_field_and_exponents()
     _ns = ExprTuple(*_ns)
     _m = _ns.num_elements()
     inclusion = tensor_prod_of_cart_exps_within_cart_exp.instantiate({
         K: _K,
         m: _m,
         n: _ns
     })
     return inclusion.derive_superset_membership(self.element)
Ejemplo n.º 4
0
 def deriveSomeFromAnd(self, idx, assumptions=USE_DEFAULTS):
     '''
     added by JML 7/8/19
     From (A and ... and B and ... C) derive any one index even if it is an iteration. 
     '''
     from proveit import ExprTuple
     from proveit.logic.boolean.conjunction._theorems_ import someFromAnd
     lVal = ExprTuple(*self.operands[:idx]).len()
     mVal = ExprTuple(self.operands[idx]).len()
     nVal = ExprTuple(*self.operands[idx + 1:]).len()
     return someFromAnd.specialize(
         {
             l: lVal,
             m: mVal,
             n: nVal,
             AA: self.operands[:idx],
             BB: self.operands[idx],
             CC: self.operands[idx + 1:]
         },
         assumptions=assumptions)
Ejemplo n.º 5
0
def apply_disassociation_thm(expr, idx, thm=None, assumptions=USE_DEFAULTS):
    from proveit import ExprTuple
    if idx < 0: idx = len(expr.operands) + idx  # use wrap-around indexing
    if idx >= len(expr.operands):
        raise IndexError("'idx' out of range for disassociation")
    if not isinstance(expr.operands[idx], expr.__class__):
        raise ValueError(
            "Expecting %d index of %s to be grouped (i.e., a nested expression of the same type)"
            % (idx, str(expr)))
    i, j, k, A, B, C = thm.allInstanceVars()
    _A = ExprTuple(*expr.operands[:idx])
    _B = expr.operands[idx].operands
    _C = ExprTuple(*expr.operands[idx + 1:])
    _i = _A.length(assumptions)
    _j = _B.length(assumptions)
    _k = _C.length(assumptions)
    return thm.specialize({
        i: _i,
        j: _j,
        k: _k,
        A: _A,
        B: _B,
        C: _C
    },
                          assumptions=assumptions)
Ejemplo n.º 6
0
 def _formatted(self, formatType, fence=False, **kwargs):
     outStr = ''
     explicit_conditions = ExprTuple(*self.explicitConditions())
     inner_fence = (len(explicit_conditions) > 0)
     formatted_instance_element = self.instanceElement.formatted(
         formatType, fence=inner_fence)
     explicit_domains = self.explicitDomains()
     domain_conditions = ExprTuple(*self.domainConditions())
     if formatType == 'latex': outStr += r"\left\{"
     else: outStr += "{"
     outStr += formatted_instance_element
     if len(explicit_conditions) > 0:
         formatted_conditions = explicit_conditions.formatted(formatType,
                                                              fence=False)
         if formatType == 'latex': outStr += r'~|~'
         else: outStr += ' s.t. '  # such that
         outStr += formatted_conditions
     if formatType == 'latex': outStr += r"\right\}"
     else: outStr += "}"
     outStr += '_{'
     instance_param_or_params = self.instanceParamOrParams
     if explicit_domains == [explicit_domains[0]] * len(explicit_domains):
         # all in the same domain
         outStr += instance_param_or_params.formatted(
             formatType, operatorOrOperators=',', fence=False)
         outStr += r' \in ' if formatType == 'latex' else ' in '
         outStr += explicit_domains[0].formatted(formatType)
     else:
         outStr += domain_conditions.formatted(formatType,
                                               operatorOrOperators=',',
                                               fence=False)
     outStr += '}'
     return outStr
Ejemplo n.º 7
0
    def oneElimination(self, idx, assumptions=USE_DEFAULTS):
        '''
        Equivalence method that derives a simplification in which
        a single factor of one, at the given index, is eliminated.  
        For example:
            x*y*1*z = x*y*z
        '''
        from proveit.number import one
        from ._theorems_ import elimOneLeft, elimOneRight, elimOneAny

        if self.operands[idx] != one:
            raise ValueError(
                "Operand at the index %d expected to be zero for %s" %
                (idx, str(self)))

        if len(self.operands) == 2:
            if idx == 0:
                return elimOneLeft.specialize({x: self.operands[1]},
                                              assumptions=assumptions)
            else:
                return elimOneRight.specialize({x: self.operands[0]},
                                               assumptions=assumptions)
        _a = ExprTuple(*self.operands[:idx])
        _b = ExprTuple(*self.operands[idx + 1:])
        _i = _a.length(assumptions)
        _j = _b.length(assumptions)
        return elimOneAny.specialize({
            i: _i,
            j: _j,
            a: _a,
            b: _b
        },
                                     assumptions=assumptions)
Ejemplo n.º 8
0
 def deriveBundled(self, assumptions=USE_DEFAULTS):
     '''
     From a nested forall statement, derive the bundled forall statement.  For example,
     forall_{x | Q(x)} forall_{y | R(y)} P(x, y) becomes forall_{x, y | Q(x), R(y)} P(x, y).
     '''
     raise NotImplementedError("Need to update")
     from ._theorems_ import bundling
     assert isinstance(self.instanceExpr, Forall), "Can only bundle nested forall statements"
     innerForall = self.instanceExpr
     composedInstanceVars = ExprTuple([self.instanceVars, innerForall.instanceVars])
     P_op, P_op_sub = Operation(P, composedInstanceVars), innerForall.instanceExpr
     Q_op, Q_op_sub = Operation(Qmulti, self.instanceVars), self.conditions
     R_op, R_op_sub = Operation(Rmulti, innerForall.instanceVars), innerForall.conditions
     return bundling.specialize({xMulti:self.instanceVars, yMulti:innerForall.instanceVars, P_op:P_op_sub, Q_op:Q_op_sub, R_op:R_op_sub, S:self.domain}).deriveConclusion(assumptions)
Ejemplo n.º 9
0
 def latex(self, performUsabilityCheck=True):
     '''
     If the KnownTruth was proven under any assumptions, display the 
     double-turnstyle notation to show that the set of assumptions proves
     the statement/expression.  Otherwise, simply display the expression.
     '''
     from proveit import ExprTuple
     if performUsabilityCheck and not self.isUsable():
         self.raiseUnusableProof()
     if len(self.assumptions) > 0:
         assumptionsLatex = ExprTuple(*self.assumptions).formatted(
             'latex', fence=False)
         return r'{' + assumptionsLatex + r'} \vdash ' + self.expr.latex()
     return r'\vdash ' + self.expr.string()
Ejemplo n.º 10
0
 def __init__(self, operand):
     '''
     Len takes a single operand which should properly be an
     ExprTuple or an expression (such as a variable) that
     represents a tuple.
     '''
     operand = single_or_composite_expression(operand)
     if isinstance(operand, ExprTuple):
         # Nest an ExprTuple operand in an extra ExprTuple as
         # a clear indication that Len has a single operand
         # that is an ExprTuple rather than multiple operands.
         operand = ExprTuple(operand)
     # In order to always recognize that Len only takes a single
     # operand, we must wrap it as an ExprTuple with one entry.
     Operation.__init__(self, Len._operator_, operand)
Ejemplo n.º 11
0
 def deduceInBool(self, assumptions=USE_DEFAULTS):
     '''
     Attempt to deduce, then return, that this forall expression is in the set of BOOLEANS,
     as all forall expressions are (they are taken to be false when not true).
     '''
     from proveit.number import one
     from ._axioms_ import forall_in_bool
     _x = self.instanceParams
     P_op, _P_op = Operation(P, _x), self.instanceExpr
     _n = _x.length(assumptions)
     x_1_to_n = ExprTuple(ExprRange(k, IndexedVar(x, k), one, _n))
     return forall_in_bool.specialize({
         n: _n,
         P_op: _P_op,
         x_1_to_n: _x
     },
                                      assumptions=assumptions)
Ejemplo n.º 12
0
 def __init__(self, operands, *, styles=None):
     '''
     Len can take an explicit ExprTuple as operands, or
     it may take an expression (such as a varaible) that
     represents a tuple.  Either way, this expression is
     taken as the 'operands'.
     '''
     if isinstance(operands, ExprRange):
         # An ExprRange cannot represent an ExprTuple,
         # so we must want this wrapped in an ExprTuple.
         operands = ExprTuple(operands)
     # In order to always recognize that Len only takes a single
     # operand, we must wrap it as an ExprTuple with one entry.
     Operation.__init__(self,
                        Len._operator_,
                        operands=operands,
                        styles=styles)
Ejemplo n.º 13
0
 def _formatted(self, format_type, fence=False, **kwargs):
     from proveit import ExprRange
     out_str = ''
     explicit_conditions = ExprTuple(*self.explicit_conditions())
     inner_fence = (explicit_conditions.num_entries() > 0)
     formatted_instance_element = self.instance_element.formatted(
         format_type, fence=inner_fence)
     explicit_domains = self.explicit_domains()
     domain_conditions = ExprTuple(*self.domain_conditions())
     if format_type == 'latex':
         out_str += r"\left\{"
     else:
         out_str += "{"
     out_str += formatted_instance_element
     if explicit_conditions.num_entries() > 0:
         formatted_conditions = explicit_conditions.formatted(format_type,
                                                              fence=False)
         if format_type == 'latex':
             out_str += r'~|~'
         else:
             out_str += ' s.t. '  # such that
         out_str += formatted_conditions
     if format_type == 'latex':
         out_str += r"\right\}"
     else:
         out_str += "}"
     out_str += '_{'
     instance_param_or_params = self.instance_param_or_params
     if (not any(
             isinstance(entry, ExprRange) for entry in explicit_domains)
             and explicit_domains
             == [explicit_domains[0]] * len(explicit_domains)):
         # all in the same domain
         out_str += instance_param_or_params.formatted(
             format_type, operator_or_operators=',', fence=False)
         out_str += r' \in ' if format_type == 'latex' else ' in '
         out_str += explicit_domains[0].formatted(format_type)
     else:
         out_str += domain_conditions.formatted(format_type,
                                                operator_or_operators=',',
                                                fence=False)
     out_str += '}'
     return out_str
Ejemplo n.º 14
0
 def deduce_in_bool(self, assumptions=USE_DEFAULTS):
     '''
     Attempt to deduce, then return, that this forall expression
     is in the set of BOOLEANS, as all forall expressions are
     (they are taken to be false when not true).
     '''
     from proveit.numbers import one
     from . import forall_in_bool
     _x = self.instance_params
     P_op, _P_op = Function(P, _x), self.instance_expr
     _n = _x.num_elements(assumptions)
     x_1_to_n = ExprTuple(ExprRange(k, IndexedVar(x, k), one, _n))
     return forall_in_bool.instantiate({
         n: _n,
         P_op: _P_op,
         x_1_to_n: _x
     },
                                       assumptions=assumptions)
Ejemplo n.º 15
0
def possibly_wrap_html_display_objects(orig):
    from proveit import ExprTuple
    try:
        if hasattr(orig, '_repr_html_'):
            # No need to wrap.  Already has _repr_html.
            return orig
        all_expr_objs = True
        for obj in orig:
            if not isinstance(obj, Expression):
                all_expr_objs = False
            if not hasattr(obj, '_repr_html_'):
                return orig
        if all_expr_objs:
            # If they are all expression objects, wrap it in
            # an ExprTuple.
            return ExprTuple(*orig)
        return HTML_DisplayObjects(orig)
    except:
        return orig
Ejemplo n.º 16
0
 def _formatted(self, format_type, **kwargs):
     '''
     Format the binary relation operation.  Note: it may
     be reversed if the "direction" style is "reversed".
     '''
     from proveit import ExprTuple
     wrap_positions=self.wrap_positions()
     justification=self.get_style('justification')
     fence =  kwargs.get('fence', False)
     subFence =  kwargs.get('subFence', True)
     operator_str = self.operator.formatted(format_type)
     operands = self.operands
     if self.is_reversed():
         operator_str = self.__class__.reversed_operator_str(format_type)
         operands = ExprTuple(*reversed(operands.entries))
     return Operation._formattedOperation(
             format_type, fence=fence, subFence=subFence, 
             operator_or_operators=operator_str, operands=operands,
             wrap_positions=wrap_positions, 
             justification=justification)
Ejemplo n.º 17
0
    def choose(self, *skolem_constants, print_message=True):
        '''
        From the existential expression
        self = exists_{x_1,...,x_n | Q(x_1,...,x_n)} P(x_1,...,x_n),
        generate Skolem constants a_1,...,a_n in correspondence with
        the instance params x_1,...,x_n. The process will:
        (1) add Q(a_1,...,a_n) and P(a_1,...,a_n) to the default
            assumptions;
        (2) register the Skolem constants a_1,...,a_n in the
            skolem_consts_to_existential dictionary so they can be
            eliminated later using the eliminate() method;
        (3) return the newly-generated assumptions Q(a_1,...,a_n) and
            P(a_1,...,a_n)
        '''
        # Register this particular collection of Skolem constants
        # in the dictionary as a key linking them to this Exists object
        Exists.skolem_consts_to_existential[skolem_constants] = self

        # build the Skolemized versions of the conditions Q and the
        # instance expression P
        repl_dict = {
            param: skolem_const
            for param, skolem_const in zip(self.instance_params,
                                           skolem_constants)
        }
        P_skolem = self.instance_expr.basic_replaced(repl_dict)
        Q_skolem = self.conditions.basic_replaced(repl_dict)

        # Update the default assumptions with the Skolem versions
        # of the conditions and instance expression
        defaults.assumptions = (*defaults.assumptions, *Q_skolem.entries,
                                P_skolem)
        if print_message:
            print(
                "Creating Skolem 'constant(s)': {0}.\n"
                "Call the Judgment.eliminate{0} to complete the "
                "Skolemization\n(when the 'constant(s)' are no longer needed).\n"
                "Adding to defaults.assumptions:".format(
                    skolem_constants, (*Q_skolem.entries)))

        return ExprTuple(*Q_skolem.entries, P_skolem)
Ejemplo n.º 18
0
    def negSimplification(self, idx, assumptions=USE_DEFAULTS):
        '''
        Equivalence method that derives a simplification in which
        a specific negated factor, at the given index, is factored out.
        For example:
            w*(-x)*y*z = -(w*x*y*z)
        '''
        from proveit.number import Neg
        from ._theorems_ import multNegLeft, multNegRight, multNegAny

        if not isinstance(self.operands[idx], Neg):
            raise ValueError(
                "Operand at the index %d expected to be a negation for %s" %
                (idx, str(self)))

        if len(self.operands) == 2:
            if idx == 0:
                _x = self.operands[0].operand
                _y = self.operands[1]
                return multNegLeft.specialize({
                    x: _x,
                    y: _y
                },
                                              assumptions=assumptions)
            else:
                _x = self.operands[0]
                _y = self.operands[1].operand
                return multNegRight.specialize({
                    x: _x,
                    y: _y
                },
                                               assumptions=assumptions)
        _a = ExprTuple(*self.operands[:idx])
        _b = self.operands[idx].operand
        _c = ExprTuple(*self.operands[idx + 1:])
        _i = _a.length(assumptions)
        _j = _c.length(assumptions)
        return multNegAny.specialize({
            i: _i,
            j: _j,
            a: _a,
            b: _b,
            c: _c
        },
                                     assumptions=assumptions)
Ejemplo n.º 19
0
    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
Ejemplo n.º 20
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
Ejemplo n.º 21
0
    def doReducedEvaluation(self, assumptions=USE_DEFAULTS, **kwargs):
        '''
        Derive and return this multiplication expression equated with an irreducible value.
        Handle the trivial case of a zero factor or do pairwise evaluation
        after simplifying negations and eliminating one factors.
        '''
        from ._theorems_ import multZeroLeft, multZeroRight, multZeroAny
        from proveit.logic import isIrreducibleValue, EvaluationError
        from proveit.number import zero

        # First check for any zero factors -- quickest way to do an evaluation.
        try:
            zeroIdx = self.operands.index(zero)
            if len(self.operands) == 2:
                if zeroIdx == 0:
                    return multZeroLeft.specialize({x: self.operands[1]},
                                                   assumptions=assumptions)
                else:
                    return multZeroRight.specialize({x: self.operands[0]},
                                                    assumptions=assumptions)
            _a = self.operands[:zeroIdx]
            _b = self.operands[zeroIdx + 1:]
            _i = ExprTuple(*_a.length(assumptions))
            _j = ExprTuple(*_b.length(assumptions))
            return multZeroAny.specialize({
                i: _i,
                j: _j,
                a: _a,
                b: _b
            },
                                          assumptions=assumptions)
        except (ValueError, ProofFailure):
            pass  # No such "luck" regarding a simple multiplication by zero.

        expr = self

        # A convenience to allow successive update to the equation via transitivities.
        # (starting with self=self).
        eq = TransRelUpdater(self, assumptions)

        # Simplify negations -- factor them out.
        expr = eq.update(expr.negSimplifications(assumptions))

        if not isinstance(expr, Mult):
            # The expression may have changed to a negation after doing
            # negSimplification.  Start the simplification of this new
            # expression fresh at this point.
            eq.update(expr.evaluation(assumptions))
            return eq.relation

        # Eliminate any factors of one.
        expr = eq.update(expr.oneEliminations(assumptions))

        if isIrreducibleValue(expr):
            return eq.relation  # done

        if len(self.operands) > 2:
            eq.update(pairwiseEvaluation(expr, assumptions))
            return eq.relation

        raise EvaluationError(self, assumptions)
Ejemplo n.º 22
0
 def distribution(self, idx=None, assumptions=USE_DEFAULTS):
     r'''
     Distribute through the operand at the given index.  
     Returns the equality that equates self to this new version.
     Examples: 
         :math:`a (b + c + a) d = a b d + a c d + a a d`
         :math:`a (b - c) d = a b d - a c d`
         :math:`a \left(\sum_x f(x)\right c = \sum_x a f(x) c`
     Give any assumptions necessary to prove that the operands are in Complexes so that
     the associative and commutation theorems are applicable.            
     '''
     from ._theorems_ import distributeThroughSum, distributeThroughSubtract  #, distributeThroughSummation
     from proveit.number.division._theorems_ import prodOfFracs  #, fracInProd
     from proveit.number import Add, Div, Neg, Sum
     if idx is None and len(self.factors) == 2 and all(
             isinstance(factor, Div) for factor in self.factors):
         return prodOfFracs.specialize(
             {
                 x: self.factors[0].numerator,
                 y: self.factors[1].numerator,
                 z: self.factors[0].denominator,
                 w: self.factors[1].denominator
             },
             assumptions=assumptions)
     operand = self.operands[idx]
     _a = ExprTuple(*self.operands[:idx])
     _c = ExprTuple(*self.operands[idx + 1:])
     _i = _a.length(assumptions)
     _k = _c.length(assumptions)
     if isinstance(operand, Add):
         _b = self.operands[idx].operands
         _j = _b.length(assumptions)
         return distributeThroughSum.specialize(
             {
                 i: _i,
                 j: _j,
                 k: _k,
                 a: _a,
                 b: _b,
                 c: _c
             },
             assumptions=assumptions)
     elif (isinstance(operand, Add) and len(operand.operands) == 2
           and isinstance(operand.operands[0], Neg)):
         _j = _k
         _x = self.operands[idx].operands[0]
         _y = self.operands[idx].operands[1].operand
         return distributeThroughSubtract.specialize(
             {
                 i: _i,
                 j: _j,
                 a: _a,
                 x: _x,
                 y: _y,
                 c: _c
             },
             assumptions=assumptions)
     elif isinstance(operand, Div):
         raise NotImplementedError("Mult.distribution must be updated "
                                   "for Div case.")
         '''
         eqn = fracInProd.specialize({wMulti:self.operands[:idx], x:self.operands[idx].operands[0], y:self.operands[idx].operands[1], zMulti:self.operands[idx+1:]}, assumptions=assumptions)            
         try:
             # see if the numerator can simplify (e.g., with a one factor)
             numerSimplification = eqn.rhs.numerator.simplification(assumptions=assumptions)
             dummyVar = eqn.safeDummyVar()
             return numerSimplification.subRightSideInto(Equals(eqn.lhs, frac(dummyVar, eqn.rhs.denominator)), dummyVar)
         except:
             return eqn
         '''
     elif isinstance(operand, Sum):
         raise NotImplementedError("Mult.distribution must be updated "
                                   "for Sum case.")
         '''
         yMultiSub = operand.indices
         Pop, Pop_sub = Operation(P, operand.indices), operand.summand
         S_sub = operand.domain
         xDummy, zDummy = self.safeDummyVars(2)
         spec1 = distributeThroughSummation.specialize({Pop:Pop_sub, S:S_sub, yMulti:yMultiSub, 
                                                        xMulti:Etcetera(MultiVariable(xDummy)), zMulti:Etcetera(MultiVariable(zDummy))}, assumptions=assumptions)
         return spec1.deriveConclusion().specialize({Etcetera(MultiVariable(xDummy)):self.operands[:idx], \
                                                     Etcetera(MultiVariable(zDummy)):self.operands[idx+1:]}, assumptions=assumptions)
         '''
     else:
         raise Exception("Unsupported operand type to distribute over: " +
                         str(operand.__class__))
Ejemplo n.º 23
0
 def derive_via_multi_dilemma(self, conclusion, assumptions=USE_DEFAULTS):
     '''
     From (A or B) as self, and assuming A => C, B => D, and A, B, C, and D are Boolean,
     derive and return the conclusion, C or D.
     '''
     from . import constructive_dilemma, destructive_dilemma, constructive_multi_dilemma, destructive_multi_dilemma
     from proveit.logic import Not, Or
     from proveit import ExprTuple
     assert (isinstance(conclusion, Or) and
             (conclusion.operands.num_entries()
             == self.operands.num_entries())), \
             ("derive_via_multi_dilemma requires conclusion to be a "
              "disjunction, the same number of operands as self.")
     # Check for destructive versus constructive dilemma cases.
     if all(isinstance(operand, Not) for operand in self.operands) and all(
             isinstance(operand, Not) for operand in conclusion.operands):
         # destructive case.
         if self.operands.is_double() and destructive_dilemma.is_usable():
             # From Not(C) or Not(D), A => C, B => D, conclude Not(A) or
             # Not(B)
             return destructive_dilemma.instantiate(
                 {
                     C: self.operands[0].operand,
                     D: self.operands[1].operand,
                     A: conclusion.operands[0].operand,
                     B: conclusion.operands[1].operand
                 },
                 assumptions=assumptions)
         elif destructive_multi_dilemma.is_usable():
             # raise NotImplementedError("Generalized destructive multi-dilemma not implemented yet.")
             # Iterated destructive case.  From (Not(A) or Not(B) or Not(C)
             # or Not(D)) as self
             negated_operands_self = [
                 operand.operand for operand in self.operands
             ]
             negated_operands_conc = [
                 operand.operand for operand in conclusion.operands
             ]
             _A = ExprTuple(*negated_operands_self)
             _B = ExprTuple(*negated_operands_conc)
             _m = _A.num_elements(assumptions)
             return destructive_multi_dilemma.instantiate(
                 {
                     m: _m,
                     A: _A,
                     B: _B
                 }, assumptions=assumptions)
     # constructive case.
     if self.operands.is_double():
         # From (A or B), A => C, B => D, conclude C or D.
         return constructive_dilemma.instantiate(
             {
                 A: self.operands[0],
                 B: self.operands[1],
                 C: conclusion.operands[0],
                 D: conclusion.operands[1]
             },
             assumptions=assumptions)
     #raise NotImplementedError("Generalized constructive multi-dilemma not implemented yet.")
     _A = self.operands
     _B = conclusion.operands
     _m = _A.num_elements(assumptions)
     return constructive_multi_dilemma.instantiate({
         m: _m,
         A: _A,
         B: _B
     },
                                                   assumptions=assumptions)
Ejemplo n.º 24
0
    def eliminate(skolem_constants, judgment, **defaults_config):
        '''
        For the provided judgment of the form S |– alpha and the tuple
        of Skolem constants skolem_constants that had been specified
        earlier using the Exists.choose(), derive and return a new
        judgment S' |– alpha where all assumptions in S involving only
        the given skolem_constants are now eliminated.
        This process will only work if the provided skolem_constants
        exactly match a set of Skolem constants used earlier in an
        Exists.choose() method to produce the Skolem constant-based
        subset of assumptions you wish to eliminate from S.
        '''
        from proveit import Lambda
        from proveit import n, P, Q, alpha
        from proveit.logic import And
        from proveit.core_expr_types import (x_1_to_n, y_1_to_n)
        from proveit.logic.booleans.quantification.existence import (
            skolem_elim)
        if skolem_constants not in Exists.skolem_consts_to_existential:
            raise KeyError("In calling Exists.eliminate(), the Skolem "
                           "constants provided were: {}, but you can only "
                           "eliminate Skolem constants that were chosen "
                           "earlier when using Exists.choose() and the "
                           "Skolem constants to be eliminated must appear "
                           "exactly as specified in the original "
                           "Exists.choose() method.".format(skolem_constants))
        existential = Exists.skolem_consts_to_existential[skolem_constants]
        skolem_assumptions = set(
            existential.choose(*skolem_constants, print_message=False))
        with defaults.temporary() as temp_defaults:
            temp_defaults.assumptions = (
                assumption for assumption in defaults.assumptions
                if assumption not in skolem_assumptions)

            _P = Lambda(existential.instance_params, existential.instance_expr)
            if hasattr(existential, 'condition'):
                _Q = Lambda(existential.instance_params, existential.condition)
            else:
                # There is no condition but we still need to provide
                # something for _Q so we provide an empty conjunction,
                # And().
                _Q = Lambda(existential.instance_params, And())
            _alpha = judgment
            _n = existential.instance_params.num_elements()
            x_1_to__n = ExprTuple(x_1_to_n.basic_replaced({n: _n}))
            y_1_to__n = ExprTuple(y_1_to_n.basic_replaced({n: _n}))

            # express the judgment as an implication to match details of
            # the skolem_elim theorem being instantiated further below
            P_implies_alpha = _alpha.as_implication(hypothesis=_P.apply(
                *skolem_constants))
            # the generalization to further match theorem details
            # can be handled through automation
            # P_implies_alpha.generalize(
            #         skolem_constants,
            #         conditions=[_Q.apply(*skolem_constants)])

            return skolem_elim.instantiate(
                {
                    n: _n,
                    P: _P,
                    Q: _Q,
                    alpha: _alpha,
                    x_1_to__n: skolem_constants,
                    y_1_to__n: existential.instance_params
                },
                preserve_all=True).derive_consequent()
Ejemplo n.º 25
0
    def factorization_of_scalars(self, **defaults_config):
        '''
        Prove equality with a Qmult in which the complex
        numbers are pulled to the front andassociated together via Mult.
        For example,
            (a A b B c C d D e) = ((a*b*c*d*e) A B C D)
        where a, b, c, d, and e are complex numbers, '*' denotes
        number multiplication, and spaces, here, denote the Qmult
        operation.
        
        Also see scalar_mult_factorization.
        '''
        from . import (QmultCodomain, qmult_pulling_scalar_out_front,
                       qmult_pulling_scalars_out_front,
                       qmult_scalar_association)
        expr = self
        eq = TransRelUpdater(expr)

        # First, lets prove the Qmult is well-formed and, in the
        # process, ensure to know which operands are Complex.
        if not InClass(self, QmultCodomain).proven():
            QmultCodomain.membership_object(self).conclude()

        # Go through the operands in reverse order so the complex
        # factors will be in the original order out front in the end.
        n_complex_entries = 0
        for _k, operand in enumerate(reversed(self.operands.entries)):
            _idx = self.operands.num_entries() - _k - 1 + n_complex_entries
            if InSet(operand, Complex).proven():
                # We have a complex number to pull out in front.
                _A = expr.operands[:_idx]
                _b = operand
                _C = expr.operands[_idx + 1:]
                _l = _A.num_elements()
                _n = _C.num_elements()
                expr = eq.update(
                    qmult_pulling_scalar_out_front.instantiate(
                        {
                            l: _l,
                            n: _n,
                            b: _b,
                            A: _A,
                            C: _C
                        }, preserve_all=True))
                n_complex_entries += 1
            elif isinstance(operand, ExprRange):
                if ExprRange(operand.parameter, InSet(operand.body, Complex),
                             operand.true_start_index,
                             operand.true_end_index).proven():
                    # We have a range of complex numbers to pull out in
                    # front.
                    _A = expr.operands[:_idx]
                    _b = ExprTuple(operand)
                    _C = expr.operands[_idx + 1:]
                    _j = _b.num_elements()
                    _l = _A.num_elements()
                    _n = _C.num_elements()
                    thm = qmult_pulling_scalars_out_front
                    expr = eq.update(
                        thm.instantiate(
                            {
                                j: _j,
                                l: _l,
                                n: _n,
                                b: _b,
                                A: _A,
                                C: _C
                            },
                            preserve_all=True))
                    n_complex_entries += 1

        # Associate the complex numbers, now out in front.
        _b = expr.operands[:n_complex_entries]
        _A = expr.operands[n_complex_entries:]
        _j = _b.num_elements()
        _l = _A.num_elements()
        if (_b.num_entries() > 0 and not _b.is_single()
                and _A.num_entries() > 0):
            expr = eq.update(
                qmult_scalar_association.instantiate(
                    {
                        j: _j,
                        l: _l,
                        b: _b,
                        A: _A
                    }, preserve_expr=expr))
            # The multiplication of complex numbers is complex.
            expr.operands[0].deduce_in_number_set(Complex)

        return eq.relation