refactor(library/blast): make sure all actions return action_result, add macros for simplifying strategy definition

revert is not an action, but a procedure for helping us to build actions.
This commit is contained in:
Leonardo de Moura 2015-11-18 12:27:24 -08:00
parent 59e676e4fa
commit 453e90261b
13 changed files with 118 additions and 148 deletions

View file

@ -1,5 +1,5 @@
add_library(blast OBJECT expr.cpp state.cpp blast.cpp blast_tactic.cpp add_library(blast OBJECT expr.cpp state.cpp blast.cpp blast_tactic.cpp
init_module.cpp simplifier.cpp simple_actions.cpp intros_action.cpp proof_expr.cpp init_module.cpp simplifier.cpp simple_actions.cpp intros_action.cpp proof_expr.cpp
options.cpp choice_point.cpp simple_strategy.cpp backward_action.cpp util.cpp options.cpp choice_point.cpp simple_strategy.cpp backward_action.cpp util.cpp
gexpr.cpp revert_action.cpp subst_action.cpp no_confusion_action.cpp gexpr.cpp revert.cpp subst_action.cpp no_confusion_action.cpp
simplify_actions.cpp strategy.cpp recursor_action.cpp congruence_closure.cpp) simplify_actions.cpp strategy.cpp recursor_action.cpp congruence_closure.cpp)

View file

@ -21,9 +21,9 @@ public:
private: private:
kind m_kind; kind m_kind;
expr m_proof; expr m_proof;
public:
action_result(bool b = true):m_kind(b ? NewBranch : Failed) {} action_result(bool b = true):m_kind(b ? NewBranch : Failed) {}
action_result(expr const & pr):m_kind(Solved), m_proof(pr) {} action_result(expr const & pr):m_kind(Solved), m_proof(pr) {}
public:
kind get_kind() const { return m_kind; } kind get_kind() const { return m_kind; }
expr get_proof() const { lean_assert(m_kind == Solved); return m_proof; } expr get_proof() const { lean_assert(m_kind == Solved); return m_proof; }
static action_result failed() { return action_result(false); } static action_result failed() { return action_result(false); }
@ -34,4 +34,7 @@ public:
inline bool failed(action_result const & r) { return r.get_kind() == action_result::Failed; } inline bool failed(action_result const & r) { return r.get_kind() == action_result::Failed; }
inline bool solved(action_result const & r) { return r.get_kind() == action_result::Solved; } inline bool solved(action_result const & r) { return r.get_kind() == action_result::Solved; }
#define Try(Code) { action_result r = Code; if (!failed(r)) return r; }
#define TrySolve(Code) { action_result r = Code; if (solved(r)) return r.to_opt_expr(); }
}} }}

View file

@ -23,13 +23,13 @@ struct intros_proof_step_cell : public proof_step_cell {
virtual bool is_silent() const override { return true; } virtual bool is_silent() const override { return true; }
}; };
bool intros_action(unsigned max) { action_result intros_action(unsigned max) {
if (max == 0) if (max == 0)
return true; return action_result::new_branch();
state & s = curr_state(); state & s = curr_state();
expr target = whnf(s.get_target()); expr target = whnf(s.get_target());
if (!is_pi(target)) if (!is_pi(target))
return false; return action_result::failed();
auto pcell = new intros_proof_step_cell(); auto pcell = new intros_proof_step_cell();
s.push_proof_step(pcell); s.push_proof_step(pcell);
buffer<expr> new_hs; buffer<expr> new_hs;
@ -48,10 +48,10 @@ bool intros_action(unsigned max) {
} }
pcell->m_new_hs = to_list(new_hs); pcell->m_new_hs = to_list(new_hs);
s.set_target(target); s.set_target(target);
return true; return action_result::new_branch();
} }
bool intros_action() { action_result intros_action() {
return intros_action(std::numeric_limits<unsigned>::max()); return intros_action(std::numeric_limits<unsigned>::max());
} }
}} }}

View file

