Esempio n. 1
0
 def shallow_simplification(self, *, must_evaluate=False,
                            **defaults_config):
     '''
     Returns a proven simplification equation for this Mod
     expression assuming the operands have been simplified.
     
     Specifically, performs reductions of the form
     (x mod L) = x where applicable and
     [(a mod L + b) mod L] = [(a + b) mod L].
     '''
     from . import (int_mod_elimination, real_mod_elimination,
                    redundant_mod_elimination, 
                    redundant_mod_elimination_in_sum)
     from proveit.numbers import (
         NaturalPos, RealPos, Interval, IntervalCO, subtract, zero, one)
     deduce_number_set(self.dividend)
     divisor_ns = deduce_number_set(self.divisor).domain
     if (NaturalPos.includes(divisor_ns) and 
             InSet(self.dividend, 
                   Interval(zero, 
                            subtract(self.divisor, one))).proven()):
         # (x mod L) = x if L in N+ and x in {0 .. L-1}
         return int_mod_elimination.instantiate(
             {x:self.dividend, L:self.divisor})
     if (RealPos.includes(divisor_ns) and 
             InSet(self.dividend, 
                   IntervalCO(zero, self.divisor)).proven()):
         # (x mod L) = x if L in R+ and x in [0, L)
         return real_mod_elimination.instantiate(
             {x:self.dividend, L:self.divisor})
     return Mod._redundant_mod_elimination(
             self, redundant_mod_elimination, 
             redundant_mod_elimination_in_sum)
Esempio n. 2
0
    def range_expansion(self, **defaults_config):
        '''
        For self an ExprTuple with a single entry that is an ExprRange
        of the form f(i),...,f(j), where 0 <= (j-i) <= 9 (i.e. the
        ExprRange represents 1 to 10 elements), derive and
        return an equality between self and an ExprTuple with explicit
        entries replacing the ExprRange. For example, if
            self = ExprTuple(f(3),...,f(6)),
        then self.range_expansion() would return:
        |- ExprTuple(f(3),...,f(6)) = ExprTuple(f(3), f(4), f(5), f(6))
        '''

        # Check that we have a an ExprTuple with
        # (1) a single entry
        # (2) and the single entry is an ExprRange
        # (these restrictions can be relaxed later to make the
        # method more general)

        # ExprTuple with single entry
        if not self.num_entries() == 1:
            raise ValueError(
                    "ExprTuple.range_expansion() implemented only for "
                    "ExprTuples with a single entry (and the single "
                    "entry must be an ExprRange). Instead, the ExprTuple "
                    "{0} has {1} entries.".format(self, self.num_entries))

        # and the single entry is an ExprRange:
        from proveit import ExprRange
        if not isinstance(self.entries[0], ExprRange):
            raise ValueError(
                    "ExprTuple.range_expansion() implemented only for "
                    "ExprTuples with a single entry (and the single "
                    "entry must be an ExprRange). Instead, the ExprTuple "
                    "is {0}.".format(self))

        from proveit import Function
        from proveit.logic import EvaluationError
        from proveit.numbers import subtract

        _the_expr_range = self[0]

        # _n = self.num_elements()
        try:
            _n = subtract(self[0].true_end_index, self[0].true_start_index).evaluated()
        except EvaluationError as the_error:
            _diff = subtract(self[0].true_end_index, self[0].true_start_index)
            print("EvaluationError: {0}. The ExprRange {1} must represent "
                  "a known, finite number of elements, but all we know is "
                  "that it represents {2} elements.".format(
                    the_error, self[0], _diff))
            raise EvaluationError(
                subtract(self[0].true_end_index, self[0].true_start_index))
        
        _n = _n.as_int() + 1 # actual number of elems being represented
        if not (1 <= _n and _n <= 9):
            raise ValueError(
                    "ExprTuple.range_expansion() implemented only for "
                    "ExprTuples with a single entry, with the single "
                    "entry being an ExprRange representing a finite "
                    "number of elements n with 1 <= n <= 9. Instead, "
                    "the ExprTuple is {0} with number of elements equal "
                    "to {1}.".format(self[0], _n))

        # id the correct theorem for the number of entries
        import proveit.numbers.numerals.decimals
        expansion_thm = proveit.numbers.numerals.decimals\
                        .__getattr__('range_%d_expansion' % _n)

        # instantiate and return the identified expansion theorem
        _f, _i, _j = expansion_thm.instance_vars
        _safe_var = self.safe_dummy_var()
        _idx_param = _the_expr_range.parameter
        _fxn_sub = _the_expr_range.body.basic_replaced(
                {_idx_param: _safe_var})
        _i_sub = _the_expr_range.true_start_index
        _j_sub = _the_expr_range.true_end_index
        return expansion_thm.instantiate(
                {Function(_f, _safe_var): _fxn_sub, _i: _i_sub, _j: _j_sub})
