diff --git a/CMakeLists.txt b/CMakeLists.txt index 76c16ae8c7273ce1a11fd686af331f07bad3cb27..2e034fb85ef087c1bac88b42f75a87109282284c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ # CMakeLists files in this project can -# refer to the root source directory of the project as ${lela_SOURCE_DIR} and -# to the root binary directory of the project as ${lela_BINARY_DIR}. +# refer to the root source directory of the project as ${limbo_SOURCE_DIR} and +# to the root binary directory of the project as ${limbo_BINARY_DIR}. cmake_minimum_required (VERSION 2.8.11) -project (lela) +project (limbo) include (CTest) @@ -45,9 +45,6 @@ endif () add_subdirectory (src) -#add_subdirectory (eclipse-clp) -#add_subdirectory (bats) - add_subdirectory (tests) add_subdirectory (examples) diff --git a/README.md b/README.md index bde5a1437b088005c16558866d64825004e415ab..c54a2d413d440dbf9ebb8b6655b96dc469f93af8 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,28 @@ -# Limited Epistemic Logic in Action +# A Reasoning System for First-Order Limited Belief -LELA (**L**imited **E**pistemic **L**ogic in **A**ction) is a C++ library for -*decidable reasoning in first-order knowledge bases* based on the theory of -*limited belief* from [1,2,3,4]. [Here are some web -demos](http://www.cse.unsw.edu.au/~cschwering/demo/). +Limbo is a *reasoning system for a first-order logic of **lim**ited +**b**elief*. Limited belief allows for decidable reasoning in first-order +knowledge bases, and in the propositional case it is even tractable. See +[\[1,2,3,4\]](#references) for details on the theory, or click +[here](http://www.cse.unsw.edu.au/~cschwering/limbo/) to see some web demos. ## Features -The library provides a logical language to encode and reason about knowledge -and belief. +Limbo provides a logical language to represent and reason about (possibly +incomplete) knowledge and belief. -The logical language features functions of different sorts, standard names that -designate distinct objects, first-order variables and quantification, equality, -and modal operators for knowledge and conditional belief. (Predicates are not -built-in but can be simulated without overhead using boolean functions.) +This logical language is a first-order modal dialect: it features functions and +equality, standard names to designate distinct objects, first-order variables +and quantification, sorts, and modal operators for knowledge and conditional +belief. (Predicates are not built-in but can be simulated without overhead +using boolean functions.) An agent's knowledge and beliefs are encoded in this language. This knowledge base is subject to a syntactic restriction: it must be in clausal form, and all -variables must be universally quantified. (Existentially quantified variables -in the knowledge base can be simulated through Skolemization.) For instance, -assuming a birthday scenario and a gift box, we could say that we know the box -contains a gift. +variables must be universally quantified; however, as existentially quantified +variables can be simulated through Skolemization, this is no effective +restriction. An example knowledge base might assume a birthday scenario and +stipulate that that a certain gift box is known to contain an (unknown) gift. Reasoning in such knowledge bases is done with queries expressed in this language. For example, we can say that we believe that something is in the box @@ -44,22 +46,21 @@ For more theoretical background see the papers linked below. ## Examples -* This [web demo](http://www.cse.unsw.edu.au/~cschwering/demo/tui/) allows to +* This [web demo](http://www.cse.unsw.edu.au/~cschwering/limbo/tui/) allows to define and query a knowledge base through a simple text interface. You can also use it from the command line; the the code is [here](examples/tui/). * There's are - [Minesweeper](http://www.cse.unsw.edu.au/~cschwering/demo/minesweeper/) and - [Sudoku](http://www.cse.unsw.edu.au/~cschwering/demo/sudoku/) web demos. You + [Minesweeper](http://www.cse.unsw.edu.au/~cschwering/limbo/minesweeper/) and + [Sudoku](http://www.cse.unsw.edu.au/~cschwering/limbo/sudoku/) web demos. You can use them it from the command line as well; the the code is [here](examples/minesweeper/) and [here](examples/sudoku/). -* The main part of the C++ API is [kb.h](src/lela/kb.h), - [formula.h](src/lela/formula.h), [clause.h](src/lela/clause.h), - [literal.h](src/lela/literal.h), [term.h](src/lela/term.h), in this - order. - To make creating formulas a little more convenient, - [format/cpp/syntax.h](src/lela/format/cpp/syntax.h) overloads some C++ - operators. +* The main part of the C++ API is [kb.h](src/limbo/kb.h), + [formula.h](src/limbo/formula.h), [clause.h](src/limbo/clause.h), + [literal.h](src/limbo/literal.h), [term.h](src/limbo/term.h), in this + order. Optional C++ operator overloadings are provided by + [format/cpp/syntax.h](src/limbo/format/cpp/syntax.h) to make creating + formulas a little more convenient. ## Future work diff --git a/examples/minesweeper/CMakeLists.txt b/examples/minesweeper/CMakeLists.txt index 72456fa1201153cdb3a93e54f67e84c8ac69b01b..e62b5962c79331dda1bbb5666a502eae66304bdd 100644 --- a/examples/minesweeper/CMakeLists.txt +++ b/examples/minesweeper/CMakeLists.txt @@ -1,5 +1,5 @@ add_executable (minesweeper minesweeper.cc) -target_link_libraries (minesweeper LINK_PUBLIC lela) +target_link_libraries (minesweeper LINK_PUBLIC limbo) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_DETERMINES") #set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DEND_GAME_CLAUSES") @@ -10,7 +10,7 @@ if (EMCC) separate_arguments(FLAGS UNIX_COMMAND "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}") add_custom_command (OUTPUT minesweeper-js.js minesweeper-js.js.mem COMMAND ${EMCC} ${FLAGS} -I../../src minesweeper-js.cc -o minesweeper-js.js - -s EXPORTED_FUNCTIONS=['_lela_init','_lela_play_turn'] + -s EXPORTED_FUNCTIONS=['_limbo_init','_limbo_play_turn'] -s TOTAL_MEMORY=67108864 DEPENDS minesweeper-js.cc "*.h" VERBATIM) diff --git a/examples/minesweeper/agent.h b/examples/minesweeper/agent.h index 6fd17ed062d7ac303bfc92a4d87a3d6331125778..dbea1d8155cd258ff233baed75af43d5adf671b8 100644 --- a/examples/minesweeper/agent.h +++ b/examples/minesweeper/agent.h @@ -45,7 +45,7 @@ class Agent { if (g_->opened(p) || g_->flagged(p)) { continue; } - const lela::internal::Maybe r = kb_->IsMine(p, k); + const limbo::internal::Maybe r = kb_->IsMine(p, k); if (r.yes) { if (r.val) { logger_.flagged(p, k); diff --git a/examples/minesweeper/kb.h b/examples/minesweeper/kb.h index e6ad7804e884cce3881265edf6f8abdb4d39da78..caa520f09d852cc97b43987e8aee1fcb30f05720 100644 --- a/examples/minesweeper/kb.h +++ b/examples/minesweeper/kb.h @@ -7,11 +7,11 @@ #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include "game.h" #include "timer.h" @@ -61,75 +61,75 @@ class KnowledgeBase { F(ctx_.CreateName(Bool)), #endif MineF(ctx_.CreateFunction(Bool, 2)) { - lela::format::RegisterSort(Bool, ""); - lela::format::RegisterSort(XPos, ""); - lela::format::RegisterSort(YPos, ""); - lela::format::RegisterSymbol(T.symbol(), "T"); + limbo::format::RegisterSort(Bool, ""); + limbo::format::RegisterSort(XPos, ""); + limbo::format::RegisterSort(YPos, ""); + limbo::format::RegisterSymbol(T.symbol(), "T"); #ifdef USE_DETERMINES - lela::format::RegisterSymbol(F.symbol(), "F"); + limbo::format::RegisterSymbol(F.symbol(), "F"); #endif - lela::format::RegisterSymbol(MineF, "Mine"); + limbo::format::RegisterSymbol(MineF, "Mine"); X.resize(g_->width()); for (size_t i = 0; i < g_->width(); ++i) { X[i] = ctx_.CreateName(XPos); std::stringstream ss; ss << "#X" << i; - lela::format::RegisterSymbol(X[i].symbol(), ss.str()); + limbo::format::RegisterSymbol(X[i].symbol(), ss.str()); } Y.resize(g_->height()); for (size_t i = 0; i < g_->height(); ++i) { Y[i] = ctx_.CreateName(YPos); std::stringstream ss; ss << "#Y" << i; - lela::format::RegisterSymbol(Y[i].symbol(), ss.str()); + limbo::format::RegisterSymbol(Y[i].symbol(), ss.str()); } processed_.resize(g_->n_fields(), false); } size_t max_k() const { return max_k_; } - const lela::Solver& solver() const { return ctx_.solver(); } - const lela::Setup& setup() const { return solver().setup(); } + const limbo::Solver& solver() const { return ctx_.solver(); } + const limbo::Setup& setup() const { return solver().setup(); } - lela::internal::Maybe IsMine(Point p, int k) { + limbo::internal::Maybe IsMine(Point p, int k) { t_.start(); - lela::internal::Maybe r = lela::internal::Nothing; + limbo::internal::Maybe r = limbo::internal::Nothing; #ifdef USE_DETERMINES // In this case we need a second name to represent falsity. The reason is // that without such a name, falsity is represented by "/= T", which is for // Determines() just means the term's value is not determined, whereas we'd // want it to be determined as false. - lela::internal::Maybe is_mine = solver()->Determines(k, Mine(p), lela::Solver::kConsistencyGuarantee); + limbo::internal::Maybe is_mine = solver()->Determines(k, Mine(p), limbo::Solver::kConsistencyGuarantee); assert(!is_mine || is_mine.val == T || is_mine.val == F); - assert(solver()->Determines(k, Mine(p), lela::Solver::kNoConsistencyGuarantee) == is_mine); - assert(solver()->Entails(k, *lela::Formula::Factory::Atomic(lela::Clause{MineLit(true, p)}), - lela::Solver::kConsistencyGuarantee) == + assert(solver()->Determines(k, Mine(p), limbo::Solver::kNoConsistencyGuarantee) == is_mine); + assert(solver()->Entails(k, *limbo::Formula::Factory::Atomic(limbo::Clause{MineLit(true, p)}), + limbo::Solver::kConsistencyGuarantee) == (is_mine && is_mine.val == T)); - assert(solver()->Entails(k, *lela::Formula::Factory::Atomic(lela::Clause{MineLit(true, p)}), - lela::Solver::kConsistencyGuarantee) == - solver()->Entails(k, *lela::Formula::Factory::Atomic(lela::Clause{MineLit(true, p)}), - lela::Solver::kNoConsistencyGuarantee)); - assert(solver()->Entails(k, *lela::Formula::Factory::Atomic(lela::Clause{MineLit(false, p)}), - lela::Solver::kConsistencyGuarantee) == - solver()->Entails(k, *lela::Formula::Factory::Atomic(lela::Clause{MineLit(false, p)}), - lela::Solver::kNoConsistencyGuarantee)); - assert(solver()->Entails(k, *lela::Formula::Factory::Atomic(lela::Clause{MineLit(false, p)}), - lela::Solver::kConsistencyGuarantee) == + assert(solver()->Entails(k, *limbo::Formula::Factory::Atomic(limbo::Clause{MineLit(true, p)}), + limbo::Solver::kConsistencyGuarantee) == + solver()->Entails(k, *limbo::Formula::Factory::Atomic(limbo::Clause{MineLit(true, p)}), + limbo::Solver::kNoConsistencyGuarantee)); + assert(solver()->Entails(k, *limbo::Formula::Factory::Atomic(limbo::Clause{MineLit(false, p)}), + limbo::Solver::kConsistencyGuarantee) == + solver()->Entails(k, *limbo::Formula::Factory::Atomic(limbo::Clause{MineLit(false, p)}), + limbo::Solver::kNoConsistencyGuarantee)); + assert(solver()->Entails(k, *limbo::Formula::Factory::Atomic(limbo::Clause{MineLit(false, p)}), + limbo::Solver::kConsistencyGuarantee) == (is_mine && is_mine.val == F)); if (is_mine) { assert(!is_mine.val.null()); - r = lela::internal::Just(is_mine.val == T); + r = limbo::internal::Just(is_mine.val == T); assert(g_->mine(p) == r.val); } #else - lela::Formula::Ref yes_mine = lela::Formula::Factory::Atomic(lela::Clause{MineLit(true, p)}); - lela::Formula::Ref no_mine = lela::Formula::Factory::Atomic(lela::Clause{MineLit(false, p)}); - if (solver()->Entails(k, *yes_mine, lela::Solver::kConsistencyGuarantee)) { + limbo::Formula::Ref yes_mine = limbo::Formula::Factory::Atomic(limbo::Clause{MineLit(true, p)}); + limbo::Formula::Ref no_mine = limbo::Formula::Factory::Atomic(limbo::Clause{MineLit(false, p)}); + if (solver()->Entails(k, *yes_mine, limbo::Solver::kConsistencyGuarantee)) { assert(g_->mine(p)); - r = lela::internal::Just(true); - } else if (solver()->Entails(k, *no_mine, lela::Solver::kConsistencyGuarantee)) { + r = limbo::internal::Just(true); + } else if (solver()->Entails(k, *no_mine, limbo::Solver::kConsistencyGuarantee)) { assert(!g_->mine(p)); - r = lela::internal::Just(false); + r = limbo::internal::Just(false); } #endif t_.stop(); @@ -157,22 +157,22 @@ class KnowledgeBase { void ResetTimer() { t_.reset(); } private: - lela::Term Mine(Point p) const { + limbo::Term Mine(Point p) const { return MineF(X[p.x], Y[p.y]); } - lela::Literal MineLit(bool is, Point p) const { - lela::Term t = Mine(p); + limbo::Literal MineLit(bool is, Point p) const { + limbo::Term t = Mine(p); #ifdef USE_DETERMINES - return lela::Literal::Eq(t, is ? T : F); + return limbo::Literal::Eq(t, is ? T : F); #else - return is ? lela::Literal::Eq(t, T) : lela::Literal::Neq(t, T); + return is ? limbo::Literal::Eq(t, T) : limbo::Literal::Neq(t, T); #endif } - lela::Clause MineClause(bool sign, const std::vector ns) const { - auto r = lela::internal::transform_range(ns.begin(), ns.end(), [this, sign](Point p) { return MineLit(sign, p); }); - return lela::Clause(r.begin(), r.end()); + limbo::Clause MineClause(bool sign, const std::vector ns) const { + auto r = limbo::internal::transform_range(ns.begin(), ns.end(), [this, sign](Point p) { return MineLit(sign, p); }); + return limbo::Clause(r.begin(), r.end()); } bool Update(Point p) { @@ -183,11 +183,11 @@ class KnowledgeBase { return false; } case Game::FLAGGED: { - AddClause(lela::Clause{MineLit(true, p)}); + AddClause(limbo::Clause{MineLit(true, p)}); return true; } case Game::HIT_MINE: { - AddClause(lela::Clause{MineLit(true, p)}); + AddClause(limbo::Clause{MineLit(true, p)}); return true; } default: { @@ -199,7 +199,7 @@ class KnowledgeBase { for (const std::vector& ps : util::Subsets(ns, m + 1)) { AddClause(MineClause(false, ps)); } - AddClause(lela::Clause{MineLit(false, p)}); + AddClause(limbo::Clause{MineLit(false, p)}); return true; } } @@ -221,12 +221,12 @@ class KnowledgeBase { } } - void AddClause(const lela::Clause& c) { + void AddClause(const limbo::Clause& c) { #ifdef USE_DETERMINES - for (lela::Literal a : c) { - lela::Term t = a.lhs(); + for (limbo::Literal a : c) { + limbo::Term t = a.lhs(); if (closure_added_.find(t) == closure_added_.end()) { - solver()->AddClause(lela::Clause{lela::Literal::Eq(t, T), lela::Literal::Eq(t, F)}); + solver()->AddClause(limbo::Clause{limbo::Literal::Eq(t, T), limbo::Literal::Eq(t, F)}); closure_added_.insert(t); } } @@ -234,26 +234,26 @@ class KnowledgeBase { solver()->AddClause(c); } - lela::Solver* solver() { return ctx_.solver(); } + limbo::Solver* solver() { return ctx_.solver(); } const Game* g_; size_t max_k_; - lela::format::cpp::Context ctx_; + limbo::format::cpp::Context ctx_; - lela::Symbol::Sort Bool; - lela::Symbol::Sort XPos; - lela::Symbol::Sort YPos; - lela::format::cpp::HiTerm T; // name for positive truth value + limbo::Symbol::Sort Bool; + limbo::Symbol::Sort XPos; + limbo::Symbol::Sort YPos; + limbo::format::cpp::HiTerm T; // name for positive truth value #ifdef USE_DETERMINES - lela::format::cpp::HiTerm F; // name for negative truth value + limbo::format::cpp::HiTerm F; // name for negative truth value #endif - std::vector X; // names for X positions - std::vector Y; // names for Y positions - lela::format::cpp::HiSymbol MineF; + std::vector X; // names for X positions + std::vector Y; // names for Y positions + limbo::format::cpp::HiSymbol MineF; #ifdef USE_DETERMINES - std::unordered_set closure_added_; + std::unordered_set closure_added_; #endif std::vector processed_; diff --git a/examples/minesweeper/minesweeper-js.cc b/examples/minesweeper/minesweeper-js.cc index 5b207b5bc7a80378b873583e8cfd0651561c755e..ba4f1a349582780c0fbecea194f84a6f7a11a91b 100644 --- a/examples/minesweeper/minesweeper-js.cc +++ b/examples/minesweeper/minesweeper-js.cc @@ -35,8 +35,8 @@ static Timer* timer_overall = nullptr; static std::vector* split_counts = nullptr; void Finalize() { - lela::Symbol::Factory::Reset(); - lela::Term::Factory::Reset(); + limbo::Symbol::Factory::Reset(); + limbo::Term::Factory::Reset(); if (game) delete game; if (kb) @@ -91,11 +91,11 @@ bool PlayTurn() { } // namespace game -extern "C" void lela_init(size_t width, size_t height, size_t n_mines, size_t seed, int max_k) { +extern "C" void limbo_init(size_t width, size_t height, size_t n_mines, size_t seed, int max_k) { game::Init(width, height, n_mines, seed, max_k); } -extern "C" int lela_play_turn() { +extern "C" int limbo_play_turn() { return game::PlayTurn() ? 1 : 0; } diff --git a/examples/minesweeper/minesweeper-js.html b/examples/minesweeper/minesweeper-js.html index 8567eec77f6a9903778150bbec9fb270c5874af4..85672c96e757559785de2b5d347c7ad63c3939dc 100644 --- a/examples/minesweeper/minesweeper-js.html +++ b/examples/minesweeper/minesweeper-js.html @@ -85,6 +85,11 @@ For further demos and details on the reasoner, click here.