示例#1
0
 def simplification(self, *, must_evaluate=False, **defaults_config):
     '''
     Proves that this ExprTuple is equal to an ExprTuple
     with all of its entries simplified (and ExprRanges reduced).
     '''
     from proveit.relation import TransRelUpdater
     from proveit import ExprRange
     expr = self
     eq = TransRelUpdater(expr)
     _k = 0
     for entry in self.entries:
         if isinstance(entry, ExprRange):
             entry_simp = entry.reduction(preserve_all=True)
             num_entries = entry_simp.rhs.num_entries()
         else:
             if must_evaluate:
                 entry_simp = entry.evaluation()
             else:
                 entry_simp = entry.simplification()
             num_entries = 1
         if entry_simp.lhs != entry_simp.rhs:
             expr = eq.update(expr.substitution(entry_simp, 
                                                start_idx=_k))
         _k += num_entries
     return eq.relation
示例#2
0
 def simplification_of_operands(self, **defaults_config):
     '''
     Prove this Operation equal to a form in which its operands
     have been simplified.
     '''
     from proveit.relation import TransRelUpdater
     from proveit import ExprRange, NamedExprs
     from proveit.logic import is_irreducible_value
     if any(isinstance(operand, ExprRange) for operand in self.operands):
         # If there is any ExprRange in the operands, simplify the
         # operands together as an ExprTuple.
         return self.inner_expr().operands[:].simplification()
     else:
         expr = self
         eq = TransRelUpdater(expr)
         with defaults.temporary() as temp_defaults:
             # No auto-simplification or replacements here;
             # just simplify operands one at a time.
             temp_defaults.preserve_all = True
             operands = self.operands
             if isinstance(operands, NamedExprs):
                 # operands as NamedExprs
                 for key in operands.keys():
                     operand = operands[key]
                     if not is_irreducible_value(operand):
                         inner_operand = getattr(expr.inner_expr(), key)
                         expr = eq.update(inner_operand.simplification())
             else:
                 # operands as ExprTuple
                 for k, operand in enumerate(operands):
                     if not is_irreducible_value(operand):
                         inner_operand = expr.inner_expr().operands[k]
                         expr = eq.update(inner_operand.simplification())
     return eq.relation
示例#3
0
 def _redundant_mod_elimination(
         expr, mod_elimination_thm, mod_elimination_in_sum_thm):
     '''
     For use by Mod and ModAbs for shallow_simplification.
     '''
     dividend = expr.dividend
     divisor = expr.divisor
     if isinstance(dividend, Mod) and dividend.divisor==divisor:
         # [(a mod b) mod b] = [a mod b]
         return mod_elimination_thm.instantiate(
                 {a:dividend.dividend, b:divisor})
     elif isinstance(dividend, Add):
         # Eliminate 'mod L' from each term.
         eq = TransRelUpdater(expr)
         _L = divisor
         mod_terms = []
         for _k, term in enumerate(dividend.terms):
             if isinstance(term, Mod) and term.divisor==_L:
                 mod_terms.append(_k)
         for _k in mod_terms:
             # Use preserve_all=True for all but the last term.
             preserve_all = (_k != mod_terms[-1])
             _a = expr.dividend.terms[:_k]
             _b = expr.dividend.terms[_k].dividend
             _c = expr.dividend.terms[_k+1:]
             _i = _a.num_elements()
             _j = _c.num_elements()
             expr = eq.update(
                     mod_elimination_in_sum_thm
                     .instantiate(
                             {i:_i, j:_j, a:_a, b:_b, c:_c, L:_L},
                             preserve_all=preserve_all))
         return eq.relation
     return Equals(expr, expr).conclude_via_reflexivity()
示例#4
0
 def simplification(self, assumptions=USE_DEFAULTS, automation=True):
     if not automation:
         return Expression.simplification(self,
                                          assumptions,
                                          automation=False)
     from proveit.relation import TransRelUpdater
     eq = TransRelUpdater(self, assumptions)
     expr = eq.update(self.computation(assumptions))
     expr = eq.update(expr.simplification(assumptions))
     return eq.relation
示例#5
0
    def shallow_simplification(self, *, must_evaluate=False,
                               **defaults_config):
        '''
        Returns a proven simplification equation for this NumKet
        expression assuming the operands have been simplified.
        
        Currently deals only with:
        (1) simplifying a NumKet with register size = 1 to a
            simple Ket. It's not immediately clear that we always want
            to do such a thing, but here goes.
        '''

        if self.size == one:
            # from . import single_qubit_register_ket
            from . import single_qubit_num_ket
            return single_qubit_num_ket.instantiate(
                    {b: self.num}, preserve_all=True)

        # Else simply return self=self.
        # Establishing some minimal infrastructure
        # for future development
        expr = self
        # for convenience updating our equation:
        eq = TransRelUpdater(expr)
        # Future processing possible here.
        return eq.relation
示例#6
0
    def do_reduced_simplification(self, assumptions=USE_DEFAULTS):
        '''
        For trivial cases, a zero or one exponent or zero or one base,
        derive and return this exponential expression equated with a
        simplified form. Assumptions may be necessary to deduce
        necessary conditions for the simplification.
        '''
        from proveit.logic import Equals, InSet
        from proveit.numbers import one, two, Rational, Real, Abs
        from proveit.relation import TransRelUpdater
        from . import complex_x_to_first_power_is_x
        if self.exponent == one:
            return complex_x_to_first_power_is_x.instantiate({a: self.base})
        if (isinstance(self.base, Exp) and isinstance(self.base.exponent, Div)
                and self.base.exponent.numerator == one
                and self.base.exponent.denominator == self.exponent):
            from . import nth_power_of_nth_root
            _n, _x = nth_power_of_nth_root.instance_params
            return nth_power_of_nth_root.instantiate(
                {
                    _n: self.exponent,
                    _x: self.base.base
                },
                assumptions=assumptions)

        expr = self
        # for convenience updating our equation:
        eq = TransRelUpdater(expr, assumptions)
        if self.exponent == two and isinstance(self.base, Abs):
            from . import (square_abs_rational_simp, square_abs_real_simp)
            # |a|^2 = a if a is real
            rational_base = InSet(self.base, Rational).proven(assumptions)
            real_base = InSet(self.base, Real).proven(assumptions)
            thm = None
            if rational_base:
                thm = square_abs_rational_simp
            elif real_base:
                thm = square_abs_real_simp
            if thm is not None:
                simp = thm.instantiate({a: self.base.operand},
                                       assumptions=assumptions)
                expr = eq.update(simp)
                # A further simplification may be possible after
                # eliminating the absolute value.
                expr = eq.update(expr.simplification(assumptions))

        return eq.relation
