refactor(kernel): remove normalizer

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2014-04-25 12:02:33 -07:00
parent a55c3c617d
commit a39a5b4195
4 changed files with 0 additions and 494 deletions

View file

@ -1,244 +0,0 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <algorithm>
#include <limits>
#include "util/list.h"
#include "util/flet.h"
#include "util/freset.h"
#include "util/buffer.h"
#include "util/interrupt.h"
#include "util/sexpr/options.h"
#include "kernel/normalizer.h"
#include "kernel/environment.h"
#include "kernel/expr_maps.h"
#include "kernel/free_vars.h"
#include "kernel/instantiate.h"
#include "kernel/replace_fn.h"
#include "kernel/kernel_exception.h"
#ifndef LEAN_KERNEL_NORMALIZER_MAX_DEPTH
#define LEAN_KERNEL_NORMALIZER_MAX_DEPTH std::numeric_limits<unsigned>::max()
#endif
namespace lean {
static name g_kernel_normalizer_max_depth {"kernel", "normalizer", "max_depth"};
RegisterUnsignedOption(g_kernel_normalizer_max_depth, LEAN_KERNEL_NORMALIZER_MAX_DEPTH, "(kernel) maximum recursion depth for expression normalizer");
unsigned get_normalizer_max_depth(options const & opts) {
return opts.get_unsigned(g_kernel_normalizer_max_depth, LEAN_KERNEL_NORMALIZER_MAX_DEPTH);
}
typedef list<expr> value_stack;
value_stack extend(value_stack const & s, expr const & v) {
lean_assert(!is_lambda(v) && !is_pi(v) && !is_metavar(v) && !is_let(v));
return cons(v, s);
}
/**
\brief Internal value used to store closures.
This is a transient value that is only used during normalization.
*/
class closure : public macro {
expr m_expr;
value_stack m_stack;
public:
closure(expr const & e, value_stack const & s):m_expr(e), m_stack(s) {}
virtual ~closure() {}
virtual expr get_type(buffer<expr> const & ) const { lean_unreachable(); } // LCOV_EXCL_LINE
virtual void write(serializer & ) const { lean_unreachable(); } // LCOV_EXCL_LINE
virtual expr expand1(buffer<expr> const & ) const { lean_unreachable(); }
virtual expr expand(buffer<expr> const & ) const { lean_unreachable(); }
virtual name get_name() const { return name("Closure"); }
virtual void display(std::ostream & out) const {
out << "(Closure " << m_expr << " [";
bool first = true;
for (auto v : m_stack) {
if (first) first = false; else out << " ";
out << v;
}
out << "])";
}
expr const & get_expr() const { return m_expr; }
value_stack const & get_stack() const { return m_stack; }
virtual bool is_atomic_pp(bool /* unicode */, bool /* coercion */) const { return false; } // NOLINT
};
expr mk_closure(expr const & e, value_stack const & s) { return mk_macro(new closure(e, s)); }
bool is_closure(expr const & e) { return is_macro(e) && dynamic_cast<closure const *>(&to_macro(e)) != nullptr; }
closure const & to_closure(expr const & e) { lean_assert(is_closure(e)); return static_cast<closure const &>(to_macro(e)); }
/** \brief Expression normalizer. */
class normalizer::imp {
typedef expr_map<expr> cache;
ro_environment::weak_ref m_env;
cache m_cache;
unsigned m_max_depth;
unsigned m_depth;
ro_environment env() const { return ro_environment(m_env); }
expr lookup(value_stack const & s, unsigned i) {
unsigned j = i;
value_stack const * it1 = &s;
while (*it1) {
if (j == 0)
return head(*it1);
--j;
it1 = &tail(*it1);
}
throw kernel_exception(env(), "normalizer failure, unexpected occurrence of free variable");
}
/** \brief Convert the value \c v back into an expression in a context that contains \c k binders. */
expr reify(expr const & v, unsigned k) {
return replace(v, [&](expr const & e, unsigned DEBUG_CODE(offset)) -> optional<expr> {
lean_assert(offset == 0);
lean_assert(!is_lambda(e) && !is_pi(e) && !is_let(e));
if (is_var(e)) {
// de Bruijn level --> de Bruijn index
return some_expr(mk_var(k - var_idx(e) - 1));
} else if (is_closure(e)) {
return some_expr(reify_closure(to_closure(e), k));
} else {
return none_expr();
}
});
}
/** \brief Convert the closure \c c into an expression in a context that contains \c k binders. */
expr reify_closure(closure const & c, unsigned k) {
expr const & e = c.get_expr();
value_stack const & s = c.get_stack();
lean_assert(is_binder(e));
freset<cache> reset(m_cache);
expr new_d = reify(normalize(binder_domain(e), s, k), k);
m_cache.clear(); // make sure we do not reuse cached values from the previous call
expr new_b = reify(normalize(binder_body(e), extend(s, mk_var(k)), k+1), k+1);
return update_binder(e, new_d, new_b);
}
/** \brief Normalize the expression \c a in a context composed of stack \c s and \c k binders. */
expr normalize(expr const & a, value_stack const & s, unsigned k) {
flet<unsigned> l(m_depth, m_depth+1);
check_system("normalizer");
if (m_depth > m_max_depth)
throw kernel_exception(env(), "normalizer maximum recursion depth exceeded");
bool shared = false;
if (is_shared(a)) {
shared = true;
auto it = m_cache.find(a);
if (it != m_cache.end())
return it->second;
}
expr r;
switch (a.kind()) {
case expr_kind::Pi: case expr_kind::Lambda:
r = mk_closure(a, s);
break;
case expr_kind::Var:
r = lookup(s, var_idx(a));
break;
case expr_kind::Constant: {
optional<object> obj = env()->find_object(const_name(a));
if (should_unfold(obj)) {
// TODO(Leo): instantiate level parameters
freset<cache> reset(m_cache);
r = normalize(obj->get_value(), value_stack(), 0);
} else {
r = a;
}
break;
}
case expr_kind::Sort: case expr_kind::Macro: case expr_kind::Local: case expr_kind::Meta:
r = a;
break;
case expr_kind::App: {
buffer<expr> new_args;
expr const * it = &a;
while (is_app(*it)) {
new_args.push_back(normalize(app_arg(*it), s, k));
it = &app_fn(*it);
}
expr f = normalize(*it, s, k);
unsigned i = 0;
unsigned n = new_args.size();
while (true) {
if (is_closure(f) && is_lambda(to_closure(f).get_expr())) {
// beta reduction
expr fv = to_closure(f).get_expr();
value_stack new_s = to_closure(f).get_stack();
while (is_lambda(fv) && i < n) {
new_s = extend(new_s, new_args[n - i - 1]);
i++;
fv = binder_body(fv);
}
{
freset<cache> reset(m_cache);
f = normalize(fv, new_s, k);
}
if (i == n) {
r = f;
break;
}
} else {
if (is_macro(f) && !is_closure(f)) {
buffer<expr> reified_args;
unsigned i = new_args.size();
while (i > 0) {
--i;
reified_args.push_back(reify(new_args[i], k));
}
r = to_macro(f).expand(reified_args);
} else {
new_args.push_back(f);
std::reverse(new_args.begin(), new_args.end());
r = mk_app(new_args);
}
break;
}
}
break;
}
case expr_kind::Let: {
expr v = normalize(let_value(a), s, k);
{
freset<cache> reset(m_cache);
r = normalize(let_body(a), extend(s, v), k);
}
break;
}}
if (shared) {
m_cache[a] = r;
}
return r;
}
public:
imp(ro_environment const & env, unsigned max_depth):
m_env(env) {
m_max_depth = max_depth;
m_depth = 0;
}
expr operator()(expr const & e) {
unsigned k = 0;
return reify(normalize(e, value_stack(), k), k);
}
void clear() { m_cache.clear(); }
};
normalizer::normalizer(ro_environment const & env, unsigned max_depth):m_ptr(new imp(env, max_depth)) {}
normalizer::normalizer(ro_environment const & env):normalizer(env, std::numeric_limits<unsigned>::max()) {}
normalizer::normalizer(ro_environment const & env, options const & opts):normalizer(env, get_normalizer_max_depth(opts)) {}
normalizer::~normalizer() {}
expr normalizer::operator()(expr const & e) { return (*m_ptr)(e); }
void normalizer::clear() { m_ptr->clear(); }
expr normalize(expr const & e, ro_environment const & env) { return normalizer(env)(e); }
}