@ -5,11 +5,13 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura Author: Leonardo de Moura
*/ */
#pragma once #pragma once
#include "library/blast/action_result.h"
namespace lean { namespace lean {
namespace blast { namespace blast {
/** \brief Introduce upto \c max hypotheses. /** \brief Introduce upto \c max hypotheses.
Return false if there is nothing to introduce, that is, target is not a Pi-type. */ Return failed if there is nothing to introduce, that is, target is not a Pi-type.
bool intros_action(unsigned max); \remark if max == 0, and it returns new_branch. */
action_result intros_action(unsigned max);
/** \brief Keep introducing until target is not a Pi-type. */ /** \brief Keep introducing until target is not a Pi-type. */
bool intros_action(); action_result intros_action();
}} }}

View file

@ -7,7 +7,7 @@ Author: Leonardo de Moura
#include "kernel/instantiate.h" #include "kernel/instantiate.h"
#include "kernel/inductive/inductive.h" #include "kernel/inductive/inductive.h"
#include "library/user_recursors.h" #include "library/user_recursors.h"
#include "library/blast/revert_action.h" #include "library/blast/revert.h"
#include "library/blast/blast.h" #include "library/blast/blast.h"
namespace lean { namespace lean {
@ -174,7 +174,7 @@ action_result recursor_action(hypothesis_idx hidx, name const & R) {
s.collect_direct_forward_deps(hidx, to_revert); s.collect_direct_forward_deps(hidx, to_revert);
for (auto i : indices) for (auto i : indices)
s.collect_direct_forward_deps(href_index(i), to_revert); s.collect_direct_forward_deps(href_index(i), to_revert);
revert_action(to_revert); revert(to_revert);
expr target = s.get_target(); expr target = s.get_target();
auto target_level = get_type_context().get_level_core(target); auto target_level = get_type_context().get_level_core(target);
@ -290,4 +290,40 @@ action_result recursor_action(hypothesis_idx hidx) {
} }
return action_result::failed(); return action_result::failed();
} }
action_result recursor_preprocess_action(hypothesis_idx hidx) {
if (optional<name> R = is_recursor_action_target(hidx)) {
unsigned num_minor = get_num_minor_premises(*R);
if (num_minor == 1) {
action_result r = recursor_action(hidx, *R);
if (!failed(r)) {
// if (!preprocess) display_action("recursor");
return r;
}
} else {
// If the hypothesis recursor has more than 1 minor premise, we
// put it in a priority queue.
// TODO(Leo): refine
// TODO(Leo): the following weight computation is too simple...
double w = 1.0 / (static_cast<double>(hidx) + 1.0);
if (!is_recursive_recursor(*R)) {
// TODO(Leo): we need a better strategy for handling recursive recursors...
w += static_cast<double>(num_minor);
curr_state().add_to_rec_queue(hidx, w);
return action_result::new_branch();
}
}
}
return action_result::failed();
}
action_result recursor_action() {
while (auto hidx = curr_state().select_rec_hypothesis()) {
if (optional<name> R = is_recursor_action_target(*hidx)) {
Try(recursor_action(*hidx, *R));
}
}
return action_result::failed();
}
}} }}

View file

@ -9,13 +9,6 @@ Author: Leonardo de Moura
#include "library/blast/hypothesis.h" #include "library/blast/hypothesis.h"
namespace lean { namespace lean {
namespace blast { namespace blast {
/** \brief Return the name of the recursor that can be used with the given hypothesis */ action_result recursor_preprocess_action(hypothesis_idx hidx);
optional<name> is_recursor_action_target(hypothesis_idx hidx); action_result recursor_action();
/** \brief Return the number of minor premises of the given recursor */
unsigned get_num_minor_premises(name const & R);
bool is_recursive_recursor(name const & R);
action_result recursor_action(hypothesis_idx hidx, name const & R);
action_result recursor_action(hypothesis_idx hidx);
}} }}

View file