示例#7
0
    def doReducedSimplification(self, assumptions=USE_DEFAULTS, **kwargs):
        '''
        Derive and return this negation expression equated with a simpler form.
        Deals with double negation specifically.
        '''
        from proveit.relation import TransRelUpdater

        expr = self
        # For convenience updating our equation:
        eq = TransRelUpdater(expr, assumptions)
        # Handle double negation:
        if isinstance(self.operand, Neg):
            # simplify double negation
            expr = eq.update(self.doubleNegSimplification(assumptions))
            # simplify what is inside the double-negation.
            expr = eq.update(expr.simplification(assumptions))
        return eq.relation
示例#8
0
    def simplification(self, **defaults_config):
        from proveit.relation import TransRelUpdater
        from proveit.logic import And

        expr = self
        eq = TransRelUpdater(expr)
        if True:  #not self.value.is_simplified():
            # Simplify the 'value'.
            expr = eq.update(
                expr.value_substitution(self.value.simplification()))
        if isinstance(self.condition, And):
            # Simplify the conditions.
            if True:  #any(not condition.is_simplified() for condition
                #    in self.condition.operands.entries):
                inner_condition = expr.inner_expr().condition
                expr = eq.update(inner_condition.simplification_of_operands())
        elif True:  #not self.condition.is_simplified():
            # Simplify the condition.
            expr = eq.update(
                expr.condition_substitution(self.condition.simplification()))
        # Perform a shallow simplifation on this Conditional.
        # If the expression has not been reduced yet, no need for an
        # "evaluation check" by the @prover decorator.
        _no_eval_check = (expr == self)
        eq.update(expr.shallow_simplification(_no_eval_check=_no_eval_check))

        return eq.relation
示例#9
0
 def sub_expr_substitution(self, new_sub_exprs, **defaults_config):
     '''
     Given new sub-expressions to replace existing sub-expressions,
     return the equality between this Expression and the new
     one with the new sub-expressions.
     '''
     from proveit.logic import Equals
     from proveit.relation import TransRelUpdater
     assert len(new_sub_exprs) == 2, (
         "Expecting 2 sub-expressions: operator and operands")
     eq = TransRelUpdater(self)
     expr = self
     if new_sub_exprs[0] != self.sub_expr(0):
         expr = eq.update(
             expr.operator_substitution(
                 Equals(self.sub_expr(0), new_sub_exprs[0])))
     if new_sub_exprs[1] != self.sub_expr(1):
         expr = eq.update(
             expr.operands_substitution(
                 Equals(self.sub_expr(1), new_sub_exprs[1])))
     return eq.relation
示例#10
0
    def distribution(self, assumptions=USE_DEFAULTS):
        '''
        Distribute negation through a sum, deducing and returning
        the equality between the original and distributed forms.
        '''
        from ._theorems_ import distributeNegThroughBinarySum
        from ._theorems_ import distributeNegThroughSubtract, distributeNegThroughSum
        from proveit.number import Add, num
        from proveit.relation import TransRelUpdater
        expr = self
        eq = TransRelUpdater(
            expr, assumptions)  # for convenience updating our equation

        if isinstance(self.operand, Add):
            # Distribute negation through a sum.
            add_expr = self.operand
            if len(add_expr.operands) == 2:
                # special case of 2 operands
                if isinstance(add_expr.operands[1], Neg):
                    expr = eq.update(
                        distributeNegThroughSubtract.specialize(
                            {
                                a: add_expr.operands[0],
                                b: add_expr.operands[1].operand
                            },
                            assumptions=assumptions))
                else:
                    expr = eq.update(
                        distributeNegThroughBinarySum.specialize(
                            {
                                a: add_expr.operands[0],
                                b: add_expr.operands[1]
                            },
                            assumptions=assumptions))
            else:
                # distribute the negation over the sum
                expr = eq.update(distributeNegThroughSum.specialize({
                    n:
                    num(len(add_expr.operands)),
                    xx:
                    add_expr.operands
                }),
                                 assumptions=assumptions)
            assert isinstance(
                expr, Add
            ), "distributeNeg theorems are expected to yield an Add expression"
            # check for double negation
            for k, operand in enumerate(expr.operands):
                assert isinstance(
                    operand, Neg
                ), "Each term from distributeNegThroughSum is expected to be negated"
                if isinstance(operand.operand, Neg):
                    expr = eq.update(
                        expr.innerExpr().operands[k].doubleNegSimplification())
            return eq.relation
        else:
            raise Exception(
                'Only negation distribution through a sum or subtract is implemented'
            )
示例#11
0
 def shallow_simplification(self, *, must_evaluate=False,
                            **defaults_config):
     '''
     Proves that this ExprTuple is equal to an ExprTuple
     with ExprRanges reduced unless these are "preserved"
     expressions.
     '''
     from proveit.relation import TransRelUpdater
     from proveit import ExprRange
     from proveit.logic import is_irreducible_value, EvaluationError
     expr = self
     eq = TransRelUpdater(expr)
     if defaults.preserve_all:
         # Preserve all sub-expressions -- don't simplify.
         return eq.relation
     _k = 0
     for entry in self.entries:
         if isinstance(entry, ExprRange):
             if entry in defaults.preserved_exprs:
                 if must_evaluate:
                     # An ExprRange is not irreducible.
                     raise EvaluationError(self)
                 # Preserve this entry -- don't simplify it.
                 _k += ExprTuple(entry).num_entries()
                 continue
             entry_simp = entry.reduction()
             if must_evaluate and not is_irreducible_value(entry_simp.rhs):
                 raise EvaluationError(self)
             if entry_simp.lhs != entry_simp.rhs:
                 substitution = expr.substitution(
                         entry_simp, start_idx=_k, preserve_all=True)
                 expr = eq.update(substitution)
             _k += entry_simp.rhs.num_entries()
         else:
             if must_evaluate and not is_irreducible_value(entry):
                 raise EvaluationError(self)
             _k += 1
     return eq.relation
