Feat: accept gml as input file

Chore: included make_graphs and make_planar
This commit is contained in:
Richard Wong 2023-09-09 21:29:57 +09:00
parent 7f277b1768
commit 1e86ec5b1e
Signed by: richard
GPG Key ID: 5BD36BA2E9EE33D0
7 changed files with 411 additions and 91 deletions

3
.README Normal file
View File

@ -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".

4
.gitignore vendored
View File

@ -1,2 +1,6 @@
.vscode .vscode
deferred_planarity_test/bin deferred_planarity_test/bin
make_graphs/bin
make_graphs/test_graphs
make_planar/bin
test_area

View File

@ -3,33 +3,14 @@
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
#include <iostream> #include <iostream>
#include <fstream>
#include <cstdlib> #include <cstdlib>
#include <climits> #include <climits>
#include <string>
#include "mps.h" #include "mps.h"
using namespace std; 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. // Main function.
@ -37,34 +18,8 @@ void complete_graph_generator(int n, ofstream* out) {
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
if (argc == 1) { string input_file = argv[1];
cout << "Usages:" << endl; std::cout << "Number of removed edges: " << find_mps(input_file) << std::endl;
cout << "======================" << endl;
cout << "mps_testing -mps <infile> <outfile>" << endl;
cout << " Process the infile, and output the resulting maximal" << endl
<< " planar subgraph in the outfile." << endl;
cout << "mps_testing -gen <infile> <outfile>" << 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);
}
return 0; return 0;
} }

View File

@ -141,11 +141,10 @@ class maximal_planar_subgraph_finder
public: public:
maximal_planar_subgraph_finder(); maximal_planar_subgraph_finder();
~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); node* get_new_node(node_type t);
void read_from_file(ifstream* in); void read_from_gml(string input_file);
void output(ofstream* out); int output_removed_edge_size();
void output_deleted_edges(ofstream* out);
void postOrderTraversal(); void postOrderTraversal();
void sort_adj_list(); void sort_adj_list();
void determine_edges(); void determine_edges();

View File

