if expressions.num_entries() == 0: from proveit.logic.booleans.conjunction import \ empty_conjunction return empty_conjunction if expressions.is_double(): from . import and_if_both return and_if_both.instantiate({ A: expressions[0], B: expressions[1] }, assumptions=assumptions) else: from . import and_if_all _m = expressions.num_elements(assumptions) return and_if_all.instantiate({ m: _m, A: expressions }, assumptions=assumptions) # Register these expression equivalence methods: InnerExpr.register_equivalence_method(And, 'commutation', 'commuted', 'commute') InnerExpr.register_equivalence_method(And, 'group_commutation', 'group_commuted', 'group_commute') InnerExpr.register_equivalence_method(And, 'association', 'associated', 'associate') InnerExpr.register_equivalence_method(And, 'disassociation', 'disassociated', 'disassociate')
def reduce_operands(inner_expr, in_place=True, must_evaluate=False, assumptions=USE_DEFAULTS): ''' Attempt to return an InnerExpr object that is provably equivalent to the given inner_expr but with simplified operands at the inner-expression level. If in_place is True, the top-level expression must be a Judgment and the simplified Judgment is derived instead of an equivalence relation. If must_evaluate is True, the simplified operands must be irreducible values (see is_irreducible_value). ''' # Any of the operands that can be simplified must be replaced with # their simplification. from proveit import InnerExpr, ExprRange assert isinstance(inner_expr, InnerExpr), \ "Expecting 'inner_expr' to be of type 'InnerExpr'" inner = inner_expr.expr_hierarchy[-1] substitutions = [] while True: all_reduced = True for operand in inner.operands: if (not is_irreducible_value(operand) and not isinstance(operand, ExprRange)): # The operand isn't already irreducible, so try to # simplify it. if must_evaluate: operand_eval = operand.evaluation(assumptions=assumptions) else: operand_eval = operand.simplification( assumptions=assumptions) if must_evaluate and not is_irreducible_value( operand_eval.rhs): msg = 'Evaluations expected to be irreducible values' raise EvaluationError(msg, assumptions) if operand_eval.lhs != operand_eval.rhs: # Compose map to replace all instances of the # operand within the inner expression. global_repl = Lambda.global_repl(inner, operand) lambda_map = inner_expr.repl_lambda().compose(global_repl) # substitute in the evaluated value if in_place: subbed = operand_eval.sub_right_side_into(lambda_map) inner_expr = InnerExpr(subbed, inner_expr.inner_expr_path) else: sub = operand_eval.substitution(lambda_map) inner_expr = InnerExpr(sub.rhs, inner_expr.inner_expr_path) substitutions.append(sub) all_reduced = False # Start over since there may have been multiple # substitutions: break if all_reduced: break # done! inner = inner_expr.expr_hierarchy[-1] if not in_place and len(substitutions) > 1: # When there have been multiple substitutions, apply # transtivity over the chain of substitutions to equate the # end-points. Equals.apply_transitivities(substitutions, assumptions) return inner_expr
''' return groupCommutation(self, initIdx, finalIdx, length, disassociate, assumptions) def association(self, startIdx, length, assumptions=USE_DEFAULTS): ''' Given numerical operands, deduce that this expression is equal to a form in which operands in the range [startIdx, startIdx+length) are grouped together. For example, (a + b + ... + y + z) = (a + b ... + (l + ... + m) + ... + y + z) ''' from ._theorems_ import association return apply_association_thm(self, startIdx, length, association, assumptions) def disassociation(self, idx, assumptions=USE_DEFAULTS): ''' Given numerical operands, deduce that this expression is equal to a form in which the operand at index idx is no longer grouped together. For example, (a + b ... + (l + ... + m) + ... + y+ z) = (a + b + ... + y + z) ''' from ._theorems_ import disassociation return apply_disassociation_thm(self, idx, disassociation, assumptions) # Register these expression equivalence methods: InnerExpr.register_equivalence_method(Mult, 'commutation', 'commuted', 'commute') InnerExpr.register_equivalence_method(Mult, 'groupCommutation', 'groupCommuted', 'groupCommute') InnerExpr.register_equivalence_method(Mult, 'association', 'associated', 'associate') InnerExpr.register_equivalence_method(Mult, 'disassociation', 'disassociated', 'disassociate') InnerExpr.register_equivalence_method(Mult, 'distribution', 'distributed', 'distribute') InnerExpr.register_equivalence_method(Mult, 'factorization', 'factorized', 'factor') InnerExpr.register_equivalence_method(Mult, 'exponentCombination', 'combinedExponents', 'combineExponents')
else: return range_len_is_nat.instantiate( { f: range_lambda, i: range_start, j: range_end }, assumptions=assumptions) def do_reduced_simplification(self, assumptions=USE_DEFAULTS, **kwargs): ''' A simplification of a Len operation computes the length as a sum of the lengths of each item of the ExprTuple operand, returning the equality between the Len expression and this computation, simplified to the extent possible. An item may be a singular element (contribution 1 to the length) or an iteration contributing its length. ''' return self._computation(must_evaluate=False, assumptions=assumptions) def do_reduced_evaluation(self, assumptions=USE_DEFAULTS, **kwargs): ''' Return the evaluation of the length which equates that Len expression to an irreducible result. ''' return self._computation(must_evaluate=True, assumptions=assumptions) # Register these expression equivalence methods: InnerExpr.register_equivalence_method(Len, 'computation', 'computed', 'compute')
This works only if the operand x is an instance of the Add class at its outermost level, e.g. x = Add(a, b, …, n). The operands of that Add class can be other things, but the extraction is guaranteed to work only if the inner operands a, b, etc., are simple. ''' from . import ceil_of_real_plus_int return apply_rounding_extraction( self, ceil_of_real_plus_int, idx_to_extract, assumptions) def deduce_in_number_set(self, number_set, assumptions=USE_DEFAULTS): ''' Given a number set number_set, attempt to prove that the given Ceil expression is in that number set using the appropriate closure theorem. ''' from proveit.numbers.rounding import ceil_is_an_int from proveit.numbers.rounding import ceil_real_pos_closure return rounding_deduce_in_number_set( self, number_set, ceil_is_an_int, ceil_real_pos_closure, assumptions) # Register these generic expression equivalence methods: InnerExpr.register_equivalence_method( Ceil, 'rounding_elimination', 'rounding_eliminated', 'rounding_eliminate') InnerExpr.register_equivalence_method( Ceil, 'rounding_extraction', 'rounding_extracted', 'rounding_extract')
elem_in_set, assumptions, "Automatic deduction of membership in exponentiated sets is " "only supported for an exponent that is a literal integer") if exponent_eval.lhs != exponent_eval.rhs: # after proving that the element is in the set taken to # the evaluation of the exponent, substitute back in the # original exponent. return exponent_eval.sub_left_side_into(elem_in_set, assumptions=assumptions) return elem_in_set def side_effects(self, judgment): return yield # outside any specific class: # special Exp case of square root def sqrt(base): ''' Special function for square root version of an exponential. ''' return Exp(base, frac(one, two)) # Register these expression equivalence methods: InnerExpr.register_equivalence_method(Exp, 'distribution', 'distributed', 'distribute')
if pull == 'left': w_etc_sub = factored_numer.operands[:-1] x_sub = factored_numer.operands[-1] z_etc_sub = [] elif pull == 'right': w_etc_sub = [] x_sub = factored_numer.operands[0] z_etc_sub = factored_numer.operands[1:] eqns.append(frac_in_prod_rev.instantiate({w_etc:w_etc_sub, x:x_sub, y:self.denominator, z_etc:z_etc_sub})) num = len(the_factor.operands) if isinstance(the_factor, Mult) else 1 if group_factor and num > 1: if pull=='left': eqns.append(eqns[-1].rhs.group(end_idx=num, assumptions=assumptions)) elif pull=='right': eqns.append(eqns[-1].rhs.group(start_idx=-num, assumptions=assumptions)) return Equals(eqns[0].lhs, eqns[-1].rhs).prove(assumptions) """ def frac(numer, denom): return Div(numer, denom) # Register these expression equivalence methods: InnerExpr.register_equivalence_method(Div, 'deep_one_eliminations', 'deep_eliminated_ones', 'deep_eliminate_ones') InnerExpr.register_equivalence_method(Div, 'exponent_combination', 'combined_exponents', 'combine_exponents')
else: return multNegRightDouble.specialize( {a: mult_expr.operands[0]}, assumptions=assumptions) aVal = mult_expr.operands[:idx] bVal = mult_expr.operands[idx] cVal = mult_expr.operands[idx + 1:] mVal = num(len(aVal)) nVal = num(len(cVal)) return multNegAnyDouble.specialize( { m: mVal, n: nVal, AA: aVal, B: bVal, CC: cVal }, assumptions=assumptions) # Register these expression equivalence methods: InnerExpr.register_equivalence_method(Neg, 'doubleNegSimplification', 'doubleNegSimplified', 'doubleNegSimplify') InnerExpr.register_equivalence_method(Neg, 'distribution', 'distributed', 'distribute') InnerExpr.register_equivalence_method(Neg, 'factorization', 'factorized', 'factor') InnerExpr.register_equivalence_method(Neg, 'innerNegMultSimplification', 'innerNegMultSimplified', 'innerNegMultSimplify')
if idx == 0: return mult_neg_left_double.instantiate( {a: mult_expr.operands[1]}, assumptions=assumptions) else: return mult_neg_right_double.instantiate( {a: mult_expr.operands[0]}, assumptions=assumptions) _a = mult_expr.operands[:idx] _b = mult_expr.operands[idx] _c = mult_expr.operands[idx + 1:] _m = _a.num_elements(assumptions) _n = _c.num_elements(assumptions) return mult_neg_any_double.instantiate( {m: _m, n: _n, a: _a, b: _b, c: _c}, assumptions=assumptions) # Register these expression equivalence methods: InnerExpr.register_equivalence_method( Neg, 'double_neg_simplification', 'double_neg_simplified', 'double_neg_simplify') InnerExpr.register_equivalence_method( Neg, 'distribution', 'distributed', 'distribute') InnerExpr.register_equivalence_method( Neg, 'factorization', 'factorized', 'factor') InnerExpr.register_equivalence_method( Neg, 'inner_neg_mult_simplification', 'inner_neg_mult_simplified', 'inner_neg_mult_simplify')
def _sub_one_side_into(self, prob_relation_or_inner_expr, *, which_side, **defaults_config): ''' Helper method for sub_[left/right]_side_into. ''' from . import lhs_prob_via_equiv, rhs_prob_via_equiv equiv = self if which_side == 'left': thm = lhs_prob_via_equiv orig_circuit = equiv.rhs elif which_side == 'right': thm = rhs_prob_via_equiv orig_circuit = equiv.lhs else: raise ValueError("'which_side' must either be 'left' or 'right'") if isinstance(prob_relation_or_inner_expr, Judgment): prob_relation_or_inner_expr = prob_relation_or_inner_expr.expr if isinstance(prob_relation_or_inner_expr, InnerExpr): # This should be an inner expresion of a probability # over a quantum circuit. inner_expr = prob_relation_or_inner_expr expr_hierarchy = inner_expr.expr_hierarchy for _k, _expr in enumerate(reversed(expr_hierarchy)): circuit_or_lambda_map = None if isinstance(_expr, Prob) and isinstance( _expr.operand, Qcircuit): if _k == 2: # Just a quantum circuit in a probability circuit_or_lambda_map = _expr.operand else: # A portion of a quantum circuit. circuit_or_lambda_map = InnerExpr( _expr, inner_expr.inner_expr_path[-_k + 2:]) prob_relation_lambda = InnerExpr( expr_hierarchy[0], inner_expr.inner_expr_path[:-_k]).repl_lambda() break if circuit_or_lambda_map is None: raise NotImplementedError( "Qcircuit.sub_[left/right]_side_into only " "implemented to apply to an inner expr within " "a Prob on a Qcircuit") if circuit_or_lambda_map != orig_circuit: # Not a direct probability substitution. if which_side == 'left': equiv = equiv.derive_reversed() thm = rhs_prob_via_equiv equiv = equiv.substitution(circuit_or_lambda_map) else: # prob_relation_or_inner_expr should be a probability # relation (it isn't an InnerExpr). prob_relation = prob_relation_or_inner_expr qcircuit = Qcircuit._extract_circuit_from_prob_relation( prob_relation) if qcircuit != orig_circuit: # Let's see if orig_circuit is a portion of qcircuit: if which_side == 'left': equiv = self.derive_reversed().substitution(qcircuit) thm = rhs_prob_via_equiv else: equiv = self.substitution(qcircuit) prob_relation_lambda = Lambda.global_repl(prob_relation, Prob(qcircuit)) # Prove the probability relation. return thm.instantiate({ Q: prob_relation_lambda, A: equiv.lhs, B: equiv.rhs })
if summand_lambda not in (lesser_lambda, greater_lambda): raise ValueError("Expecting summand_relation to be a universally " "quantified number relation (< or <=) " "involving the summand, %d, not %s" % (self.summand, summand_relation)) _a = lesser_lambda _b = greater_lambda _S = self.domain if isinstance(summand_relation.instance_expr, LessEq): # Use weak form sum_rel_impl = weak_summation_from_summands_bound.instantiate( { a: _a, b: _b, S: _S }, assumptions=assumptions) else: # Use strong form sum_rel_impl = strong_summation_from_summands_bound.instantiate( { a: _a, b: _b, S: _S }, assumptions=assumptions) sum_relation = sum_rel_impl.derive_consequent(assumptions) if summand_lambda == greater_lambda: return sum_relation.with_direction_reversed() return sum_relation InnerExpr.register_equivalence_method(Sum, 'factorization', 'factorized', 'factor')
This works only if the operand x is an instance of the Add class at its outermost level, e.g. x = Add(a, b, …, n). The operands of that Add class can be other things, but the extraction is guaranteed to work only if the inner operands a, b, etc., are simple. ''' from ._theorems_ import roundOfRealPlusInt return apply_roundingExtraction(self, roundOfRealPlusInt, idx_to_extract, assumptions) def deduceInNumberSet(self, number_set, assumptions=USE_DEFAULTS): ''' Given a number set number_set, attempt to prove that the given Round expression is in that number set using the appropriate closure theorem. ''' from proveit.number.rounding._axioms_ import roundIsAnInt from proveit.number.rounding._theorems_ import roundRealPosClosure return rounding_deduceInNumberSet(self, number_set, roundIsAnInt, roundRealPosClosure, assumptions) # Register these generic expression equivalence methods: InnerExpr.register_equivalence_method(Round, 'roundingElimination', 'roundingEliminated', 'roundingEliminate') InnerExpr.register_equivalence_method(Round, 'roundingExtraction', 'roundingExtracted', 'roundingExtract')
raise IndexError("Index or indices out of bounds: {0}. " "subset indices i should satisfy " "0 ≤ i ≤ {1}.".format(unexpected_indices_set, len(valid_indices_set) - 1)) if len(subset_indices_list) > len(subset_indices_set): # we have repeated indices, so let's find them to use in # feedback/error message repeated_indices_set = set() for elem in subset_indices_set: if subset_indices_list.count(elem) > 1: repeated_indices_set.add(elem) raise ValueError("The subset_indices specification contains " "repeated indices, with repeated index or " "indices: {}. Each index value should appear at " "most 1 time.".format(repeated_indices_set)) # if we made it this far and proper_subset = True, # confirm that the subset indices are compatible with a proper # subset instead of an improper subset if proper_subset and len(subset_indices_set) == len(valid_indices_set): raise ValueError("The subset indices are not compatible with a " "proper subset (too many elements).") # Register these expression equivalence methods: InnerExpr.register_equivalence_method(Set, 'permutation', 'permuted', 'permute') InnerExpr.register_equivalence_method(Set, 'permutation_move', 'moved', 'move') InnerExpr.register_equivalence_method(Set, 'permutation_swap', 'swapped', 'swap') InnerExpr.register_equivalence_method(Set, 'reduction', 'reduced', 'reduce')