示例#12
0
 def conclude_via_inout_consolidation(self, **defaults_config):
     '''
     Prove this circuit equivalence by consolidating the input
     (at the beginning) and/or output (at the end).
     '''
     lhs_cols, rhs_cols = (self.lhs.vert_expr_array.entries,
                           self.rhs.vert_expr_array.entries)
     if len(lhs_cols) != len(rhs_cols):
         raise ValueError(
             "Cannot perform 'conclude_via_inout_consolidation' "
             "if the number of columns is different: %s ≠ %s." %
             (len(lhs_cols), len(rhs_cols)))
     if not all(
             lhs_col == rhs_col
             for lhs_col, rhs_col in zip(lhs_cols[1:-1], rhs_cols[1:-1])):
         raise ValueError(
             "Cannot perform 'conclude_via_inout_consolidation' "
             "if the inner columns do not all match.")
     expr = self.lhs
     # Use this updater to go from the lhs to the rhs via
     # transitive equivalences.
     equiv = TransRelUpdater(expr)
     if lhs_cols[0] != rhs_cols[0]:
         # The first column does not match, so try to consolidate
         # inputs.
         lhs_consolidation = Qcircuit(VertExprArray(
             lhs_cols[0])).input_consolidation()
         expr = equiv.update(lhs_consolidation.substitution(expr))
         rhs_consolidation = Qcircuit(VertExprArray(
             rhs_cols[0])).input_consolidation()
         input_equiv = QcircuitEquiv(lhs_consolidation.rhs,
                                     rhs_consolidation.rhs).prove()
         expr = equiv.update(input_equiv.substitution(expr))
         expr = equiv.update(rhs_consolidation.substitution(expr))
     if lhs_cols[-1] != rhs_cols[-1]:
         # The first column does not match, so try to consolidate
         # inputs.
         lhs_consolidation = Qcircuit(VertExprArray(
             lhs_cols[-1])).output_consolidation()
         expr = equiv.update(lhs_consolidation.substitution(expr))
         rhs_consolidation = Qcircuit(VertExprArray(
             rhs_cols[-1])).output_consolidation()
         output_equiv = QcircuitEquiv(lhs_consolidation.rhs,
                                      rhs_consolidation.rhs).prove()
         expr = equiv.update(output_equiv.substitution(expr))
         expr = equiv.update(rhs_consolidation.substitution(expr))
     return equiv.relation
示例#13
0
    def simplification(self, assumptions=USE_DEFAULTS):
        '''
        Derive and return this negation expression equated with a simpler form.
        Deals with double negation specifically.
        '''
        from proveit.number import zero
        from proveit.logic import Equals
        from ._theorems_ import doubleNegation
        from proveit.relation import TransRelUpdater

        # Handle double negation:
        if isinstance(self.operand, Neg):
            expr = self
            eq = TransRelUpdater(
                expr, assumptions)  # for convenience updating our equation

            # simplify double negation
            expr = eq.update(self.doubleNegSimplification(assumptions))
            # simplify what is inside the double-negation.
            expr = eq.update(expr.simplification(assumptions))
            return eq.relation

        # otherwise, just use the default simplification
        return Operation.simplification(self, assumptions)
示例#14
0
    def distribution(self, assumptions=USE_DEFAULTS):
        '''
        Distribute negation through a sum, deducing and returning
        the equality between the original and distributed forms.
        '''
        from . import distribute_neg_through_binary_sum
        from . import distribute_neg_through_subtract, distribute_neg_through_sum
        from proveit.numbers import Add
        from proveit.relation import TransRelUpdater
        expr = self
        # for convenience updating our equation
        eq = TransRelUpdater(expr, assumptions)

        if isinstance(self.operand, Add):
            # Distribute negation through a sum.
            add_expr = self.operand
            if add_expr.operands.is_double():
                # special case of 2 operands
                if isinstance(add_expr.operands[1], Neg):
                    expr = eq.update(distribute_neg_through_subtract.instantiate(
                        {a: add_expr.operands[0], b: add_expr.operands[1].operand}, assumptions=assumptions))
                else:
                    expr = eq.update(distribute_neg_through_binary_sum.instantiate(
                        {a: add_expr.operands[0], b: add_expr.operands[1]}, assumptions=assumptions))
            else:
                # distribute the negation over the sum
                _x = add_expr.operands
                _n = _x.num_elements(assumptions)
                expr = eq.update(distribute_neg_through_sum.instantiate(
                    {n: _n, x: _x}), assumptions=assumptions)
            assert isinstance(
                expr, Add), "distribute_neg theorems are expected to yield an Add expression"
            # check for double negation
            for k, operand in enumerate(expr.operands):
                assert isinstance(
                    operand, Neg), "Each term from distribute_neg_through_sum is expected to be negated"
                if isinstance(operand.operand, Neg):
                    expr = eq.update(
                        expr.inner_expr().operands[k].double_neg_simplification())
            return eq.relation
        else:
            raise Exception(
                'Only negation distribution through a sum or subtract is implemented')
