예제 #1
0
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
예제 #2
0
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
예제 #3
0
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
예제 #4
0
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
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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
예제 #8
0
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
예제 #9
0
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
예제 #10
0
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
예제 #11
0
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)
예제 #12
0
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
예제 #13
0
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
예제 #14
0
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
예제 #15
0
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
예제 #16
0
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
예제 #17
0
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
예제 #18
0
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
예제 #19
0
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
예제 #20
0
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
예제 #21
0
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
예제 #22
0
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
예제 #23
0
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
예제 #24
0
    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)
예제 #25
0
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
예제 #26
0
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)
예제 #27
0
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
예제 #28
0
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
예제 #29
0
    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)
예제 #30
0
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)