diff --git a/alib2graph_data/src/graph/GraphBase.hpp b/alib2graph_data/src/graph/GraphBase.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ef3e66e71e44d2c8ea0551124631c9a9b5b36430
--- /dev/null
+++ b/alib2graph_data/src/graph/GraphBase.hpp
@@ -0,0 +1,32 @@
+// GraphBase.hpp
+//
+//     Created on: 28. 02. 2018
+//         Author: Jan Uhlik
+//    Modified by:
+//
+// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
+// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library
+
+#ifndef ALIB2_GRAPHBASE_HPP
+#define ALIB2_GRAPHBASE_HPP
+
+#include <object/ObjectBase.h>
+
+namespace graph {
+
+/**
+ * Represents graph.
+ */
+class GraphBase : public object::ObjectBase {
+// ---------------------------------------------------------------------------------------------------------------------
+
+ public:
+  virtual GraphBase *clone() const = 0;
+  virtual GraphBase *plunder() &&= 0;
+
+// ---------------------------------------------------------------------------------------------------------------------
+};
+
+} // namespace graph
+
+#endif //ALIB2_GRAPHBASE_HPP
diff --git a/alib2graph_data/src/graph/GraphFeatures.hpp b/alib2graph_data/src/graph/GraphFeatures.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0e3f7c3ab9603d724efcbd0344400466563157bd
--- /dev/null
+++ b/alib2graph_data/src/graph/GraphFeatures.hpp
@@ -0,0 +1,42 @@
+// GraphFeatures.hpp
+//
+//     Created on: 01. 03. 2018
+//         Author: Jan Uhlik
+//    Modified by:
+//
+// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
+// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library
+
+#ifndef ALIB2_GRAPHFEATURES_HPP
+#define ALIB2_GRAPHFEATURES_HPP
+
+#include <common/DefaultTypes.hpp>
+
+namespace graph {
+
+class GraphBase;
+
+template<typename TNode = DefaultNodeType, typename = DefaultEdgeType>
+class GraphInterface;
+
+template<typename TNode = DefaultNodeType, typename = DefaultEdgeType>
+class UndirectedGraph;
+
+template<typename TNode = DefaultNodeType, typename = DefaultEdgeType>
+class UndirectedMultiGraph;
+
+template<typename TNode = DefaultNodeType, typename = DefaultEdgeType>
+class DirectedGraph;
+
+template<typename TNode = DefaultNodeType, typename = DefaultEdgeType>
+class DirectedMultiGraph;
+
+template<typename TNode = DefaultNodeType, typename = DefaultEdgeType>
+class MixedGraph;
+
+template<typename TNode = DefaultNodeType, typename = DefaultEdgeType>
+class MixedMultiGraph;
+
+} // namespace graph
+
+#endif //ALIB2_GRAPHFEATURES_HPP
diff --git a/alib2graph_data/src/graph/GraphInterface.hpp b/alib2graph_data/src/graph/GraphInterface.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d9c6b39d00f792b853e7b1af8bdeb695cc4bd653
--- /dev/null
+++ b/alib2graph_data/src/graph/GraphInterface.hpp
@@ -0,0 +1,56 @@
+// GraphInterface.hpp
+//
+//     Created on: 29. 01. 2018
+//         Author: Jan Uhlik
+//    Modified by:
+//
+// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
+// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library
+
+#ifndef ALIB2_GENERALGRAPH_HPP
+#define ALIB2_GENERALGRAPH_HPP
+
+#include <alib/set>
+#include <alib/vector>
+#include <functional>
+
+#include "GraphBase.hpp"
+
+namespace graph {
+
+template<typename TNode, typename TEdge>
+class GraphInterface : public GraphBase {
+// ---------------------------------------------------------------------------------------------------------------------
+ public:
+  using node_type = TNode;
+  using edge_type = TEdge;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  virtual size_t nodeCount() const = 0;
+  virtual size_t edgeCount() const = 0;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  virtual ext::set<TNode> getNodes() const = 0;
+  virtual ext::vector<TEdge> getEdges() const = 0;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  virtual ext::set<TNode> successors(const TNode &n) const = 0;
+  virtual ext::vector<TEdge> successorEdges(const TNode &n) const = 0;
+  virtual ext::set<TNode> predecessors(const TNode &n) const = 0;
+  virtual ext::vector<TEdge> predecessorEdges(const TNode &n) const = 0;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  virtual std::string name() const = 0;
+
+// ---------------------------------------------------------------------------------------------------------------------
+};
+
+// =====================================================================================================================
+
+}
+
+#endif //ALIB2_GENERALGRAPH_HPP
diff --git a/alib2graph_data/src/graph/undirected/UndirectedGraph.cpp b/alib2graph_data/src/graph/undirected/UndirectedGraph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bc233709ddd423dec341274082dc11f00aeb83f3
--- /dev/null
+++ b/alib2graph_data/src/graph/undirected/UndirectedGraph.cpp
@@ -0,0 +1,17 @@
+// UndirectedGraph.cpp
+//
+//     Created on: 28. 02. 2018
+//         Author: Jan Uhlik
+//    Modified by:
+//
+// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
+// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library
+
+#include "UndirectedGraph.hpp"
+#include <registration/ValuePrinterRegistration.hpp>
+
+namespace {
+
+static auto valuePrinter = registration::ValuePrinterRegister<graph::UndirectedGraph<> >();
+
+}
diff --git a/alib2graph_data/src/graph/undirected/UndirectedGraph.hpp b/alib2graph_data/src/graph/undirected/UndirectedGraph.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a082437d4b6e72f45f79deabe0a78fb1ba7f6173
--- /dev/null
+++ b/alib2graph_data/src/graph/undirected/UndirectedGraph.hpp
@@ -0,0 +1,385 @@
+// UndirectedGraph.hpp
+//
+//     Created on: 30. 01. 2018
+//         Author: Jan Uhlik
+//    Modified by:
+//
+// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
+// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library
+
+#ifndef ALIB2_UNDIRECTEDGRAPH_HPP
+#define ALIB2_UNDIRECTEDGRAPH_HPP
+
+#include <alib/map>
+#include <alib/pair>
+#include <alib/set>
+#include <alib/vector>
+#include <functional>
+#include <object/Object.h>
+#include <object/UniqueObject.h>
+#include <core/normalize.hpp>
+#include <alib/tuple>
+
+#include <common/Normalize.hpp>
+#include <graph/GraphInterface.hpp>
+#include <graph/GraphFeatures.hpp>
+
+namespace graph {
+
+template<typename TNode, typename TEdge>
+class UndirectedGraph : public GraphInterface<TNode, TEdge> {
+// ---------------------------------------------------------------------------------------------------------------------
+
+ public:
+  using node_type = TNode;
+  using edge_type = TEdge;
+  using normalized_type = UndirectedGraph<>;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+ protected:
+  ext::map<TNode, ext::map<TNode, TEdge>> m_adjacency_list;
+
+// =====================================================================================================================
+// Constructor, Destructor, Operators
+ public:
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  const ext::map<TNode, ext::map<TNode, TEdge>> &getAdjacencyList() const &;
+
+  ext::map<TNode, ext::map<TNode, TEdge>> &&getAdjacencyList() &&;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+// =====================================================================================================================
+// ObjectBase interface
+ public:
+  GraphBase *clone() const override;
+
+  GraphBase *plunder() &&override;
+
+  int compare(const object::ObjectBase &other) const override;
+
+  virtual int compare(const UndirectedGraph &other) const;
+
+  object::ObjectBase *inc() &&override;
+
+  void operator>>(std::ostream &ostream) const override;
+
+  explicit operator std::string() const override;
+
+// =====================================================================================================================
+// Graph interface
+
+ public:
+  void addNode(const TNode &n);
+  void addNode(TNode &&n);
+  template<typename ... Params>
+  void addNode(Params &&... params);
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  bool addEdge(const TEdge &e);
+  bool addEdge(TEdge &&e);
+  template<typename ... Params>
+  bool addEdge(Params &&... params);
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+// =====================================================================================================================
+// GraphInterface interface
+ public:
+
+  size_t nodeCount() const override;
+  size_t edgeCount() const override;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  ext::set<TNode> getNodes() const override;
+  ext::vector<TEdge> getEdges() const override;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  ext::set<TNode> successors(const TNode &n) const override;
+  ext::vector<TEdge> successorEdges(const TNode &n) const override;
+  ext::set<TNode> predecessors(const TNode &n) const override;
+  ext::vector<TEdge> predecessorEdges(const TNode &n) const override;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  std::string name() const override;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+};
+
+// =====================================================================================================================
+
+template<typename TNode, typename TEdge>
+const ext::map<TNode, ext::map<TNode, TEdge>> &UndirectedGraph<TNode,
+                                                               TEdge>::getAdjacencyList() const &{
+  return m_adjacency_list;
+}
+
+template<typename TNode, typename TEdge>
+ext::map<TNode, ext::map<TNode, TEdge>> &&UndirectedGraph<TNode, TEdge>::getAdjacencyList() &&{
+  return std::move(m_adjacency_list);
+}
+
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+void UndirectedGraph<TNode, TEdge>::addNode(const TNode &n) {
+  m_adjacency_list[n];
+}
+
+template<typename TNode, typename TEdge>
+void UndirectedGraph<TNode, TEdge>::addNode(TNode &&n) {
+  m_adjacency_list[std::forward<TNode>(n)];
+}
+
+template<typename TNode, typename TEdge>
+template<typename... Params>
+void UndirectedGraph<TNode, TEdge>::addNode(Params &&... params) {
+  addNode(TNode(std::forward<Params>(params) ...));
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+bool UndirectedGraph<TNode, TEdge>::addEdge(const TEdge &e) {
+  if (e.first == e.second) {
+    return false;
+  }
+
+  if (m_adjacency_list.find(e.first) != m_adjacency_list.end()
+      && m_adjacency_list[e.first].find(e.second) != m_adjacency_list[e.first].end()) {
+    return false;
+  }
+
+  m_adjacency_list[e.first].insert(ext::make_pair(e.second, e));
+  m_adjacency_list[e.second].insert(ext::make_pair(e.first, e));
+
+  return true;
+}
+
+template<typename TNode, typename TEdge>
+bool UndirectedGraph<TNode, TEdge>::addEdge(TEdge &&e) {
+  if (e.first == e.second) {
+    return false;
+  }
+
+  if (m_adjacency_list.find(e.first) != m_adjacency_list.end()
+      && m_adjacency_list[e.first].find(e.second) != m_adjacency_list[e.first].end()) {
+    return false;
+  }
+
+  m_adjacency_list[e.first].insert(ext::make_pair(e.second, e));
+  m_adjacency_list[e.second].insert(ext::make_pair(e.first, std::forward<TEdge>(e)));
+
+  return true;
+}
+
+template<typename TNode, typename TEdge>
+template<typename... Params>
+bool UndirectedGraph<TNode, TEdge>::addEdge(Params &&... params) {
+  return addEdge(TEdge(std::forward<Params>(params) ...));
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+size_t UndirectedGraph<TNode, TEdge>::nodeCount() const {
+  return m_adjacency_list.size();
+}
+
+template<typename TNode, typename TEdge>
+size_t UndirectedGraph<TNode, TEdge>::edgeCount() const {
+  size_t cnt = 0;
+
+  for (const auto &i: m_adjacency_list) {
+    cnt += i.second.size();
+  }
+
+  return cnt / 2;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+ext::set<TNode> UndirectedGraph<TNode, TEdge>::getNodes() const {
+  ext::set<TNode> set;
+
+  for (const auto &i: m_adjacency_list) {
+    set.insert(i.first);
+  }
+
+  return set;
+}
+
+template<typename TNode, typename TEdge>
+ext::vector<TEdge> UndirectedGraph<TNode, TEdge>::getEdges() const {
+  ext::vector<TEdge> vec;
+
+  for (const auto &i:m_adjacency_list) {
+    for (const auto &j : i.second) {
+      vec.push_back(j.second);
+    }
+  }
+
+  return vec;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+ext::set<TNode> UndirectedGraph<TNode, TEdge>::successors(const TNode &n) const {
+  ext::set<TNode> set;
+
+  if (m_adjacency_list.count(n) == 0) {
+    return set;
+  }
+
+  for (const auto &i: m_adjacency_list.at(n)) {
+    set.insert(i.first);
+  }
+
+  return set;
+}
+
+template<typename TNode, typename TEdge>
+ext::vector<TEdge> UndirectedGraph<TNode, TEdge>::successorEdges(const TNode &n) const {
+  ext::vector<TEdge> vec;
+
+  if (m_adjacency_list.count(n) == 0) {
+    return vec;
+  }
+
+  for (const auto &i: m_adjacency_list.at(n)) {
+    vec.push_back(i.second);
+  }
+
+  return vec;
+}
+
+template<typename TNode, typename TEdge>
+ext::set<TNode> UndirectedGraph<TNode, TEdge>::predecessors(const TNode &n) const {
+  return successors(n);
+}
+
+template<typename TNode, typename TEdge>
+ext::vector<TEdge> UndirectedGraph<TNode, TEdge>::predecessorEdges(const TNode &n) const {
+  return successorEdges(n);
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+std::string UndirectedGraph<TNode, TEdge>::name() const {
+  return "UndirectedGraph";
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+GraphBase *UndirectedGraph<TNode, TEdge>::clone() const {
+  return new UndirectedGraph(*this);
+}
+
+template<typename TNode, typename TEdge>
+GraphBase *UndirectedGraph<TNode, TEdge>::plunder() &&{
+  return new UndirectedGraph(std::move(*this));
+}
+
+template<typename TNode, typename TEdge>
+int UndirectedGraph<TNode, TEdge>::compare(const object::ObjectBase &other) const {
+  if (ext::type_index(typeid(*this)) == ext::type_index(typeid(other))) return this->compare((decltype(*this)) other);
+  return ext::type_index(typeid(*this)) - ext::type_index(typeid(other));
+}
+
+template<typename TNode, typename TEdge>
+int UndirectedGraph<TNode, TEdge>::compare(const UndirectedGraph &other) const {
+  auto first = ext::tie(m_adjacency_list);
+  auto second = ext::tie(other.getAdjacencyList());
+
+  static ext::compare<decltype(first)> comp;
+
+  return comp(first, second);
+}
+
+template<typename TNode, typename TEdge>
+object::ObjectBase *UndirectedGraph<TNode, TEdge>::inc() &&{
+  return new object::UniqueObject(object::Object(std::move(*this)), primitive::Integer(0));
+}
+
+template<typename TNode, typename TEdge>
+void UndirectedGraph<TNode, TEdge>::operator>>(std::ostream &ostream) const {
+  ostream << "(" << name() << " ";
+  for (const auto &i : m_adjacency_list) {
+    ostream << i.first << " <-->" << std::endl;
+
+    for (const auto &j : i.second) {
+      ostream << "\t\t" << j.first << " " << j.second << std::endl;
+    }
+  }
+  ostream << ")";
+}
+
+template<typename TNode, typename TEdge>
+UndirectedGraph<TNode, TEdge>::operator std::string() const {
+  std::stringstream ss;
+  ss << "<";
+  for (const auto &i : m_adjacency_list) {
+    ss << i.first << " <-->" << std::endl;
+
+    for (const auto &j : i.second) {
+      ss << "\t\t" << j.first << " " << j.second << std::endl;
+    }
+  }
+  ss << ">";
+  return std::move(ss).str();
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+} // namespace graph
+
+// =====================================================================================================================
+
+namespace core {
+
+/**
+ * Helper for normalisation of types specified by templates used as internal datatypes of symbols and states.
+ *
+ * \returns new instance of the graph with default template parameters or unmodified instance if the template parameters were already the default ones
+ */
+
+template<typename TNode, typename TEdge>
+struct normalize<graph::UndirectedGraph<TNode, TEdge>, typename std::enable_if<!std::is_same<
+    graph::UndirectedGraph<TNode, TEdge>, graph::UndirectedGraph<> >::value>::type> {
+  static graph::UndirectedGraph<> eval(graph::UndirectedGraph<TNode, TEdge> &&value) {
+    graph::UndirectedGraph<> graph;
+
+    for (auto &&i: ext::make_moveable_map(std::move(value).getAdjacencyList())) {
+      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
+      for (auto &&j: i.second) {
+        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
+        DefaultEdgeType edge = common::Normalize::normalizeEdge(std::move(j.second));
+
+        graph.addNode(first);
+        graph.addNode(second);
+        graph.addEdge(edge);
+      }
+    }
+
+    return graph;
+  }
+};
+
+}
+
+// =====================================================================================================================
+
+#endif //ALIB2_UNDIRECTEDGRAPH_HPP
diff --git a/alib2graph_data/src/graph/undirected/UndirectedMultiGraph.cpp b/alib2graph_data/src/graph/undirected/UndirectedMultiGraph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..07460c1f170d07a0f27fb68c01e2c0b8d28fe3ac
--- /dev/null
+++ b/alib2graph_data/src/graph/undirected/UndirectedMultiGraph.cpp
@@ -0,0 +1,17 @@
+// UndirectedMultiGraph.cpp
+//
+//     Created on: 04. 03. 2018
+//         Author: Jan Uhlik
+//    Modified by:
+//
+// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
+// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library
+
+#include "UndirectedMultiGraph.hpp"
+#include <registration/ValuePrinterRegistration.hpp>
+
+namespace {
+
+static auto valuePrinter = registration::ValuePrinterRegister<graph::UndirectedMultiGraph<> >();
+
+}
diff --git a/alib2graph_data/src/graph/undirected/UndirectedMultiGraph.hpp b/alib2graph_data/src/graph/undirected/UndirectedMultiGraph.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cb87d22b5e8f9b6bfbf48aa163e7dcfd1343683a
--- /dev/null
+++ b/alib2graph_data/src/graph/undirected/UndirectedMultiGraph.hpp
@@ -0,0 +1,362 @@
+// UndirectedMultiGraph.hpp
+//
+//     Created on: 05. 02. 2018
+//         Author: Jan Uhlik
+//    Modified by:
+//
+// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
+// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library
+
+#ifndef ALIB2_UNDIRECTEDMULTIGRAPH_HPP
+#define ALIB2_UNDIRECTEDMULTIGRAPH_HPP
+
+#include <alib/map>
+#include <alib/pair>
+#include <alib/set>
+#include <alib/vector>
+#include <alib/tuple>
+#include <functional>
+#include <object/Object.h>
+#include <object/UniqueObject.h>
+#include <core/normalize.hpp>
+
+#include <common/Normalize.hpp>
+#include <graph/GraphInterface.hpp>
+#include <graph/GraphFeatures.hpp>
+
+namespace graph {
+
+template<typename TNode, typename TEdge>
+class UndirectedMultiGraph : public GraphInterface<TNode, TEdge> {
+// ---------------------------------------------------------------------------------------------------------------------
+
+ public:
+  using node_type = TNode;
+  using edge_type = TEdge;
+  using normalized_type = UndirectedMultiGraph<>;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+ protected:
+  ext::map<TNode, std::multimap<TNode, TEdge>> m_adjacency_list;
+
+// =====================================================================================================================
+// Constructor, Destructor, Operators
+ public:
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  const ext::map<TNode, std::multimap<TNode, TEdge>> &getAdjacencyList() const &;
+
+  ext::map<TNode, std::multimap<TNode, TEdge>> &&getAdjacencyList() &&;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+// =====================================================================================================================
+// ObjectBase interface
+ public:
+  GraphBase *clone() const override;
+
+  GraphBase *plunder() &&override;
+
+  int compare(const object::ObjectBase &other) const override;
+
+  virtual int compare(const UndirectedMultiGraph &other) const;
+
+  object::ObjectBase *inc() &&override;
+
+  void operator>>(std::ostream &ostream) const override;
+
+  explicit operator std::string() const override;
+
+// =====================================================================================================================
+// Graph interface
+// ---------------------------------------------------------------------------------------------------------------------
+
+ public:
+  void addNode(const TNode &n);
+  void addNode(TNode &&n);
+  template<typename ... Params>
+  void addNode(Params &&... params);
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  bool addEdge(const TEdge &e);
+  bool addEdge(TEdge &&e);
+  template<typename ... Params>
+  bool addEdge(Params &&... params);
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+// =====================================================================================================================
+// GraphInterface interface
+
+  size_t nodeCount() const override;
+  size_t edgeCount() const override;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  ext::set<TNode> getNodes() const override;
+  ext::vector<TEdge> getEdges() const override;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  ext::set<TNode> successors(const TNode &n) const override;
+  ext::vector<TEdge> successorEdges(const TNode &n) const override;
+  ext::set<TNode> predecessors(const TNode &n) const override;
+  ext::vector<TEdge> predecessorEdges(const TNode &n) const override;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+  std::string name() const override;
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+};
+
+// =====================================================================================================================
+
+template<typename TNode, typename TEdge>
+const ext::map<TNode, std::multimap<TNode, TEdge>> &UndirectedMultiGraph<TNode, TEdge>::getAdjacencyList() const &{
+  return m_adjacency_list;
+}
+
+template<typename TNode, typename TEdge>
+ext::map<TNode, std::multimap<TNode, TEdge>> &&UndirectedMultiGraph<TNode, TEdge>::getAdjacencyList() &&{
+  return std::move(m_adjacency_list);
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+void UndirectedMultiGraph<TNode, TEdge>::addNode(const TNode &n) {
+  m_adjacency_list[n];
+}
+
+template<typename TNode, typename TEdge>
+void UndirectedMultiGraph<TNode, TEdge>::addNode(TNode &&n) {
+  m_adjacency_list[std::forward<TNode>(n)];
+}
+
+template<typename TNode, typename TEdge>
+template<typename... Params>
+void UndirectedMultiGraph<TNode, TEdge>::addNode(Params &&... params) {
+  addNode(TNode(std::forward<Params>(params) ...));
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+bool UndirectedMultiGraph<TNode, TEdge>::addEdge(const TEdge &e) {
+  m_adjacency_list[e.first].insert(ext::make_pair(e.second, e));
+  m_adjacency_list[e.second].insert(ext::make_pair(e.first, e));
+  return true;
+}
+
+template<typename TNode, typename TEdge>
+bool UndirectedMultiGraph<TNode, TEdge>::addEdge(TEdge &&e) {
+  m_adjacency_list[e.first].insert(ext::make_pair(e.second, e));
+  m_adjacency_list[e.second].insert(ext::make_pair(e.first, std::forward<TEdge>(e)));
+  return true;
+}
+
+template<typename TNode, typename TEdge>
+template<typename... Params>
+bool UndirectedMultiGraph<TNode, TEdge>::addEdge(Params &&... params) {
+  return addEdge(TEdge(std::forward<Params>(params) ...));
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+size_t UndirectedMultiGraph<TNode, TEdge>::nodeCount() const {
+  return m_adjacency_list.size();
+}
+
+template<typename TNode, typename TEdge>
+size_t UndirectedMultiGraph<TNode, TEdge>::edgeCount() const {
+  size_t cnt = 0;
+
+  for (const auto &i: m_adjacency_list) {
+    cnt += i.second.size();
+  }
+
+  return cnt / 2;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+ext::set<TNode> UndirectedMultiGraph<TNode, TEdge>::getNodes() const {
+  ext::set<TNode> set;
+
+  for (const auto &i: m_adjacency_list) {
+    set.insert(i.first);
+  }
+
+  return set;
+}
+
+template<typename TNode, typename TEdge>
+ext::vector<TEdge> UndirectedMultiGraph<TNode, TEdge>::getEdges() const {
+  ext::vector<TEdge> vec;
+
+  for (const auto &i:m_adjacency_list) {
+    for (const auto &j : i.second) {
+      vec.push_back(j.second);
+    }
+  }
+
+  return vec;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+ext::set<TNode> UndirectedMultiGraph<TNode, TEdge>::successors(const TNode &n) const {
+  ext::set<TNode> set;
+
+  if (m_adjacency_list.count(n) == 0) {
+    return set;
+  }
+
+  for (const auto &i: m_adjacency_list.at(n)) {
+    set.insert(i.first);
+  }
+
+  return set;
+}
+
+template<typename TNode, typename TEdge>
+ext::vector<TEdge> UndirectedMultiGraph<TNode, TEdge>::successorEdges(const TNode &n) const {
+  ext::vector<TEdge> vec;
+
+  if (m_adjacency_list.count(n) == 0) {
+    return vec;
+  }
+
+  for (const auto &i: m_adjacency_list.at(n)) {
+    vec.push_back(i.second);
+  }
+
+  return vec;
+}
+
+template<typename TNode, typename TEdge>
+ext::set<TNode> UndirectedMultiGraph<TNode, TEdge>::predecessors(const TNode &n) const {
+  return successors(n);
+}
+
+template<typename TNode, typename TEdge>
+ext::vector<TEdge> UndirectedMultiGraph<TNode, TEdge>::predecessorEdges(const TNode &n) const {
+  return successorEdges(n);
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+std::string UndirectedMultiGraph<TNode, TEdge>::name() const {
+  return "UndirectedMultiGraph";
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+template<typename TNode, typename TEdge>
+GraphBase *UndirectedMultiGraph<TNode, TEdge>::clone() const {
+  return new UndirectedMultiGraph(*this);
+}
+
+template<typename TNode, typename TEdge>
+GraphBase *UndirectedMultiGraph<TNode, TEdge>::plunder() &&{
+  return new UndirectedMultiGraph(std::move(*this));
+}
+
+template<typename TNode, typename TEdge>
+int UndirectedMultiGraph<TNode, TEdge>::compare(const object::ObjectBase &other) const {
+  if (ext::type_index(typeid(*this)) == ext::type_index(typeid(other))) return this->compare((decltype(*this)) other);
+  return ext::type_index(typeid(*this)) - ext::type_index(typeid(other));
+}
+
+template<typename TNode, typename TEdge>
+int UndirectedMultiGraph<TNode, TEdge>::compare(const UndirectedMultiGraph &other) const {
+  auto first = ext::tie(m_adjacency_list);
+  auto second = ext::tie(other.getAdjacencyList());
+
+  static ext::compare<decltype(first)> comp;
+
+  return comp(first, second);
+}
+
+template<typename TNode, typename TEdge>
+object::ObjectBase *UndirectedMultiGraph<TNode, TEdge>::inc() &&{
+  return new object::UniqueObject(object::Object(std::move(*this)), primitive::Integer(0));
+}
+
+template<typename TNode, typename TEdge>
+void UndirectedMultiGraph<TNode, TEdge>::operator>>(std::ostream &ostream) const {
+  ostream << "(" << name() << " ";
+  for (const auto &i : m_adjacency_list) {
+    ostream << i.first << " <-->" << std::endl;
+
+    for (const auto &j : i.second) {
+      ostream << "\t\t" << j.first << " " << j.second << std::endl;
+    }
+  }
+  ostream << ")";
+}
+
+template<typename TNode, typename TEdge>
+UndirectedMultiGraph<TNode, TEdge>::operator std::string() const {
+  std::stringstream ss;
+  ss << "<";
+  for (const auto &i : m_adjacency_list) {
+    ss << i.first << " <-->" << std::endl;
+
+    for (const auto &j : i.second) {
+      ss << "\t\t" << j.first << " " << j.second << std::endl;
+    }
+  }
+  ss << ">";
+  return std::move(ss).str();
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+
+} // namespace graph
+
+// =====================================================================================================================
+
+namespace core {
+
+/**
+ * Helper for normalisation of types specified by templates used as internal datatypes of symbols and states.
+ *
+ * \returns new instance of the graph with default template parameters or unmodified instance if the template parameters were already the default ones
+ */
+
+template<typename TNode, typename TEdge>
+struct normalize<graph::UndirectedMultiGraph<TNode, TEdge>, typename std::enable_if<!std::is_same<
+    graph::UndirectedMultiGraph<TNode, TEdge>, graph::UndirectedMultiGraph<> >::value>::type> {
+  static graph::UndirectedMultiGraph<> eval(graph::UndirectedMultiGraph<TNode, TEdge> &&value) {
+    graph::UndirectedMultiGraph<> graph;
+
+    for (auto &&i: ext::make_moveable_map(std::move(value).getAdjacencyList())) {
+      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
+      for (auto &&j: i.second) {
+        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
+        DefaultEdgeType edge = common::Normalize::normalizeEdge(std::move(j.second));
+
+        graph.addNode(first);
+        graph.addNode(second);
+        graph.addEdge(edge);
+      }
+    }
+
+    return graph;
+  }
+};
+
+}
+
+// =====================================================================================================================
+#endif //ALIB2_UNDIRECTEDMULTIGRAPH_HPP