diff --git a/alib2algo/src/graph/isomorphism/Isomorphism.cpp b/alib2algo/src/graph/isomorphism/Isomorphism.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5009e3f1d5798e3035760a93da1617b62d9c8535
--- /dev/null
+++ b/alib2algo/src/graph/isomorphism/Isomorphism.cpp
@@ -0,0 +1,124 @@
+#include "Isomorphism.h"
+
+#include <exception/AlibException.h>
+
+namespace graph
+{
+
+namespace isomorphism
+{
+
+static inline std::set<UndirectedEdge> stripNames(const std::set<UndirectedEdge> &s)
+{
+	std::set<UndirectedEdge> out;
+	for (const UndirectedEdge &v : s)
+		out.insert(UndirectedEdge(v.getFirstNode(), v.getSecondNode()));
+	return out;
+}
+
+static inline std::set<DirectedEdge> stripNames(const std::set<DirectedEdge> &s)
+{
+	std::set<DirectedEdge> out;
+	for (const DirectedEdge &v : s)
+		out.insert(DirectedEdge(v.getFromNode(), v.getToNode()));
+	return out;
+}
+
+static bool is_equal(const std::set<UndirectedEdge> &first, const std::set<UndirectedEdge> &second, const std::unordered_map<Node, Node> &mapping)
+{
+	if (first.size() != second.size())
+		return false;
+
+	std::set<UndirectedEdge> frst = stripNames(first);
+	std::set<UndirectedEdge> scnd = stripNames(second);
+
+	for (const UndirectedEdge &e : frst) {
+		UndirectedEdge other1(mapping.at(e.getFirstNode()), mapping.at(e.getSecondNode()), e.getName());
+		UndirectedEdge other2(mapping.at(e.getSecondNode()), mapping.at(e.getFirstNode()), e.getName());
+		if (scnd.find(other1) == scnd.end() && scnd.find(other2) == scnd.end())
+			return false;
+	}
+
+	return true;
+}
+
+static bool is_equal(const std::set<DirectedEdge> &first, const std::set<DirectedEdge> &second, const std::unordered_map<Node, Node> &mapping)
+{
+	if (first.size() != second.size())
+		return false;
+
+	std::set<DirectedEdge> frst = stripNames(first);
+	std::set<DirectedEdge> scnd = stripNames(second);
+
+	for (const DirectedEdge &e : frst) {
+		DirectedEdge other(mapping.at(e.getFromNode()), mapping.at(e.getToNode()), e.getName());
+		if (scnd.find(other) == scnd.end())
+			return false;
+	}
+
+	return true;
+}
+
+template<typename T>
+static bool impl_r(const T &first, const T &second, const std::vector<Node> &nodes1, const std::vector<Node> &nodes2, std::unordered_map<Node, Node> &mapping)
+{
+	if (nodes1.empty()) {
+		for (const Node &n : first.getNodes()) {
+			if (!is_equal(first.neighborEdges(n), second.neighborEdges(mapping.at(n)), mapping)) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	std::vector<Node> nds1 = nodes1;
+	Node node = nodes1.back();
+	nds1.pop_back();
+
+	for (size_t i = 0; i < nodes2.size(); ++i) {
+		std::vector<Node> nds2 = nodes2;
+		mapping.erase(node);
+		mapping.insert({node, nodes2[i]});
+		nds2.erase(nds2.begin() + i);
+		if (impl_r(first, second, nds1, nds2, mapping))
+			return true;
+	}
+
+	return false;
+}
+
+template<typename T>
+static bool impl(const T &first, const T &second)
+{
+	const std::set<Node> &nodes1 = first.getNodes();
+	const std::set<Node> &nodes2 = second.getNodes();
+
+	if (nodes1.size() != nodes2.size())
+		return false;
+
+	std::vector<Node> n1;
+	std::vector<Node> n2;
+	std::unordered_map<Node, Node> mapping;
+	std::copy(nodes1.begin(), nodes1.end(), std::back_inserter(n1));
+	std::copy(nodes2.begin(), nodes2.end(), std::back_inserter(n2));
+	return impl_r(first, second, n1, n2, mapping);
+}
+
+bool Isomorphism::isomorphism(const Graph &first, const Graph &second)
+{
+	return getInstance().dispatch(first.getData(), second.getData());
+}
+
+bool Isomorphism::isomorphism(const DirectedGraph &first, const DirectedGraph &second)
+{
+	return impl(first, second);
+}
+
+bool Isomorphism::isomorphism(const UndirectedGraph &first, const UndirectedGraph &second)
+{
+	return impl(first, second);
+}
+
+} // namespace isomorphism
+
+} // namespace graph
diff --git a/alib2algo/src/graph/isomorphism/Isomorphism.h b/alib2algo/src/graph/isomorphism/Isomorphism.h
new file mode 100644
index 0000000000000000000000000000000000000000..c99499d4c53f3ee0e85426ed38186478923eee50
--- /dev/null
+++ b/alib2algo/src/graph/isomorphism/Isomorphism.h
@@ -0,0 +1,34 @@
+#ifndef GRAPH_ISOMORPHISM_H_
+#define GRAPH_ISOMORPHISM_H_
+
+#include <common/multipleDispatch.hpp>
+
+#include <graph/Graph.h>
+#include <graph/directed/DirectedGraph.h>
+#include <graph/undirected/UndirectedGraph.h>
+
+namespace graph
+{
+
+namespace isomorphism
+{
+
+class Isomorphism : public std::DoubleDispatch<bool, graph::GraphBase, graph::GraphBase>
+{
+public:
+	static bool isomorphism(const Graph &first, const Graph &second);
+
+	static bool isomorphism(const DirectedGraph &first, const DirectedGraph &second);
+	static bool isomorphism(const UndirectedGraph &first, const UndirectedGraph &second);
+
+	static Isomorphism &getInstance() {
+		static Isomorphism res;
+		return res;
+	}
+};
+
+} // namespace isomorphism
+
+} // namespace graph
+
+#endif // GRAPH_ISOMORPHISM_H_
diff --git a/alib2algo/test-src/graph/isomorphism/IsomorphismTest.cpp b/alib2algo/test-src/graph/isomorphism/IsomorphismTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d5793c2835a69b7b1e7c77980c78d7b0744e8fd
--- /dev/null
+++ b/alib2algo/test-src/graph/isomorphism/IsomorphismTest.cpp
@@ -0,0 +1,239 @@
+#include "IsomorphismTest.h"
+
+#include "graph/isomorphism/Isomorphism.h"
+
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(GraphIsomorphismTest, "graph");
+CPPUNIT_TEST_SUITE_REGISTRATION(GraphIsomorphismTest);
+
+void GraphIsomorphismTest::testSameGraphs()
+{
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+
+	// Undirected
+	graph::UndirectedGraph ug1;
+	ug1.addEdge(graph::UndirectedEdge(n1, n2));
+	ug1.addEdge(graph::UndirectedEdge(n1, n3));
+	ug1.addEdge(graph::UndirectedEdge(n1, n4));
+	ug1.addEdge(graph::UndirectedEdge(n2, n3));
+	ug1.addEdge(graph::UndirectedEdge(n3, n4));
+	ug1.addEdge(graph::UndirectedEdge(n5, n4));
+	ug1.addEdge(graph::UndirectedEdge(n5, n3));
+	ug1.addEdge(graph::UndirectedEdge(n5, n2));
+
+	graph::UndirectedGraph ug2;
+	ug2.addEdge(graph::UndirectedEdge(n1, n2));
+	ug2.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge1"));
+	ug2.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge2"));
+	ug2.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge3"));
+	ug2.addEdge(graph::UndirectedEdge(n1, n3));
+	ug2.addEdge(graph::UndirectedEdge(n1, n4));
+	ug2.addEdge(graph::UndirectedEdge(n1, n4, "multi-edge4"));
+	ug2.addEdge(graph::UndirectedEdge(n2, n3));
+	ug2.addEdge(graph::UndirectedEdge(n2, n3, "multi-edge5"));
+	ug2.addEdge(graph::UndirectedEdge(n3, n4));
+	ug2.addEdge(graph::UndirectedEdge(n5, n4));
+	ug2.addEdge(graph::UndirectedEdge(n5, n4, "multi-edge6"));
+	ug2.addEdge(graph::UndirectedEdge(n5, n3));
+	ug2.addEdge(graph::UndirectedEdge(n5, n2));
+	ug2.addEdge(graph::UndirectedEdge(n5, n5, "loop1"));
+	ug2.addEdge(graph::UndirectedEdge(n5, n5, "loop2"));
+
+	// Same simple graphs
+	CPPUNIT_ASSERT_EQUAL(true, graph::isomorphism::Isomorphism::isomorphism(ug1, ug1));
+
+	// Same graphs with multi-edges and loops
+	CPPUNIT_ASSERT_EQUAL(true, graph::isomorphism::Isomorphism::isomorphism(ug2, ug2));
+
+	CPPUNIT_ASSERT_EQUAL(false, graph::isomorphism::Isomorphism::isomorphism(ug1, ug2));
+	CPPUNIT_ASSERT_EQUAL(false, graph::isomorphism::Isomorphism::isomorphism(ug2, ug1));
+
+	// Directed
+	graph::DirectedGraph dg1;
+	dg1.addEdge(graph::DirectedEdge(n1, n2));
+	dg1.addEdge(graph::DirectedEdge(n1, n3));
+	dg1.addEdge(graph::DirectedEdge(n1, n4));
+	dg1.addEdge(graph::DirectedEdge(n2, n3));
+	dg1.addEdge(graph::DirectedEdge(n3, n4));
+	dg1.addEdge(graph::DirectedEdge(n5, n4));
+	dg1.addEdge(graph::DirectedEdge(n5, n3));
+	dg1.addEdge(graph::DirectedEdge(n5, n2));
+
+	graph::DirectedGraph dg2;
+	dg2.addEdge(graph::DirectedEdge(n1, n2));
+	dg2.addEdge(graph::DirectedEdge(n1, n2, "multi-edge1"));
+	dg2.addEdge(graph::DirectedEdge(n1, n2, "multi-edge2"));
+	dg2.addEdge(graph::DirectedEdge(n1, n2, "multi-edge3"));
+	dg2.addEdge(graph::DirectedEdge(n1, n3));
+	dg2.addEdge(graph::DirectedEdge(n1, n4));
+	dg2.addEdge(graph::DirectedEdge(n1, n4, "multi-edge4"));
+	dg2.addEdge(graph::DirectedEdge(n2, n3));
+	dg2.addEdge(graph::DirectedEdge(n2, n3, "multi-edge5"));
+	dg2.addEdge(graph::DirectedEdge(n3, n4));
+	dg2.addEdge(graph::DirectedEdge(n5, n4));
+	dg2.addEdge(graph::DirectedEdge(n5, n4, "multi-edge6"));
+	dg2.addEdge(graph::DirectedEdge(n5, n3));
+	dg2.addEdge(graph::DirectedEdge(n5, n2));
+	dg2.addEdge(graph::DirectedEdge(n5, n5, "loop1"));
+	dg2.addEdge(graph::DirectedEdge(n5, n5, "loop2"));
+
+	// Same simple graphs
+	CPPUNIT_ASSERT_EQUAL(true, graph::isomorphism::Isomorphism::isomorphism(dg1, dg1));
+
+	// Same graphs with multi-edges and loops
+	CPPUNIT_ASSERT_EQUAL(true, graph::isomorphism::Isomorphism::isomorphism(dg2, dg2));
+
+	CPPUNIT_ASSERT_EQUAL(false, graph::isomorphism::Isomorphism::isomorphism(dg1, dg2));
+	CPPUNIT_ASSERT_EQUAL(false, graph::isomorphism::Isomorphism::isomorphism(dg2, dg1));
+}
+
+void GraphIsomorphismTest::testIsomorphicGraphs()
+{
+	graph::Node n1("n1");
+	graph::Node n2("n2");
+	graph::Node n3("n3");
+	graph::Node n4("n4");
+	graph::Node n5("n5");
+
+	graph::Node n1_("n1_");
+	graph::Node n2_("n2_");
+	graph::Node n3_("n3_");
+	graph::Node n4_("n4_");
+	graph::Node n5_("n5_");
+
+	// Undirected
+	graph::UndirectedGraph ug1;
+	ug1.addEdge(graph::UndirectedEdge(n1, n2));
+	ug1.addEdge(graph::UndirectedEdge(n1, n3));
+	ug1.addEdge(graph::UndirectedEdge(n1, n4));
+	ug1.addEdge(graph::UndirectedEdge(n2, n3));
+	ug1.addEdge(graph::UndirectedEdge(n3, n4));
+	ug1.addEdge(graph::UndirectedEdge(n5, n4));
+	ug1.addEdge(graph::UndirectedEdge(n5, n3));
+	ug1.addEdge(graph::UndirectedEdge(n5, n2));
+
+	graph::UndirectedGraph ug1_;
+	ug1_.addEdge(graph::UndirectedEdge(n2_, n1_));
+	ug1_.addEdge(graph::UndirectedEdge(n3_, n2_));
+	ug1_.addEdge(graph::UndirectedEdge(n4_, n1_));
+	ug1_.addEdge(graph::UndirectedEdge(n5_, n4_));
+	ug1_.addEdge(graph::UndirectedEdge(n3_, n5_));
+	ug1_.addEdge(graph::UndirectedEdge(n3_, n1_));
+	ug1_.addEdge(graph::UndirectedEdge(n5_, n2_));
+	ug1_.addEdge(graph::UndirectedEdge(n4_, n3_));
+
+	graph::UndirectedGraph ug2;
+	ug2.addEdge(graph::UndirectedEdge(n1, n2));
+	ug2.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge1"));
+	ug2.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge2"));
+	ug2.addEdge(graph::UndirectedEdge(n1, n2, "multi-edge3"));
+	ug2.addEdge(graph::UndirectedEdge(n1, n3));
+	ug2.addEdge(graph::UndirectedEdge(n1, n4));
+	ug2.addEdge(graph::UndirectedEdge(n1, n4, "multi-edge4"));
+	ug2.addEdge(graph::UndirectedEdge(n2, n3));
+	ug2.addEdge(graph::UndirectedEdge(n2, n3, "multi-edge5"));
+	ug2.addEdge(graph::UndirectedEdge(n3, n4));
+	ug2.addEdge(graph::UndirectedEdge(n5, n4));
+	ug2.addEdge(graph::UndirectedEdge(n5, n4, "multi-edge6"));
+	ug2.addEdge(graph::UndirectedEdge(n5, n3));
+	ug2.addEdge(graph::UndirectedEdge(n5, n2));
+	ug2.addEdge(graph::UndirectedEdge(n5, n5, "loop1"));
+	ug2.addEdge(graph::UndirectedEdge(n5, n5, "loop2"));
+
+	graph::UndirectedGraph ug2_;
+	ug2_.addEdge(graph::UndirectedEdge(n1_, n2_));
+	ug2_.addEdge(graph::UndirectedEdge(n1_, n2_, "multi-edge1_"));
+	ug2_.addEdge(graph::UndirectedEdge(n1_, n2_, "multi-edge2_"));
+	ug2_.addEdge(graph::UndirectedEdge(n1_, n2_, "multi-edge3_"));
+	ug2_.addEdge(graph::UndirectedEdge(n1_, n3_));
+	ug2_.addEdge(graph::UndirectedEdge(n1_, n4_));
+	ug2_.addEdge(graph::UndirectedEdge(n1_, n4_, "multi-edge4_"));
+	ug2_.addEdge(graph::UndirectedEdge(n2_, n3_));
+	ug2_.addEdge(graph::UndirectedEdge(n2_, n3_, "multi-edge5_"));
+	ug2_.addEdge(graph::UndirectedEdge(n3_, n4_));
+	ug2_.addEdge(graph::UndirectedEdge(n5_, n4_));
+	ug2_.addEdge(graph::UndirectedEdge(n5_, n4_, "multi-edge6_"));
+	ug2_.addEdge(graph::UndirectedEdge(n5_, n3_));
+	ug2_.addEdge(graph::UndirectedEdge(n5_, n2_));
+	ug2_.addEdge(graph::UndirectedEdge(n5_, n5_, "loop1_"));
+	ug2_.addEdge(graph::UndirectedEdge(n5_, n5_, "loop2_"));
+
+	// Isomorphic simple graphs
+	CPPUNIT_ASSERT_EQUAL(true, graph::isomorphism::Isomorphism::isomorphism(ug1, ug1_));
+
+	// Isomorphic graphs with multi-edges and loops
+	CPPUNIT_ASSERT_EQUAL(true, graph::isomorphism::Isomorphism::isomorphism(ug2, ug2_));
+
+	CPPUNIT_ASSERT_EQUAL(false, graph::isomorphism::Isomorphism::isomorphism(ug1, ug2));
+	CPPUNIT_ASSERT_EQUAL(false, graph::isomorphism::Isomorphism::isomorphism(ug1, ug2_));
+
+	// Directed
+	graph::DirectedGraph dg1;
+	dg1.addEdge(graph::DirectedEdge(n1, n2));
+	dg1.addEdge(graph::DirectedEdge(n1, n3));
+	dg1.addEdge(graph::DirectedEdge(n1, n4));
+	dg1.addEdge(graph::DirectedEdge(n2, n3));
+	dg1.addEdge(graph::DirectedEdge(n3, n4));
+	dg1.addEdge(graph::DirectedEdge(n5, n4));
+	dg1.addEdge(graph::DirectedEdge(n5, n3));
+	dg1.addEdge(graph::DirectedEdge(n5, n2));
+
+	graph::DirectedGraph dg1_;
+	dg1_.addEdge(graph::DirectedEdge(n5_, n4_));
+	dg1_.addEdge(graph::DirectedEdge(n1_, n4_));
+	dg1_.addEdge(graph::DirectedEdge(n1_, n3_));
+	dg1_.addEdge(graph::DirectedEdge(n5_, n2_));
+	dg1_.addEdge(graph::DirectedEdge(n1_, n2_));
+	dg1_.addEdge(graph::DirectedEdge(n3_, n4_));
+	dg1_.addEdge(graph::DirectedEdge(n2_, n3_));
+	dg1_.addEdge(graph::DirectedEdge(n5_, n3_));
+
+	graph::DirectedGraph dg2;
+	dg2.addEdge(graph::DirectedEdge(n1, n2));
+	dg2.addEdge(graph::DirectedEdge(n1, n2, "multi-edge1"));
+	dg2.addEdge(graph::DirectedEdge(n1, n2, "multi-edge2"));
+	dg2.addEdge(graph::DirectedEdge(n1, n2, "multi-edge3"));
+	dg2.addEdge(graph::DirectedEdge(n1, n3));
+	dg2.addEdge(graph::DirectedEdge(n1, n4));
+	dg2.addEdge(graph::DirectedEdge(n1, n4, "multi-edge4"));
+	dg2.addEdge(graph::DirectedEdge(n2, n3));
+	dg2.addEdge(graph::DirectedEdge(n2, n3, "multi-edge5"));
+	dg2.addEdge(graph::DirectedEdge(n3, n4));
+	dg2.addEdge(graph::DirectedEdge(n5, n4));
+	dg2.addEdge(graph::DirectedEdge(n5, n4, "multi-edge6"));
+	dg2.addEdge(graph::DirectedEdge(n5, n3));
+	dg2.addEdge(graph::DirectedEdge(n5, n2));
+	dg2.addEdge(graph::DirectedEdge(n5, n5, "loop1"));
+	dg2.addEdge(graph::DirectedEdge(n5, n5, "loop2"));
+
+	graph::DirectedGraph dg2_;
+	dg2_.addEdge(graph::DirectedEdge(n1_, n2_));
+	dg2_.addEdge(graph::DirectedEdge(n1_, n2_, "multi-edge3_"));
+	dg2_.addEdge(graph::DirectedEdge(n1_, n2_, "multi-edge1_"));
+	dg2_.addEdge(graph::DirectedEdge(n1_, n3_));
+	dg2_.addEdge(graph::DirectedEdge(n1_, n4_, "multi-edge4_"));
+	dg2_.addEdge(graph::DirectedEdge(n1_, n4_));
+	dg2_.addEdge(graph::DirectedEdge(n1_, n2_, "multi-edge2_"));
+	dg2_.addEdge(graph::DirectedEdge(n5_, n4_));
+	dg2_.addEdge(graph::DirectedEdge(n2_, n3_, "multi-edge5_"));
+	dg2_.addEdge(graph::DirectedEdge(n3_, n4_));
+	dg2_.addEdge(graph::DirectedEdge(n5_, n3_));
+	dg2_.addEdge(graph::DirectedEdge(n5_, n5_, "loop1_"));
+	dg2_.addEdge(graph::DirectedEdge(n2_, n3_));
+	dg2_.addEdge(graph::DirectedEdge(n5_, n2_));
+	dg2_.addEdge(graph::DirectedEdge(n5_, n4_, "multi-edge6_"));
+	dg2_.addEdge(graph::DirectedEdge(n5_, n5_, "loop2_"));
+
+	// Isomorphic simple graphs
+	CPPUNIT_ASSERT_EQUAL(true, graph::isomorphism::Isomorphism::isomorphism(dg1, dg1_));
+
+	// Isomorphic graphs with multi-edges and loops
+	CPPUNIT_ASSERT_EQUAL(true, graph::isomorphism::Isomorphism::isomorphism(dg2, dg2_));
+
+	CPPUNIT_ASSERT_EQUAL(false, graph::isomorphism::Isomorphism::isomorphism(dg1, dg2));
+	CPPUNIT_ASSERT_EQUAL(false, graph::isomorphism::Isomorphism::isomorphism(dg1, dg2_));
+}
+
diff --git a/alib2algo/test-src/graph/isomorphism/IsomorphismTest.h b/alib2algo/test-src/graph/isomorphism/IsomorphismTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..ff967f2ad5572951765222b72a6f97ff8a07b1a7
--- /dev/null
+++ b/alib2algo/test-src/graph/isomorphism/IsomorphismTest.h
@@ -0,0 +1,18 @@
+#ifndef ISOMORPHISM_TEST_H_
+#define ISOMORPHISM_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class GraphIsomorphismTest : public CppUnit::TestFixture
+{
+	CPPUNIT_TEST_SUITE(GraphIsomorphismTest);
+	CPPUNIT_TEST(testSameGraphs);
+	CPPUNIT_TEST(testIsomorphicGraphs);
+	CPPUNIT_TEST_SUITE_END();
+
+public:
+	void testSameGraphs();
+	void testIsomorphicGraphs();
+};
+
+#endif // HOPCROFT_TEST_H_