View file

@ -1,30 +0,0 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include <memory>
#include "kernel/expr.h"
namespace lean {
class ro_environment;
class options;
/** \brief Functional object for normalizing expressions */
class normalizer {
class imp;
std::unique_ptr<imp> m_ptr;
public:
normalizer(ro_environment const & env);
normalizer(ro_environment const & env, unsigned max_depth);
normalizer(ro_environment const & env, options const & opts);
~normalizer();
expr operator()(expr const & e);
void clear();
};
/** \brief Normalize \c e using the environment \c env and context \c ctx */
expr normalize(expr const & e, ro_environment const & env);
}

View file

@ -10,9 +10,6 @@ add_test(expr ${CMAKE_CURRENT_BINARY_DIR}/expr)
add_executable(max_sharing max_sharing.cpp)
target_link_libraries(max_sharing ${EXTRA_LIBS})
add_test(max_sharing ${CMAKE_CURRENT_BINARY_DIR}/max_sharing)
# add_executable(normalizer normalizer.cpp)
# target_link_libraries(normalizer ${EXTRA_LIBS})
# add_test(normalizer ${CMAKE_CURRENT_BINARY_DIR}/normalizer)
# add_executable(threads threads.cpp)
# target_link_libraries(threads ${EXTRA_LIBS})
# add_test(threads ${CMAKE_CURRENT_BINARY_DIR}/threads)

