Ejemplo n.º 1
0
    def includes(self, other_set):
        '''
        Return True if this NumberSet includes the 'other_set' set.
        '''
        from proveit.numbers.number_operation import (
            sorted_number_sets, standard_number_sets, deduce_number_set)
        if other_set == self: return True
        if SubsetEq(other_set, self).proven():
            return True

        if other_set not in standard_number_sets:    
            # For example, 'other_set' could be an integer Interval
            # or real IntervalCC, IntervalOC, ...), so let's see if
            # we can prove that an arbitrary variable in the other_set
            # is also in self.
            _x = safe_dummy_var(self, other_set)
            assumptions = defaults.assumptions + (InSet(_x, other_set),)
            deduce_number_set(_x, assumptions=assumptions)
            if InSet(_x, self).proven(assumptions=assumptions):
                SubsetEq(other_set, self).conclude_as_folded()
                return True

        # Try one level of indirection via SubsetEq.
        for number_set in sorted_number_sets:
            if number_set in (other_set, self):
                continue
            if (SubsetEq(other_set, number_set).proven() and
                    SubsetEq(number_set, self).proven()):
                return True

        return False # Not known to include the 'other'
Ejemplo n.º 2
0
def multi_wire(size):
    '''
    Create an ExprRange of identity gates that may be represented
    as a multi-wire in a Qcircuit.
    '''
    from proveit import safe_dummy_var
    from proveit.physics.quantum import I
    from proveit.numbers import one
    return ExprRange(safe_dummy_var(), Gate(I), one, size)
Ejemplo n.º 3
0
def multi_elem_entries(element_from_part,
                       start_qubit_idx,
                       end_qubit_idx,
                       *part_start_and_ends,
                       check_part_index_span=True):
    '''
    Yield consecutive vertical entries for MultiQubitElem to 
    represent all parts of a multi-qubit operation in a quantum circuit
    involving all qubits from 'start_qubit_idx' to 'end_qubit_idx.
    There will be an entry for each "part" start/end pair of indices.
    In total, these must start from one, be consecutive, and cover the 
    range from the 'start_qubit_idx' to the 'end_qubit_idx.
    The element_from_part function must return an element corresponding
    to a given 'part'.
    '''
    targets = Interval(start_qubit_idx, end_qubit_idx)
    multi_qubit_gate_from_part = (
        lambda part: MultiQubitElem(element_from_part(part), targets))
    part = one
    if len(part_start_and_ends) == 0:
        raise ValueError("Must specify one or more 'part' start and end "
                         "indices, starting from one and covering the range "
                         "from %s to %s" % (start_qubit_idx, end_qubit_idx))
    for part_start, part_end in part_start_and_ends:
        #try:
        #    Equals(part, part_start).prove()
        #except ProofFailure:
        if part != part_start:
            raise ValueError("Part indices must be provably consecutive "
                             "starting from 1: %s ≠ %s" % (part_start, part))
        if part_start == part_end:  # just a single element
            yield multi_qubit_gate_from_part(part)
        else:
            param = safe_dummy_var()
            yield ExprRange(param, multi_qubit_gate_from_part(param),
                            part_start, part_end)
        part = Add(part_end, one).quick_simplified()
    if not check_part_index_span:
        lhs = Add(part_end, num(-1)).quick_simplified()
        rhs = Add(end_qubit_idx, Neg(start_qubit_idx)).quick_simplified()
        try:
            try:
                lhs = lhs.simplified()
            except:
                pass
            try:
                rhs = rhs.simplified()
            except:
                pass
            Equals(lhs, rhs).prove()
        except ProofFailure:
            raise ValueError("Part indices must span the range of the "
                             "multi qubit operation: %s ≠ %s" % (lhs, rhs))
