{"id":21033555,"url":"https://github.com/gabyx/executiongraph","last_synced_at":"2025-05-15T13:32:16.161Z","repository":{"id":18254614,"uuid":"83893035","full_name":"gabyx/ExecutionGraph","owner":"gabyx","description":"Fast Generic Execution Graph/Network","archived":false,"fork":false,"pushed_at":"2022-11-10T20:23:13.000Z","size":26045,"stargazers_count":46,"open_issues_count":14,"forks_count":7,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-03T09:41:37.594Z","etag":null,"topics":["data-analysis","data-processing","execution-graph","execution-pipeline","graph","simulink"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gabyx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-04T12:51:13.000Z","updated_at":"2024-10-29T04:41:40.000Z","dependencies_parsed_at":"2023-01-11T20:28:30.271Z","dependency_job_id":null,"html_url":"https://github.com/gabyx/ExecutionGraph","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabyx%2FExecutionGraph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabyx%2FExecutionGraph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabyx%2FExecutionGraph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabyx%2FExecutionGraph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gabyx","download_url":"https://codeload.github.com/gabyx/ExecutionGraph/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254349471,"owners_count":22056355,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-analysis","data-processing","execution-graph","execution-pipeline","graph","simulink"],"created_at":"2024-11-19T12:57:50.769Z","updated_at":"2025-05-15T13:32:11.132Z","avatar_url":"https://github.com/gabyx.png","language":"C++","readme":"**This library is in alpha and still under development. First usable version will be `v1.1`. Note: This library is under heavy development on the `dev` branch! Forking should mainly be considered for looking around, or contributing of course. At the moment there are some major design flaws which make the Library not less usable but unpractical in the future, these flaws need to be fixed first.**\n\n\u003cimg src=\"gui/executionGraphGui/client/apps/electron/resources/icon.svg\" height=\"150px\" style=\"display: inline;vertical-align: middle; horizontal-align:center\"/\u003e\n\n# ExecutionGraph \n[![Build Status](https://travis-ci.org/gabyx/ExecutionGraph.svg?branch=master)](https://travis-ci.org/gabyx/ExecutionGraph)      \n![system](https://img.shields.io/badge/system-linux,osx-blue.svg) ![std](https://img.shields.io/badge/std-c++17-green.svg)   \n[![Gitter chat](https://badges.gitter.im/ExecutionGraph/gitter.png)](https://gitter.im/ExecutionGraph/community)      \n[![Live](https://img.shields.io/badge/live-gui--demo-blue.svg)](https://gabyx.github.io/ExecutionGraph/demo)      \n\n**Fast Execution Graph consisting of Execution Nodes**    \n\nBe able to design and run such input/output dataflow graphs, such as the ones used for the work [here](http://gabyx.github.io/GRSFramework/#videos) (using this [graph](https://cdn.rawgit.com/gabyx/GRSFramework/b1414aa0/simulations/examples/jobs/simulationStudies/avalanche1M-Tree-SimStudy/analyzeStartJob/analyzerLogic/FindStart.svg)). A generic, independent GUI is provided in from of a single-page Angular application with a backend HTTP server which allows interactive design/manipulation and execution of graphs: \n\n![Current GUI](docs/ExecutionGraphGui.png)\n\n\u003c!-- @import \"[TOC]\" {cmd=\"toc\" depthFrom=1 depthTo=6 orderedList=false} --\u003e\n\n\u003c!-- code_chunk_output --\u003e\n\n* [ExecutionGraph](#executiongraph)\n\t* [Installing and Dependencies](#installing-and-dependencies)\n\t\t* [Library](#library)\n\t\t* [GUI Backend](#gui-backend)\n\t\t* [GUI Client](#gui-client)\n\t\t* [Testing](#testing)\n\t\t* [OS X](#os-x)\n\t* [Building](#building)\n* [Contributing](#contributing)\n\t* [General Development Setup](#general-development-setup)\n\t\t* [Codeformatting](#codeformatting)\n\t\t* [GUI](#gui)\n\t* [Introduction](#introduction)\n\t\t* [Example 1:](#example-1)\n* [Contributors](#contributors)\n\n\u003c!-- /code_chunk_output --\u003e\n\n## Installing and Dependencies\nTo build the library, the tests and the example you need the build tool [cmake](\nhttp://www.cmake.org).\nThis library has these dependencies:\n\n### Library\n- [meta](https://github.com/ericniebler/meta) (meta programming)\n- [crossguid](https://github.com/graeme-hill/crossguid) (guid implementation)\n- [rttr](https://github.com/rttrorg/rttr) (runtime type information, serialization only)\n- [fmt](https://github.com/fmtlib/fmt.git) (asserts, exception formatting)\n### GUI Backend\n- [args](https://github.com/Taywee/args) (argument parser)\n- [memory](https://github.com/foonathan/memory.git) (memory allocators)\n- [spdlog](https://github.com/gabime/spdlog) (logs)\n### GUI Client\n- [node](https://nodejs.org/) (client build)\n### Testing\n- [googletest](https://github.com/google/googletest) (for tests)\n- [benchmark](https://github.com/google/benchmark) (for benchmarks)\n\nFor easy building, all dependencies are searched, downloaded and built if not found, during the first super build run.\n\n### OS X\nInstall the latest `clang` with [homebrew](https://brew.sh) by **updateing your xcode installation**, \ninstalling a [code-sign certificate](https://llvm.org/svn/llvm-project/lldb/trunk/docs/code-signing.txt)\nfor lldb and then running:\n```bash\nbrew install --HEAD llvm --with-toolchain --with-lldb\n```\nor manually install\n```bash\ngit clone https://github.com/llvm/llvm-project llvm-project\nmkdir llvm-build \u0026\u0026 cd llvm-build\ncmake ../llvm-project/llvm -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=\"clang;libcxx;libcxxabi;lldb;compiler-rt;lld;polly\" -DCMAKE_INSTALL_PREFIX=\"/usr/local/opt/llvm-latest\"\nmake -j install\n```\n\n\nNow you should be ready to configure with cmake:\n\n## Building\nSource the `tools/.enable-compiler.sh` and use `enableCompiler \"clang\"` which uses the `tools/.clang-flags-llvm-latest.cfg` to setup the compiler before you configure with: \n\n```bash\ncd \u003cpathToRepo\u003e\nmkdir build\ncd build\n# configuring the superbuild (-DUSE_SUPERBUILD=ON is default)\ncmake .. -DUSE_SUPERBUILD=ON \\\n        -DExecutionGraph_BUILD_TESTS=true \\\n        -DExecutionGraph_BUILD_LIBRARY=true \\\n        -DExecutionGraph_BUILD_GUI=true \\\n        -DExecutionGraph_EXTERNAL_BUILD_DIR=\"$(pwd)/external\" \\\n        -DExecutionGraph_USE_ADDRESS_SANITIZER=true \\\n        -DCMAKE_EXPORT_COMPILE_COMMANDS=true\n# building the superbuild configuration \nmake -j all\n# configuring the actual build\ncmake ..\n# building the library/gui\nmake -j \u003ctargetName\u003e\n```\nWe use a super build setup. The first cmake configure and build by using `-DUSE_SUPERBUILD=ON` (automatically set at first run) will download every dependency and build the ones which need installing.\n See the cmake variable `ExecutionGraph_EXTERNAL_BUILD_DIR` which is used for building all external dependencies to be able to quickly delete certain dependencies without deleting the normal build folder `build`). \nAfter the super build, the cmake cache file `CMakeCache.txt` is setup with all necessary variables, that **later** cmake *configure* invocations will find all dependencies \nand configure the actual project. This works also with VS Code and the cmake extension.\n\n# Contributing\nThis project supports [Visual Studio Code](https://code.visualstudio.com/) which is warmly recommended.\n\n**Note:** Dont use the [multi-root workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces) feature in VS Code since the C++ Extension does not yet support this and code completion won't work properly.\n\n## General Development Setup\nIf you start developing, install the pre-commit/post-commit hooks with:\n```bash\nsudo npm install -g json-fmt xmllint prettier\nsudo apt-get install plantuml # or sudo brew install plantuml\ncd .git \u0026\u0026 mv hooks hooks.old \u0026\u0026 ln -s ../tools/git-hooks hooks\n```\n\n### Codeformatting\nYou can run the same pre-commit hook by doing\n``` \ntools/formatAll.sh\n```\nwhich will format all files for which formatting styles have been defined in the repository.\n\n### GUI\nThe UI is made up of an [Angular](https://angular.io) application that uses the [Angular CLI](https://cli.angular.io) to create the web assets that are ultimately displayed in an [electron app](https://electronjs.org/) browser.\nThe client backend consists of a http server which provides the executiong graph.\nPlease visit the Angular CLI website for its prerequisites (node.js and npm respectively, also a globally installed Angular CLI aka `ng`).\nOnce you installed the prerequisites build with\n\n```bash\ncd gui/executionGraphGui/client\nnpm install\nnpm run serve\n```\n\nFor more information about the development of the client application please refer to the dedicated [client documentation](gui/client/README.md)\n\n## Introduction\nThe execution graph implemented in `ExecutionTree` is a directed acyclic graph consisting of several connected nodes derived from `LogicNode` which define a simple input/output control flow.\nEach node in the execution graph contains several input/output sockets (`LogicSocket`) with a certain type out of the predefined types defined in `LogicSocketTypes`. \n\nAn execution graph works in the way that each node contains a specific `compute` routine which provides values for the output sockets by using the values from the input sockets. \nEach output of a node can be linked to an input of the same type of another node. This means an output socket of the arithmetic type `double` cannot be linked to an input socket of integral type `int` for example. \nEach node can be assigned to one or more execution groups which are collections of nodes and form directed acyclic subgraphs. \n\nFor each execution group, an execution order is computed such that the data flow defined by the input/output links in the group is respected.\n\nAn execution order of an execution group is called a *topological ordering* in computer science, and such an ordering always exists for a directed acyclic graph, despite being non-unique. A topological ordering of an execution group is an ordering of all nodes such that for all connections from a node `A` to `B`, `A` precedes `B` in the ordering. Each execution graph network consists of several input nodes whose output sockets are initialized before executing the network. \nThe implementation in `LogicSocket` allows two types of directed links between an input and output socket, namely a *get* and a *write* connection. \n\nA *write* link is a link from an output socket `i` of a node `A` to an input socket `j` of some node `B`, denoted as `{A,i} -\u003e {j,B}`.\nA *write* link basically duplicates a write request to the output socket `i` of `A` also to an additional write request to the input socket `j` of `B`.\n\nA *get* link is the exact opposite and is a link from an input socket `j` of a node `B` to an output socket `i` of a node `A`, denoted as \n`{A,i} \u003c- {j,B}`. \nA *get* link basically forwards any read access on the input socket `j` of `B` to a read access on the input socket `i` of `A`.\n\nMost of the time only *get* links are necessary but as soon as the execution graph becomes more complex and certain switching behavior should be reproduced, the additional *write* links are a convenient tool to realize this. \n\nCyclic paths between nodes are detected and result in an error when building the execution network.\nThe write and read access to input and output sockets is implemented using a fast static type dispatch system in `LogicSocket`.\n\nStatic type dispatching avoids the use of virtual calls when using polymorphic objects in object-oriented programming languages.\n\n### Example 1: \nSource: `examples/libraryUsage`   \nLet us build the simple directed graph below: \n\n![Example1](docs/Example1.svg)\n\nThis execution tree consists of 4 input nodes, e.g. Node `1a`, `1b`, `2a`, `2b`, and 1 output node `4a`.\nEach node has 2 input sockets, e.g. denoted as `i0` and `i1`, and one output socket `o0`.\nThe type of the input and output sockets in this example is simply `int`.\nEach node computes the sum of both input sockets `i0` and `i1` and stores it in the output socket `i1` (of course this is kind of stupid, it is only an example =).\n\nFirst, we define our node type called `IntegerNode\u003c...\u003e`:\n```c++\ntemplate\u003ctypename TConfig\u003e\nclass IntegerNode : public typename TConfig::NodeBaseType\n{\npublic:\n    using Config = TConfig;\n    using NodeBaseType = typename Config::NodeBaseType;\n    enum Ins\n    {\n        Value1,\n        Value2\n    };\n    enum Outs\n    {\n        Result1,\n    };\n```\nWe start of by deriving our `IntegerNode` from `TConfig::NodeBaseType`. The template parameter `TConfig` lets us configure our execution graph (especially the socket type list).\nThe type `TConfig::NodeBaseType` is the basis class for all nodes (resulting in `LogicNode\u003cConfig\u003e`).\nThe two enumerations `Ins` and `Outs` let us define some handy abbreviations for our input sockets (`Value1` and `Value2`) and our output socket (`Result1`). The sequential ordering of the enumerations in `Ins` and `Outs` does not matter at all! So far so good. Now we use some macro for letting us specify the input/output ordering:\n```c++\nprivate:\n    EXECGRAPH_DEFINE_SOCKET_TRAITS(Ins, Outs);\n    // Define the input socket decleration list:\n    using InSockets  = InSocketDeclList\u003cInSocketDecl\u003cValue1, int\u003e,\n                                        InSocketDecl\u003cValue2, int\u003e\u003e;\n    // Define the input socket decleration list:\n    using OutSockets = OutSocketDeclList\u003cOutSocketDecl\u003cResult1, int\u003e\u003e;\n```\nWhat we are specifying here is the following:\nThe type `InSockets` is a *socket declaration list* which says that the input socket with enumeration value `Value1` is of type `int` and is the first input `i0`. The second entry defines the second input socket with enumeration value `Value2` which is of type `int` too.\nThe same is done for our output by defining `OutSockets`.\n\nNow we define two other handy macros:\n```c++\n    EXECGRAPH_DEFINE_LOGIC_NODE_GET_TYPENAME();\n    EXECGRAPH_DEFINE_LOGIC_NODE_VALUE_GETTERS(Ins, InSockets, Outs, OutSockets);\n```\nThe first one is not so important. It only defines some `virtual std::string getTypeName()` function which demangles the type of this node at runtime (for debugging purposes).\nThe second one defines some handy value-getters and setters for easy access (by means of the enumerations `Ins` and `Outs`) to the sockets values (more later).\n\nLet us define the constructor of our `IntegerNode\u003c...\u003e`:\n```c++\ntemplate\u003ctypename... Args\u003e\n    IntegerNode(Args\u0026\u0026... args)\n        : NodeBaseType(std::forward\u003cArgs\u003e(args)...)\n    {\n        this-\u003etemplate addSockets\u003cInSockets\u003e(std::make_tuple(2,2));\n        this-\u003etemplate addSockets\u003cOutSockets\u003e(std::make_tuple(0));\n    }\n```\nIn the constructor, we create (add) the input and output sockets to the node. The parameter `std::tuple\u003c...\u003e` contains the default (constructor) values for the values stored in the sockets. So in the above snippet, we set the input sockets both to the value `2` and the output socket to the value `0`.\nNext we define the actual computation which is performed when this node is executed:\n```c++\n    void compute() override {\n        getOutVal\u003cResult1\u003e() = getInVal\u003cValue1\u003e() + getInVal\u003cValue2\u003e();\n    }\n}; // end of class declaration\n```\nHere we simply add both input values ( `getInVal\u003c...\u003e()` return a reference) and store the result in the output socket.\n\nNow, let us build the main ingredient of this example: the execution tree. \nFirst we allocate the 7 nodes by\n```c++\nusing namespace executionGraph; \nint main(){\n    using Config = GeneralConfig\u003c\u003e; // we use the default configuration\n    \n    auto node1a = std::make_unique\u003cIntegerNode\u003cConfig\u003e\u003e(0);\n    auto node1b = std::make_unique\u003cIntegerNode\u003cConfig\u003e\u003e(1);\n    auto node2a = std::make_unique\u003cIntegerNode\u003cConfig\u003e\u003e(2);\n    auto node2b = std::make_unique\u003cIntegerNode\u003cConfig\u003e\u003e(3);\n    auto node3a = std::make_unique\u003cIntegerNode\u003cConfig\u003e\u003e(4);\n    auto node3b = std::make_unique\u003cIntegerNode\u003cConfig\u003e\u003e(5);\n    auto node4a = std::make_unique\u003cIntegerNode\u003cConfig\u003e\u003e(6);\n    auto resultNode = node4a.get();\n```\nEach node is given a unique id `[0,...,6]`, which enables us to identify the nodes easier.\nNext we create the *get* links which connect the in- and outputs. \n```c++\n    int i0 = 0; int i1 = 1; int o0 = 0;\n    node4a-\u003esetGetLink(*node3a,o0,i0);\n    node4a-\u003esetGetLink(*node3b,o0,i1);\n\n    node3a-\u003esetGetLink(*node1a,o0,i0);\n    node3a-\u003esetGetLink(*node1b,o0,i1);\n    \n    node3b-\u003esetGetLink(*node2a,o0,i0);\n    node3b-\u003esetGetLink(*node2b,o0,i1);\n```\nThe syntax `node4a-\u003esetGetLink(*node3a,o0,i0);` denotes that the output node `node4a` gets its first input value `i0 = 0` from the single output `o0 = 0` of node `node3a`. The above snippet builds the execution tree given at the begining.\nFinally we create the ExecutionTree `ExecutionTree`, add all nodes to it, set the proper node classfication (if its an input or output node, setup the graph (which computes the execution order) and execute the default execution group `0` as\n```c++\n    // Make the execution tree and add all nodes\n    ExecutionTree\u003cConfig\u003e execTree;\n    execTree.addNode(std::move(node1a)); // The execution tree owns the nodes!\n    execTree.addNode(std::move(node1b));\n    execTree.addNode(std::move(node2a));\n    execTree.addNode(std::move(node2b));\n    execTree.addNode(std::move(node3a));\n    execTree.addNode(std::move(node3b));\n    execTree.addNode(std::move(node4a));\n\n    // Set all node classifications\n    execTree.setNodeClass(0, ExecutionTree\u003cConfig\u003e::NodeClassification::InputNode);\n    execTree.setNodeClass(1, ExecutionTree\u003cConfig\u003e::NodeClassification::InputNode);\n    execTree.setNodeClass(2, ExecutionTree\u003cConfig\u003e::NodeClassification::InputNode);\n    execTree.setNodeClass(3, ExecutionTree\u003cConfig\u003e::NodeClassification::InputNode);\n    execTree.setNodeClass(6, ExecutionTree\u003cConfig\u003e::NodeClassification::OutputNode);\n    \n    // Setup the execution tree\n    execTree.setup();\n    EXECGRAPH_LOG_INFO(execTree.getExecutionOrderInfo());\n    execTree.execute(0); // execute the default execution group (=0)\n    EXECGRAPH_LOG_INFO(\"Result : '{0}'\", resultNode-\u003egetOutVal\u003cIntegerNode\u003cConfig\u003e::Result1\u003e());\n```\nThis outputs the following execution order:\n```c++\nExecution order for group id: 0\n    NodeId  |  Priority  |  NodeType            \n    ------  |  --------  |  --------            \n         0  |         2  |  IntegerNode\u003cexecutionGraph::GeneralConfig\u003c...\u003e \u003e*\n         1  |         2  |  IntegerNode\u003cexecutionGraph::GeneralConfig\u003c...\u003e \u003e*\n         2  |         2  |  IntegerNode\u003cexecutionGraph::GeneralConfig\u003c...\u003e \u003e*\n         3  |         2  |  IntegerNode\u003cexecutionGraph::GeneralConfig\u003c...\u003e \u003e*\n         5  |         1  |  IntegerNode\u003cexecutionGraph::GeneralConfig\u003c...\u003e \u003e*\n         4  |         1  |  IntegerNode\u003cexecutionGraph::GeneralConfig\u003c...\u003e \u003e*\n         6  |         0  |  IntegerNode\u003cexecutionGraph::GeneralConfig\u003c...\u003e \u003e*\n    ------  |  --------  |  --------  \n```\n\n# Contributors\n**Gabriel Nützi** and many thanks to:\n- **[Simon Spörri](https://github.com/simonspoerri)**\nfor his nice and ellaborate take on the client gui application!\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgabyx%2Fexecutiongraph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgabyx%2Fexecutiongraph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgabyx%2Fexecutiongraph/lists"}