示例#15
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
示例#16
0
    def merger(self, **defaults_config):
        '''
        If this is an tuple of expressions that can be directly merged
        together into a single ExprRange, return this proven
        equivalence.  For example,
        {j \in Natural, k-(j+1) \in Natural}
        |- (x_1, .., x_j, x_{j+1}, x_{j+2}, ..., x_k) = (x_1, ..., x_k)
        '''
        from proveit._core_.expression.lambda_expr import (
                Lambda, ArgumentExtractionError)
        from .expr_range import ExprRange, simplified_index
        from proveit.relation import TransRelUpdater
        from proveit.core_expr_types.tuples import (
            merge, merge_front, merge_back, merge_extension,
            merge_pair, merge_series)
        from proveit import f, i, j, k, l, x
        from proveit.numbers import one, Add, subtract

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

        # Determine the position of the first ExprRange item and get the
        # lambda map.
        first_range_pos = len(self.entries)
        lambda_map = None
        for _k, item in enumerate(self):
            if isinstance(item, ExprRange):
                lambda_map = Lambda(item.lambda_map.parameter,
                                    item.lambda_map.body)
                first_range_pos = _k
                break

        if 1 < first_range_pos:
            if lambda_map is None:
                raise NotImplementedError("Means of extracting a lambda "
                                          "map has not been implemented")
                pass  # need the lambda map
            # Collapse singular items at the beginning.
            front_singles = ExprTuple(eq.expr[:first_range_pos])
            i_sub = lambda_map.extract_argument(front_singles[0])
            j_sub = lambda_map.extract_argument(front_singles[-1])
            if len(front_singles.entries) == 2:
                # Merge a pair of singular items.
                front_merger = merge_pair.instantiate(
                    {f: lambda_map, i: i_sub, j: j_sub})
            else:
                # Merge a series of singular items in one shot.
                front_merger = merge_series.instantiate(
                    {f: lambda_map, x: front_singles, i: i_sub, j: j_sub})
            eq.update(
                front_merger.substitution(
                    self.inner_expr()[:first_range_pos]))

        if eq.expr.num_entries() == 1:
            # We have accomplished a merger down to one item.
            return eq.relation

        if eq.expr.num_entries() == 2:
            # Merge a pair.
            if isinstance(eq.expr[0], ExprRange):
                if isinstance(eq.expr[1], ExprRange):
                    # Merge a pair of ExprRanges.
                    item = eq.expr[1]
                    other_lambda_map = Lambda(item.lambda_map.parameter,
                                              item.lambda_map.body)
                    if other_lambda_map != lambda_map:
                        raise ExprTupleError(
                            "Cannot merge together ExprRanges "
                            "with different lambda maps: %s vs %s" %
                            (lambda_map, other_lambda_map))
                    _i, _j = eq.expr[0].true_start_index, eq.expr[0].true_end_index
                    _k, _l = eq.expr[1].true_start_index, eq.expr[1].true_end_index
                    merger = \
                        merge.instantiate(
                                {f: lambda_map, i: _i, j: _j, k: _k, l: _l})
                else:
                    # Merge an ExprRange and a singular item.
                    _i, _j = eq.expr[0].true_start_index, eq.expr[0].true_end_index
                    try:
                        _k = lambda_map.extract_argument(eq.expr[1])
                    except ArgumentExtractionError:
                        _k = simplified_index(Add(_j, one))
                    if _k == Add(_j, one):
                        merger = merge_extension.instantiate(
                            {f: lambda_map, i: _i, j: _j})
                    else:
                        merger = merge_back.instantiate(
                            {f: lambda_map, i: _i, j: _j, k: _k})
            else:
                # Merge a singular item and ExprRange.
                _i = simplified_index(
                    subtract(eq.expr[1].true_start_index, one))
                _j, _k = eq.expr[1].true_start_index, eq.expr[1].true_end_index
                merger = \
                    merge_front.instantiate({f: lambda_map, i: _i, 
                                             j: _j, k: _k})
            all_decreasing = all(expr.is_decreasing() for expr in eq.expr
                                 if isinstance(expr, ExprRange))
            if all_decreasing:
                # Apply the 'decreasing' order style to match what we
                # had originally.
                for _i in (0, 1):
                    if isinstance(eq.expr[_i], ExprRange):
                        merger = (merger.inner_expr().lhs[_i]
                                  .with_decreasing_order())
                merger = merger.inner_expr().rhs[0].with_decreasing_order()
            eq.update(merger)
            return eq.relation

        while eq.expr.num_entries() > 1:
            front_merger = ExprTuple(*eq.expr[:2].entries).merger()
            eq.update(front_merger.substitution(
                eq.expr.inner_expr()[:2]))
        return eq.relation
示例#17
0
    def deduce_equality(self, equality, *,
                        eq_via_elem_eq_thm=None, **defaults_config):
        from proveit import ExprRange
        from proveit import a, b, i
        from proveit.logic import Equals
        from proveit.core_expr_types.tuples import tuple_eq_via_elem_eq
        from proveit.relation import TransRelUpdater
        if not isinstance(equality, Equals):
            raise ValueError("The 'equality' should be an Equals expression")
        if equality.lhs != self:
            raise ValueError("The left side of 'equality' should be 'self'")

        from proveit.numbers import num, one

        # Handle the special counting cases.  For example,
        #   (1, 2, 3, 4) = (1, ..., 4)
        _n = len(self.entries)
        if all(self[_k] == num(_k + 1) for _k in range(_n)):
            if (isinstance(equality.rhs, ExprTuple)
                    and equality.rhs.num_entries() == 1
                    and isinstance(equality.rhs[0], ExprRange)):
                expr_range = equality.rhs[0]
                if (expr_range.true_start_index == one and
                        expr_range.true_end_index == num(_n)):
                    if len(self.entries) >= 10:
                        raise NotImplementedError("counting range equality "
                                                  "not implemented for more "
                                                  "then 10 elements")
                    import proveit.numbers.numerals.decimals
                    equiv_thm = proveit.numbers.numerals.decimals\
                        .__getattr__('count_to_%d_range' % _n)
                    return equiv_thm
        
        lhs, rhs = equality.lhs, equality.rhs
        if (lhs.num_entries() == rhs.num_entries() == 1
                and isinstance(lhs[0], ExprRange) 
                and isinstance(rhs[0], ExprRange)):
            # Prove the equality of two ExprRanges.
            r_range = rhs[0]
            expr = lhs
            if expr[0].is_decreasing():
                # We could handle different styles later, but
                # let's be consistent with increasing order for now
                # to make this easier to implement.
                expr = expr.inner_expr()[0].with_increasing_order()
            eq = TransRelUpdater(expr)
            if expr[0].true_start_index != r_range.true_start_index:
                # Shift indices so they have the same start.
                expr = eq.update(expr[0].shift_equivalence(
                        new_start=r_range.true_start_index))
            if expr[0].lambda_map != r_range.lambda_map:
                # Change the lambda map.
                expr = eq.update(expr[0].range_fn_transformation(
                        r_range.lambda_map))
            if expr[0].true_end_index != r_range.true_end_index:
                # Make the end indices be the same:
                end_eq = Equals(expr[0].true_end_index, r_range.true_end_index).prove()
                expr = eq.update(end_eq.substitution(
                        expr.inner_expr()[0].true_end_index))
            return eq.relation
        
        # Try tuple_eq_via_elem_eq as the last resort.
        _i = lhs.num_elements()
        _a = lhs
        _b = rhs
        if eq_via_elem_eq_thm is None:
            eq_via_elem_eq_thm = tuple_eq_via_elem_eq
        return eq_via_elem_eq_thm.instantiate({i:_i, a:_a, b:_b})