@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura Author: Leonardo de Moura
*/ */
#include "library/blast/revert_action.h" #include "library/blast/revert.h"
#include "library/blast/blast.h" #include "library/blast/blast.h"
namespace lean { namespace lean {
@ -23,7 +23,7 @@ struct revert_proof_step_cell : public proof_step_cell {
virtual bool is_silent() const override { return true; } virtual bool is_silent() const override { return true; }
}; };
unsigned revert_action(hypothesis_idx_buffer_set & hidxs) { unsigned revert(hypothesis_idx_buffer_set & hidxs) {
state & s = curr_state(); state & s = curr_state();
unsigned hidxs_size = hidxs.as_buffer().size(); unsigned hidxs_size = hidxs.as_buffer().size();
for (unsigned i = 0; i < hidxs_size; i++) { for (unsigned i = 0; i < hidxs_size; i++) {
@ -40,8 +40,8 @@ unsigned revert_action(hypothesis_idx_buffer_set & hidxs) {
return hidxs.as_buffer().size(); return hidxs.as_buffer().size();
} }
unsigned revert_action(buffer<hypothesis_idx> & hidxs) { unsigned revert(buffer<hypothesis_idx> & hidxs) {
hypothesis_idx_buffer_set _hidxs(hidxs); hypothesis_idx_buffer_set _hidxs(hidxs);
return revert_action(_hidxs); return revert(_hidxs);
} }
}} }}

View file

@ -11,6 +11,6 @@ namespace lean {
namespace blast { namespace blast {
/** \brief Revert the given hypotheses and their dependencies. /** \brief Revert the given hypotheses and their dependencies.
Return the total number of hypotheses reverted. */ Return the total number of hypotheses reverted. */
unsigned revert_action(buffer<hypothesis_idx> & hidxs); unsigned revert(buffer<hypothesis_idx> & hidxs);
unsigned revert_action(hypothesis_idx_buffer_set & hidxs); unsigned revert(hypothesis_idx_buffer_set & hidxs);
}} }}

View file