Esempio n. 3
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
Esempio n. 4
0
    def conclude(self, **defaults_config):
        '''
        Prove that the 'element' is in the QmultCodomain
        (e.g., that a Qmult is well-formed).
        '''
        from proveit import ProofFailure
        from proveit.numbers import deduce_in_number_set
        from proveit.physics.quantum import (Bra, Ket, varphi, var_ket_psi,
                                             HilbertSpaces)
        from proveit.physics.quantum.algebra import (Hspace, QmultCodomain)
        from . import (
            qmult_complex_in_QmultCodomain, qmult_complex_left_closure,
            qmult_complex_right_closure, qmult_complex_ket_closure,
            qmult_ket_complex_closure, qmult_complex_op_closure,
            qmult_op_complex_closure, complex_in_QmultCodomain,
            qmult_nested_closure, qmult_ket_is_ket, qmult_ket_in_QmultCodomain,
            qmult_op_ket_is_ket, qmult_op_ket_in_QmultCodomain,
            qmult_ket_bra_is_op, qmult_ket_bra_in_QmultCodomain,
            qmult_op_op_is_op, qmult_op_op_in_QmultCodomain,
            qmult_matrix_is_linmap, qmult_matrix_in_QmultCodomain,
            qmult_bra_is_linmap, qmult_bra_in_QmultCodomain,
            qmult_op_is_linmap, qmult_op_in_QmultCodomain,
            ket_in_QmultCodomain, bra_is_linmap, bra_in_QmultCodomain,
            op_in_QmultCodomain, multi_qmult_def)
        element = self.element
        yield_known_hilbert_spaces = HilbertSpaces.yield_known_hilbert_spaces

        if isinstance(element, Qmult):
            if element.operands.num_entries() == 0:
                raise ValueError("Qmult with no operands is not defined")

            elif element.operands.is_single():
                # Unary Qmult closure
                op = element.operand

                # Handle unary Qmult on a bra.
                if isinstance(op, Bra):
                    thm = None
                    _varphi = op.operand
                    #Ket(_varphi).deduce_in_vec_space(field=Complex)
                    for _Hspace in yield_known_hilbert_spaces(Ket(_varphi)):
                        # Prove membership in the target space
                        # while we are at it.
                        qmult_bra_is_linmap.instantiate(
                            {
                                Hspace: _Hspace,
                                varphi: _varphi
                            },
                            preserve_all=True)
                        used_Hspace = _Hspace
                        thm = qmult_bra_in_QmultCodomain
                    if thm is not None:
                        # Just choose any valid Hilbert space (the last
                        # used one will do) for the QmultCodomain
                        # membership proof.
                        _Hspace = used_Hspace
                        return thm.instantiate({
                            Hspace: _Hspace,
                            varphi: _varphi
                        })
                    raise NotImplementedError(
                        "Bra, %s, with no known Hilbert space "
                        "membership for the corresponding Ket. "
                        "Cannot prove %s membership in %s" %
                        (op, element, QmultCodomain))

                # Handle unary nested Qmult
                if isinstance(op, Qmult):
                    # Prove memberships of the nested unary Qmult
                    # to fascilitate the cases below.
                    QmultCodomain.membership_object(op).conclude()
                    for _Hspace in yield_known_hilbert_spaces(op):
                        # Propagate Hspace membership
                        qmult_ket_is_ket.instantiate(
                            {
                                var_ket_psi: op,
                                Hspace: _Hspace
                            },
                            preserve_all=True)
                    for linmap in containing_hilbert_space_linmap_sets(op):
                        # Propagate LinMap membership
                        _Hspace = linmap.from_vspace
                        _X = linmap.to_vspace
                        qmult_op_is_linmap.instantiate(
                            {
                                Hspace: _Hspace,
                                X: _X,
                                A: op
                            }, preserve_all=True)
                    return qmult_nested_closure.instantiate({A: op})

                # Handle unary complex (soft, first attempt).
                if InSet(op, Complex).proven():
                    return qmult_complex_in_QmultCodomain.instantiate({c: op})

                for _attempt in (0, 1):
                    # Handle unary Qmult on a matrix.
                    if op in MatrixSpace.known_memberships:
                        mspace_memberships = MatrixSpace.known_memberships[op]
                        thm = None
                        matrix_dimensions = set()
                        for mspace_membership in mspace_memberships:
                            mspace = mspace_membership.domain
                            _m, _n = (mspace.operands['rows'],
                                      mspace.operands['columns'])
                            matrix_dimensions.add((_m, _n))
                        for _m, _n in matrix_dimensions:
                            # Prove linear map membership while we are
                            # at it.
                            qmult_matrix_is_linmap.instantiate(
                                {
                                    m: _m,
                                    n: _n,
                                    M: op
                                }, preserve_all=True)
                            used_mspace = mspace
                            thm = qmult_matrix_in_QmultCodomain
                        if thm is not None:
                            # Choose any valid matrix space (the last
                            # used ones will do) for the QmultCodomain
                            # membership proof.
                            _m, _n = (used_mspace.operands['rows'],
                                      used_mspace.operands['columns'])
                            return thm.instantiate({m: _m, n: _n, M: op})

                    # Handle unary Qmult on a ket.
                    for _Hspace in yield_known_hilbert_spaces(op):
                        return qmult_ket_in_QmultCodomain.instantiate({
                            Hspace:
                            _Hspace,
                            var_ket_psi:
                            op
                        })

                    # Handle unary Qmult on a linear map.
                    thm = None
                    for linmap in containing_hilbert_space_linmap_sets(op):
                        _Hspace, _X = linmap.operands
                        # Prove membership in the target space
                        # while we are at it.
                        qmult_op_is_linmap.instantiate(
                            {
                                Hspace: _Hspace,
                                X: _X,
                                A: op
                            }, preserve_all=True)
                        used_linmap = linmap
                        thm = qmult_op_in_QmultCodomain
                    if thm is not None:
                        # Just choose any valid linmap (the last used
                        # one will do) for the QmultCodomain membership
                        # proof.
                        _Hspace, _X = used_linmap.operands
                        return thm.instantiate({Hspace: _Hspace, X: _X, A: op})

                    if _attempt == 0:
                        # If all else fails, try to deduce that the
                        # operand is in a vector space.  This is useful
                        # for operators as well as kets since matrix
                        # spaces and linear map sets are vector spaces
                        # (though not inner product spaces, so they
                        # aren't confused with kets fortunately).
                        if hasattr(op, 'deduce_in_vec_space'):
                            op.deduce_in_vec_space(field=Complex)
                        else:
                            break  # No need for a second attempt.

                # Second attempt to prove the element is complex.
                try:
                    deduce_in_number_set(op, Complex)
                    # Complex elements are in QmultCodomain
                    return qmult_complex_in_QmultCodomain.instantiate({c: op})
                except (ProofFailure, NotImplementedError):
                    pass

            elif element.operands.is_double():
                # Binary Qmult closure
                op1, op2 = element.operands

                # Prove memberships of unary Qmult on each of the
                # operands to fascilitate the various cases below.
                QmultCodomain.membership_object(Qmult(op1)).conclude()
                QmultCodomain.membership_object(Qmult(op2)).conclude()

                # Handle the case where one of the operands is complex.
                thm = None
                if InSet(op1, Complex).proven():
                    _c, _A = op1, op2
                    thm = qmult_complex_left_closure
                    ket_closure_thm = qmult_complex_ket_closure
                    op_closure_thm = qmult_complex_op_closure
                elif InSet(op2, Complex).proven():
                    _A, _c = op1, op2
                    thm = qmult_complex_right_closure
                    ket_closure_thm = qmult_ket_complex_closure
                    op_closure_thm = qmult_op_complex_closure
                if thm is not None:
                    for _Hspace in yield_known_hilbert_spaces(_A):
                        # If the other op is in any vector space over
                        # Complex numbers (Hilbert spaces) also
                        # prove closure within the Hilbert space.
                        ket_closure_thm.instantiate(
                            {
                                c: _c,
                                Hspace: _Hspace,
                                var_ket_psi: _A
                            },
                            preserve_all=True)
                    for linmap in containing_hilbert_space_linmap_sets(_A):
                        # If the other op is in any Hilbert space linear
                        # mappings, also prove closure within the
                        # specific linear mapping set.
                        _Hspace, _X = linmap.operands
                        op_closure_thm.instantiate(
                            {
                                c: _c,
                                Hspace: _Hspace,
                                X: _X,
                                A: _A
                            },
                            preserve_all=True)
                    return thm.instantiate({c: _c, A: _A})

                # Next, handle the ket-bra case.
                if isinstance(op2, Bra):
                    thm = None
                    _varphi = op2.operand
                    for _Hspace in yield_known_hilbert_spaces(op1):
                        for _X in yield_known_hilbert_spaces(Ket(_varphi)):
                            # Prove linear map membership while we are
                            # at it.
                            qmult_ket_bra_is_op.instantiate(
                                {
                                    Hspace: _Hspace,
                                    X: _X,
                                    var_ket_psi: op1,
                                    varphi: _varphi
                                },
                                preserve_all=True)
                            used_Hspaces = (_Hspace, _X)
                            thm = qmult_ket_bra_in_QmultCodomain
                        if thm is None:
                            raise NotImplementedError(
                                "Bra, %s, with no known Hilbert space "
                                "membership for the corresponding Ket. "
                                "Cannot prove %s membership in %s" %
                                (op2, element, QmultCodomain))
                    if thm is not None:
                        # Just choose any valid Hilbert spaces (the
                        # last used one will do) for the QmultCodomain
                        # membership proof.
                        _Hspace, _X = used_Hspaces
                        return thm.instantiate({
                            Hspace: _Hspace,
                            X: _X,
                            varphi: _varphi,
                            var_ket_psi: op1
                        })

                # If the first op is a bra, let's make sure we
                # know it is a linear map.
                if isinstance(op1, Bra):
                    # By proving the bra is in QmultCodomain, we
                    # get that it is a linear map as a side-effect.
                    QmultCodomain.membership_object(Qmult(op1)).conclude()

                # Next, handle the op-ket case.
                thm = None
                for _Hspace in yield_known_hilbert_spaces(op2):
                    for linmap in containing_hilbert_space_linmap_sets(op1):
                        if linmap.from_vspace == _Hspace:
                            _X = linmap.to_vspace
                            # Prove membership in the target space
                            # while we are at it.
                            qmult_op_ket_is_ket.instantiate(
                                {
                                    Hspace: _Hspace,
                                    X: _X,
                                    A: op1,
                                    var_ket_psi: op2
                                },
                                preserve_all=True)
                            used_linmap = linmap
                            thm = qmult_op_ket_in_QmultCodomain
                if thm is not None:
                    # Just choose any valid linmap (the last used one
                    # will do) for the QmultCodomain membership proof.
                    _Hspace, _X = used_linmap.operands
                    return thm.instantiate({
                        Hspace: _Hspace,
                        X: _X,
                        A: op1,
                        var_ket_psi: op2
                    })

                # Finally handle the op-op case.
                thm = None
                for linmap1 in containing_hilbert_space_linmap_sets(op1):
                    for linmap2 in containing_hilbert_space_linmap_sets(op2):
                        if linmap1.from_vspace == linmap2.to_vspace:
                            _Hspace = linmap2.from_vspace
                            _X = linmap1.from_vspace
                            _Y = linmap1.to_vspace
                            # Prove linear map membership while we are
                            # at it.
                            qmult_op_op_is_op.instantiate(
                                {
                                    Hspace: _Hspace,
                                    X: _X,
                                    Y: _Y,
                                    A: op1,
                                    B: op2
                                },
                                preserve_all=True)
                            used_linmaps = (linmap1, linmap2)
                            thm = qmult_op_op_in_QmultCodomain
                if thm is not None:
                    # Just choose any valid linmaps (the last used ones
                    # will do) for the QmultCodomain membership proof.
                    linmap1, linmap2 = used_linmaps
                    _Hspace = linmap2.from_vspace
                    _X = linmap1.from_vspace
                    _Y = linmap1.to_vspace
                    return thm.instantiate({
                        Hspace: _Hspace,
                        X: _X,
                        Y: _Y,
                        A: op1,
                        B: op2
                    })

                raise UnsatisfiedPrerequisites(
                    "Binary Qmult operand membership is not known "
                    "to produce a well-formed Qmult: %s" % element)

            else:
                # n-ary Qmult closure
                # Decompose an n-ary operation to a binary operation.
                if not isinstance(element.operands[-1], ExprRange):
                    _A = element.operands[:-1]
                    _B = element.operands[-1]
                    _m = _A.num_elements()
                else:
                    # There is an ExprRange at the end.  Split off
                    # the last entry and try again.
                    if element.operands[-1:].num_elements() == 0:
                        raise NotImplementedError(
                            "Empty ExprRange at the end of the Qmult "
                            "is not handled; why not simplify the Qmult "
                            "before proving it is well-formed")
                    expr_range = element.operands[-1]
                    partition_idx = subtract(expr_range.true_end_index, one)
                    partition = element.inner_expr().operands[-1].partition(
                        partition_idx)
                    membership = InClass(partition.rhs, QmultCodomain)
                    if not membership.proven():
                        QmultCodomain.membership_object(
                            partition.rhs).conclude()
                    membership = membership.prove()
                    return partition.sub_left_side_into(membership)
                multi_def = multi_qmult_def.instantiate({
                    m: _m,
                    A: _A,
                    B: _B
                },
                                                        preserve_all=True)
                # Prove the binary case then substitute.
                binary_membership = InClass(multi_def.rhs, QmultCodomain)
                if not binary_membership.proven():
                    QmultCodomain.membership_object(multi_def.rhs).conclude()
                binary_membership = binary_membership.prove()
                # We need to propogate the side-effect memberships.
                _rhs = multi_def.rhs
                for _Hspace in yield_known_hilbert_spaces(_rhs):
                    multi_def.sub_left_side_into(InSet(_rhs, _Hspace))
                for linmap in containing_hilbert_space_linmap_sets(_rhs):
                    multi_def.sub_left_side_into(InSet(_rhs, linmap))
                return multi_def.sub_left_side_into(binary_membership)
        else:
            # The element is not a Qmult.

            # Handle bras
            if isinstance(element, Bra):
                thm = None
                _varphi = element.operand
                #Ket(_varphi).deduce_in_vec_space(field=Complex)
                for _Hspace in yield_known_hilbert_spaces(Ket(_varphi)):
                    # Prove membership in the target space
                    # while we are at it.
                    bra_is_linmap.instantiate(
                        {
                            Hspace: _Hspace,
                            varphi: _varphi
                        }, preserve_all=True)
                    used_Hspace = _Hspace
                    thm = bra_in_QmultCodomain
                if thm is not None:
                    # Just choose any valid Hilbert space (the last
                    # used one will do) for the QmultCodomain
                    # membership proof.
                    _Hspace = used_Hspace
                    return thm.instantiate({Hspace: _Hspace, varphi: _varphi})
                raise NotImplementedError(
                    "Bra, %s, with no known Hilbert space "
                    "membership for the corresponding Ket. "
                    "Cannot prove %s membership in %s" %
                    (element, self, QmultCodomain))

            # Handle complex numbers as as special case
            # (first, soft attempt).
            if InSet(element, Complex).proven():
                # Complex elements are in QmultCodomain
                return complex_in_QmultCodomain.instantiate({c: element})

            for _attempt in (0, 1):
                # Handle kets
                for _Hspace in HilbertSpaces.yield_known_hilbert_spaces(
                        element):
                    return ket_in_QmultCodomain.instantiate({
                        Hspace:
                        _Hspace,
                        var_ket_psi:
                        element
                    })

                # Handle linear maps
                for linmap in containing_hilbert_space_linmap_sets(element):
                    _Hspace, _X = linmap.operands
                    return op_in_QmultCodomain.instantiate({
                        Hspace: linmap.from_vspace,
                        X: _X,
                        A: element
                    })

                if _attempt == 0:
                    # If all else fails, try to deduce that the element
                    # is in a vector space.  This is useful for
                    # operators as well as kets since matrix spaces and
                    # linear map sets are  vector spaces (though not
                    # inner product spaces, so they aren't confused
                    # with kets fortunately).
                    if hasattr(element, 'deduce_in_vec_space'):
                        element.deduce_in_vec_space(field=Complex)
                    else:
                        break  # No need for a second attempt.

            # Second attempt to prove the element is complex.
            try:
                deduce_in_number_set(element, Complex)
                # Complex elements are in QmultCodomain
                return complex_in_QmultCodomain.instantiate({c: element})
            except (ProofFailure, NotImplementedError):
                pass

        raise UnsatisfiedPrerequisites(
            "%s is not known to be a complex number, vector in a "
            "vector space over complex numbers, or a linear "
            "mapping from one such vector space to another" % (element))
