def action_negate(proof_step, selected_objects: [MathObject], target_selected: bool = True) -> CodeForLean: """ Translate into string of lean code corresponding to the action If no hypothesis has been previously selected: transform the target in an equivalent one with its negations 'pushed'. If a hypothesis has been previously selected: do the same to the hypothesis. """ test_selection(selected_objects, target_selected) goal = proof_step.goal if len(selected_objects) == 0: if not goal.target.is_not(): raise WrongUserInput(error=_("Target is not a negation 'NOT P'")) code = CodeForLean.from_string('push_neg') code.add_success_msg(_("Negation pushed on target")) elif len(selected_objects) == 1: if not selected_objects[0].is_not(): error = _("Selected property is not a negation 'NOT P'") raise WrongUserInput(error) selected_hypo = selected_objects[0].info["name"] code = CodeForLean.from_string(f'push_neg at {selected_hypo}') code.add_success_msg( _(f"Negation pushed on property " f"{selected_hypo}")) else: raise WrongUserInput(error=_('Only one property at a time')) return code
def action_compute(proof_step, selected_objects, target_selected: bool = True): """ If the target is an equality, an inequality (or 'False'), then send tactics trying to prove the goal by (mainly linearly) computing. """ goal = proof_step.goal target = goal.target if not (target.is_equality() or target.is_inequality() or target.is_false()): error = _("target is not an equality, an inequality, " "nor a contradiction") raise WrongUserInput(error) # try_before = "try {apply div_pos}, " \ # + "try { all_goals {norm_num at *}}" \ # + ", " # simplify_1 = "simp only " \ # + "[*, ne.symm, ne.def, not_false_iff, lt_of_le_of_ne]" # possible_code = [try_before + "linarith", # try_before + simplify_1] # "finish" "norm_num *" # if user_config.getboolean('use_library_search_for_computations'): # possible_code.append('library_search') code1 = CodeForLean.from_string("norm_num at *").solve1() code2 = CodeForLean.from_string("compute_n 10") code3 = CodeForLean.from_string("norm_num at *").try_().and_then(code2) possible_code = code1.or_else(code3) #possible_code = [solve1_wrap("norm_num at *"), # solve1_wrap("try {norm_num at *}, compute_n 10")] return possible_code
def construct_or(proof_step, user_input: [str]) -> CodeForLean: """ Assuming target is a disjunction 'P OR Q', choose to prove either P or Q. """ target = proof_step.goal.target left = target.math_type.children[0].to_display() right = target.math_type.children[1].to_display() choices = [(_("Left"), left), (_("Right"), right)] if not user_input: raise MissingParametersError(InputType.Choice, choices, title=_("Choose new goal"), output=_("Which property will you " "prove?")) code = None if len(user_input) == 1: i = user_input[0] if i == 0: code = CodeForLean.from_string("left") code.add_success_msg(_("Target replaced by the left alternative")) else: code = CodeForLean.from_string("right") code.add_success_msg(_("Target replaced by the right alternative")) return code
def action_assumption(proof_step, selected_objects: [MathObject], target_selected: bool = True) -> CodeForLean: """ Translate into string of lean code corresponding to the action. """ goal = proof_step.goal # (1) First trials target = goal.target improved_assumption = solve_target(target) codes = [improved_assumption] # (2) Use user selection if len(selected_objects) == 1: apply_user = CodeForLean.from_string( f'apply {selected_objects[0].info["name"]}') codes.append(apply_user.solve1()) # (3) Conjunctions: try to split hypotheses once # TODO: recursive splitting in hypo # And then retry many things (in improved_assumption_2). split_conj = split_conjunctions_in_context(proof_step) improved_assumption_2 = solve_target(target) # Split target if target.is_and(): # TODO: recursive splitting in target, and_then for each subgoal # apply improved_assumption split_code = CodeForLean.from_string('split, assumption, assumption') improved_assumption_2 = improved_assumption_2.or_else(split_code) # Try norm_num if (goal.target.is_equality() or goal.target.is_inequality() or goal.target.is_false()): # Do not remove above test, since norm_num may solve some # not-so-trivial goal, e.g. implications norm_num_code = CodeForLean.from_string('norm_num at *').solve1() improved_assumption_2 = improved_assumption_2.or_else(norm_num_code) # Try specific properties more_assumptions = search_specific_prop(proof_step) if not more_assumptions.is_empty(): improved_assumption_2 = improved_assumption_2.or_else(more_assumptions) more_code = split_conj.and_then(improved_assumption_2) codes.append(more_code) code = CodeForLean.or_else_from_list(codes) code = code.solve1() code.add_error_msg(_("I don't know how to conclude")) return code
def solve_target(prop): """ Try to solve 'prop' as a target, without splitting conjunctions. """ code = CodeForLean.empty_code() # if len(l) == 0: code = code.or_else('assumption') code = code.or_else('contradiction') # (1) Equality, inequality, iff if prop.is_equality() or prop.is_iff(): code = code.or_else(solve_equality(prop)) elif prop.is_inequality(): # try to permute members of the goal equality code = code.or_else('apply ne.symm, assumption') # (2) Try some symmetry rules if prop.is_iff(): code = code.or_else('apply iff.symm, assumption') elif prop.is_and(): # todo: change to "id_and_like" code = code.or_else('apply and.symm, assumption') # The following will be tried only after context splitting: # code = code.or_else('split, assumption, assumption') elif prop.is_or(): code = code.or_else('apply or.symm, assumption') # The following is too much? code = code.or_else('left, assumption') code = code.or_else('right, assumption') return code
def rw_using_statement(goal: Goal, selected_objects: [MathObject], statement) -> CodeForLean: """ Return codes trying to use statement for rewriting. This should be reserved to iff or equalities. This function is called by action_definition, and by action_theorem in case the theorem is an iff statement. """ codes = CodeForLean.empty_code() defi = statement.lean_name if len(selected_objects) == 0: target_msg = _('definition applied to target') \ if statement.is_definition() else _('theorem applied to target') \ if statement.is_theorem() else _('exercise applied to target') codes = codes.or_else(f'rw {defi}') codes = codes.or_else(f'simp_rw {defi}') codes = codes.or_else(f'rw <- {defi}') codes = codes.or_else(f'simp_rw <- {defi}') codes.add_success_msg(target_msg) else: names = [item.info['name'] for item in selected_objects] arguments = ' '.join(names) context_msg = _('definition applied to') \ if statement.is_definition() else _('theorem applied to') \ if statement.is_theorem() else _('exercise applied to') context_msg += ' ' + arguments codes = codes.or_else(f'rw {defi} at {arguments}') codes = codes.or_else(f'simp_rw {defi} at {arguments}') codes = codes.or_else(f'rw <- {defi} at {arguments}') codes = codes.or_else(f'simp_rw <- {defi} at {arguments}') codes.add_success_msg(context_msg) return codes
def construct_or_on_hyp(proof_step, selected_property: [MathObject], user_input: [str] = None) -> CodeForLean: """ Construct a property 'P or Q' from property 'P' or property 'Q'. Here we assume selected_object contains 1 or 2 items. """ if not user_input: user_input = [] possible_codes = [] first_hypo_name = selected_property[0].info["name"] hypo = selected_property[0].math_type.to_display() if len(selected_property) == 2: if not (selected_property[0].math_type.is_prop() and selected_property[1].math_type.is_prop()): error = _("Selected items are not properties") raise WrongUserInput(error) else: second_selected_property = selected_property[1].info["name"] elif len(selected_property) == 1: if not selected_property[0].math_type.is_prop(): error = _("Selected item is not a property") raise WrongUserInput(error) if not user_input: raise MissingParametersError( InputType.Text, title=_("Obtain 'P OR Q'"), output=_("Enter the proposition you want to use:")) else: second_selected_property = user_input[0] user_input = user_input[1:] if not user_input: raise MissingParametersError( InputType.Choice, [(_("Left"), f'({hypo}) OR ({second_selected_property})'), (_('Right'), f'({second_selected_property}) OR ({hypo})')], title=_("Choose side"), output=_(f'On which side do you want') + f' {hypo} ?') new_hypo_name = get_new_hyp(proof_step) if user_input[0] == 0: possible_codes.append(f'have {new_hypo_name} := ' f'@or.inl _ ({second_selected_property}) ' f'({first_hypo_name})') elif user_input[0] == 1: possible_codes.append(f'have {new_hypo_name} := ' f'@or.inr ({second_selected_property}) _ ' f'({first_hypo_name})') else: raise WrongUserInput("Unexpected error") code = CodeForLean.or_else_from_list(possible_codes) code.add_success_msg( _('Property {} added to the context').format(new_hypo_name)) return code
def apply_implicate(proof_step, selected_object: [MathObject]) -> CodeForLean: """ Here selected_object contains a single property which is an implication P ⇒ Q; if the target is Q then it will be replaced by P. """ selected_hypo = selected_object[0].info["name"] code = CodeForLean.from_string(f'apply {selected_hypo}') code.add_success_msg( _("Target modified using implication {}").format(selected_hypo)) return code
def search_specific_prop(proof_step): """Search context for some specific properties to conclude the proof.""" goal = proof_step.goal more_code = CodeForLean.empty_code() for prop in goal.context: math_type = prop.math_type # Conclude from "x in empty set" if (math_type.node == "PROP_BELONGS" and math_type.children[1].node == "SET_EMPTY"): hypo = prop.info['name'] if goal.target.node != "PROP_FALSE": more_code = CodeForLean.from_string("exfalso") else: more_code = CodeForLean.empty_code() more_code = more_code.and_then(f"exact set.not_mem_empty _ {hypo}") return more_code
def action_theorem(proof_step, selected_objects: [MathObject], theorem, target_selected: bool = True ): """ Apply theorem on selected objects or target. """ # test_selection(selected_objects, target_selected) # TODO: For an iff statement, use rewriting # test for iff or equality is removed since it works only with # pkl files goal = proof_step.goal codes = rw_using_statement(goal, selected_objects, theorem) h = get_new_hyp(proof_step) th = theorem.lean_name if len(selected_objects) == 0: codes = codes.or_else(f'apply {th}', success_msg=_('theorem applied to target')) codes = codes.or_else(f'have {h} := @{th}', success_msg=_('theorem added to the context')) else: command = f'have {h} := {th}' command_implicit = f'have {h} := @{th}' names = [item.info['name'] for item in selected_objects] arguments = ' '.join(names) # up to 4 implicit arguments more_codes = [f'apply {th} {arguments}', f'apply @{th} {arguments}'] more_codes += [command + arguments, command_implicit + arguments, command + ' _ ' + arguments, command_implicit + ' _ ' + arguments, command + ' _ _ ' + arguments, command_implicit + ' _ _ ' + arguments, command + ' _ _ _ ' + arguments, command_implicit + ' _ _ _ ' + arguments, command + ' _ _ _ _ ' + arguments, command_implicit + ' _ _ _ _ ' + arguments ] more_codes = CodeForLean.or_else_from_list(more_codes) context_msg = _('theorem') + ' ' + _('applied to') + ' ' + arguments more_codes.add_success_msg(context_msg) codes = codes.or_else(more_codes) codes.add_error_msg(_("unable to apply theorem")) return codes
def method_absurdum(proof_step, selected_objects: [MathObject]) -> CodeForLean: """ If no selection, engage in a proof by contradiction. """ if len(selected_objects) == 0: new_hypo = get_new_hyp(proof_step) code = CodeForLean.from_string(f'by_contradiction {new_hypo}') code.add_success_msg(_("Negation of target added to the context")) return code else: error = _('Proof by contradiction only applies to target') raise WrongUserInput(error)
def construct_implicate(proof_step) -> CodeForLean: """ Here the target is assumed to be an implication P ⇒ Q, P is added to the context, and the target becomes Q. """ if not proof_step.goal.target.is_implication(): raise WrongUserInput(error=_("Target is not an implication 'P ⇒ Q'")) else: new_hypo_name = get_new_hyp(proof_step) code = CodeForLean.from_string(f'intro {new_hypo_name}') code.add_success_msg( _("Property {} added to the context").format(new_hypo_name)) return code
def construct_forall(proof_step) -> CodeForLean: """ Here goal.target is assumed to be a universal property "∀ x:X, ...", and variable x is introduced. """ goal = proof_step.goal math_object = goal.target.math_type math_type = math_object.children[0] variable = math_object.children[1] body = math_object.children[2] hint = variable.display_name # not optimal if math_type.node == "PRODUCT": [math_type_1, math_type_2] = math_type.children x = give_global_name(proof_step=proof_step, math_type=math_type_1) y = give_global_name(proof_step=proof_step, math_type=math_type_2) possible_codes = CodeForLean.from_string(f'rintro ⟨ {x}, {y} ⟩') name = f"({x},{y})" else: x = give_global_name(proof_step=proof_step, math_type=math_type, hints=[hint]) possible_codes = CodeForLean.from_string(f'intro {x}') name = f"{x}" possible_codes.add_success_msg( _("Object {} added to the context").format(name)) if body.is_implication(is_math_type=True): # If math_object has the form # ∀ x:X, (x R ... ==> ...) # where R is some inequality relation # then introduce the inequality on top of x premise = body.children[0] # children (2,0) if premise.is_inequality(is_math_type=True): h = get_new_hyp(proof_step) # Add and_then intro h possible_codes = possible_codes.and_then(f'intro {h}') return possible_codes
def apply_and(proof_step, selected_objects) -> CodeForLean: """ Destruct a property 'P and Q'. Here selected_objects is assumed to contain exactly one conjunction property. """ selected_hypo = selected_objects[0].info["name"] h1 = get_new_hyp(proof_step) h2 = get_new_hyp(proof_step) code = CodeForLean.from_string(f'cases {selected_hypo} with {h1} {h2}') code.add_success_msg( _("Split property {} into {} and {}").format(selected_hypo, h1, h2)) return code
def split_conjunctions_in_context(proof_step): goal = proof_step.goal code = CodeForLean.empty_code() counter = 0 for prop in goal.context: if prop.is_and(): name = prop.info['name'] h0 = f"H_aux_{counter}" # these hypotheses will disappear h1 = f"H_aux_{counter + 1}" # so names are unimportant code = code.and_then(f"cases {name} with {h0} {h1}") counter += 2 return code
def construct_iff(proof_step, user_input: [str]) -> CodeForLean: """ Assuming target is an iff, split into two implications. """ target = proof_step.goal.target.math_type code = CodeForLean.empty_code() left = target.children[0] right = target.children[1] if not user_input: choices = [("⇒", f'({left.to_display()}) ⇒ ({right.to_display()})'), ("⇐", f'({right.to_display()}) ⇒ ({left.to_display()})')] raise MissingParametersError( InputType.Choice, choices, title=_("Choose sub-goal"), output=_("Which implication to prove first?")) elif len(user_input) == 1: if user_input[0] == 1: code = CodeForLean.from_string("rw iff.comm") left, right = right, left code = code.and_then("split") else: raise WrongUserInput(error=_("Undocumented error")) code.add_success_msg(_("Iff split in two implications")) impl1 = MathObject(info={}, node="PROP_IMPLIES", children=[left, right], math_type="PROP") impl2 = MathObject(info={}, node="PROP_IMPLIES", children=[right, left], math_type="PROP") code.add_conjunction(target, impl1, impl2) return code
def apply_or(proof_step, selected_objects: [MathObject], user_input: [str]) -> CodeForLean: """ Assuming selected_objects is one disjunction 'P OR Q', engage in a proof by cases. """ # if not selected_objects[0].is_or(): # raise WrongUserInput(error=_("Selected property is not " # "a disjunction 'P OR Q'")) selected_hypo = selected_objects[0] code = CodeForLean.empty_code() left = selected_hypo.math_type.children[0] right = selected_hypo.math_type.children[1] if not user_input: choices = [(_("Left"), left.to_display()), (_("Right"), right.to_display())] raise MissingParametersError(InputType.Choice, choices=choices, title=_("Choose case"), output=_("Which case to assume first?")) else: # len(user_input) == 1 if user_input[0] == 1: # If user wants the second property first, then first permute code = f'rw or.comm at {selected_hypo.info["name"]}' code = CodeForLean.from_string(code) left, right = right, left h1 = get_new_hyp(proof_step) h2 = get_new_hyp(proof_step) # Destruct the disjunction code = code.and_then(f'cases {selected_hypo.info["name"]} with {h1} {h2}') code.add_success_msg(_("Proof by cases")) code.add_disjunction(selected_hypo, left, right) return code
def construct_and_hyp(proof_step, selected_objects: [MathObject]) \ -> CodeForLean: """ Construct 'P AND Q' from properties P and Q. Here selected_objects is assumed to contain exactly two properties. """ h1 = selected_objects[0].info["name"] h2 = selected_objects[1].info["name"] new_hypo_name = get_new_hyp(proof_step) code = f'have {new_hypo_name} := and.intro {h1} {h2}' code = CodeForLean.from_string(code) code.add_success_msg( _("Conjunction {} added to the context").format(new_hypo_name)) return code
def construct_and(proof_step, user_input: [str]) -> CodeForLean: """ Split the target 'P AND Q' into two sub-goals. """ target = proof_step.goal.target.math_type if not target.is_and(is_math_type=True): raise WrongUserInput(error=_("Target is not a conjunction 'P AND Q'")) left = target.children[0] right = target.children[1] if not user_input: # User choice choices = [(_("Left"), left.to_display()), (_("Right"), right.to_display())] raise MissingParametersError( InputType.Choice, choices, title=_("Choose sub-goal"), output=_("Which property to prove first?")) else: if user_input[0] == 1: # Prove second property first if target.node == "PROP_∃": # In this case, first rw target as a conjunction code = CodeForLean.and_then_from_list( ["rw exists_prop", "rw and.comm"]) else: code = CodeForLean.from_string("rw and.comm") left, right = right, left else: code = CodeForLean.empty_code() code = code.and_then("split") code.add_success_msg(_('Target split')) code.add_conjunction(target, left, right) return code
def construct_iff_on_hyp(proof_step, selected_objects: [MathObject]) -> CodeForLean: """ Construct property 'P iff Q' from both implications. len(selected_objects) should be 2. """ new_hypo_name = get_new_hyp(proof_step) h1 = selected_objects[0].info["name"] h2 = selected_objects[1].info["name"] code_string = f'have {new_hypo_name} := iff.intro {h1} {h2}' code = CodeForLean.from_string(code_string) code.add_success_msg( _("Logical equivalence {} added to the context").format(new_hypo_name)) return code
def destruct_iff_on_hyp(proof_step, selected_objects: [MathObject]) -> CodeForLean: """ Split a property 'P iff Q' into two implications. len(selected_objects) should be 1. """ possible_codes = [] hypo_name = selected_objects[0].info["name"] h1 = get_new_hyp(proof_step) h2 = get_new_hyp(proof_step) possible_codes.append(f'cases (iff_def.mp {hypo_name}) with {h1} {h2}') code = CodeForLean.or_else_from_list(possible_codes) code.add_success_msg( _("property {} split into {} and {}").format(hypo_name, h1, h2)) return code
def construct_exists(proof_step, user_input: [str]) -> CodeForLean: """ Assuming the target is an existential property '∃ x, P(x)', prove it by providing a witness x and proving P(x). """ if not user_input: raise MissingParametersError( InputType.Text, title=_("Exist"), output=_("Enter element you want to use:")) x = user_input[0] code = CodeForLean.from_string(f'use {x}, dsimp') code = code.or_else(f'use {x}') code.add_success_msg(_("now prove {} suits our needs").format(x)) return code
def solve_equality(prop: MathObject) -> CodeForLean: """ Assuming prop is an equality or an iff, return a list of tactics trying to solve prop as a target. """ code = CodeForLean.empty_code() if prop.math_type.children[0] == prop.math_type.children[1]: code = code.or_else('refl') # try to use associativity and commutativity code = code.or_else('ac_reflexivity') # try to permute members of the goal equality code = code.or_else('apply eq.symm, assumption') # congruence closure, solves e.g. (a=b, b=c : f a = f c) code = code.or_else('cc') return code
def __init__(self, nursery): super().__init__() self.log = logging.getLogger("ServerInterface") # Lean environment self.lean_env: LeanEnvironment = LeanEnvironment(inst) # Lean attributes self.lean_file: LeanFile = None self.lean_server: LeanServer = LeanServer(nursery, self.lean_env) self.nursery: trio.Nursery = nursery # Set server callbacks self.lean_server.on_message_callback = self.__on_lean_message self.lean_server.running_monitor.on_state_change_callback = \ self.__on_lean_state_change # Current exercise self.exercise_current = None # Current proof state + Events self.file_invalidated = trio.Event() self.__proof_state_valid = trio.Event() # __proof_receive_done is set when enough information have been # received, i.e. either we have context and target and all effective # codes, OR an error message. self.__proof_receive_done = trio.Event() # self.__proof_receive_error = trio.Event() # Set if request # failed self.__tmp_hypo_analysis = "" self.__tmp_targets_analysis = "" # When some CodeForLean iss sent to the __update method, it will be # duplicated and stored in __tmp_effective_code. This attribute will # be progressively modified into an effective code which is devoid # of or_else combinator, according to the "EFFECTIVE CODE" messages # sent by Lean. self.__tmp_effective_code = CodeForLean.empty_code() self.proof_state = None # Errors memory channels self.error_send, self.error_recv = \ trio.open_memory_channel(max_buffer_size=1024)
def destruct_iff(proof_step) -> CodeForLean: """ Check if target is a conjunction of two implications (but not if these implications are logical inverses). If so, return code that builds the equivalent iff statement. If not, return None. """ goal = proof_step.goal code = None target = goal.target if target.is_and(): left = target.math_type.children[0] right = target.math_type.children[1] if left.is_implication(is_math_type=True) \ and right.is_implication(is_math_type=True): code = CodeForLean.from_string("apply iff_def.mp") code.add_success_msg(_("target replaced by iff property")) return code
def method_contrapose(proof_step, selected_objects: [MathObject]) -> CodeForLean: """ If target is an implication, turn it to its contrapose. """ goal = proof_step.goal if len(selected_objects) == 0: if goal.target.math_type.node == "PROP_IMPLIES": code = CodeForLean.from_string("contrapose") code.add_success_msg(_("Target replaced by contrapositive")) return code else: error = _('Proof by contraposition only applies when target is ' 'an implication') else: error = _('Proof by contraposition only applies to target') raise WrongUserInput(error)
def have_new_property(arrow: MathObject, variable_names: [str], new_hypo_name: str) -> CodeForLean: """ :param arrow: a MathObject which is either an implication or a universal property :param variable_names: a list of names of variables (or properties) to which "arrow" will be applied :param new_hypo_name: a fresh name for the new property return: Lean Code to produce the wanted new property, taking into account implicit parameters """ selected_hypo = arrow.info["name"] command = f'have {new_hypo_name} := {selected_hypo}' command_explicit = f'have {new_hypo_name} := @{selected_hypo}' arguments = ' '.join(variable_names) # try with up to 4 implicit parameters implicit_codes = [ command + ' ' + arguments, command + ' _ ' + arguments, command + ' _ _ ' + arguments, command + ' _ _ _ ' + arguments, command + ' _ _ _ _ ' + arguments ] explicit_codes = [ command_explicit + ' ' + arguments, command_explicit + ' _ ' + arguments, command_explicit + ' _ _ ' + arguments, command_explicit + ' _ _ _ ' + arguments, command_explicit + ' _ _ _ _ ' + arguments ] possible_codes = implicit_codes + explicit_codes code = CodeForLean.or_else_from_list(possible_codes) code.add_success_msg( _("Property {} added to the context").format(new_hypo_name)) return code
def apply_function(proof_step, selected_objects: [MathObject]): """ Apply l[-1], which is assumed to be a function f, to previous elements of l, which can be: - an equality - an object x (then create the new object f(x) ) l should have length at least 2 """ log.debug('Applying function') codes = CodeForLean.empty_code() # let us check the input is indeed a function function = selected_objects[-1] # if function.math_type.node != "FUNCTION": # raise WrongUserInput f = function.info["name"] Y = selected_objects[-1].math_type.children[1] while len(selected_objects) != 1: new_h = get_new_hyp(proof_step) # if function applied to a property, presumed to be an equality if selected_objects[0].math_type.is_prop(): h = selected_objects[0].info["name"] codes = codes.or_else(f'have {new_h} := congr_arg {f} {h}') codes.add_success_msg(_("function {} applied to {}").format(f, h)) # if function applied to element x: # create new element y and new equality y=f(x) else: x = selected_objects[0].info["name"] y = give_global_name(proof_step =proof_step, math_type=Y, hints=[Y.info["name"].lower()]) msg = _("new objet {} added to the context").format(y) codes = codes.or_else(f'set {y} := {f} {x} with {new_h}', success_msg=msg) selected_objects = selected_objects[1:] return codes
async def code_insert(self, label: str, lean_code: CodeForLean): """ Inserts code in the Lean virtual file. """ # Add "no meta vars" + "effective code nb" # and keep track of node_counters lean_code, code_string = lean_code.to_decorated_string() code_string = code_string.strip() if not code_string.endswith(","): code_string += "," if not code_string.endswith("\n"): code_string += "\n" self.log.info("CodeForLean: ") self.log.info(lean_code) self.log.info("Code sent to Lean: " + code_string) self.lean_file.insert(label=label, add_txt=code_string) await self.__update(lean_code)
def introduce_fun(proof_step, selected_objects: [MathObject]) -> CodeForLean: """ If a hypothesis of form ∀ a ∈ A, ∃ b ∈ B, P(a,b) has been previously selected: use the axiom of choice to introduce a new function f : A → B and add ∀ a ∈ A, P(a, f(a)) to the properties. """ goal = proof_step.goal error = _('select a property "∀ x, ∃ y, P(x,y)" to get a function') success = _("function {} and property {} added to the context") if len(selected_objects) == 1: h = selected_objects[0].info["name"] # Finding expected math_type for the new function universal_property = selected_objects[0] if universal_property.is_for_all(): source_type = universal_property.math_type.children[0] exist_property = universal_property.math_type.children[2] if exist_property.is_exists(is_math_type=True): target_type = exist_property.children[0] math_type = MathObject(node="FUNCTION", info={}, children=[source_type, target_type], math_type=NO_MATH_TYPE) hf = get_new_hyp(proof_step) f = give_global_name(math_type=math_type, proof_step=proof_step) code = CodeForLean.from_string(f'cases ' f'classical.axiom_of_choice ' f'{h} with {f} {hf}, ' f'dsimp at {hf}, ' f'dsimp at {f}') code.add_error_msg(error) success = success.format(f, hf) code.add_success_msg(success) return code raise WrongUserInput(error)