From 1e86ec5b1ef8e5672374946427ab7baa8dc8f40d Mon Sep 17 00:00:00 2001 From: Richard Wong Date: Sat, 9 Sep 2023 21:29:57 +0900 Subject: [PATCH] Feat: accept gml as input file Chore: included make_graphs and make_planar --- .README | 3 + .gitignore | 4 + deferred_planarity_test/main.cpp | 55 +---- deferred_planarity_test/mps.h | 7 +- deferred_planarity_test/mps_test.cpp | 73 +++--- make_graphs/make_graphs.cpp | 32 +++ make_planar/make_planar.cpp | 328 +++++++++++++++++++++++++++ 7 files changed, 411 insertions(+), 91 deletions(-) create mode 100644 .README create mode 100644 make_graphs/make_graphs.cpp create mode 100644 make_planar/make_planar.cpp diff --git a/.README b/.README new file mode 100644 index 0000000..2f4b4c5 --- /dev/null +++ b/.README @@ -0,0 +1,3 @@ +Deferred Planarity Test implementation is from +https://code.google.com/archive/p/planarity-algorithms/. The original code is +licensed under the "New BSD License". diff --git a/.gitignore b/.gitignore index aedf8ed..9d73845 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ .vscode deferred_planarity_test/bin +make_graphs/bin +make_graphs/test_graphs +make_planar/bin +test_area diff --git a/deferred_planarity_test/main.cpp b/deferred_planarity_test/main.cpp index f656e39..9ff7ed2 100644 --- a/deferred_planarity_test/main.cpp +++ b/deferred_planarity_test/main.cpp @@ -3,33 +3,14 @@ //----------------------------------------------------------------------------------- #include -#include #include #include +#include #include "mps.h" using namespace std; -void find_mps(ifstream*, ofstream*); +int find_mps(string input_file); -//n nodes, with the probability of existence of each edge being p. -void random_graph_generator(int n, double p, ofstream* out) { - (*out) << n << endl; - for (int i = 0; i < n; ++i) { - for (int j = i+1; j < n; ++j) { - if ((double)rand()/RAND_MAX <= p) (*out) << i << " " << j << endl; - } - } -} - -//Complete graph K_n. -void complete_graph_generator(int n, ofstream* out) { - (*out) << n << endl; - for (int i = 0; i < n; ++i) { - for (int j = i+1; j < n; ++j) { - (*out) << i << " " << j << endl; - } - } -} //----------------------------------------------------------------------------------- // Main function. @@ -37,34 +18,8 @@ void complete_graph_generator(int n, ofstream* out) { int main(int argc, char* argv[]) { - if (argc == 1) { - cout << "Usages:" << endl; - cout << "======================" << endl; - cout << "mps_testing -mps " << endl; - cout << " Process the infile, and output the resulting maximal" << endl - << " planar subgraph in the outfile." << endl; - cout << "mps_testing -gen " << endl; - cout << " Generate a random graph as the spec given in the infile." << endl; - while (true) { - int x = 0; - cin >> x; - } - } - ifstream in; - in.open(argv[2]); - ofstream out; - out.open(argv[3]); - if (!in.is_open() || !out.is_open()) { - cout << "An error occurs when opening file." << endl; - return 0; - } - if (argv[1][1] == 'm') find_mps(&in, &out); - else if (argv[1][1] == 'g') { - int n; - double p; - in >> n; - in >> p; - random_graph_generator(n, p, &out); - } + string input_file = argv[1]; + std::cout << "Number of removed edges: " << find_mps(input_file) << std::endl; + return 0; } diff --git a/deferred_planarity_test/mps.h b/deferred_planarity_test/mps.h index 1e9ac47..e43b745 100644 --- a/deferred_planarity_test/mps.h +++ b/deferred_planarity_test/mps.h @@ -141,11 +141,10 @@ class maximal_planar_subgraph_finder public: maximal_planar_subgraph_finder(); ~maximal_planar_subgraph_finder(); - void find_mps(ifstream* in, ofstream* out); + int find_mps(string input_file); node* get_new_node(node_type t); - void read_from_file(ifstream* in); - void output(ofstream* out); - void output_deleted_edges(ofstream* out); + void read_from_gml(string input_file); + int output_removed_edge_size(); void postOrderTraversal(); void sort_adj_list(); void determine_edges(); diff --git a/deferred_planarity_test/mps_test.cpp b/deferred_planarity_test/mps_test.cpp index 4b1a3f2..819954a 100644 --- a/deferred_planarity_test/mps_test.cpp +++ b/deferred_planarity_test/mps_test.cpp @@ -3,62 +3,61 @@ //----------------------------------------------------------------------------------- #include "mps.h" +#include //----------------------------------------------------------------------------------- // Finding MPS //----------------------------------------------------------------------------------- -void find_mps(ifstream* in, ofstream* out) { +int find_mps(string input_file) { maximal_planar_subgraph_finder m; - m.find_mps(in, out); + return m.find_mps(input_file); } -void maximal_planar_subgraph_finder::find_mps(ifstream* in, ofstream* out) { - read_from_file(in); +int maximal_planar_subgraph_finder::find_mps(string input_file) { + read_from_gml(input_file); postOrderTraversal(); sort_adj_list(); determine_edges(); back_edge_traversal(); - output(out); + return output_removed_edge_size(); } //----------------------------------------------------------------------------------- // Imput, output //----------------------------------------------------------------------------------- -//First line: a single integer indicates the number of nodes. -//The rest: a pair of integers (i, j) represents an edge (i, j) -//0 <= i, j < n-1, where n is number of nodes. -void maximal_planar_subgraph_finder::read_from_file(ifstream* in) { - int node_num, n1, n2; - //Number of nodes. - (*in) >> node_num; - //initialize all the nodes. - for (int i = 0; i < node_num; ++i) { + + +// read input file of gml format +void maximal_planar_subgraph_finder::read_from_gml(string input_file) { + ogdf::Graph G; + + // utilize OGDF readGML + if (!ogdf::GraphIO::read(G, input_file, ogdf::GraphIO::readGML)) { + std::cerr << "Could not read " << input_file << ".gml" << std::endl; + } + + // create nodes + for (int i = 0; i < G.numberOfNodes(); ++i) { _node_list.push_back(new node(P_NODE)); _node_list[i]->set_id(i); } - //Set the adj-list. - while ((*in) >> n1 >> n2) { - _node_list[n1]->add_adj(_node_list[n2]); - _node_list[n2]->add_adj(_node_list[n1]); - } + + // create edges + for (ogdf::edge e : G.edges) { + ogdf::node source = e->source(); + ogdf::node target = e->target(); + _node_list[source->index()]->add_adj(_node_list[target->index()]); + _node_list[target->index()]->add_adj(_node_list[source->index()]); + } } + + //Output a maximal planar subgraph in the same format as input. -void maximal_planar_subgraph_finder::output(ofstream* out) { - (*out) << _node_list.size() << endl; - for (int i = 0; i < _edge_list.size(); ++i) { - (*out) << _edge_list[i].first->node_id() << " " << _edge_list[i].second->node_id() << endl; - } - for (int i = 0; i < _back_edge_list.size(); ++i) { - if (_is_back_edge_eliminate[i]) continue; - (*out) << _back_edge_list[i].first->node_id() << " " << _back_edge_list[i].second->node_id() << endl; - } -} - -void maximal_planar_subgraph_finder::output_deleted_edges(ofstream* out) { - (*out) << _node_list.size() << endl; - for (int i = 0; i < _back_edge_list.size(); ++i) { - if (!_is_back_edge_eliminate[i]) continue; - (*out) << _back_edge_list[i].first->node_id() << " " << _back_edge_list[i].second->node_id() << endl; - } -} +int maximal_planar_subgraph_finder::output_removed_edge_size() { + int sum = 0; + for (int i = 0; i < _back_edge_list.size(); ++i) { + if (_is_back_edge_eliminate[i]) ++sum; + } + return sum; +} \ No newline at end of file diff --git a/make_graphs/make_graphs.cpp b/make_graphs/make_graphs.cpp new file mode 100644 index 0000000..1df1355 --- /dev/null +++ b/make_graphs/make_graphs.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include + +using namespace ogdf; + +int main(int argc, char **argv) { + int nodeCount = std::stoi(argv[1]); + int edgeCount = std::stoi(argv[2]); + int copies = std::stoi(argv[3]); + int seed = std::stoi(argv[4]); + bool enforcePlanar = std::stoi(argv[5]); + std::string outputFolder = argv[6]; + + setSeed(seed); + for (int i = 0; i < copies; i++) { + Graph G; + if (enforcePlanar) { + randomPlanarBiconnectedGraph(G, nodeCount, edgeCount, false); + } else { + randomBiconnectedGraph(G, nodeCount, edgeCount); + } + + std::stringstream ss; + ss << outputFolder << "/graphn" << nodeCount << "e" << edgeCount << "s" << seed << "i" << i + << (enforcePlanar ? "planar" : "") << ".gml"; + std::string s = ss.str(); + GraphIO::write(G, s, GraphIO::writeGML); + } + +} diff --git a/make_planar/make_planar.cpp b/make_planar/make_planar.cpp new file mode 100644 index 0000000..a78c93e --- /dev/null +++ b/make_planar/make_planar.cpp @@ -0,0 +1,328 @@ +#include + +// #include "hsuPC/include/PCTree.h" +// #include "hsuPC/include/PCNode.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace ogdf; + +using std::chrono::time_point; +using std::chrono::high_resolution_clock; +using std::chrono::duration_cast; +using std::chrono::nanoseconds; + + +void planarizePQ(const Graph &G, NodeArray &numbering, List &delEdges) { + using PlanarLeafKey = booth_lueker::PlanarLeafKey; + + NodeArray> inLeaves(G); + NodeArray> outLeaves(G); + Array table(G.numberOfNodes()+1); + + for(node v : G.nodes) { + for(adjEntry adj : v->adjEntries) { + edge e = adj->theEdge(); + if (numbering[e->opposite(v)] > numbering[v]) { // sideeffect: ignores selfloops + PlanarLeafKey* L = new PlanarLeafKey(e); + inLeaves[v].pushFront(L); + } + } + table[numbering[v]] = v; + } + + for(node v : G.nodes) { + for (PlanarLeafKey *L : inLeaves[v]) { + outLeaves[L->userStructKey()->opposite(v)].pushFront(L); + } + } + + SList*> totalEliminatedKeys; + + PlanarSubgraphPQTree T; + T.Initialize(inLeaves[table[1]]); + for (int i = 2; i < G.numberOfNodes(); i++) { + SList*> eliminatedKeys; // handle key elimination + T.Reduction(outLeaves[table[i]], eliminatedKeys); // handle key elimination + totalEliminatedKeys.conc(eliminatedKeys); // handle key elimination + T.ReplaceRoot(inLeaves[table[i]]); + T.emptyAllPertinentNodes(); + } + + // append eliminated edges to delEdges + for (PQLeafKey *key : totalEliminatedKeys) { + edge e = key->userStructKey(); + delEdges.pushBack(e); + } + + //cleanup + for(node v : G.nodes) { + while (!inLeaves[v].empty()) { + PlanarLeafKey *L = inLeaves[v].popFrontRet(); + delete L; + } + } + + T.Cleanup(); // Explicit call for destructor necessary. This allows to call virtual + // function CleanNode for freeing node information class. + } + + +template +void planarizePC(const Graph &G, const NodeArray &numbering, List &delEdges) { + // initialize PCTree instance + PCT T; + + // vector to store nodes of graph in order specified in numbering + std::vector order(G.numberOfNodes(), nullptr); + for (node n : G.nodes) { + order.at(numbering[n] - 1) = n; + } + + // store leaf representations of edges in PCTree + std::vector leafRepresentation(G.maxEdgeIndex() + 1, nullptr); + // vectors declared for later use + std::vector outEdges; + std::vector consecutiveLeaves; + + // iterates over nodes in the 'order' vector + for (node n : order) { + // quit condition: if current node is last node + if (n == order[G.numberOfNodes() - 1]) { + break; + } + + // clear both vectors in for each node in iteration + consecutiveLeaves.clear(); + outEdges.clear(); + + // leafToEdge + // create a map of PCN to edge + std::map leafToEdge; + + // iterate over adjacent entries of current node + for (adjEntry adj : n->adjEntries) + { + // check if opposite node has higher numbering (belong to later node) + // add to outEdges + if (numbering[adj->theEdge()->opposite(n)] > numbering[n]) { + outEdges.push_back(adj->theEdge()); + // add to consecutiveLeaves otherwise (declared earlier) + // leafRepresentation will be filled later on + } else { + consecutiveLeaves.push_back(leafRepresentation[adj->theEdge()->index()]); + // use leafToEdge to map leafRep object to edge object + // then store the edge in a collection + // <- insert code here -> + leafToEdge[leafRepresentation[adj->theEdge()->index()]] = adj->theEdge(); + + + } + } + + PCN *mergedLeaf; + // If first node, new P-node is created + if (n == order[0]) { + mergedLeaf = T.newNode(PCNT::PNode); + // consecutive leaves are merged + } else { + // T.makeConsecutive + // the function returns a boolean for success + // I think it checks if its possible to make consecutive + + // IMPORTANT HERE + // here we can identify edges that result in non-planarity + if (!T.makeConsecutive(consecutiveLeaves)){ + + // keep trying different combinations until is passes + std::vector goodLeaves; + for (PCN* leaf: consecutiveLeaves) { + goodLeaves.push_back(leaf); + if (T.makeConsecutive(goodLeaves)) { + continue; + } else { + goodLeaves.pop_back(); + delEdges.pushBack(leafToEdge[leaf]); + } + } + consecutiveLeaves.clear(); + consecutiveLeaves.insert(consecutiveLeaves.end(), + goodLeaves.begin(), + goodLeaves.end()); + + + // for rejected leaves, + // return false; + } + // remove all consecutive leaves except the first + // returns a single leaf + // second argument is "assumeConsecutive", which makeConsecutive checks + mergedLeaf = T.mergeLeaves(consecutiveLeaves, true); + } + + OGDF_ASSERT(!outEdges.empty()); + + // if there is more than 1 outEdge + if (outEdges.size() > 1) { + // clear and re-use consecutiveLeaves variable + consecutiveLeaves.clear(); + // make a reference variabled + std::vector &addedLeaves = consecutiveLeaves; + + // if mergedLeaf if a P-node + // PCTree_construction::insertLeaves + if (mergedLeaf->getNodeType() == PCNT::PNode) { + T.insertLeaves(outEdges.size(), mergedLeaf, &addedLeaves); + // otherwise mergedLeaf is a Leaf node + // PCTree_construction::replaceLeaf + } else { + OGDF_ASSERT(mergedLeaf->getNodeType() == PCNT::Leaf); + T.replaceLeaf(outEdges.size(), mergedLeaf, &addedLeaves); + } + + // leaf representations are updated accordingly + for (int i = 0; i < outEdges.size(); i++) { + leafRepresentation[outEdges[i]->index()] = addedLeaves[i]; + } + // if there is only 1 edge + // first outEdge is updated to mergedLeaf node + } else { + leafRepresentation[outEdges.front()->index()] = mergedLeaf; + } + } + +} + + + +int main(int argc, char* argv[]) +{ + + string inputFile = argv[1]; + string selection = argv[2]; + string planarity = argv[3]; + Graph G; + + if (!GraphIO::read(G, inputFile, GraphIO::readGML)) { + std::cerr << "Could not read input.gml" << std::endl; + return 1; + } + + int b; + std::istringstream(planarity) >> b; + + if (b) { + // create map from integer to node + // find edge and change its attribute + // implicit assumption that nodes retain order as given by input + std::unordered_map nodeMap; + // Add nodes to the graph and map them to integers + int index = 0; + for (ogdf::node v : G.nodes) { + nodeMap[index] = v; + ++index; + } + + // manually add our own random edges + int numEdgesToAdd = b; // Number of edges to add + int count = 0; + std::srand(150); // Seed the random number generator + int numNodes = G.numberOfNodes(); + + while (count < numEdgesToAdd) { + int sourceIndex = std::rand() % numNodes; // Random source node index + int targetIndex = std::rand() % numNodes; // Random target node index + + if (sourceIndex == targetIndex) continue; + ogdf::edge targetEdge = G.searchEdge(nodeMap[sourceIndex], nodeMap[targetIndex], false); + if (targetEdge == nullptr) { + G.newEdge(nodeMap[sourceIndex], nodeMap[targetIndex]); + std::cout << sourceIndex << ", " << targetIndex << std::endl; + ++count; + } + } + } + + NodeArray numbering(G); + // int num = computeSTNumbering(G, numbering, 0); + int num = computeSTNumbering(G, numbering, nullptr, nullptr, true); + OGDF_ASSERT(num == G.numberOfNodes()); + + // print after input + // graphPrinter(G); + std::cout << "G Planarity: " << ogdf::isPlanar(G) << std::endl; + std::cout << "Original number of nodes: " << G.numberOfNodes() << std::endl; + std::cout << "Original number of edges: " << G.numberOfEdges() << std::endl; + + // separator for planarization + // <--------------> + + // PQ implementation to make planar subgraph + std::cout << "start planarization" << std::endl; + List *delEdges = new List; // empty list + // changed to PC + if (selection == "pc") { + planarizePC(G, numbering, *delEdges); + } else if (selection == "pq") { + planarizePQ(G, numbering, *delEdges); + // } else if (selection == "bm") { + // SubgraphPlanarizer *crossMin = new SubgraphPlanarizer; + // crossMin->call(G) + } else if (selection == "fast") { + // PlanarSubgraphModule *ps = new PlanarSubgraphFast; + // ogdf::MaximalPlanarSubgraphSimple mps(*(new PlanarSubgraphBoyerMyrvold)); + ogdf::MaximalPlanarSubgraphSimple mps(*(new PlanarSubgraphFast)); + mps.call(G, *delEdges); + + } else { + std::cout << "running maximum" << std::endl; + ogdf::MaximumPlanarSubgraph mps; + mps.call(G, *delEdges); + } + + std::cout << "Edges removed:" << delEdges->size() << std::endl; + + for (edge e: *delEdges) { + // print removed edges + std::cout << e->adjSource() << std::endl; + G.delEdge(e); + } + + + GraphIO::write(G, "output.gml", GraphIO::writeGML); + + + // edgeListPrinter(*delEdges); + // std::cout << std::endl; + // std::cout << "subG planarity: " << ogdf::isPlanar(subgraph) << std::endl; + std::cout << "G planarity: " << ogdf::isPlanar(G) << std::endl; + std::cout << "Original number of nodes: " << G.numberOfNodes() << std::endl; + std::cout << "Subgraph number of edges: " << G.numberOfEdges() << std::endl; + // graphPrinter(G); + // std::cout << isPlanarPC(G, numbering) << std::endl; + // bool result = isPlanarPC(subG, numbering); + // std::cout << result << std::endl; + + + return 0; +}