@ -11,16 +11,16 @@ Author: Leonardo de Moura
namespace lean { namespace lean {
namespace blast { namespace blast {
// TODO(Leo): we should create choice points when there are meta-variables // TODO(Leo): we should create choice points when there are meta-variables
optional<expr> assumption_action() { action_result assumption_action() {
state const & s = curr_state(); state const & s = curr_state();
expr const & target = s.get_target(); expr const & target = s.get_target();
for (hypothesis_idx hidx : s.get_head_related()) { for (hypothesis_idx hidx : s.get_head_related()) {
hypothesis const * h = s.get_hypothesis_decl(hidx); hypothesis const * h = s.get_hypothesis_decl(hidx);
lean_assert(h); lean_assert(h);
if (is_def_eq(h->get_type(), target)) if (is_def_eq(h->get_type(), target))
return some_expr(h->get_self()); return action_result(h->get_self());
} }
return none_expr(); return action_result::failed();
} }
/* Close branch IF h is of the form (H : a ~ a) where ~ is a reflexive relation */ /* Close branch IF h is of the form (H : a ~ a) where ~ is a reflexive relation */
@ -39,20 +39,20 @@ static optional<expr> try_not_refl_relation(hypothesis const & h) {
return none_expr(); return none_expr();
} }
optional<expr> assumption_contradiction_actions(hypothesis_idx hidx) { action_result assumption_contradiction_actions(hypothesis_idx hidx) {
state const & s = curr_state(); state const & s = curr_state();
app_builder & b = get_app_builder(); app_builder & b = get_app_builder();
hypothesis const * h = s.get_hypothesis_decl(hidx); hypothesis const * h = s.get_hypothesis_decl(hidx);
lean_assert(h); lean_assert(h);
expr const & type = h->get_type(); expr const & type = h->get_type();
if (blast::is_false(type)) { if (blast::is_false(type)) {
return some_expr(b.mk_false_rec(s.get_target(), h->get_self())); return action_result(b.mk_false_rec(s.get_target(), h->get_self()));
} }
if (is_def_eq(type, s.get_target())) { if (is_def_eq(type, s.get_target())) {
return some_expr(h->get_self()); return action_result(h->get_self());
} }
if (auto pr = try_not_refl_relation(*h)) if (auto pr = try_not_refl_relation(*h))
return pr; return action_result(*pr);
expr p1 = type; expr p1 = type;
bool is_neg1 = is_not(type, p1); bool is_neg1 = is_not(type, p1);
/* try to find complement */ /* try to find complement */
@ -64,39 +64,39 @@ optional<expr> assumption_contradiction_actions(hypothesis_idx hidx) {
if (is_neg1 != is_neg2) { if (is_neg1 != is_neg2) {
if (is_def_eq(p1, p2)) { if (is_def_eq(p1, p2)) {
if (is_neg1) { if (is_neg1) {
return some_expr(b.mk_app(get_absurd_name(), {s.get_target(), h2->get_self(), h->get_self()})); return action_result(b.mk_app(get_absurd_name(), {s.get_target(), h2->get_self(), h->get_self()}));
} else { } else {
lean_assert(is_neg2); lean_assert(is_neg2);
return some_expr(b.mk_app(get_absurd_name(), {s.get_target(), h->get_self(), h2->get_self()})); return action_result(b.mk_app(get_absurd_name(), {s.get_target(), h->get_self(), h2->get_self()}));
} }
} }
} }
} }
return none_expr(); return action_result::failed();
} }
optional<expr> trivial_action() { action_result trivial_action() {
try { try {
state s = curr_state(); state s = curr_state();
expr target = s.get_target(); expr target = s.get_target();
/* ignore if target contains meta-variables */ /* ignore if target contains meta-variables */
if (has_expr_metavar(target)) if (has_expr_metavar(target))
return none_expr(); return action_result::failed();
/* true */ /* true */
if (target == mk_true()) { if (target == mk_true()) {
return some_expr(mk_true_intro()); return action_result(mk_true_intro());
} }
/* a ~ a */ /* a ~ a */
name rop; expr lhs, rhs; name rop; expr lhs, rhs;
if (is_relation_app(target, rop, lhs, rhs) && is_def_eq(lhs, rhs)) { if (is_relation_app(target, rop, lhs, rhs) && is_def_eq(lhs, rhs)) {
return some_expr(get_app_builder().mk_refl(rop, lhs)); return action_result(get_app_builder().mk_refl(rop, lhs));
} }
return none_expr(); return action_result::failed();
} catch (app_builder_exception &) { } catch (app_builder_exception &) {
return none_expr(); return action_result::failed();
} }
} }
@ -129,4 +129,13 @@ bool discard(hypothesis_idx hidx) {
} }
return false; return false;
} }
action_result discard_action(hypothesis_idx hidx) {
if (discard(hidx)) {
curr_state().del_hypothesis(hidx);
return action_result::new_branch();
} else {
return action_result::failed();
}
}
}} }}

View file

@ -7,14 +7,15 @@ Author: Leonardo de Moura
#pragma once #pragma once
#include "kernel/expr.h" #include "kernel/expr.h"
#include "library/blast/hypothesis.h" #include "library/blast/hypothesis.h"
#include "library/blast/action_result.h"
namespace lean { namespace lean {
namespace blast { namespace blast {
optional<expr> assumption_action(); action_result assumption_action();
/** \brief Apply assumption and contradiction actions using the given hypothesis. /** \brief Apply assumption and contradiction actions using the given hypothesis.
\remark This action is supposed to be applied when a hypothesis is activated. */ \remark This action is supposed to be applied when a hypothesis is activated. */
optional<expr> assumption_contradiction_actions(hypothesis_idx hidx); action_result assumption_contradiction_actions(hypothesis_idx hidx);
/** \brief Solve trivial targets (e.g., true, a = a, a <-> a, etc). */ /** \brief Solve trivial targets (e.g., true, a = a, a <-> a, etc). */
optional<expr> trivial_action(); action_result trivial_action();
/** \brief Return true if the given hypothesis is "redundant". We consider a hypothesis /** \brief Return true if the given hypothesis is "redundant". We consider a hypothesis
redundant if it is a proposition, no other hypothesis type depends on it, redundant if it is a proposition, no other hypothesis type depends on it,
@ -30,5 +31,5 @@ optional<expr> trivial_action();
(well, it may be useful for the HoTT library). (well, it may be useful for the HoTT library).
TODO(Leo): subsumption. 3 is just a very simple case of subsumption. */ TODO(Leo): subsumption. 3 is just a very simple case of subsumption. */
bool discard(hypothesis_idx hidx); action_result discard_action(hypothesis_idx hidx);
}} }}