Esempio n. 5
0
    def joining(self, second_summation, **defaults_config):
        '''
        Join the "second summation" with "this" (self) summation,
        deducing and returning the equivalence of the sum of the self
        and second_summation with the joined summation.
        Both summations must be over integer Intervals.
        The relation between the first summation upper bound, UB1,
        and the second summation lower bound, LB2, must be *explicitly*
        either UB1 = LB2-1 or LB2=UB1+1 *or* easily-derivable
        mathematical equivalents of those equalities.
        Example usage: let S1 = Sum(i, i^2, Interval(1,10)) and
        S2 = Sum(i, i^2, Interval(1,10)). Then S1.join(S2) returns
        |- S1 + S2 = Sum(i, i^2, Interval(1,20))
        '''

        if (not isinstance(self.domain, Interval)
                or not isinstance(second_summation.domain, Interval)):
            raise NotImplementedError(
                "Sum.join() is only implemented for summations with a "
                "single index over an integer Interval. The sum {0} has "
                "indices {1} and domain {2}; the sum {3} has indices "
                "{4} and domain {5}.".format(self, self.indices, self.domain,
                                             second_summation,
                                             second_summation.indices,
                                             second_summation.domain))

        if self.summand != second_summation.summand:
            raise ValueError(
                "Sum joining using Sum.join() is only allowed when the "
                "summands are identical. The sum {0} has summand {1} "
                "while the sum {2} has summand {3}. If the summands are "
                "equal but do not appear identical, you will have to "
                "establish an appropriate substituion before calling the "
                "Sum.join() method.".format(self, self.summand,
                                            second_summation,
                                            second_summation.summand))

        from . import sum_split_after, sum_split_before
        from proveit import a

        _i = self.index
        _a1 = self.domain.lower_bound
        _b1 = self.domain.upper_bound
        _a2 = second_summation.domain.lower_bound
        _b2 = second_summation.domain.upper_bound
        f_op, f_op_sub = Function(f, self.index), self.summand

        # Create low-effort, simplified versions of transition index
        # values, if possible
        _b1_plus_1_simplified = Add(_b1, one).shallow_simplified()
        _a2_minus_1_simplified = subtract(_a2, one).shallow_simplified()

        # This breaks into four cases (despite the temptation to
        # combine some of the cases):
        if (_b1 == subtract(_a2, one)):
            # UB1 == LB2 - 1 (literally)
            sum_split = sum_split_before
            split_index = _a2
        elif (_a2 == Add(_b1, one)):
            # LB2 == UB1 + 1 (literally)
            sum_split = sum_split_after
            split_index = _b1
        elif (_b1 == _a2_minus_1_simplified):
            # UB1 == LB2 - 1 (after simplification)
            sum_split = sum_split_before
            split_index = _a2
        elif (_a2 == _b1_plus_1_simplified):
            # LB2 == UB1 + 1 (after simplification)
            sum_split = sum_split_after
            split_index = _b1
        else:
            raise UnsatisfiedPrerequisites(
                "Sum joining using Sum.join() only implemented for when "
                "there is an explicit (or easily verified) increment "
                "of one unit from the first summation's upper bound "
                "to the second summation's lower bound (or decrement "
                "of one unit from second summation's lower bound to "
                "first summation's upper bound). We have first "
                "summation upper bound of {0} with the second summation "
                "lower bound of {1}. If these appear to have the "
                "necessary relationship, you might need to prove this "
                "before calling the Sum.join() method.".format(_b1, _a2))

        # Preserve the original summations that will be on the
        # left side of the equation.
        preserved_exprs = set(defaults.preserved_exprs)
        preserved_exprs.add(self)
        preserved_exprs.add(second_summation)

        return sum_split.instantiate(
            {
                f_op: f_op_sub,
                a: _a1,
                b: split_index,
                c: _b2,
                x: _i
            },
            preserved_exprs=preserved_exprs).derive_reversed()