示例#18
0
def bundle(expr, bundle_thm, num_levels=2, *, assumptions=USE_DEFAULTS):
    '''
    Given a nested OperationOverInstances, derive or equate an
    equivalent form in which a given number of nested levels is
    bundled together.  Use the given theorem specific to the particular
    OperationOverInstances.

    For example,
        \forall_{x, y | Q(x, y)} \forall_{z | R(z)} P(x, y, z)
    can become
        \forall_{x, y, z | Q(x, y), R(z)} P(x, y, z)
    via bundle with num_levels=2.

    For example of the form of the theorem required, see
    proveit.logic.boolean.quantification.bundling or
    proveit.logic.boolean.quantification.bundling_equality.
    '''
    from proveit.relation import TransRelUpdater
    from proveit.logic import Implies, Equals
    # Make a TransRelUpdater only if the bundle_thm yield an
    # equation, in which case we'll want the result to be an equation.
    eq = None
    bundled = expr
    while num_levels >= 2:
        if (not isinstance(bundled, OperationOverInstances) or
                not isinstance(bundled.instanceExpr, OperationOverInstances)):
            raise ValueError(
                "May only 'bundle' nested OperationOverInstances, "
                "not %s" % bundled)
        _m = bundled.instanceParams.length()
        _n = bundled.instanceExpr.instanceParams.length()
        _P = bundled.instanceExpr.instanceExpr
        _Q = bundled.effectiveCondition()
        _R = bundled.instanceExpr.effectiveCondition()
        m, n = bundle_thm.instanceVars
        P, Q, R = bundle_thm.instanceExpr.instanceVars
        correspondence = bundle_thm.instanceExpr.instanceExpr
        if isinstance(correspondence, Implies):
            if (not isinstance(correspondence.antecedent,
                               OperationOverInstances)
                    or not len(correspondence.consequent.instanceParams) == 2):
                raise ValueError("'bundle_thm', %s, does not have the "
                                 "expected form with the bundled form as "
                                 "the consequent of the implication, %s" %
                                 (bundle_thm, correspondence))
            x_1_to_m, y_1_to_n = correspondence.consequent.instanceParams
        elif isinstance(correspondence, Equals):
            if not isinstance(
                    correspondence.rhs, OperationOverInstances
                    or not len(correspondence.antecedent.instanceParams) == 2):
                raise ValueError("'bundle_thm', %s, does not have the "
                                 "expected form with the bundled form on "
                                 "right of the an equality, %s" %
                                 (bundle_thm, correspondence))
            x_1_to_m, y_1_to_n = correspondence.rhs.instanceParams

        all_params = bundled.instanceParams + bundled.instanceExpr.instanceParams
        Pxy = Function(P, all_params)
        Qx = Function(Q, bundled.instanceParams)
        Rxy = Function(R, all_params)
        x_1_to_m = x_1_to_m.replaced({m: _m})
        y_1_to_n = y_1_to_n.replaced({n: _n})
        instantiation = bundle_thm.instantiate(
            {
                m: _m,
                n: _n,
                ExprTuple(x_1_to_m): bundled.instanceParams,
                ExprTuple(y_1_to_n): bundled.instanceExpr.instanceParams,
                Pxy: _P,
                Qx: _Q,
                Rxy: _R
            },
            assumptions=assumptions)
        if isinstance(instantiation.expr, Implies):
            bundled = instantiation.deriveConsequent()
        elif isinstance(instantiation.expr, Equals):
            if eq is None:
                eq = TransRelUpdater(bundled)
            try:
                bundled = eq.update(instantiation)
            except ValueError:
                raise ValueError(
                    "Instantiation of bundle_thm %s is %s but "
                    "should match %s on one side of the equation." %
                    (bundle_thm, instantiation, bundled))
        else:
            raise ValueError("Instantiation of bundle_thm %s is %s but "
                             "should be an Implies or Equals expression." %
                             (bundle_thm, instantiation))
        num_levels -= 1
    if eq is None:
        # Return the bundled result.
        return bundled
    else:
        # Return the equality between the original expression and
        # the bundled result.
        return eq.relation