View file

@ -25,54 +25,13 @@ class simple_strategy : public strategy {
action_result activate_hypothesis(bool preprocess) { action_result activate_hypothesis(bool preprocess) {
auto hidx = curr_state().activate_hypothesis(); auto hidx = curr_state().activate_hypothesis();
if (!hidx) return action_result::failed(); if (!hidx) return action_result::failed();
if (!preprocess) display_action("activate"); if (!preprocess) display_action("activate");
if (auto pr = assumption_contradiction_actions(*hidx)) { Try(assumption_contradiction_actions(*hidx));
if (!preprocess) display_action("assumption-contradiction"); Try(subst_action(*hidx));
return action_result::solved(*pr); Try(no_confusion_action(*hidx));
} Try(discard_action(*hidx));
Try(recursor_preprocess_action(*hidx));
if (subst_action(*hidx)) {
if (!preprocess) display_action("subst");
return action_result::new_branch();
}
action_result r = no_confusion_action(*hidx);
if (!failed(r)) {
if (!preprocess) display_action("no_confusion");
return r;
}
if (discard(*hidx)) {
if (!preprocess) display_action("discard redudant hypothesis");
curr_state().del_hypothesis(*hidx);
return action_result::new_branch();
}
if (optional<name> R = is_recursor_action_target(*hidx)) {
unsigned num_minor = get_num_minor_premises(*R);
if (num_minor == 1) {
action_result r = recursor_action(*hidx, *R);
if (!failed(r)) {
if (!preprocess) display_action("recursor");
return r;
}
} else {
// If the hypothesis recursor has more than 1 minor premise, we
// put it in a priority queue.
// TODO(Leo): refine
// TODO(Leo): the following weight computation is too simple...
double w = 1.0 / (static_cast<double>(*hidx) + 1.0);
if (!is_recursive_recursor(*R)) {
// TODO(Leo): we need a better strategy for handling recursive recursors...
w += static_cast<double>(num_minor);
curr_state().add_to_rec_queue(*hidx, w);
}
}
}
return action_result::new_branch(); return action_result::new_branch();
} }
@ -82,57 +41,24 @@ class simple_strategy : public strategy {
virtual optional<expr> preprocess() { virtual optional<expr> preprocess() {
display_msg("* Preprocess"); display_msg("* Preprocess");
while (true) { while (true) {
if (intros_action()) if (!failed(intros_action()))
continue; continue;
auto r = activate_hypothesis(true); auto r = activate_hypothesis(true);
if (solved(r)) return r.to_opt_expr(); if (solved(r)) return r.to_opt_expr();
if (failed(r)) break; if (failed(r)) break;
} }
if (auto pr = assumption_action()) TrySolve(assumption_action());
return pr; TrySolve(simplify_target_action());
return simplify_target_action().to_opt_expr(); return none_expr();
} }
virtual action_result next_action() { virtual action_result next_action() {
if (intros_action()) { Try(intros_action());
display_action("intros"); Try(activate_hypothesis(false));
return action_result::new_branch(); Try(trivial_action());
} Try(assumption_action());
Try(recursor_action());
action_result r = activate_hypothesis(false); Try(constructor_action());
if (!failed(r)) return r;
if (auto pr = trivial_action()) {
display_action("trivial");
return action_result::solved(*pr);
}
if (auto pr = assumption_action()) {
// Remark: this action is only relevant
// when the target has been modified.
display_action("assumption");
return action_result::solved(*pr);
}
while (auto hidx = curr_state().select_rec_hypothesis()) {
if (optional<name> R = is_recursor_action_target(*hidx)) {
r = recursor_action(*hidx, *R);
if (!failed(r)) {
display_action("recursor");
return r;
}
}
}
r = constructor_action();
if (!failed(r)) {
display_action("constructor");
return r;
}
// TODO(Leo): add more actions...
display_msg(">>> FAILED <<<");
return action_result::failed(); return action_result::failed();
} }
}; };

