202 lines
8.8 KiB
C++
202 lines
8.8 KiB
C++
/*
|
|
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
|
|
Author: Leonardo de Moura
|
|
*/
|
|
#include <utility>
|
|
#include "util/sstream.h"
|
|
#include "kernel/kernel.h"
|
|
#include "kernel/type_checker.h"
|
|
#include "library/equality.h"
|
|
#include "library/simplifier/congr.h"
|
|
|
|
namespace lean {
|
|
typedef congr_theorem_info::app_arg_info app_arg_info;
|
|
/**
|
|
\brief Return true iff arg_info contains an entry s.t. m_proof_arg_pos or m_proof_new_arg_pos is pos.
|
|
*/
|
|
static bool contains_pos(buffer<app_arg_info> const & arg_info, unsigned pos) {
|
|
return std::any_of(arg_info.begin(), arg_info.end(),
|
|
[&](app_arg_info const & info) {
|
|
return
|
|
info.get_pos_at_proof() == pos ||
|
|
(info.get_new_pos_at_proof() && *info.get_new_pos_at_proof() == pos);
|
|
});
|
|
}
|
|
|
|
static void check_conclusion_lhs_rhs(expr const & lhs, expr const & rhs, unsigned num) {
|
|
if (!is_var(lhs) || !is_var(rhs))
|
|
throw exception("invalid congruence theorem, the arguments in the left and right-hand-sides must be variables");
|
|
if (var_idx(lhs) >= num)
|
|
throw exception("invalid congruence theorem, left-hand-side contains free variables");
|
|
if (var_idx(rhs) >= num)
|
|
throw exception("invalid congruence theorem, right-hand-side contains free variables");
|
|
}
|
|
|
|
static void check_arg_lhs_rhs(expr const & lhs, expr const & rhs, unsigned num) {
|
|
if (!is_var(lhs) || !is_var(rhs))
|
|
throw exception(sstream() << "invalid congruence theorem, type of argument #" << (num+1) << " is not an equality between variables");
|
|
if (var_idx(lhs) >= num)
|
|
throw exception(sstream() << "invalid congruence theorem, left-hand-side of argument #" << (num+1) << " contains free variables");
|
|
if (var_idx(rhs) >= num)
|
|
throw exception(sstream() << "invalid congruence theorem, right-hand-side of argument #" << (num+1) << " contains free variables");
|
|
}
|
|
|
|
static buffer<app_arg_info>::iterator find_arg_info(buffer<app_arg_info> & arg_infos, unsigned proof_arg_pos, unsigned proof_new_arg_pos) {
|
|
return std::find_if(arg_infos.begin(), arg_infos.end(), [&](app_arg_info const & info) {
|
|
return info.get_pos_at_proof() == proof_arg_pos && info.get_new_pos_at_proof() && *info.get_new_pos_at_proof() == proof_new_arg_pos;
|
|
});
|
|
}
|
|
|
|
static pair<unsigned, bool> find_hypothesis(buffer<app_arg_info> & arg_infos, unsigned vidx, unsigned num) {
|
|
for (auto const & info : arg_infos) {
|
|
if (vidx == info.get_pos_at_proof()) {
|
|
return mk_pair(info.get_arg_pos(), false);
|
|
} else if (info.get_new_pos_at_proof() && vidx == *info.get_new_pos_at_proof()) {
|
|
return mk_pair(info.get_arg_pos(), true);
|
|
}
|
|
}
|
|
throw exception(sstream() << "invalid congruence theorem, invalid hypothesis for argument #" << num
|
|
<< ", variable must occur in the left or right hand side of the conclusion of the theorem");
|
|
}
|
|
|
|
void congr_theorem_info::context::display(std::ostream & out) const {
|
|
if (!m_pos)
|
|
out << "!";
|
|
out << "#" << m_arg;
|
|
if (m_new)
|
|
out << "'";
|
|
}
|
|
|
|
void congr_theorem_info::app_arg_info::display(std::ostream & out) const {
|
|
out << "#" << m_arg_pos << ": ";
|
|
if (m_context) {
|
|
m_context->display(out);
|
|
out << " -> ";
|
|
}
|
|
out << "#" << m_proof_arg_pos;
|
|
if (m_proof_new_arg_pos)
|
|
out << " #" << *m_proof_new_arg_pos << " #" << *m_proof_proof_pos;
|
|
}
|
|
|
|
void congr_theorem_info::display(std::ostream & out) const {
|
|
out << m_fun << " " << m_num_proof_args << "\n" << m_proof << "\n";
|
|
for (auto const & info : m_arg_info) {
|
|
info.display(out);
|
|
out << "\n";
|
|
}
|
|
out << "\n";
|
|
}
|
|
|
|
congr_theorem_info check_congr_theorem(ro_environment const & env, expr const & e) {
|
|
expr t = type_checker(env).infer_type(e);
|
|
expr b = t;
|
|
unsigned num = 0;
|
|
while (is_pi(b)) {
|
|
b = abst_body(b);
|
|
num++;
|
|
}
|
|
expr lhs, rhs;
|
|
if (!is_equality(b, lhs, rhs))
|
|
throw exception("invalid congruence theorem, conclusion is not an equality");
|
|
if (!is_app(lhs))
|
|
throw exception("invalid congruence theorem, left-hand-side of the conclusion is not a function application");
|
|
if (!is_app(rhs))
|
|
throw exception("invalid congruence theorem, right-hand-side of the conclusion is not a function application");
|
|
if (arg(lhs, 0) != arg(rhs, 0))
|
|
throw exception("invalid congruence theorem, the functions in the left and right-hand-sides are different");
|
|
if (num_args(lhs) != num_args(rhs))
|
|
throw exception("invalid congruence theorem, the number of arguments in the left and right-hand-sides is different");
|
|
|
|
congr_theorem_info r;
|
|
r.m_fun = arg(lhs, 0);
|
|
r.m_proof = e;
|
|
r.m_num_proof_args = num;
|
|
|
|
buffer<app_arg_info> arg_infos;
|
|
for (unsigned i = 1; i < num_args(lhs); i++) {
|
|
expr a = arg(lhs, i);
|
|
expr new_a = arg(rhs, i);
|
|
check_conclusion_lhs_rhs(a, new_a, num);
|
|
unsigned proof_arg_pos = num - var_idx(a) - 1;
|
|
unsigned proof_new_arg_pos = num - var_idx(new_a) - 1;
|
|
|
|
if (contains_pos(arg_infos, proof_arg_pos) ||
|
|
contains_pos(arg_infos, proof_new_arg_pos))
|
|
throw exception("invalid congruence theorem, variables can only occur once in the left and right-hand sides");
|
|
|
|
if (proof_arg_pos == proof_new_arg_pos) {
|
|
// this argument does not need proof, then add it directly to
|
|
// r.m_arg_info
|
|
r.m_arg_info.push_back(app_arg_info(i, proof_arg_pos));
|
|
} else {
|
|
// we have to find where the proof for this one is located
|
|
arg_infos.push_back(app_arg_info(i, proof_arg_pos, proof_new_arg_pos));
|
|
}
|
|
}
|
|
|
|
bool progress = true;
|
|
while (progress) {
|
|
progress = false;
|
|
expr b = t;
|
|
num = 0;
|
|
while (is_pi(b)) {
|
|
expr d = abst_domain(b);
|
|
expr lhs, rhs;
|
|
if (is_equality(d, lhs, rhs)) {
|
|
check_arg_lhs_rhs(lhs, rhs, num);
|
|
auto it = find_arg_info(arg_infos, num - var_idx(lhs) - 1, num - var_idx(rhs) - 1);
|
|
if (it == arg_infos.end())
|
|
throw exception(sstream() << "invalid congruence theorem, argument #" << num << " does not match conclusion of the theorem");
|
|
if (!it->m_proof_proof_pos) {
|
|
progress = true;
|
|
it->m_proof_proof_pos = num;
|
|
r.m_arg_info.push_back(*it);
|
|
}
|
|
} else if (is_pi(d) && is_equality(abst_body(d), lhs, rhs)) {
|
|
check_arg_lhs_rhs(lhs, rhs, num+1);
|
|
auto it = find_arg_info(arg_infos, num - var_idx(lhs), num - var_idx(rhs));
|
|
if (it == arg_infos.end())
|
|
throw exception(sstream() << "invalid congruence theorem, argument #" << num
|
|
<< " does not match conclusion of the theorem");
|
|
if (!it->m_proof_proof_pos) {
|
|
bool ctx_pos;
|
|
pair<unsigned, bool> p;
|
|
if (is_var(abst_domain(d))) {
|
|
ctx_pos = true;
|
|
p = find_hypothesis(arg_infos, num - var_idx(abst_domain(d)) - 1, num);
|
|
} else if (is_not(abst_domain(d)) && is_var(arg(abst_domain(d), 1))) {
|
|
ctx_pos = false;
|
|
p = find_hypothesis(arg_infos, num - var_idx(arg(abst_domain(d), 1)) - 1, num);
|
|
} else {
|
|
throw exception(sstream() << "invalid congruence theorem, hypothesis for argument #" << num
|
|
<< " must be a variable or the negation of variable");
|
|
}
|
|
progress = true;
|
|
unsigned ctx_arg = p.first;
|
|
bool ctx_new = p.second;
|
|
it->m_proof_proof_pos = num;
|
|
it->m_context = congr_theorem_info::context(ctx_arg, ctx_pos, ctx_new);
|
|
r.m_arg_info.push_back(*it);
|
|
}
|
|
}
|
|
b = abst_body(b);
|
|
num++;
|
|
}
|
|
}
|
|
buffer<bool> found_args;
|
|
found_args.resize(num, false);
|
|
for (auto const & info : r.m_arg_info) {
|
|
found_args[info.get_pos_at_proof()] = true;
|
|
if (info.get_new_pos_at_proof())
|
|
found_args[*info.get_new_pos_at_proof()] = true;
|
|
if (info.get_proof_pos_at_proof())
|
|
found_args[*info.get_proof_pos_at_proof()] = true;
|
|
}
|
|
for (unsigned i = 0; i < num; i++)
|
|
if (!found_args[i])
|
|
throw exception(sstream() << "invalid congruence theorem, cannot synthesize argument #" << i);
|
|
return r;
|
|
}
|
|
}
|