def _lambda_expr(lambda_map, expr_being_replaced, assumptions=USE_DEFAULTS): from proveit import ExprRange, InnerExpr if isinstance(lambda_map, InnerExpr): lambda_map = lambda_map.repl_lambda() if not isinstance(lambda_map, Lambda): # as a default, do a global replacement lambda_map = Lambda.global_repl(lambda_map, expr_being_replaced) if lambda_map.parameters.num_entries() != 1: raise ValueError("When substituting, expecting a single " "'lambda_map' parameter entry which may " "be a single parameter or a range; got " "%s as 'lambda_map'" % lambda_map) if isinstance(lambda_map.parameters[0], ExprRange): from proveit.numbers import one if lambda_map.parameters[0].start_index != one: raise ValueError("When substituting a range, expecting " "the 'lambda_map' parameter range to " "have a starting index of 1; got " "%s as 'lambda_map'" % lambda_map) return lambda_map
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
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 })