예제 #1
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
예제 #2
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
예제 #3
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
예제 #4
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)
예제 #5
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
예제 #6
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
예제 #7
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
예제 #8
0
def method_cbr(proof_step,
               selected_objects: [MathObject],
               user_input: [str] = []) -> CodeForLean:
    """
    Engage in a proof by cases.
    - If selection is empty, then ask the user to choose a property
    - If a disjunction is selected, use this disjunction for the proof by cases
    _ If anything else is selected try to discuss on this property
    (useful only in propositional calculus?)
    In any case, ask the user which case she wants to prove first.
    """

    if len(selected_objects) == 0:
        # NB: user_input[1] contains the needed property
        if len(user_input) == 1:
            raise MissingParametersError(
                 InputType.Text,
                 title=_("cases"),
                 output=_("Enter the property you want to discriminate on:")
                                        )
        else:
            h0 = user_input[1]
            h1 = get_new_hyp(proof_step)
            h2 = get_new_hyp(proof_step)
            code = CodeForLean.from_string(f"cases (classical.em ({h0})) "
                                           f"with {h1} {h2}")
            code.add_success_msg(_("Proof by cases"))
            code.add_disjunction(h0, h1, h2)  # Strings, not MathObject
    else:
        prop = selected_objects[0]
        if not prop.is_or():
            error = _("Selected property is not a disjunction")
            raise WrongUserInput(error)
        else:
            code = apply_or(proof_step, selected_objects, user_input)

    return code
예제 #9
0
def apply_implicate_to_hyp(proof_step,
                           selected_objects: [MathObject]) -> CodeForLean:
    """
    Try to apply last selected property on the other ones.
    The last property should be an implication
    (or equivalent to such after unfolding definitions)
    """

    implication = selected_objects[-1]
    new_hypo_name = get_new_hyp(proof_step)
    variable_names = [
        variable.info['name'] for variable in selected_objects[:-1]
    ]

    return have_new_property(implication, variable_names, new_hypo_name)
예제 #10
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
예제 #11
0
def construct_exists_on_hyp(proof_step,
                            selected_objects: [MathObject]) -> CodeForLean:
    """
    Try to construct an existence property from some object and some property
    Here len(l) = 2
    """

    x = selected_objects[0].info["name"]
    hx = selected_objects[1].info["name"]
    if (not selected_objects[0].math_type.is_prop()) \
            and selected_objects[1].math_type.is_prop():
        new_hypo = get_new_hyp(proof_step)
        code_string = f'have {new_hypo} := exists.intro {x} {hx}'
    elif (not selected_objects[1].math_type.is_prop()) \
            and selected_objects[0].math_type.is_prop():
        x, hx = hx, x
        new_hypo = get_new_hyp(proof_step)
        code_string = f'have {new_hypo} := exists.intro {x} {hx}'
    else:
        error = _("I cannot build an existential property with this")
        raise WrongUserInput(error)
    code = CodeForLean.from_string(code_string)
    code.add_success_msg(_("get new existential property {}").format(new_hypo))
    return code
예제 #12
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
예제 #13
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
예제 #14
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
예제 #15
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)
예제 #16
0
def apply_exists(proof_step, selected_object: [MathObject]) -> CodeForLean:
    """
    Apply a property '∃ x, P(x)' to get an x with property P(x).
    """

    selected_hypo = selected_object[0].math_type
    hypo_name = selected_object[0].info["name"]
    x = give_global_name(proof_step=proof_step,
                         math_type=selected_hypo.children[0],
                         hints=[selected_hypo.children[1].to_display()])
    new_hypo_name = get_new_hyp(proof_step)

    if selected_hypo.children[2].node == "PROP_∃":
        code = f'rcases {hypo_name} with ' \
                f'⟨ {x}, ⟨ {new_hypo_name}, {hypo_name} ⟩ ⟩'
    else:
        code = f'cases {hypo_name} with {x} {new_hypo_name}'
    code = CodeForLean.from_string(code)
    if selected_hypo.node == 'QUANT_∃!':
        # We have to add the "simp" tactic to avoid appearance of lambda expr
        code = code.and_then(f'simp at {new_hypo_name}')
    code.add_success_msg(
        _("new object {} with property {}").format(x, new_hypo_name))
    return code