Ejemplo n.º 4
0
    def conclude(self, assumptions=USE_DEFAULTS):
        from proveit import ProofFailure
        from proveit.logic import Equals, SetOfAll, SetEquiv

        # Equal sets include each other.
        if Equals(*self.operands.entries).proven(assumptions):
            return self.conclude_via_equality(assumptions)

        # Equivalent sets include each other.
        if SetEquiv(*self.operands.entries).proven(assumptions):
            return self.conclude_via_equivalence(assumptions)

        # Check for special case of set comprehension
        # [{x | Q*(x)}_{x \in S}] \subseteq S
        if isinstance(self.subset, SetOfAll):
            from proveit.logic.sets.comprehension import (
                comprehension_is_subset)
            set_of_all = self.subset
            if (len(set_of_all.all_instance_vars()) == 1 and
                set_of_all.instance_element == set_of_all.all_instance_vars()[0] and
                    set_of_all.domain == self.superset):
                Q_op, Q_op_sub = (
                    Operation(Q, set_of_all.all_instance_vars()),
                    set_of_all.conditions)
                concluded = comprehension_is_subset.instantiate(
                    {S: set_of_all.domain, Q_op: Q_op_sub},
                    relabel_map={x: set_of_all.all_instance_vars()[0]},
                    assumptions=assumptions)
                return concluded.with_matching_style(self)

        _A, _B = self.operands.entries
        if hasattr(_B, 'deduce_subset_eq_relation'):
            try:
                return _B.deduce_subset_eq_relation(_A, assumptions)
            except NotImplementedError:
                pass

        try:
            # Attempt to conclude A subseteq B via
            # forall_{x in A} x in B.
            return self.conclude_as_folded(
                elem_instance_var=safe_dummy_var(self), assumptions=assumptions)
        except ProofFailure as e:
            raise ProofFailure(self, assumptions,
                           "Failed to conclude as folded: %s.\n"
                           "To try to prove %s via transitive "
                           "relations, try 'conclude_via_transitivity'."
                           %(str(self), str(e)))