示例#19
0
    def merger(self, assumptions=USE_DEFAULTS):
        '''
        If this is an tuple of expressions that can be directly merged 
        together into a single ExprRange, return this proven 
        equivalence.  For example,
        {j \in Naturals, k-(j+1) \in Naturals} 
        |- (x_1, .., x_j, x_{j+1}, x_{j+2}, ..., x_k) = (x_1, ..., x_k)
        '''
        from proveit._core_.expression.lambda_expr import Lambda
        from .expr_range import ExprRange
        from proveit.relation import TransRelUpdater
        from proveit.core_expr_types.tuples._theorems_ import (
            merge, merge_front, merge_back, merge_extension, merge_pair,
            merge_series)
        from proveit._common_ import f, i, j, k, l, x
        from proveit.number import Add, one

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

        # Determine the position of the first ExprRange item and get the
        # lambda map.
        first_range_pos = len(self)
        lambda_map = None
        for _k, item in enumerate(self):
            if isinstance(item, ExprRange):
                lambda_map = Lambda(item.lambda_map.parameter,
                                    item.lambda_map.body)
                first_range_pos = _k
                break

        if 1 < first_range_pos:
            if lambda_map is None:
                raise NotImplementedError("Means of extracting a lambda "
                                          "map has not been implemented")
                pass  # need the lambda map
            # Collapse singular items at the beginning.
            front_singles = ExprTuple(eq.expr[:first_range_pos])
            i_sub = lambda_map.extractArgument(front_singles[0])
            j_sub = lambda_map.extractArgument(front_singles[-1])
            if len(front_singles) == 2:
                # Merge a pair of singular items.
                front_merger = merge_pair.specialize(
                    {
                        f: lambda_map,
                        i: i_sub,
                        j: j_sub
                    },
                    assumptions=assumptions)
            else:
                # Merge a series of singular items in one shot.
                front_merger = merge_series.specialize(
                    {
                        f: lambda_map,
                        x: front_singles,
                        i: i_sub,
                        j: j_sub
                    },
                    assumptions=assumptions)
            eq.update(
                front_merger.substitution(self.innerExpr()[:first_range_pos],
                                          assumptions=assumptions))

        if len(eq.expr) == 1:
            # We have accomplished a merger down to one item.
            return eq.relation

        if len(eq.expr) == 2:
            # Merge a pair.
            if isinstance(eq.expr[0], ExprRange):
                if isinstance(eq.expr[1], ExprRange):
                    # Merge a pair of ExprRanges.
                    item = eq.expr[1]
                    other_lambda_map = Lambda(item.lambda_map.parameter,
                                              item.lambda_map.body)
                    if other_lambda_map != lambda_map:
                        raise ExprTupleError(
                            "Cannot merge together ExprRanges "
                            "with different lambda maps: %s vs %s" %
                            (lambda_map, other_lambda_map))
                    _i, _j = eq.expr[0].start_index, eq.expr[0].end_index
                    _k, _l = eq.expr[1].start_index, eq.expr[1].end_index
                    merger = \
                        merge.specialize({f:lambda_map, i:_i, j:_j, k:_k, l:_l},
                                         assumptions=assumptions)
                else:
                    # Merge an ExprRange and a singular item.
                    _i, _j = eq.expr[0].start_index, eq.expr[0].end_index
                    _k = lambda_map.extractArgument(eq.expr[1])
                    if _k == Add(_j, one):
                        merger = merge_extension.specialize(
                            {
                                f: lambda_map,
                                i: _i,
                                j: _j
                            },
                            assumptions=assumptions)
                    else:
                        merger = merge_back.specialize(
                            {
                                f: lambda_map,
                                i: _i,
                                j: _j,
                                k: _k
                            },
                            assumptions=assumptions)
            else:
                # Merge a singular item and ExprRange.
                iSub = lambda_map.extractArgument(eq.expr[0])
                jSub, kSub = eq.expr[1].start_index, eq.expr[1].end_index
                merger = \
                    merge_front.specialize({f:lambda_map, i:iSub, j:jSub,
                                            k:kSub}, assumptions=assumptions)
            eq.update(merger)
            return eq.relation

        while len(eq.expr) > 1:
            front_merger = ExprTuple(*eq.expr[:2]).merger(assumptions)
            eq.update(
                front_merger.substitution(eq.expr.innerExpr(assumptions)[:2],
                                          assumptions=assumptions))
        return eq.relation