View file

@ -6,7 +6,7 @@ Author: Leonardo de Moura
*/ */
#include "kernel/abstract.h" #include "kernel/abstract.h"
#include "kernel/instantiate.h" #include "kernel/instantiate.h"
#include "library/blast/revert_action.h" #include "library/blast/revert.h"
#include "library/blast/intros_action.h" #include "library/blast/intros_action.h"
#include "library/blast/blast.h" #include "library/blast/blast.h"
@ -57,7 +57,7 @@ bool subst_core(hypothesis_idx hidx) {
to_revert, to_revert,
[&](hypothesis_idx d) { return d != hidx; }); [&](hypothesis_idx d) { return d != hidx; });
s.collect_direct_forward_deps(hidx, to_revert); s.collect_direct_forward_deps(hidx, to_revert);
unsigned num = revert_action(to_revert); unsigned num = revert(to_revert);
expr target = s.get_target(); expr target = s.get_target();
expr new_target = abstract(target, h->get_self()); expr new_target = abstract(target, h->get_self());
bool dep = !closed(new_target); bool dep = !closed(new_target);
@ -84,7 +84,7 @@ bool subst_core(hypothesis_idx hidx) {
} }
} }
bool subst_action(hypothesis_idx hidx) { action_result subst_action(hypothesis_idx hidx) {
state & s = curr_state(); state & s = curr_state();
app_builder & b = get_app_builder(); app_builder & b = get_app_builder();
hypothesis const * h = s.get_hypothesis_decl(hidx); hypothesis const * h = s.get_hypothesis_decl(hidx);
@ -92,14 +92,14 @@ bool subst_action(hypothesis_idx hidx) {
expr type = h->get_type(); expr type = h->get_type();
expr lhs, rhs; expr lhs, rhs;
if (!is_eq(type, lhs, rhs)) if (!is_eq(type, lhs, rhs))
return false; return action_result::failed();
if (is_href(rhs)) { if (is_href(rhs)) {
return subst_core(hidx); return action_result(subst_core(hidx));
} else if (is_href(lhs)) { } else if (is_href(lhs)) {
if (s.has_forward_deps(href_index(lhs))) { if (s.has_forward_deps(href_index(lhs))) {
// TODO(Leo): we don't handle this case yet. // TODO(Leo): we don't handle this case yet.
// Other hypotheses depend on this equality. // Other hypotheses depend on this equality.
return false; return action_result::failed();
} }
state saved = s; state saved = s;
try { try {
@ -107,17 +107,17 @@ bool subst_action(hypothesis_idx hidx) {
expr new_pr = b.mk_eq_symm(h->get_self()); expr new_pr = b.mk_eq_symm(h->get_self());
expr new_href = s.mk_hypothesis(new_eq, new_pr); expr new_href = s.mk_hypothesis(new_eq, new_pr);
if (subst_core(href_index(new_href))) { if (subst_core(href_index(new_href))) {
return true; return action_result::new_branch();
} else { } else {
s = saved; s = saved;
return false; return action_result::failed();
} }
} catch (app_builder_exception &) { } catch (app_builder_exception &) {
s = saved; s = saved;
return false; return action_result::failed();
} }
} else { } else {
return false; return action_result::failed();
} }
} }
}} }}

View file

@ -9,6 +9,6 @@ Author: Leonardo de Moura
namespace lean { namespace lean {
namespace blast { namespace blast {
/** \brief If the given hypothesis is of the form (H : t = x) or (H : x = s), then /** \brief If the given hypothesis is of the form (H : t = x) or (H : x = s), then
eliminate x (and H). Return true if success. */ eliminate x (and H). */
bool subst_action(hypothesis_idx hidx); action_result subst_action(hypothesis_idx hidx);
}} }}