Ejemplo n.º 5
0
    def _computation(self, assumptions=USE_DEFAULTS, must_evaluate=False):
        # Currently not doing anything with must_evaluate
        # What it should do is make sure it evaluates to a number
        # and can circumvent any attempt that will not evaluate to
        # number.
        from proveit.numbers import one
        if not isinstance(self.operand, ExprTuple):
            # Don't know how to compute the length if the operand is
            # not a tuple. For example, it could be a variable that
            # represent a tuple.  So just return the self equality.
            from proveit.logic import Equals
            return Equals(self, self).prove()
        entries = self.operand.entries
        has_range = any(isinstance(entry, ExprRange) for entry in entries)
        if (len(entries) == 1 and has_range
                and not isinstance(entries[0].body, ExprRange)):
            # Compute the length of a single range.  Examples:
            # |(f(1), ..., f(n))| = n
            # |(f(i), ..., f(j))| = j-i+1
            range_entry = entries[0]
            start_index = range_entry.start_index
            end_index = range_entry.end_index
            lambda_map = range_entry.lambda_map
            if start_index == one:
                from proveit.core_expr_types.tuples import \
                    range_from1_len
                return range_from1_len.instantiate(
                    {
                        f: lambda_map,
                        i: end_index
                    }, assumptions=assumptions)
            else:
                from proveit.core_expr_types.tuples import range_len
                return range_len.instantiate(
                    {
                        f: lambda_map,
                        i: start_index,
                        j: end_index
                    },
                    assumptions=assumptions)
        elif not has_range:
            # Case of all non-range entries.
            if len(entries) == 0:
                # zero length.
                from proveit.core_expr_types.tuples import tuple_len_0
                return tuple_len_0
            elif len(entries) < 10:
                # Automatically get the count and equality with
                # the length of the proper iteration starting from
                # 1.  For example,
                # |(a, b, c)| = 3
                # |(a, b, c)| = |(1, .., 3)|
                import proveit.numbers.numerals.decimals
                _n = len(entries)
                len_thm = proveit.numbers.numerals.decimals\
                    .__getattr__('tuple_len_%d' % _n)
                repl_map = dict()
                for param, entry in zip(len_thm.explicit_instance_params(),
                                        entries):
                    repl_map[param] = entry
                return len_thm.instantiate(repl_map)
            else:
                # raise NotImplementedError("Can't handle length computation "
                #                        ">= 10 for %s"%self)
                from proveit.core_expr_types.tuples import tuple_len_incr
                from proveit.numbers import num
                from proveit.logic import Equals

                eq = tuple_len_incr.instantiate(
                    {
                        i: num(len(entries) - 1),
                        a: entries[:-1],
                        b: entries[-1]
                    },
                    assumptions=assumptions)

                rhs_simp = eq.rhs._integerBinaryEval(assumptions=assumptions)

                return rhs_simp.sub_right_side_into(eq,
                                                    assumptions=assumptions)
                # return Equals(eq.lhs, eq.rhs._integerBinaryEval(assumptions=assumptions).rhs).prove(assumptions=assumptions)
                # raise NotImplementedError("Can't handle length computation "
                #                         ">= 10 for %s"%self)
        elif (len(entries) == 2 and not isinstance(entries[1], ExprRange)
              and not isinstance(entries[0].body, ExprRange)):
            # Case of an extended range:
            # |(a_1, ..., a_n, b| = n+1
            from proveit.core_expr_types.tuples import \
                extended_range_len, extended_range_from1_len
            assert isinstance(entries[0], ExprRange)
            range_lambda = entries[0].lambda_map
            range_start = entries[0].start_index
            range_end = entries[0].end_index
            if range_start == one:
                return extended_range_from1_len.instantiate(
                    {
                        f: range_lambda,
                        b: entries[1],
                        i: range_end
                    },
                    assumptions=assumptions)
            else:
                return extended_range_len.instantiate(
                    {
                        f: range_lambda,
                        b: entries[1],
                        i: range_start,
                        j: range_end
                    },
                    assumptions=assumptions)
        else:
            # Handle the general cases via general_len_val,
            # len_of_ranges_with_repeated_indices,
            # len_of_ranges_with_repeated_indices_from_1,
            # or len_of_empty_range_of_range
            from proveit.core_expr_types.tuples import (
                general_len, len_of_ranges_with_repeated_indices,
                len_of_ranges_with_repeated_indices_from_1,
                len_of_empty_range_of_ranges)
            _x = safe_dummy_var(self)

            def entry_map(entry):
                if isinstance(entry, ExprRange):
                    if isinstance(entry.body, ExprRange):
                        # Return an ExprRange of lambda maps.
                        return ExprRange(entry.parameter,
                                         entry.body.lambda_map,
                                         entry.start_index, entry.end_index)
                    else:
                        # Use the ExprRange entry's map.
                        return entry.lambda_map
                # For individual elements, just map to the
                # elemental entry.
                return Lambda(_x, entry)

            def entry_start(entry):
                if isinstance(entry, ExprRange):
                    if isinstance(entry.body, ExprRange):
                        # Return an ExprRange of lambda maps.
                        return ExprRange(entry.parameter,
                                         entry.body.start_index,
                                         entry.start_index, entry.end_index)
                    else:
                        return entry.start_index
                return one  # for individual elements, use start=end=1

            def entry_end(entry):
                if isinstance(entry, ExprRange):
                    if isinstance(entry.body, ExprRange):
                        # Return an ExprRange of lambda maps.
                        return ExprRange(entry.parameter, entry.body.end_index,
                                         entry.start_index, entry.end_index)
                    else:
                        return entry.end_index
                return one  # for individual elements, use start=end=1

            def empty_range(_i, _j, _f, assumptions):
                # If the start and end are literal ints and form an
                # empty range, then it should be straightforward to
                # prove that the range is empty.
                from proveit.numbers import is_literal_int, Add
                from proveit.logic import Equals
                from proveit import m
                _m = entries[0].start_index
                _n = entries[0].end_index
                empty_req = Equals(Add(_n, one), _m)
                if is_literal_int(_m) and is_literal_int(_n):
                    if _n.as_int() + 1 == _m.as_int():
                        empty_req.prove()
                if empty_req.proven(assumptions):
                    _f = Lambda(
                        (entries[0].parameter, entries[0].body.parameter),
                        entries[0].body.body)
                    _i = entry_map(_i)
                    _j = entry_map(_j)
                    return len_of_empty_range_of_ranges.instantiate(
                        {
                            m: _m,
                            n: _n,
                            f: _f,
                            i: _i,
                            j: _j
                        },
                        assumptions=assumptions)

            _f = [entry_map(entry) for entry in entries]
            _i = [entry_start(entry) for entry in entries]
            _j = [entry_end(entry) for entry in entries]
            _n = Len(_i).computed(assumptions=assumptions, simplify=False)

            from proveit.numbers import is_literal_int
            if len(entries) == 1 and isinstance(entries[0], ExprRange):
                if (is_literal_int(entries[0].start_index)
                        and is_literal_int(entries[0].end_index)):
                    if (entries[0].end_index.as_int() +
                            1 == entries[0].start_index.as_int()):
                        return empty_range(_i[0], _j[0], _f, assumptions)

            if all(_ == _i[0] for _ in _i) and all(_ == _j[0] for _ in _j):
                if isinstance(_i[0], ExprRange):
                    if _i[0].is_parameter_independent:
                        # A parameter independent range means they
                        # are all the same.
                        _i = [_i[0].body]
                if isinstance(_j[0], ExprRange):
                    if _j[0].is_parameter_independent:
                        # A parameter independent range means they
                        # are all the same.
                        _j = [_j[0].body]
                if (not isinstance(_i[0], ExprRange)
                        and not isinstance(_j[0], ExprRange)):
                    # special cases where the indices are repeated
                    if _i[0] == one:
                        thm = len_of_ranges_with_repeated_indices_from_1
                        return thm.instantiate({
                            n: _n,
                            f: _f,
                            i: _j[0]
                        },
                                               assumptions=assumptions)
                    else:
                        thm = len_of_ranges_with_repeated_indices
                        return thm.instantiate(
                            {
                                n: _n,
                                f: _f,
                                i: _i[0],
                                j: _j[0]
                            },
                            assumptions=assumptions)

            return general_len.instantiate({
                n: _n,
                f: _f,
                i: _i,
                j: _j
            },
                                           assumptions=assumptions)