@ -3,62 +3,61 @@
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
#include "mps.h" #include "mps.h"
#include <ogdf/fileformats/GraphIO.h>
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
// Finding MPS // Finding MPS
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
void find_mps(ifstream* in, ofstream* out) { int find_mps(string input_file) {
maximal_planar_subgraph_finder m; 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) { int maximal_planar_subgraph_finder::find_mps(string input_file) {
read_from_file(in); read_from_gml(input_file);
postOrderTraversal(); postOrderTraversal();
sort_adj_list(); sort_adj_list();
determine_edges(); determine_edges();
back_edge_traversal(); back_edge_traversal();
output(out); return output_removed_edge_size();
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
// Imput, output // 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. // read input file of gml format
void maximal_planar_subgraph_finder::read_from_file(ifstream* in) { void maximal_planar_subgraph_finder::read_from_gml(string input_file) {
int node_num, n1, n2; ogdf::Graph G;
//Number of nodes.
(*in) >> node_num; // utilize OGDF readGML
//initialize all the nodes. if (!ogdf::GraphIO::read(G, input_file, ogdf::GraphIO::readGML)) {
for (int i = 0; i < node_num; ++i) { 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.push_back(new node(P_NODE));
_node_list[i]->set_id(i); _node_list[i]->set_id(i);
} }
//Set the adj-list.
while ((*in) >> n1 >> n2) { // create edges
_node_list[n1]->add_adj(_node_list[n2]); for (ogdf::edge e : G.edges) {
_node_list[n2]->add_adj(_node_list[n1]); 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. //Output a maximal planar subgraph in the same format as input.
void maximal_planar_subgraph_finder::output(ofstream* out) { int maximal_planar_subgraph_finder::output_removed_edge_size() {
(*out) << _node_list.size() << endl; int sum = 0;
for (int i = 0; i < _edge_list.size(); ++i) { for (int i = 0; i < _back_edge_list.size(); ++i) {
(*out) << _edge_list[i].first->node_id() << " " << _edge_list[i].second->node_id() << endl; if (_is_back_edge_eliminate[i]) ++sum;
} }
for (int i = 0; i < _back_edge_list.size(); ++i) { return sum;
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;
}
}

View File

@ -0,0 +1,32 @@
#include <ogdf/basic/basic.h>
#include <ogdf/basic/Graph.h>
#include <ogdf/fileformats/GraphIO.h>
#include <ogdf/basic/graph_generators.h>
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);
}
}

328
make_planar/make_planar.cpp Normal file
View File

@ -0,0 +1,328 @@
#include <PCTree/PCTree.h>
// #include "hsuPC/include/PCTree.h"
// #include "hsuPC/include/PCNode.h"
#include <ogdf/basic/STNumbering.h>
#include <ogdf/basic/graph_generators.h>
#include <ogdf/basic/GraphCopy.h>
#include <ogdf/fileformats/GraphIO.h>
#include <ogdf/planarity/BoothLueker.h>
#include <ogdf/planarity/BoyerMyrvold.h>
#include <ogdf/basic/extended_graph_alg.h>
#include <ogdf/planarity/booth_lueker/IndInfo.h>
#include <ogdf/planarity/booth_lueker/PlanarPQTree.h>
#include <ogdf/planarity/planar_subgraph_fast/PlanarSubgraphPQTree.h>
#include <ogdf/planarity/PlanarSubgraphFast.h>
#include <ogdf/planarity/PlanarSubgraphBoyerMyrvold.h>
#include <ogdf/planarity/PlanarSubgraphPC.h>
#include <ogdf/planarity/MaximumPlanarSubgraph.h>
#include <ogdf/planarity/MaximalPlanarSubgraphSimple.h>
#include <iostream>
#include <map>
#include <chrono>
#include <getopt.h>
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<int> &numbering, List<edge> &delEdges) {
using PlanarLeafKey = booth_lueker::PlanarLeafKey<whaInfo*>;
NodeArray<SListPure<PlanarLeafKey*>> inLeaves(G);
NodeArray<SListPure<PlanarLeafKey*>> outLeaves(G);
Array<node> 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<PQLeafKey<edge,whaInfo*,bool>*> totalEliminatedKeys;
PlanarSubgraphPQTree T;
T.Initialize(inLeaves[table[1]]);
for (int i = 2; i < G.numberOfNodes(); i++) {
SList<PQLeafKey<edge,whaInfo*,bool>*> 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<edge, whaInfo*, bool> *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<typename PCT, typename PCN, typename PCNT>
void planarizePC(const Graph &G, const NodeArray<int> &numbering, List<edge> &delEdges) {
// initialize PCTree instance
PCT T;
// vector to store nodes of graph in order specified in numbering
std::vector<node> order(G.numberOfNodes(), nullptr);
for (node n : G.nodes) {
order.at(numbering[n] - 1) = n;
}
// store leaf representations of edges in PCTree
std::vector<PCN *> leafRepresentation(G.maxEdgeIndex() + 1, nullptr);
// vectors declared for later use
std::vector<edge> outEdges;
std::vector<PCN *> 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<PCN *, edge> 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<PCN *> 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<PCN *> &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<int, ogdf::node> 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<int> 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<edge> *delEdges = new List<edge>; // empty list
// changed to PC
if (selection == "pc") {
planarizePC<pc_tree::PCTree, pc_tree::PCNode, pc_tree::PCNodeType>(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<int> *ps = new PlanarSubgraphFast<int>;
// ogdf::MaximalPlanarSubgraphSimple<int> mps(*(new PlanarSubgraphBoyerMyrvold));
ogdf::MaximalPlanarSubgraphSimple<int> mps(*(new PlanarSubgraphFast<int>));
mps.call(G, *delEdges);
} else {
std::cout << "running maximum" << std::endl;
ogdf::MaximumPlanarSubgraph<int> 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<pc_tree::hsu::PCTree, pc_tree::hsu::PCNode, pc_tree::hsu::PCTree::PCNodeType>(G, numbering) << std::endl;
// bool result = isPlanarPC<pc_tree::PCTree, pc_tree::PCNode, pc_tree::PCNodeType>(subG, numbering);
// std::cout << result << std::endl;
return 0;
}