示例#20
0
    def shallow_simplification(self, *, must_evaluate=False,
                               **defaults_config):
        '''
        Returns a proven simplification equation for this Exp
        expression assuming the operands have been simplified.

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

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

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

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

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

        return eq.relation
示例#21
0
 def deduce_bound(self,
                  inner_expr_bound_or_bounds,
                  inner_exprs_to_bound=None,
                  **defaults_config):
     '''
     Return a bound of this arithmetic expression based upon
     the bounds of any number of inner expressions.  The inner 
     expression should appear on the left side of the corresponding
     bound which should be a number ordering relation (< or <=).
     The returned, proven bound will have this expression on the 
     left-hand side.  The bounds of the inner expressions will be
     processed in the order they are provided.
     
     If inner_exprs_to_bound is provided, restrict the bounding
     to these particular InnerExpr objects.  Otherwise, all inner
     expressions are fair game.
     '''
     if isinstance(inner_expr_bound_or_bounds, Judgment):
         inner_expr_bound_or_bounds = inner_expr_bound_or_bounds.expr
     if isinstance(inner_expr_bound_or_bounds, ExprTuple):
         inner_expr_bounds = inner_expr_bound_or_bounds.entries
     elif isinstance(inner_expr_bound_or_bounds, Expression):
         inner_expr_bounds = [inner_expr_bound_or_bounds]
     else:
         inner_expr_bounds = inner_expr_bound_or_bounds
     inner_expr_bounds = deque(inner_expr_bounds)
     inner_relations = dict()
     if len(inner_expr_bounds) == 0:
         raise ValueError("Expecting one or more 'inner_expr_bounds'")
     while len(inner_expr_bounds) > 0:
         inner_expr_bound = inner_expr_bounds.popleft()
         print('inner_expr_bound', inner_expr_bound)
         if isinstance(inner_expr_bound, TransRelUpdater):
             # May be one of the internally generated
             # TransRelUpdater for percolating bounds up through
             # the expression hierarchy to the root.
             inner_expr_bound = inner_expr_bound.relation
         elif isinstance(inner_expr_bound, Judgment):
             inner_expr_bound = inner_expr_bound.expr
         inner = inner_expr_bound.lhs
         if inner == self:
             raise ValueError(
                 "Why supply a bound for the full expression when "
                 "calling 'deduce_bound'? There is nothing to deduce.")
         no_such_inner_expr = True
         no_such_number_op_inner_expr = True
         # Apply bound to each inner expression as applicable.
         if inner_exprs_to_bound is None:
             inner_exprs = generate_inner_expressions(self, inner)
         else:
             inner_exprs = inner_exprs_to_bound
         for inner_expr in inner_exprs:
             no_such_inner_expr = False
             inner_expr_depth = len(inner_expr.expr_hierarchy)
             assert inner_expr_depth > 1, (
                 "We already checked that the inner expression was not "
                 "equal to the full expression. What's the deal?")
             # Create/update the relation for the container of this
             # inner expression.
             if inner_expr_depth >= 3:
                 container = inner_expr.expr_hierarchy[-2]
                 if isinstance(container, ExprTuple):
                     # Skip an ExprTuple layer.
                     if inner_expr_depth >= 4:
                         container = inner_expr.expr_hierarchy[-3]
                     else:
                         container = self
             else:
                 container = self
             if not isinstance(container, NumberOperation):
                 # Skip over any 'container' that is not a
                 # NumberOperation.
                 continue
             no_such_number_op_inner_expr = False
             container_relation = inner_relations.setdefault(
                 container, TransRelUpdater(container))
             expr = container_relation.expr
             # Don't simplify or make replacements if there
             # is more to go:
             preserve_all = (len(inner_expr_bounds) > 0)
             container_relation.update(
                 expr.bound_via_operand_bound(inner_expr_bound,
                                              preserve_all=preserve_all))
             # Append the relation for processing
             if container is self:
                 # No further processing needed when the container
                 continue  # is self.
             if (len(inner_expr_bounds) == 0
                     or inner_expr_bounds[-1] != container_relation):
                 inner_expr_bounds.append(container_relation)
         if no_such_inner_expr:
             raise ValueError(
                 "The left side of %s does not appear within %s" %
                 (inner_expr_bound, self))
         if no_such_number_op_inner_expr:
             raise ValueError(
                 "The left side of %s is not contained within a "
                 "NumberOperation expression" % (inner_expr_bound, self))
     assert self in inner_relations, (
         "If there are more than one inner bounds and they are "
         "valid, they should have percolated to the top")
     return inner_relations[self].relation
示例#22
0
def unbundle(expr,
             unbundle_thm,
             num_param_entries=(1, ),
             *,
             assumptions=USE_DEFAULTS):
    '''
    Given a nested OperationOverInstances, derive or equate an
    equivalent form in which the parameter entries are split in
    number according to 'num_param_entries'.  Use the given theorem
    specific to the particular OperationOverInstances.

    For example,
        \forall_{x, y, z | Q(x, y), R(z)} P(x, y, z)
    can become
        \forall_{x, y | Q(x, y)} \forall_{z | R(z)} P(x, y, z)
    via bundle with num_param_entries=(2, 1) or
    num_param_entries=(2,) -- the last number can be implied
    by the remaining number of parameters.

    For example of the form of the theorem required, see
    proveit.logic.boolean.quantification.unbundling or
    proveit.logic.boolean.quantification.bundling_equality.
    '''
    from proveit.relation import TransRelUpdater
    from proveit.logic import Implies, Equals, And
    # Make a TransRelUpdater only if the bundle_thm yield an
    # equation, in which case we'll want the result to be an equation.
    eq = None
    unbundled = expr
    net_indicated_param_entries = sum(num_param_entries)
    num_actual_param_entries = len(expr.instanceParams)
    for n in num_param_entries:
        if not isinstance(n, int) or n <= 0:
            raise ValueError(
                "Each of 'num_param_entries', must be an "
                "integer greater than 0.  %s fails this requirement." %
                (num_param_entries))
    if net_indicated_param_entries > num_actual_param_entries:
        raise ValueError(
            "Sum of 'num_param_entries', %s=%d should not "
            "be greater than the number of parameter entries "
            "of %s for unbundling." %
            (num_param_entries, net_indicated_param_entries, expr))
    if net_indicated_param_entries < num_actual_param_entries:
        diff = num_actual_param_entries - net_indicated_param_entries
        num_param_entries = list(num_param_entries) + [diff]
    else:
        num_param_entries = list(num_param_entries)
    while len(num_param_entries) > 1:
        n_last_entries = num_param_entries.pop(-1)
        first_params = ExprTuple(*unbundled.instanceParams[:-n_last_entries])
        first_param_vars = {getParamVar(param) for param in first_params}
        remaining_params = \
            ExprTuple(*unbundled.instanceParams[-n_last_entries:])
        _m = first_params.length()
        _n = remaining_params.length()
        _P = unbundled.instanceExpr
        # Split up the conditions between the outer
        # OperationOverInstances and inner OperationOverInstances
        condition = unbundled.effectiveCondition()
        if isinstance(condition, And):
            _nQ = 0
            for cond in condition.operands:
                cond_vars = free_vars(cond, err_inclusively=True)
                if first_param_vars.isdisjoint(cond_vars): break
                _nQ += 1
            if _nQ == 0:
                _Q = And()
            elif _nQ == 1:
                _Q = condition.operands[0]
            else:
                _Q = And(*condition.operands[:_nQ])
            _nR = len(condition.operands) - _nQ
            if _nR == 0:
                _R = And()
            elif _nR == 1:
                _R = condition.operands[-1]
            else:
                _R = And(*condition.operands[_nQ:])
        elif first_param_vars.isdisjoint(
                free_vars(condition, err_inclusively=True)):
            _Q = condition
            _R = And()
        else:
            _Q = And()
            _R = condition
        m, n = unbundle_thm.instanceVars
        P, Q, R = unbundle_thm.instanceExpr.instanceVars
        correspondence = unbundle_thm.instanceExpr.instanceExpr
        if isinstance(correspondence, Implies):
            if (not isinstance(correspondence.antecedent,
                               OperationOverInstances)
                    or not len(correspondence.antecedent.instanceParams) == 2):
                raise ValueError("'unbundle_thm', %s, does not have the "
                                 "expected form with the bundled form as "
                                 "the antecedent of the implication, %s" %
                                 (unbundle_thm, correspondence))
            x_1_to_m, y_1_to_n = correspondence.antecedent.instanceParams
        elif isinstance(correspondence, Equals):
            if not isinstance(
                    correspondence.rhs, OperationOverInstances
                    or not len(correspondence.antecedent.instanceParams) == 2):
                raise ValueError("'unbundle_thm', %s, does not have the "
                                 "expected form with the bundled form on "
                                 "right of the an equality, %s" %
                                 (unbundle_thm, correspondence))
            x_1_to_m, y_1_to_n = correspondence.rhs.instanceParams
        else:
            raise ValueError("'unbundle_thm', %s, does not have the expected "
                             "form with an equality or implication  "
                             "correspondence, %s" %
                             (unbundle_thm, correspondence))

        Qx = Function(Q, first_params)
        Rxy = Function(R, unbundled.instanceParams)
        Pxy = Function(P, unbundled.instanceParams)
        x_1_to_m = x_1_to_m.replaced({m: _m})
        y_1_to_n = y_1_to_n.replaced({n: _n})
        instantiation = unbundle_thm.instantiate(
            {
                m: _m,
                n: _n,
                ExprTuple(x_1_to_m): first_params,
                ExprTuple(y_1_to_n): remaining_params,
                Pxy: _P,
                Qx: _Q,
                Rxy: _R
            },
            assumptions=assumptions)
        if isinstance(instantiation.expr, Implies):
            unbundled = instantiation.deriveConsequent()
        elif isinstance(instantiation.expr, Equals):
            if eq is None:
                eq = TransRelUpdater(unbundled)
            try:
                unbundled = eq.update(instantiation)
            except ValueError:
                raise ValueError(
                    "Instantiation of bundle_thm %s is %s but "
                    "should match %s on one side of the equation." %
                    (unbundle_thm, instantiation, unbundled))
        else:
            raise ValueError("Instantiation of bundle_thm %s is %s but "
                             "should be an Implies or Equals expression." %
                             (unbundle_thm, instantiation))
    if eq is None:
        # Return the unbundled result.
        return unbundled
    else:
        # Return the equality between the original expression and
        # the unbundled result.
        return eq.relation
示例#23
0
    def shallow_simplification(self,
                               *,
                               must_evaluate=False,
                               **defaults_config):
        '''
        Returns a proven simplification equation for this Qmult
        expression assuming the operands have been simplified.
        
        Currently deals only with:
        (1) simplify unary case
        (2) Ungrouping nested tensor products.
        (3) Factoring out scalars.
        '''
        from proveit.linear_algebra import ScalarMult
        from proveit.physics.quantum import (Bra, Ket, HilbertSpaces, varphi,
                                             var_ket_psi)
        from proveit.physics.quantum.algebra import Hspace
        yield_known_hilbert_spaces = HilbertSpaces.yield_known_hilbert_spaces

        if self.operands.is_single():
            # Handle unary cases
            from . import (qmult_of_ket, qmult_of_bra, qmult_of_complex,
                           qmult_of_linmap)
            operand = self.operands[0]
            if InSet(operand, Complex).proven():
                # Qmult of a complex number is just the complex number
                return qmult_of_complex.instantiate({c: operand})
            elif isinstance(operand, Bra):
                # Qmult of a bra is the bra (equal to the
                # linear map it represents).
                for _Hspace in yield_known_hilbert_spaces(Ket(
                        operand.operand)):
                    return qmult_of_bra.instantiate({
                        Hspace: _Hspace,
                        varphi: operand.operand
                    })
            elif operand in MatrixSpace.known_memberships:
                # Qmult of a matrix.  It equates to a lambda
                # map, but this isn't considered a simplification.
                # Use linmap_reduction instead if this is desired.
                return Equals(self, self).conclude_via_reflexivity()
            else:
                for _Hspace in yield_known_hilbert_spaces(operand):
                    # Qmult of a ket is just the ket
                    return qmult_of_ket.instantiate({
                        Hspace: _Hspace,
                        var_ket_psi: operand
                    })
                for linmap in containing_hilbert_space_linmap_sets(operand):
                    # Qmult of a linear map is just the linear map
                    _Hspace, _X = linmap.from_vspace, linmap.to_vspace
                    return qmult_of_linmap.instantiate({
                        Hspace: _Hspace,
                        X: _X,
                        A: operand
                    })
                return Equals(self, self).conclude_via_reflexivity()

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

        if Qmult._simplification_directives_.ungroup:
            # ungroup the expression (disassociate nested additions).
            _n = 0
            length = expr.operands.num_entries() - 1
            # loop through all operands
            while _n < length:
                operand = expr.operands[_n]
                # print("n, length", n, length)
                if isinstance(operand, Qmult):
                    # if it is grouped, ungroup it
                    expr = eq.update(expr.disassociation(_n,
                                                         preserve_all=True))
                elif isinstance(operand, ScalarMult):
                    # ungroup contained ScalarMult's
                    expr = eq.update(
                        expr.scalar_mult_absorption(_n, preserve_all=True))
                length = expr.operands.num_entries()
                _n += 1

        if Qmult._simplification_directives_.factor_scalars:
            # Next, pull out scalar factors
            expr = eq.update(expr.factorization_of_scalars())

        if Qmult._simplification_directives_.use_scalar_mult:
            # Finally, use ScalarMult for any scalar operands.
            if (isinstance(expr, Qmult) and expr.operands.num_entries() > 1
                    and InSet(expr.operands[0], Complex).proven()):
                expr = eq.update(expr.scalar_mult_factorization())

        return eq.relation