Ejemplo n.º 6
0
    def computation(self, **defaults_config):
        '''
        Compute this Len expression, returning the equality
        between self and the expression for its computed value.
        Examples:
            |(a, b, c)| = 3
            |(x_1, ..., x_n, y)| = n+1
            |(f(i), ..., f(j), x_1, ..., x_n)| = (j-i+1) + (n-1+1)
            |x| = |x|
        In the last case, the 'x' represents an unknown tuple,
        so there is not anything we can do to compute it.
        '''
        # Currently not doing anything with must_evaluate
        # What it should do is make sure it evaluates to a number
        # and can circumvent any attempt that will not evaluate to
        # number.
        from proveit.numbers import one
        #print(self.operands.entries[0].__class__)
        if not isinstance(self.operands, ExprTuple):
            # Don't know how to compute the length if the operand is
            # not a tuple. For example, it could be a variable that
            # represent a tuple.  So just return the self equality.
            from proveit.logic import Equals
            return Equals(self, self).conclude_via_reflexivity()
        entries = self.operands.entries
        has_range = any(isinstance(entry, ExprRange) for entry in entries)
        if (len(entries) == 1 and has_range
                and not isinstance(entries[0].body, ExprRange)):
            # Compute the length of a single range.  Examples:
            # |(f(1), ..., f(n))| = n
            # |(f(i), ..., f(j))| = j-i+1
            range_entry = entries[0]
            start_index = range_entry.true_start_index
            end_index = range_entry.true_end_index
            lambda_map = range_entry.lambda_map
            if start_index == one:
                from proveit.core_expr_types.tuples import range_from1_len
                len_comp = range_from1_len.instantiate(
                    {
                        f: lambda_map,
                        i: end_index
                    }, auto_simplify=False)
            else:
                from proveit.core_expr_types.tuples import range_len
                len_comp = range_len.instantiate(
                    {
                        f: lambda_map,
                        i: start_index,
                        j: end_index
                    },
                    auto_simplify=False)
        elif not has_range:
            # Case of all non-range entries.
            if len(entries) == 0:
                # zero length.
                from proveit.core_expr_types.tuples import tuple_len_0
                return tuple_len_0
            elif len(entries) < 10:
                # Automatically get the count and equality with
                # the length of the proper iteration starting from
                # 1.  For example,
                # |(a, b, c)| = 3
                # |(a, b, c)| = |(1, .., 3)|
                import proveit.numbers.numerals.decimals
                _n = len(entries)
                len_thm = proveit.numbers.numerals.decimals\
                    .__getattr__('tuple_len_%d' % _n)
                repl_map = dict()
                for param, entry in zip(len_thm.explicit_instance_params(),
                                        entries):
                    repl_map[param] = entry
                return len_thm.instantiate(repl_map, auto_simplify=False)
            else:
                # raise NotImplementedError("Can't handle length computation "
                #                        ">= 10 for %s"%self)
                from proveit.core_expr_types.tuples import tuple_len_incr
                from proveit.numbers import num
                from proveit.logic import Equals
                # We turn on automation because this length equality should
                # be true (we know the number of elements is equal to the
                # number of entries since there are no ExprRange entries).
                # Since we know it's true, why not commit ourselves to
                # proving it?
                return tuple_len_incr.instantiate(
                    {
                        i: num(len(entries) - 1),
                        a: entries[:-1],
                        b: entries[-1]
                    },
                    automation=True)
                # return Equals(eq.lhs, eq.rhs._integerBinaryEval(assumptions=assumptions).rhs).prove(assumptions=assumptions)
                # raise NotImplementedError("Can't handle length computation "
                #                         ">= 10 for %s"%self)
        elif (len(entries) == 2 and not isinstance(entries[1], ExprRange)
              and not isinstance(entries[0].body, ExprRange)):
            # Case of an extended range:
            # |(a_1, ..., a_n, b| = n+1
            from proveit.core_expr_types.tuples import \
                extended_range_len, extended_range_from1_len
            assert isinstance(entries[0], ExprRange)
            range_lambda = entries[0].lambda_map
            range_start = entries[0].true_start_index
            range_end = entries[0].true_end_index
            if range_start == one:
                len_comp = extended_range_from1_len.instantiate({
                    f: range_lambda,
                    x: entries[1],
                    i: range_end
                })
            else:
                len_comp = extended_range_len.instantiate({
                    f: range_lambda,
                    x: entries[1],
                    i: range_start,
                    j: range_end
                })
        else:
            # Handle the general cases via general_len_val,
            # len_of_ranges_with_repeated_indices,
            # len_of_ranges_with_repeated_indices_from_1,
            # or len_of_empty_range_of_range
            from proveit.core_expr_types.tuples import (
                general_len, len_of_ranges_with_repeated_indices,
                len_of_ranges_with_repeated_indices_from_1,
                len_of_empty_range_of_ranges)
            _x = safe_dummy_var(self)
            preserved_exprs = defaults.preserved_exprs

            def entry_map(entry):
                # Don't auto-simplify the entry.
                preserved_exprs.add(entry)
                if isinstance(entry, ExprRange):
                    if isinstance(entry.body, ExprRange):
                        # Return an ExprRange of lambda maps.
                        return ExprRange(entry.parameter,
                                         entry.body.lambda_map,
                                         entry.true_start_index,
                                         entry.true_end_index)
                    else:
                        return entry.lambda_map
                # For individual elements, just map to the
                # elemental entry.
                return Lambda(_x, entry)

            def entry_start(entry):
                if isinstance(entry, ExprRange):
                    if isinstance(entry.body, ExprRange):
                        # Return an ExprRange of lambda maps.
                        return ExprRange(entry.parameter,
                                         entry.body.true_start_index,
                                         entry.true_start_index,
                                         entry.true_end_index)
                    else:
                        return entry.true_start_index
                return one  # for individual elements, use start=end=1

            def entry_end(entry):
                if isinstance(entry, ExprRange):
                    if isinstance(entry.body, ExprRange):
                        # Return an ExprRange of lambda maps.
                        return ExprRange(entry.parameter,
                                         entry.body.true_end_index,
                                         entry.true_start_index,
                                         entry.true_end_index)
                    else:
                        return entry.true_end_index
                return one  # for individual elements, use start=end=1

            def empty_range(_i, _j, _f):
                # If the start and end are literal ints and form an
                # empty range, then it should be straightforward to
                # prove that the range is empty.
                from proveit.numbers import is_literal_int, Add
                from proveit.logic import Equals
                from proveit import m
                _m = entries[0].true_start_index
                _n = entries[0].true_end_index
                empty_req = Equals(Add(_n, one), _m)
                if is_literal_int(_m) and is_literal_int(_n):
                    if _n.as_int() + 1 == _m.as_int():
                        empty_req.prove()
                if empty_req.proven():
                    _f = Lambda(
                        (entries[0].parameter, entries[0].body.parameter),
                        entries[0].body.body)
                    _i = entry_map(_i)
                    _j = entry_map(_j)
                    return len_of_empty_range_of_ranges.instantiate({
                        m: _m,
                        n: _n,
                        f: _f,
                        i: _i,
                        j: _j
                    }).with_wrapping_at()

            _f = [entry_map(entry) for entry in entries]
            _i = [entry_start(entry) for entry in entries]
            _j = [entry_end(entry) for entry in entries]
            _n = Len(_i).computed()

            from proveit.numbers import is_literal_int
            if len(entries) == 1 and isinstance(entries[0], ExprRange):
                if (is_literal_int(entries[0].true_start_index)
                        and is_literal_int(entries[0].true_end_index)):
                    if (entries[0].true_end_index.as_int() +
                            1 == entries[0].true_start_index.as_int()):
                        return empty_range(_i[0], _j[0], _f)

            len_comp = None
            if all(_ == _i[0] for _ in _i) and all(_ == _j[0] for _ in _j):
                if isinstance(_i[0], ExprRange):
                    if _i[0].is_parameter_independent:
                        # A parameter independent range means they
                        # are all the same.
                        _i = [_i[0].body]
                if isinstance(_j[0], ExprRange):
                    if _j[0].is_parameter_independent:
                        # A parameter independent range means they
                        # are all the same.
                        _j = [_j[0].body]
                if (not isinstance(_i[0], ExprRange)
                        and not isinstance(_j[0], ExprRange)):
                    # special cases where the indices are repeated
                    if _i[0] == one:
                        thm = len_of_ranges_with_repeated_indices_from_1
                        len_comp = thm.instantiate({
                            n: _n,
                            f: _f,
                            i: _j[0]
                        }).with_wrapping_at()
                    else:
                        thm = len_of_ranges_with_repeated_indices
                        len_comp = thm.instantiate({
                            n: _n,
                            f: _f,
                            i: _i[0],
                            j: _j[0]
                        }).with_wrapping_at()
            if len_comp is None:
                len_comp = general_len.instantiate(
                    {
                        n: _n,
                        f: _f,
                        i: _i,
                        j: _j
                    },
                    preserved_exprs=preserved_exprs).with_wrapping_at()
                if not defaults.auto_simplify and len_comp.lhs != self:
                    # Make sure the left side is reduced to the
                    # original expression (self) which is preserved.
                    return len_comp.inner_expr().lhs.shallow_simplify(
                        preserved_exprs=preserved_exprs)
        if defaults.auto_simplify:
            # Ensure the right side is simplified which may have
            # been prevented due to automatic preservation of
            # instatiation expressions.
            return len_comp.inner_expr().rhs.simplify()
        return len_comp