View file

@ -1,217 +0,0 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <algorithm>
#include "util/thread.h"
#include "util/test.h"
#include "util/trace.h"
#include "util/exception.h"
#include "util/interrupt.h"
#include "kernel/normalizer.h"
#include "kernel/expr_sets.h"
#include "kernel/abstract.h"
#include "kernel/kernel_exception.h"
#include "kernel/free_vars.h"
using namespace lean;
expr normalize(expr const & e) {
environment env;
return normalize(e, env);
}
static void eval(expr const & e, environment & env) { std::cout << e << " --> " << normalize(e, env) << "\n"; }
static expr t() { return Const("t"); }
static expr lam(expr const & e) { return mk_lambda("_", t(), e); }
static expr lam(expr const & t, expr const & e) { return mk_lambda("_", t, e); }
static expr v(unsigned i) { return Var(i); }
static expr zero() {
// fun (t : T) (s : t -> t) (z : t) z
return lam(t(), lam(mk_arrow(v(0), v(0)), lam(v(1), v(0))));
}
static expr one() {
// fun (t : T) (s : t -> t) s
return lam(t(), lam(mk_arrow(v(0), v(0)), v(0)));
}
static expr num() { return Const("num"); }
static expr plus() {
// fun (m n : numeral) (A : Type 0) (f : A -> A) (x : A) => m A f (n A f x).
expr x = v(0), f = v(1), A = v(2), n = v(3), m = v(4);
expr body = m(A, f, n(A, f, x));
return lam(num(), lam(num(), lam(t(), lam(mk_arrow(v(0), v(0)), lam(v(1), body)))));
}
static expr two() { return mk_app({plus(), one(), one()}); }
static expr three() { return mk_app({plus(), two(), one()}); }
static expr four() { return mk_app({plus(), two(), two()}); }
static expr times() {
// fun (m n : numeral) (A : Type 0) (f : A -> A) (x : A) => m A (n A f) x.
expr x = v(0), f = v(1), A = v(2), n = v(3), m = v(4);
expr body = m(A, n(A, f), x);
return lam(num(), lam(num(), lam(t(), lam(mk_arrow(v(0), v(0)), lam(v(1), body)))));
}
static expr power() {
// fun (m n : numeral) (A : Type 0) => m (A -> A) (n A).
expr A = v(0), n = v(1), m = v(2);
expr body = n(mk_arrow(A, A), m(A));
return lam(num(), lam(num(), lam(mk_arrow(v(0), v(0)), body)));
}
unsigned count_core(expr const & a, expr_set & s) {
if (s.find(a) != s.end())
return 0;
s.insert(a);
switch (a.kind()) {
case expr_kind::Var: case expr_kind::Constant: case expr_kind::Sort:
case expr_kind::Meta: case expr_kind::Local: case expr_kind::Macro:
return 1;
case expr_kind::App:
return count_core(app_fn(a), s) + count_core(app_arg(a), s) + 1;
case expr_kind::Lambda: case expr_kind::Pi:
return count_core(binder_domain(a), s) + count_core(binder_body(a), s) + 1;
case expr_kind::Let:
return count_core(let_value(a), s) + count_core(let_body(a), s) + 1;
}
return 0;
}
unsigned count(expr const & a) {
expr_set s;
return count_core(a, s);
}
static void tst_church_numbers() {
environment env;
env->add_var("t", Type);
env->add_var("N", Type);
env->add_var("z", Const("N"));
env->add_var("s", Const("N"));
expr N = Const("N");
expr z = Const("z");
expr s = Const("s");
std::cout << normalize(mk_app(zero(), N, s, z), env) << "\n";
std::cout << normalize(mk_app(one(), N, s, z), env) << "\n";
std::cout << normalize(mk_app(two(), N, s, z), env) << "\n";
std::cout << normalize(mk_app(four(), N, s, z), env) << "\n";
std::cout << count(normalize(mk_app(four(), N, s, z), env)) << "\n";
lean_assert_eq(count(normalize(mk_app(four(), N, s, z), env)), 15);
std::cout << normalize(mk_app(mk_app(times(), four(), four()), N, s, z), env) << "\n";
std::cout << normalize(mk_app(mk_app(power(), two(), four()), N, s, z), env) << "\n";
lean_assert_eq(count(normalize(mk_app(mk_app(power(), two(), four()), N, s, z), env)), 51);
std::cout << normalize(mk_app(mk_app(times(), two(), mk_app(power(), two(), four())), N, s, z), env) << "\n";
std::cout << normalize(mk_app(mk_app(times(), four(), mk_app(power(), two(), four())), N, s, z), env) << "\n";
std::cout << count(normalize(mk_app(mk_app(times(), two(), mk_app(power(), two(), four())), N, s, z), env)) << "\n";
std::cout << count(normalize(mk_app(mk_app(times(), four(), mk_app(power(), two(), four())), N, s, z), env)) << "\n";
lean_assert_eq(count(normalize(mk_app(mk_app(times(), four(), mk_app(power(), two(), four())), N, s, z), env)), 195);
expr big = normalize(mk_app(mk_app(power(), two(), mk_app(power(), two(), three())), N, s, z), env);
std::cout << count(big) << "\n";
lean_assert_eq(count(big), 771);
expr three = mk_app(plus(), two(), one());
lean_assert_eq(count(normalize(mk_app(mk_app(power(), three, three), N, s, z), env)), 84);
// expr big2 = normalize(mk_app(mk_app(power(), two(), mk_app(times(), mk_app(plus(), four(), one()), four())), N, s, z), env);
// std::cout << count(big2) << "\n";
std::cout << normalize(lam(lam(mk_app(mk_app(times(), four(), four()), N, Var(0), z))), env) << "\n";
}
static void tst1() {
environment env;
env->add_var("t", Type);
expr t = Type;
env->add_var("f", mk_arrow(t, t));
expr f = Const("f");
env->add_var("a", t);
expr a = Const("a");
env->add_var("b", t);
expr b = Const("b");
expr x = Var(0);
expr y = Var(1);
eval(mk_app(mk_lambda("x", t, x), a), env);
eval(mk_app(mk_lambda("x", t, x), a, b), env);
eval(mk_lambda("x", t, f(x)), env);
eval(mk_lambda("y", t, mk_lambda("x", t, f(y, x))), env);
eval(mk_app(mk_lambda("x", t,
mk_app(mk_lambda("f", t,
mk_app(Var(0), b)),
mk_lambda("g", t, f(Var(1))))),
a), env);
expr l01 = lam(v(0)(v(1)));
expr l12 = lam(lam(v(1)(v(2))));
eval(lam(l12(l01)), env);
lean_assert(normalize(lam(l12(l01)), env) == lam(lam(v(1)(v(1)))));
}
static void tst2() {
environment env;
env->add_var("b", Type);
expr t1 = mk_let("a", Type, Const("b"), mk_lambda("c", Type, Var(1)(Var(0))));
std::cout << t1 << " --> " << normalize(t1, env) << "\n";
lean_assert(normalize(t1, env) == mk_lambda("c", Type, Const("b")(Var(0))));
}
static expr mk_big(unsigned depth) {
if (depth == 0)
return Const("a");
else
return Const("f")(mk_big(depth - 1), mk_big(depth - 1));
}
static void tst3() {
#if !defined(__APPLE__) && defined(LEAN_MULTI_THREAD)
expr t = mk_big(18);
environment env;
env->add_var("f", Bool >> (Bool >> Bool));
env->add_var("a", Bool);
normalizer proc(env);
chrono::milliseconds dura(50);
interruptible_thread thread([&]() {
try {
proc(t);
// Remark: if the following code is reached, we
// should decrease dura.
lean_unreachable();
} catch (interrupted & it) {
std::cout << "interrupted\n";
}
});
this_thread::sleep_for(dura);
thread.request_interrupt();
thread.join();
#endif
}
static void tst4() {
environment env;
expr x = Const("x");
expr t = Fun({x, Type}, mk_app(x, x));
expr omega = mk_app(t, t);
normalizer proc(env, 512);
try {
proc(omega);
} catch (kernel_exception & ex) {
std::cout << ex.what() << "\n";
}
}
static void tst5() {
environment env;
expr f = Const("f");
env->add_var("f", Type >> (Type >> Type));
expr x = Const("x");
expr v = Const("v");
expr F = Fun({x, Type}, Let(v, Type, Bool, f(x, v)));
expr N = normalizer(env)(F);
std::cout << F << " ==> " << N << "\n";
lean_assert_eq(N, Fun({x, Type}, f(x, Bool)));
}
int main() {
save_stack_info();
tst_church_numbers();
tst1();
tst2();
tst3();
tst4();
tst5();
return has_violations() ? 1 : 0;
}