예제 #17
0
def apply_forall(proof_step, l: [MathObject]) -> CodeForLean:
    """
    Try to apply last selected property on the other ones.
    The last property should be a universal property
    (or equivalent to such after unfolding definitions)

    :param l: list of MathObjects of length ≥ 2
    :return:
    """
    # FIXME: return error msg if user try to apply "forall x:X, P(x)"
    #  to some object of wrong type (e.g. implication)
    #  For the moment "forall x, P->Q" works with "P->Q" and button forall

    goal = proof_step.goal
    universal_property = l[-1]  # The property to be applied
    unsolved_inequality_counter = 0
    # Variable_names will contain the list of variables and proofs of
    # inequalities that will be passed to universal_property
    variable_names = []
    code = CodeForLean.empty_code()
    for potential_var in l[:-1]:
        # TODO: replace by pattern matching
        # Check for "∀x>0" (and variations)
        inequality = inequality_from_pattern_matching(universal_property,
                                                      potential_var)
        variable_names.append(potential_var.info['name'])
        if inequality:
            math_types = [p.math_type for p in goal.context]
            if inequality in math_types:
                index = math_types.index(inequality)
                inequality_name = goal.context[index].display_name
                variable_names.append(inequality_name)
            else:
                inequality_name = get_new_hyp(proof_step)
                variable_names.append(inequality_name)
                unsolved_inequality_counter += 1
                # Add type indication to the variable in inequality
                math_type = inequality.children[1].math_type
                # Variable is not used explicitly, but this affects inequality:
                variable = inequality.children[0]
                variable = add_type_indication(variable, math_type)
                display_inequality = inequality.to_display(is_math_type=False,
                                                           format_='lean')
                # Code I: state corresponding inequality #
                code = code.and_then(f"have {inequality_name}: "
                                     f"{display_inequality}")
                code = code.and_then("rotate")

    # Code II: Apply universal_property #
    new_hypo_name = get_new_hyp(proof_step)
    code = code.and_then(
        have_new_property(universal_property, variable_names, new_hypo_name))

    # Code III: try to solve inequalities #     e.g.:
    #   iterate 2 { solve1 {try {norm_num at *}, try {compute_n 10}} <|>
    #               rotate},   rotate,
    more_code = CodeForLean.empty_code()
    if unsolved_inequality_counter:
        code = code.and_then("rotate")  # back to first inequality
        more_code1 = CodeForLean.from_string("norm_num at *")
        more_code1 = more_code1.try_()
        more_code2 = CodeForLean.from_string("compute_n 1")
        more_code2 = more_code2.try_()
        # Try to solve1 inequality by norm_num, maybe followed by compute:
        more_code = more_code1.and_then(more_code2)
        more_code = more_code.single_combinator("solve1")
        # If it fails, rotate to next inequality
        more_code = more_code.or_else("rotate")
        # Do this for all inequalities
        #   more_code = more_code.single_combinator(f"iterate
        #   {unsolved_inequality_counter}") --> replaced by explicit iteration
        code_list = [more_code] * unsolved_inequality_counter
        more_code = CodeForLean.and_then_from_list(code_list)
        # Finally come back to first inequality
        more_code = more_code.and_then("rotate")

    code.add_success_msg(
        _("Property {} added to the context").format(new_hypo_name))
    return code.and_then(more_code)
예제 #18
0
def action_new_object(proof_step,
                      selected_objects: [MathObject],
                      user_input: [str] = None,
                      target_selected: bool = True) -> CodeForLean:
    """
    Introduce new object / sub-goal / function
    """

    goal = proof_step.goal

    codes = []
    # Choose between object/sub-goal/function
    if not user_input:
        raise MissingParametersError(InputType.Choice,
                             [(_("Object"), _("Introduce a new object")),
                              (_("Goal"), _("Introduce a new "
                                            "intermediate sub-goal")),
                              (_("Function"), _("Introduce a new "
                                                "function"))],
                             title=_("New object"),
                             output=_("Choose what to introduce:"))
    # Choice = new object
    if user_input[0] == 0:
        if len(user_input) == 1:  # Ask for name
            raise MissingParametersError(InputType.Text,
                                         title="+",
                                         output=_("Name your object:"))
        if len(user_input) == 2:  # Ask for new object
            raise MissingParametersError(InputType.Text,
                                         title="+",
                                         output=_("Introduce a new object")
                                         + "(" + _("e.g.")
                                         + "0, {1}, f(2), ...)")
        else:  # Send code
            name = user_input[1]
            new_hypo_name = get_new_hyp(proof_step)
            new_object = user_input[2]
            codes = CodeForLean.from_string(f"let {name} := {new_object}")
            codes = codes.and_then(f"have {new_hypo_name} : {name} = "
                                                     f"{new_object}")
            codes = codes.and_then("refl")
            codes.add_success_msg(_("New object {} added to the context").
                                  format(name))
            if goal.target.is_for_all():
                # User might want to prove a universal property "∀x..."
                # and mistake "new object" for introduction of the relevant x.
                codes.add_error_msg(_("You might try the ∀ button..."))

    # Choice = new sub-goal
    elif user_input[0] == 1:
        if len(user_input) == 1:
            raise MissingParametersError(InputType.Text,
                                         title="+",
                                         output=_("Introduce new subgoal:"))
        else:
            new_hypo_name = get_new_hyp(proof_step)
            codes = CodeForLean.from_string(f"have {new_hypo_name}:"
                                                     f" ({user_input[1]})")
            codes.add_success_msg(_("New target will be added to the context "
                                    "after being proved"))
            codes.add_subgoal(user_input[1])
    # Choice = new function
    elif user_input[0] == 2:
        return introduce_fun(proof_step, selected_objects)
    return codes