def doReducedEvaluation(self, assumptions=USE_DEFAULTS, **kwargs): ''' Only handles -0 = 0 or double negation. ''' from proveit.logic import EvaluationError from ._theorems_ import negatedZero from proveit.number import zero if self.operand == zero: return negatedZero if isinstance(self.operand, Neg) and isIrreducibleValue( self.operand.operand): return self.doubleNegSimplification(assumptions) raise EvaluationError(self, assumptions)
def doReducedEvaluation(self, assumptions=USE_DEFAULTS): ''' Derive and return this multiplication expression equated with an irreducible value. Handle the trivial case of a zero factor or do pairwise evaluation after simplifying negations and eliminating one factors. ''' from ._theorems_ import multZeroLeft, multZeroRight, multZeroAny from proveit.logic import isIrreducibleValue, SimplificationError from proveit.number import zero # First check for any zero factors -- quickest way to do an evaluation. try: zeroIdx = self.operands.index(zero) if len(self.operands)==2: if zeroIdx==0: return multZeroLeft.specialize({x:self.operands[1]}, assumptions=assumptions) else: return multZeroRight.specialize({x:self.operands[0]}, assumptions=assumptions) Afactors = self.operands[:zeroIdx] Bfactors = self.operands[zeroIdx+1:] return multZeroAny.specialize({m:len(Afactors), n:len(Bfactors), AA:Afactors, BB:Bfactors}, assumptions=assumptions) except (ValueError, ProofFailure): pass # No such "luck" regarding a simple multiplication by zero. expr = self # A convenience to allow successive update to the equation via transitivities. # (starting with self=self). eq = TransRelUpdater(self, assumptions) # Simplify negations -- factor them out. expr = eq.update(expr.negSimplifications(assumptions)) if not isinstance(expr, Mult): # The expression may have changed to a negation after doing # negSimplification. Start the simplification of this new # expression fresh at this point. eq.update(expr.evaluation(assumptions)) return eq.relation # Eliminate any factors of one. expr = eq.update(expr.oneEliminations(assumptions)) if isIrreducibleValue(expr): return eq.relation # done if len(self.operands) > 2: eq.update(pairwiseEvaluation(expr, assumptions)) return eq.relation raise SimplificationError("Unable to evaluate %s"%str(self))
def irreducibleValue(self): from proveit.number import zero return isIrreducibleValue(self.operand) and self.operand != zero
def deduceEnumProperSubset(self, subset_indices=None, subset=None, assumptions=USE_DEFAULTS): ''' Deduce that this Set expression has as a proper subset the set specified by either (a) the indices in the subset_indices list OR (b) the Set specified by subset (but not both). For example, both {a, b, c, d}.deduceEnumSubset(subset_indices=[1, 3]) and {a, b, c, d}.deduceEnumSubset(subset=Set(b, d)) return |– {b, d} subset {a, b, c, d} (assuming the appropriate knowledge about either a or c (or both) not being elements of the subset {b, d}). This proper subset method is more complex than the analogous method for improper subsets. As with the improper subset case, this process is complicated by the fact that the Set class allows for multiplicity of elements without it actually representing a multi-set (thus, for example, {a, a} = {a}). Subset deductions are based on the support sets (i.e. the sets with all multiplicities reduced to 1) for the self and subsets supplied. The process is further complicated by the fact that elements in one set might not *appear* to be in the other set but might be *equal* to elements in the other set, making it challenging to confirm the proper subset relationship. ''' from proveit.logic import Set # Before bothering with much processing, quickly check that: # (1) user has specified subset_indices OR subset but not both; # (2) if only subset specification, it has the correct form; # (3) if only subset_indices, they are plausible. if subset_indices is None and subset is None: raise ValueError("Need to specify the desired subset by " "specifying the list of indices (subset_indices) " "OR an actual subset (in the form of an " "enumerated set using Set()).") if subset_indices is not None and subset is not None: raise ValueError("Need to specify the desired subset by " "specifying the list of indices (subset_indices) " "OR an actual subset, but NOT both.") if subset is not None and not isinstance(subset, Set): raise ValueError("Specified subset {} does not appear to be a " "valid Set object.".format(subset)) self_list = list(self.operands) valid_indices_list = list(range(0, len(self.operands))) if subset_indices is not None: # We must have had subset=None, so check validity of the # indices and use them to create a subset Set self._check_subset_indices_weak(valid_indices_list, subset_indices, proper_subset=True) subset_list_from_indices = [self_list[i] for i in subset_indices] subset_from_indices = Set(*subset_list_from_indices) subset = subset_from_indices # Reformat assumptions if necessary. Among other things, # convert any assumptions=None to assumptions=() assumptions = defaults.checkedAssumptions(assumptions) # We should now have a subset Set, either explicitly provided # as an argument or derived from the subset_indices. subset_list = list(subset.operands) # Try to make some appropriate substitutions into the subset, # and reduce the subset, all to make later processing (such as # finding a superset element not also in the subset) easier. # This seems like a lot of extra code, but should execute # fairly quickly because it doesn't depend on automation -- # just some list searches and theorem specializations. from proveit.logic import Equals, isIrreducibleValue from proveit import TransRelUpdater temp_subset = subset eq_temp = TransRelUpdater(temp_subset, assumptions) # perform substitutions to irreducible values when possible for elem in set(temp_subset.operands): if elem in Equals.knownEqualities: for kt in Equals.knownEqualities[elem]: if set(kt.assumptions).issubset(set(assumptions)): if (kt.lhs == elem and isIrreducibleValue(kt.rhs) and kt.lhs != kt.rhs): temp_subset = eq_temp.update( temp_subset.elem_substitution( elem=elem, sub_elem=kt.rhs, assumptions=assumptions)) break # reduce multiplicities temp_subset = eq_temp.update(temp_subset.reduction(assumptions)) subset = temp_subset subset_list = list(subset.operands) subset_to_subset_subbed_reduced_kt = eq_temp.relation # subset_was_substituted = True # ================================================= # # LATER do that same process for the superset self? # # ================================================= # # We now have a SUBSTITUTED and REDUCED subset Set. # A subset generated from the subset_indices will automatically # be a plausible subset (b/c it derived from the superset self # elements). If the subset was originally supplied as # an argument, however, we now check if it is a plausible # subset of self: it should only have elements found in self # or elements somehow proven to be equal to elements in self. if subset_indices == None: # i.e. subset provided explicitly by user # then substituted, reduced subset might not be a subset error_elem_candidates = set() error_elem_equivalences_dict = dict() for elem in set(subset_list): if elem not in set(self_list): error_elem_candidates.add(elem) if len(error_elem_candidates) > 0: # We have candidates in the supposed subset that do not # literally appear in the supposed superset self, but # the candidates might be known to be 'Equal' to # appropriate values, so we check just a little more # assiduously before returning an error message error_elems = error_elem_candidates.copy() # from proveit.logic import Equals for elem in error_elem_candidates: for super_elem in set(self_list): if Equals(elem, super_elem).proven(assumptions=assumptions): error_elems.discard(elem) break # b/c we just need 1 instance if len(error_elems) > 0: raise ValueError( "Specified subset {0} does not appear to be a " "subset of the original set {1}. The following " "elements appear in the requested subset Set but " "not in the original Set: {2}.".format( subset, self, error_elems)) # Furthermore, we need to check that at least one superset # elem does not appear in the proposed proper subset. # This is not a proof, just a superficial check that # there at least APPEAR to be elements in self that do not # appear in the subset (but we can be fooled by variables). # Those candidates will then be checked more carefully later # using the reduced forms of the sets. non_subset_elem_candidates = set() non_subset_elem_remaining = set() non_subset_elem_proven = None non_subset_elem_index = None non_subset_elem_kt = None for elem in set(self_list): if elem not in set(subset_list): non_subset_elem_candidates.add(elem) non_subset_elem_remaining.add(elem) # if no candidate elements, raise an error if len(non_subset_elem_candidates) == 0: raise ValueError( "Specified subset {0} does not appear to be a proper " "subset of the original set {1}. All of the superset " "elements appear in the specified subset.".format( subset, self)) # but if we have candidates, see if at least one can be proven # to not be in the subset else: from proveit.logic import Equals, NotEquals, NotInSet for elem in non_subset_elem_candidates: for subset_elem in set(subset_list): if Equals(elem, subset_elem).proven(assumptions=assumptions): non_subset_elem_remaining.discard(elem) break # that might have reduced the set of remaining candidates # so now check if there are any remaining. If not, raise # an error; if so, try proving one of the remaining # candidates really is not in the subset if len(non_subset_elem_remaining) == 0: raise ValueError("In calling Set.deduceEnumProperSubset(), " "the self superset {0} does not appear to " "have any elements outside of the requested " "subset {1}.".format(self, subset)) else: for elem in non_subset_elem_remaining: try: non_subset_elem_kt = NotInSet( elem, subset).prove(assumptions=assumptions) non_subset_elem_proven = elem break except: pass if non_subset_elem_proven is None: raise ValueError( "Failed to prove that the supposed Self superset {0} " "has any elements not already contained in the " "supposed proper subset {1}. Notice that this might " "be because the sets have unassigned variables".format( self, subset)) # Derive the reduced form of the self superset Set. We could # have done this earlier, but delayed until after param # checking. The eventual (proper) subset relationship will be # based on the reduced forms of the specified Sets. self_to_support_kt = self.reduction(assumptions=assumptions) self_reduced = self_to_support_kt.rhs self_reduced_list = list(self_reduced.operands) # while we're here, get the index of the non_subset_elem_proven non_subset_elem_index = self_reduced_list.index(non_subset_elem_proven) # For convenience, convert the subset_list to indices # of the self_reduced_list. Because of earlier checks, the # subset_list should contain only items in # self_reduced_list but not all the items in self_reduced_list. subset_indices_list = ([ self_reduced_list.index(elem) for elem in subset_list ]) full_indices_list = list(range(0, len(self_reduced_list))) # construct the complement of the subset_indices_list, # to use in the eventual construction of the necessary # permutation of the self superset Set. remaining_indices = list(full_indices_list) # clone for elem in subset_indices_list: remaining_indices.remove(elem) # then also remove the index for the non_subset_elem_proven remaining_indices.remove(non_subset_elem_index) # establish the desired order for eventual thm application new_order = (subset_indices_list + [non_subset_elem_index] + remaining_indices) # find superset permutation needed for thm application supersetPermRelation = generic_permutation(self_reduced, new_order, assumptions=assumptions) # construct the desired list of subset elems desired_subset_list = subset_list # construct the desired complement list of elems desired_complement_list = [non_subset_elem_proven] for elem in remaining_indices: desired_complement_list.append(self_reduced_list[elem]) # Organize info for theorem specialization # then specialize. from ._theorems_ import properSubsetOfSuperset from proveit.number import num m, n, a, b, c = properSubsetOfSuperset.allInstanceVars() a_sub = desired_subset_list b_sub = desired_complement_list[0] c_sub = desired_complement_list[1:] m_sub, n_sub = num(len(a_sub)), num(len(c_sub)) subset_of_permuted_superset = properSubsetOfSuperset.specialize( { m: m_sub, n: n_sub, a: a_sub, b: b_sub, c: c_sub }, assumptions=assumptions) # We now have # |- subset_subbed_reduced \propersubset superset_reduced. # We back-sub to get the original subset as a proper subset of # the original superset (self): # (1) Replace permuted reduced superset with unpermuted reduced # superset: reduced_subset_of_reduced_superset = ( supersetPermRelation.subLeftSideInto(subset_of_permuted_superset, assumptions=assumptions)) # (2) Replace reduced superset with original superset: reduced_subset_of_orig_superset = (self_to_support_kt.subLeftSideInto( reduced_subset_of_reduced_superset, assumptions=assumptions)) # (3) Replace the substituted, reduced subset with the original # subset (might be trivial if subsitution and reduction # were essentially identities): orig_subset_of_orig_superset = ( subset_to_subset_subbed_reduced_kt.subLeftSideInto( reduced_subset_of_orig_superset, assumptions=assumptions)) return orig_subset_of_orig_superset