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_