Git Product home page Git Product logo

loki's Introduction

Loki

Loki is a C++20 library for syntactic and semantic parsing of PDDL files.

Supported PDDL Requirements

  • :strips
  • :typing
  • :negative-preconditions
  • :disjunctive-preconditions
  • :equality
  • :existential-preconditions
  • :universal-preconditions
  • :quantified-preconditions
  • :conditional-effects
  • :fluents
  • :numeric-fluents
  • :object-fluents
  • :adl
  • :durative-actions
  • :derived-predicates
  • :timed-initial-literals
  • :preferences
  • :constraints
  • :action-costs

Dependencies

Loki depends on a fraction of Boost's header-only libraries (Fusion, Spirit x3, Container), its performance benchmarking framework depends on GoogleBenchmark, and its testing framework depends on GoogleTest.

We provide a CMake Superbuild project that takes care of downloading, building, and installing all dependencies.

# Configure dependencies
cmake -S dependencies -B dependencies/build -DCMAKE_INSTALL_PREFIX=dependencies/installs
# Build and install dependencies
cmake --build dependencies/build -j16

Build Instructions

# Configure with installation prefixes of all dependencies
cmake -S . -B build -DCMAKE_PREFIX_PATH=${PWD}/dependencies/installs
# Build
cmake --build build -j16
# Install (optional)
cmake --install build --prefix=<path/to/installation-directory>

Integration Instructions

We provide a CMake Superbuild project here that takes care of downloading, building, and installing Loki together and its dependencies. You can simply copy it to your project or integrate it in your own Superbuild and run it similarly to the Superbuild project from above. An example planning library based on Loki is available here.

Running the Examples

The examples illustrate best practices on how to use Loki.

The first example shows the incorrect handling of the ownership semantics. The example is supposed to crash when trying to print the domain for the second time.

./build/examples/undefined_behavior

The second example shows how to parse a domain and problem file which is supposed to be used in a planning system where a non-fragmented indexing of atoms and literals is preferred.

./build/examples/single_problem

The third example shows how to detect structurally equivalent problems over a common domain.

./build/examples/multiple_problems

The fourth example shows how to find the matched positions of each PDDL object in the input stream and how to report customized clang-style error reports.

./build/examples/position_cache

Running the Executables

Parsing a domain file and printing it.

./build/exe/domain benchmarks/gripper/domain.pddl

Parsing a domain and a problem file and printing both.

./build/exe/problem benchmarks/gripper/domain.pddl benchmarks/gripper/p-2-0.pddl

Running the Tests

The testing framework depends on GoogleTest and requires the additional compile flag -DBUILD_TESTS=ON to be set in the cmake configure step.

Performance Benchmarks

The benchmark framework depends on GoogleBenchmark and requires the additional compile flag -DBUILD_BENCHMARKS=ON to be set in the cmake configure step. The results from the GitHub action can be viewed here.

IDE Support

We developed Loki in Visual Studio Code. We recommend the C/C++ and CMake Tools extensions by Microsoft. To get maximum IDE support, you should set the following Cmake: Configure Args in the CMake Tools extension settings under Workspace:

  • -DCMAKE_PREFIX_PATH=${workspaceFolder}/dependencies/installs
  • -DBUILD_TESTS=ON
  • -DBUILD_BENCHMARKS=ON

After running CMake: Configure in Visual Studio Code (ctrl + shift + p), you should see all include paths being correctly resolved.

Acknowledgements

This work was partially supported by the Wallenberg AI, Autonomous Systems and Software Program (WASP) funded by the Knut and Alice Wallenberg Foundation.

loki's People

Contributors

drexlerd avatar simon-stahlberg avatar

Stargazers

Pulkit Verma avatar Nir avatar Javier Segovia avatar Jendrik Seipp avatar

Watchers

Jendrik Seipp avatar Pulkit Verma avatar Anubhav Singh avatar  avatar

loki's Issues

Detecting duplicate tasks

I like the idea of using Loki for detecting duplicate planning tasks! Can you add a short guide to the README that shows how to do this?

Maybe this should be a separate discussion, but have you thought about canonicalizing PDDL files, i.e., sorting everything in them, to detect more duplicates? And possibly even canonicalizing all (object, action, etc.) names?

Printing depends on requirements

I have troubles printing the parameter list of a Predicate because the "object" type should not be printed if :typing is disabled. Furthermore the print function should not take additional arguments since we want to overload the operator<< of sd::ostream.