Ejemplo n.º 7
0
    def deduce_equal_or_not(self, other_tuple, **defaults_config):
        '''
        Prove and return that this ExprTuple is either equal or
        not equal to other_tuple or raises an UnsatisfiedPrerequisites
        or NotImplementedError if we cannot readily prove either of
        these.
        '''
        from proveit import (ExprRange, safe_dummy_var,
                             UnsatisfiedPrerequisites)
        from proveit.logic import (
                And, Or, Equals, NotEquals, deduce_equal_or_not)
        if self == other_tuple:
            return Equals(self, other_tuple).conclude_via_reflexivity
        if not isinstance(other_tuple, ExprTuple):
            raise TypeError("Expecting 'other_tuple' to be an ExprTuple "
                            "not a %s"%other_tuple.__class__)
        _i = self.num_elements()
        _j = other_tuple.num_elements()
        size_relation = deduce_equal_or_not(_i, _j)
        if isinstance(size_relation.expr, NotEquals):
            # Not equal because the lengths are different.
            return self.not_equal(other_tuple)
        
        def raise_non_corresponding():
            raise NotImplementedError(
                    "ExprTuple.deduce_equal_or_not is only "
                    "implemented for the case when ExprRanges "
                    "match up: %s vs %s"%self, other_tuple)
            
        if self.num_entries() == other_tuple.num_entries():
            if self.num_entries()==1 and self.contains_range():
                if not other_tuple.contains_range():
                    # One ExprTuple has a range but the other doesn't.
                    # That case isn't handled.
                    raise_non_corresponding()
                lhs_range = self.entries[0]
                rhs_range = other_tuple.entries[0]
                start_index = lhs_range.start_index
                end_index = lhs_range.end_index
                if ((start_index != rhs_range.start_index) or
                        (end_index != rhs_range.end_index)):
                    # Indices must match for a proper correspondence.
                    raise_non_corresponding()
                if lhs_range.parameter != rhs_range.parameter:
                    # Use a safe common parameter.
                    param = safe_dummy_var(lhs_range.body, rhs_range.body)
                    lhs_range_body = lhs_range.body.basic_replaced(
                            {lhs_range.parameter: param})
                    rhs_range_body = rhs_range.body.basic_replaced(
                            {rhs_range.parameter: param})
                else:
                    param = lhs_range.parameter
                    lhs_range_body = lhs_range.body
                    rhs_range_body = rhs_range.body
                inner_assumptions = defaults.assumptions + (
                        lhs_range.parameter_condition(),)
                try:
                    body_relation = deduce_equal_or_not(
                            lhs_range_body, rhs_range_body,
                            assumptions=inner_assumptions)
                    if isinstance(body_relation, Equals):
                        # Every element is equal, so the ExprTuples 
                        # are equal.
                        return self.deduce_equality(
                                Equals(self, other_tuple))
                    else:
                        # Every element is not equal, so the ExprTuples 
                        # are not equal.
                        # This will enable "any" from "all".
                        And(ExprRange(
                                param, NotEquals(lhs_range_body,
                                                 rhs_range_body),
                                start_index, end_index)).prove()
                        return self.not_equal(other_tuple)
                except (NotImplementedError, UnsatisfiedPrerequisites):
                    pass
                if And(ExprRange(param, Equals(lhs_range_body, 
                                               rhs_range_body),
                                 start_index, end_index)).proven():
                    # Every element is equal, so the ExprTuples 
                    # are equal.
                    return self.deduce_equality(
                            Equals(self, other_tuple))
                elif Or(ExprRange(param, NotEquals(lhs_range_body,
                                                   rhs_range_body),
                        start_index, end_index)).proven():
                    # Some element pair is not equal, so the ExprTuples 
                    # are not equal.
                    return self.not_equal(other_tuple)
                raise UnsatisfiedPrerequisites(
                        "Could not determine whether %s = %s"
                        %(self, other_tuple))
            
            # Loop through each entry pair in correspondence and
            # see if we can readily prove whether or not they are
            # all equal.
            for idx, (_x, _y) in enumerate(
                    zip(self.entries, other_tuple.entries)):
                if isinstance(_x, ExprRange) != isinstance(_y, ExprRange):
                    raise_non_corresponding()
                if _x == _y:
                    # The expressions are the same, so we know they
                    # are equal.
                    continue
                if isinstance(_x, ExprRange):
                    # Wrap ExprRanges in ExprTuples and compare as
                    # single entry tuples.
                    _x = ExprTuple(_x)
                    _y = ExprTuple(_y)
                    _k = _x.num_elements()
                    _l = _y.num_elements()
                    size_relation = deduce_equal_or_not(_k, _l)
                    if isinstance(size_relation.expr, NotEquals):
                        # Not implemented when the ExprRanges don't
                        # correspond in size.
                        raise_non_corresponding()
                    relation = deduce_equal_or_not(_x, _y)
                else:
                    # Compare singular entries.
                    relation = deduce_equal_or_not(_x, _y)
                if isinstance(relation.expr, NotEquals):
                    # Aha! They are not equal.
                    return self.not_equal(other_tuple)
            # They are equal!
            return self.deduce_equality(Equals(self, other_tuple))

        raise NotImplementedError(
                    "ExprTuple.deduce_equal_or_not is not implemented "
                    "for ExprTuples that have a different number of "
                    "elements.")