Formatting PDDL tasks

Another use case for Loki could be to pretty-print input PDDL files. Many of them have quite some strange format. The tricky thing here is to preserve comments. Not sure the parser library can handle that. But maybe there are cases where there are no comments or comments don't need to be preserved.

Iterative syntax parsing to produce more meaningful errors?

The problem is the following. A ast::Name is a word over alpha numerical characters starting with a alphabetical character and a ast::Variable is ast::Name with prefix ?. Clearly, both are syntactically different. The syntactic parser will just fail at a position where it expects a ast::Name but a ast::Variable was given. Currently the error might be some thing like: Expected ')', which I find is a bit unsatisfying. If we would instead parse a node ast::NameOrVariable represented as an alternative and get a successful parse, we could decide to throw a more meaningful error in a later parsing step (syntactic or semantic parsing).

I think this should be the responsibility of the syntactic parsing: Ideally, we would like a boost syntax parser with only expectation operations (>) because they allow us to report a meaningful errors. When an expectation operation fails, an exception is thrown with a meaningful error message and position in the input stream depending on the parser that failed, e.g., "Expected a variable or a name". This requires splitting the syntax parser into an iterative process. Important question: can we parse only the most basic structure first, e.g., for each opening parenthesis there must follow zero or more characters, must follow closing parenthesis, i.e., node_def = lit('(') > *text_or_node() > lit(')'), text_or_node_def = (*char_ | node). I think the answer is yes. Furthermore, can we then refine the AST that we get to obtain a more concrete AST with same idea of using expectation operations only? Probably yes, but not so sure how easy that is because when the nodes are more abstract, they may contain many alternatives (|) and therefore, a resulting blowup in the number of AST nodes and parsers.

This will require a lot of rework in the syntactic parser but should be fully separate from the semantic parsing that we currently got since the resulting AST will be the exact same as the AST that we currently have. However, it is important to ensure that this will not cause changes on other sides of the code since this is highly relevant for the goal of the project.

Moving towards zero cost abstraction

The current PDDL data structure is very powerful and convenient. However, we want to provide a better abstraction that is closer to zero cost. The main issue is the usage of std::shared_ptr in the PDDL data structure which is not zero cost when copying: requires a redundant atomic increment and requires std::mutex locks to ensure that the ref count is synchronized a cross several threads.

One way to address this limitation is to translate the shared_ptr based data structure (shared_PDDL) to an analogous data structure based on a combination of std::unique_ptr and raw pointers. Since PDDL objects are const, this solves the thread synchronization problem.

Another step towards zero cost abstraction, would be to provide a non fragmented indexing scheme for each PDDL type. This can be easily addressed by splitting the PDDL factory into one for each type. To ensure non fragmented indices after user define compilation, we can provide a function to reparse the result of the compilation. To make the usage of indices safer, we could use a handle-like type for each PDDL type.

PDDL memory representation in regards of usability and multi-threaded applications

There are good reasons to move towards value semantics as opposed to pointer semantics for storing PDDL objects. 1) We don't need virtual bases classes but we currently use them, e.g., for conditions, effects. 2) Cache locality: with a combination of handles and views we can get better cache locality. A PDDL object will then be composed of Views to make it easy to access the data members.

A Handle could be implemented as follows:

template<typename T>
class Handle {
    private:
        int m_identifier;
    
    public:
        Handle(int identifier) : m_identifier(identifier) { }

        int get_identifier() const { return m_identifier; }
};

A View could be implemented as follows:

template<typename T>
class View {
    private:
        Handle<T> m_handle;
        const VectorByHandle<T>* m_vector;  

    public:
        View(Handle<T> handle, const HandleVector<T>& vector) : m_handle(handle), m_vector(&vector) { }

        Handle<T> get_handle() const { return m_handle; }
        const T& get_data() const { return (*m_vector)[m_handle]; }
};

A VectorByHandle could be implemented as follows:

template<typename T>
class VectorByHandle {
    private:
       std::vector<T> m_vector;

    public:
        // delete copy/move to not invalidate the pointer in a View
        VectorByHandle(const VectorByHandle& other) = delete;
        VectorByHandle& operator=(const VectorByHandle& other) = delete;
        VectorByHandle(VectorByHandle&& other) = delete;
        VectorByHandle& operator=(VectorByHandle&& other) = delete;

        const T& operator[](Handle<T> handle) const { return m_vector[handle.get_identifier()]; }
};

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.