조성현

variable arrange, add topk, get stream input

#include "stdafx.h"
#include "GraphItem.h"
GraphItem::GraphItem(ifstream& fin, int numOfLines)
GraphItem::GraphItem(const char *filename)
{
//numOfLines == ifstream에서 읽을 라인의 수
fin.open(filename);
if (!fin)
throw std::exception("graph file input is invalid");
else if (numOfLines < 1)
throw std::exception("invalid numOfLines");
throw std::exception("graph file is not available");
graph = new Graph();
read_more();
}
GraphItem::~GraphItem()
{
fin.close();
}
void GraphItem::read_more()
{
/**
* Parse Paper dataset
* - paper_key, [author_list], publish_year
* Column Delimiter: ||
* Author list Delimiter: &&
*/
* Parse Paper dataset
* - paper_key, [author_list], publish_year
* Column Delimiter: ||
* Author list Delimiter: &&
*/
std::string line;
vector<std::string> tokens;
vector<std::string> authors;
vector<pair<string, string>> edges;
//String <--> int 양방향 변환을 위해 bidirectional map 상숑
//map<string, int> -> <vertex label, vertex index>
/*typedef boost::bimap<string, int> bm_type;*/
/*bm_type node_ids;
vector<simple_edge> edges_indexes*/; //int로 변환된 edge
int node_cnt = 0;
/*vector<pair<string, string>> edges;*/
//vector<simple_edge> edges_indexes;
int line_cnt = 0;
qDebug() << "* graph reading start";
//한 줄씩 읽어서 Parse
int node_cnt = 0;
while (std::getline(fin, line) && !line.empty()) {
//boost::split 이용해 문자열 분리
//tokens[0]: Paper-key. ex) conf/iastedCSN/KeimS06
......@@ -39,53 +43,72 @@ GraphItem::GraphItem(ifstream& fin, int numOfLines)
//tokens[2]: Published year.
boost::split(tokens, line, boost::is_any_of("||"), boost::token_compress_on);
boost::split(authors, tokens[1], boost::is_any_of("&&"), boost::token_compress_on);
const string& paper_key = tokens[0];
if (node_ids.left.find(paper_key) == node_ids.left.end()) {
node_ids.insert(bm_type::value_type(paper_key, node_cnt++));
node_ids.insert(bm_type::value_type(paper_key, node_cnt + whole_node_cnt));
++node_cnt;
}
for (auto author : authors) {
edges.push_back(pair<string, string>(paper_key, author));
if (node_ids.left.find(author) == node_ids.left.end()) {
node_ids.insert(bm_type::value_type(author, node_cnt++));
node_ids.insert(bm_type::value_type(author, node_cnt + whole_node_cnt));
++node_cnt;
}
}
//debug
++line_cnt;
if (line_cnt >= numOfLines) break;
if (line_cnt >= READ_LINE_UNIT) break;
}
qDebug() << "* graph reading complete";
qDebug() << "* # of read line: " << line_cnt;
qDebug() << "* # of nodes: " << node_cnt;
qDebug() << "* # of edges: " << edges.size();
//qDebug() << "* # of edges: " << edges.size();
//edge conversion
//<string, string> to <int, int>
//using boost::bimap (bidirectional map)
edges_indexes.clear();
for (auto edge : edges) {
edges_indexes.push_back({
node_ids.left.find(edge.first)->get_right(),
node_ids.left.find(edge.second)->get_right()
});
}
//Graph --> defined in "PaperGraphWidget.h"
//Graph graph(edges_indexes.begin(), edges_indexes.end(), node_ids.size());
//graph = new Graph(edges_indexes.begin(), edges_indexes.end(), node_ids.size());
//for (auto& e: edges_indexes) {
// boost::add_edge(e.first, e.second, graph);
//}
if (graph) {
delete graph;
graph = nullptr;
}
graph = new Graph(edges_indexes.begin(), edges_indexes.end(), node_ids.size());
//set index property
qDebug() << "* set vertex property start";
vertex_iterator vi, vi_end;
int i = 0;
for (boost::tie(vi, vi_end)=vertices(*graph); vi!=vi_end; ++vi) {
for (boost::tie(vi, vi_end) = vertices(*graph); vi != vi_end; ++vi) {
//Vertex Property 설정
//index: 0 ~ ...
//index: n ~ ...
//name : map의 value(i) 기준으로 찾은 Key
// map --> map<string, int> (boost bidirectional map)
// map --> map<string, int> (boost bidirectional map)
std::string node_label = node_ids.right.find(i)->get_left();
boost::put(vertex_index, *graph, *vi, i);
boost::put(vertex_name, *graph, *vi, node_label);
boost::put(vertex_record, *graph, *vi, 0);
qDebug() << "** index: " << i << ", name: " << node_label.c_str();
//node type 설정
if (boost::regex_match(node_label, paper_reg)) {
......@@ -99,7 +122,7 @@ GraphItem::GraphItem(ifstream& fin, int numOfLines)
++i;
}
qDebug() << "* set vertex property end";
whole_node_cnt += node_cnt;
//qDebug("* set edges weight start");
////모든 edge의 weight를 1로 설정
......@@ -133,7 +156,7 @@ GraphItem::GraphItem(ifstream& fin, int numOfLines)
//while (current != boost::vertex(start_idx, *graph)) {
//}
//qDebug("* path highlighting end");
//graph layout calculation
//using boost::random_graph_layout and boost::kamada_kawai_spring_layout
......@@ -148,18 +171,18 @@ GraphItem::GraphItem(ifstream& fin, int numOfLines)
Topology::point_difference_type extent;
extent[0] = extent[1] = (double)SCREEN_SIZE;
rectangle_topology<> rect_top(gen,
-SCREEN_SIZE/2, -SCREEN_SIZE/2,
SCREEN_SIZE/2, SCREEN_SIZE/2);
-SCREEN_SIZE / 2, -SCREEN_SIZE / 2,
SCREEN_SIZE / 2, SCREEN_SIZE / 2);
switch (LAYOUT_MODE) {
case GRAPH_LAYOUT::RANDOM_LAYOUT:
random_graph_layout(*graph, get(vertex_position, *graph), rect_top);
break;
case GRAPH_LAYOUT::CIRCLE_LAYOUT:
circle_graph_layout(*graph, get(vertex_position, *graph), SCREEN_SIZE/2);
circle_graph_layout(*graph, get(vertex_position, *graph), SCREEN_SIZE / 2);
break;
case GRAPH_LAYOUT::FRUCHTERMAN_REINGOLD_LAYOUT:
fruchterman_reingold_force_directed_layout(*graph,
get(vertex_position, *graph),
......@@ -172,14 +195,18 @@ GraphItem::GraphItem(ifstream& fin, int numOfLines)
qDebug() << "* make graph layout end";
//clear lists
nodeList.clear();
edgeList.clear();
//add edges
auto position = get(vertex_position, *graph);
auto label = get(vertex_name, *graph);
auto nodeType = get(vertex_type, *graph);
auto position = boost::get(vertex_position, *graph);
auto label = boost::get(vertex_name, *graph);
auto nodeType = boost::get(vertex_type, *graph);
edge_iterator ei, ei_end;
vertex_descriptor u, v;
for (boost::tie(ei, ei_end)=boost::edges(*graph); ei!=ei_end; ++ei) {
for (boost::tie(ei, ei_end) = boost::edges(*graph); ei != ei_end; ++ei) {
u = source(*ei, *graph);
v = target(*ei, *graph);
Point p1 = position[u];
......@@ -187,14 +214,14 @@ GraphItem::GraphItem(ifstream& fin, int numOfLines)
//make edge item and push it to list
EdgeItem *edge;
edge = new EdgeItem(p1[0], p1[1], p2[0], p2[1], QColor(Qt::black), 0);
edge->setPos(p1[0], p1[1]);
edgeList << edge;
}
//add nodes
for (boost::tie(vi, vi_end)=vertices(*graph); vi!=vi_end; ++vi) {
for (boost::tie(vi, vi_end) = vertices(*graph); vi != vi_end; ++vi) {
Point p = position[*vi];
auto nt = nodeType[*vi];
std::string name = label[*vi];
......@@ -203,7 +230,8 @@ GraphItem::GraphItem(ifstream& fin, int numOfLines)
NodeItem *node;
if (nt == NODE_TYPE::NODE_PAPER) {
node = new NodeItem(p[0], p[1], QColor(Qt::darkGreen), QString(name.c_str()), nt);
} else {
}
else {
node = new NodeItem(p[0], p[1], QColor(Qt::green), QString(name.c_str()), nt);
}
node->setPos(QPointF(p[0], p[1]));
......@@ -262,6 +290,63 @@ void GraphItem::reset_color()
}
}
void GraphItem::topK_highlight()
{
// 저자 노드별 실적 계산
vertex_iterator vi, vi_end;
Graph::adjacency_iterator ai, ai_end;
auto nodeLabel = boost::get(vertex_name, *graph);
auto nodeType = boost::get(vertex_type, *graph);
auto numOfRecords = boost::get(vertex_record, *graph);
//실적 count 초기화
//for (boost::tie(vi, vi_end) = boost::vertices(*graph); vi != vi_end; ++vi) {
// if (nodeType[*vi] != NODE_TYPE::NODE_AUTHOR) {
// continue;
// }
// boost::put(vertex_record, *graph, *vi, 0);
//}
// <record, label>
TopKHeap<pair<int, string>> heap(TOP_K);
for (boost::tie(vi, vi_end) = boost::vertices(*graph); vi != vi_end; ++vi) {
if (nodeType[*vi] != NODE_TYPE::NODE_AUTHOR) {
continue;
}
int record_cnt = 0;
for (boost::tie(ai, ai_end) = boost::adjacent_vertices(*vi, *graph);
ai != ai_end; ++ai) {
if (nodeType[*vi] == NODE_TYPE::NODE_PAPER) {
++record_cnt;
}
}
boost::put(vertex_record, *graph, *vi, record_cnt);
heap.push(make_pair(record_cnt, nodeLabel[*vi]));
}
//get top K records
pair<int, string> topk_arr[TOP_K];
for (int i = 0; i < TOP_K; ++i) {
topk_arr[i] = heap.pop();
}
for (auto& n: nodeList) {
auto label = n->getLabel();
n->setColor(QColor(Qt::lightGray));
for (auto& p: topk_arr) {
if (label.toStdString() == p.second) {
n->setColor(QColor(Qt::red));
break;
}
}
}
//delete[] topk_arr;
}
//event handler
void GraphItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
......
......@@ -13,7 +13,12 @@ class GraphItem
: public QGraphicsItem
{
public:
GraphItem(ifstream& fin, int numOfLines);
//GraphItem(ifstream* pFin);
GraphItem(const char* filename);
virtual ~GraphItem();
//io
void read_more();
//overrides
QRectF boundingRect() const override;
......@@ -23,6 +28,7 @@ public:
//methods
void path_highlighting(std::string start, std::string end);
void reset_color();
void topK_highlight();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
......@@ -30,8 +36,12 @@ protected:
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
private:
ifstream fin;
bm_type node_ids;
vector<pair<string, string>> edges;
vector<simple_edge> edges_indexes;
int whole_node_cnt = 0;
Graph *graph = nullptr;
QList<NodeItem *> nodeList;
QList<EdgeItem *> edgeList;
......
......@@ -25,35 +25,56 @@ MainWindow::~MainWindow()
}
void MainWindow::print_graph(std::ifstream& fin)
void MainWindow::print_graph(const char * filename)
{
graphWidget->print_graph(fin);
graphWidget->print_graph(filename);
}
//void MainWindow::print_graph(std::ifstream *pFin)
//{
// graphWidget->print_graph(pFin);
//}
//////////////////////////////////////////////////////////////////
// private methods
//////////////////////////////////////////////////////////////////
void MainWindow::createActions()
{
testHighlightAct = new QAction(tr("&Highlight"), this);
readMoreAct = new QAction(tr("Read more"), this);
readMoreAct->setStatusTip(tr("read more lines from file"));
connect(readMoreAct, &QAction::triggered, this, &MainWindow::read_more);
testHighlightAct = new QAction(tr("Highlight"), this);
testHighlightAct->setStatusTip(tr("Highlighting node"));
connect(testHighlightAct, &QAction::triggered, this, &MainWindow::test_highlighting);
resetColorAct = new QAction(tr("&Reset colors"), this);
topkAct = new QAction(tr("topK"), this);
topkAct->setStatusTip(tr("highlight who was top k papers"));
connect(topkAct, &QAction::triggered, this, &MainWindow::topk);
resetColorAct = new QAction(tr("Reset colors"), this);
resetColorAct->setStatusTip(tr("Reset all node's color"));
connect(resetColorAct, &QAction::triggered, this, &MainWindow::reset_color);
}
void MainWindow::createMenus()
{
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(readMoreAct);
actionMenu = menuBar()->addMenu(tr("&Actions"));
actionMenu->addAction(testHighlightAct);
actionMenu->addAction(topkAct);
actionMenu->addAction(resetColorAct);
}
//////////////////////////////////////////////////////////////////
// slots
//////////////////////////////////////////////////////////////////
void MainWindow::read_more()
{
qDebug("* MainWindow::read_more");
graphWidget->read_more();
}
void MainWindow::test_highlighting()
{
/*QMessageBox::information(this, "test",
......@@ -62,7 +83,12 @@ void MainWindow::test_highlighting()
//graphWidget->update();
}
void MainWindow::topk()
{
graphWidget->topk();
}
void MainWindow::reset_color()
{
graphWidget->reset_color();
}
\ No newline at end of file
}
......
......@@ -12,14 +12,18 @@ public:
MainWindow(QWidget *parent = 0);
~MainWindow();
void print_graph(std::ifstream& fin);
/*void test_highlighting();*/
//void print_graph(std::ifstream *pFin);
void print_graph(const char* filename);
private:
PaperGraphWidget *graphWidget;
QMenu *fileMenu;
QAction *readMoreAct;
QMenu *actionMenu;
QAction *testHighlightAct;
QAction *topkAct;
QAction *resetColorAct;
private:
......@@ -27,7 +31,9 @@ private:
void createMenus();
private slots:
void read_more();
void test_highlighting();
void topk();
void reset_color();
};
......
......@@ -25,23 +25,47 @@ PaperGraphWidget::PaperGraphWidget(QWidget *parent)
setWindowTitle(tr("dblp paper graph visualization"));
}
void PaperGraphWidget::print_graph(ifstream& fin)
//void PaperGraphWidget::print_graph(ifstream* pFin)
//{
// qDebug("* PaperGraphWidget::print_graph");
// //QGraphicsItem *graph_item = new GraphItem(fin);
// if (graphItem)
// throw std::exception("already have graph item");
//
// graphItem = new GraphItem(pFin);
// graphItem->setPos(0, 0);
// scene->addItem(graphItem);
//}
void PaperGraphWidget::print_graph(const char * filename)
{
//QGraphicsItem *graph_item = new GraphItem(fin);
if (graphItem)
throw std::exception("already have graph item");
graphItem = new GraphItem(fin, READ_LINE_UNIT);
graphItem = new GraphItem(filename);
graphItem->setPos(0, 0);
scene->addItem(graphItem);
}
void PaperGraphWidget::read_more()
{
qDebug("* PaperGraphWidget::read_more");
graphItem->read_more();
scene->update();
}
void PaperGraphWidget::path_highlight()
{
graphItem->path_highlighting(std::string(""), std::string(""));
scene->update();
}
void PaperGraphWidget::topk()
{
graphItem->topK_highlight();
scene->update();
}
void PaperGraphWidget::reset_color()
{
graphItem->reset_color();
......
......@@ -11,9 +11,13 @@ class PaperGraphWidget : public QWidget
public:
PaperGraphWidget(QWidget *parent = 0);
void print_graph(ifstream& fin);
//void print_graph(ifstream* pFin);
void print_graph(const char* filename);
//main window slots
void read_more();
void path_highlight();
void topk();
void reset_color();
private slots:
......
......@@ -9,9 +9,9 @@ int main(int argc, char *argv[])
MainWindow m;
try {
ifstream fin(PAPER_FILENAME);
m.print_graph(fin);
fin.close();
//ifstream fin(PAPER_FILENAME);
m.print_graph(PAPER_FILENAME);
//fin.close();
} catch (const std::exception& e) {
qDebug() << "Error: " << e.what();
return EXIT_FAILURE;
......
......@@ -28,13 +28,20 @@
#include <boost/graph/topology.hpp>
#include <boost/regex.hpp>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <exception>
#include <fstream>
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <queue>
#include <string>
#include <utility>
#include <vector>
using namespace boost;
......@@ -43,9 +50,11 @@ using namespace std;
/* enums */
enum vertex_position_t { vertex_position };
enum vertex_type_t { vertex_type };
enum vertex_record_t { vertex_record };
namespace boost {
BOOST_INSTALL_PROPERTY(vertex, position);
BOOST_INSTALL_PROPERTY(vertex, type);
BOOST_INSTALL_PROPERTY(vertex, record);
}
enum NODE_TYPE {
NODE_PAPER,
......@@ -64,7 +73,8 @@ typedef square_topology<>::point_type point;
typedef boost::property<vertex_index_t, int,
boost::property<vertex_name_t, std::string,
boost::property<vertex_position_t, point,
boost::property<vertex_type_t, int>>>
boost::property<vertex_type_t, int,
boost::property<vertex_record_t, int>>>>
> VertexProperties;
typedef boost::adjacency_list<
listS, //outEdgeList
......@@ -77,6 +87,7 @@ typedef boost::adjacency_list<
> Graph;
typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
//typedef boost::graph_traits<Graph>::adjacency_iterator adjacency_iterator;
typedef boost::graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typedef square_topology<> Topology;
typedef typename Topology::point_type Point;
......@@ -95,14 +106,98 @@ namespace {
const int NODE_SIZE = 4;
const int LAYOUT_MODE = GRAPH_LAYOUT::RANDOM_LAYOUT;
const int SCREEN_SIZE = 1000;
const int READ_LINE_UNIT = 100; //한 번에 몇 라인을 읽을지
const int READ_LINE_UNIT = 5; //한 번에 몇 라인을 읽을지
/* topK */
const int TOP_K = 10; //상위 몇 개 아이템에 대해 할 지
const int TOP_K = 10; //상위 몇 개 아이템에 대해 highlight 할 지
}
/* boost */
namespace boost {
const boost::regex paper_reg("(conf|journals).*");
}
\ No newline at end of file
}
/* topK heap */
template <typename T>
class TopKHeap {
private:
int k; //max size
int size; //current size
T *heap;
private:
void reHeapDown(int root) {
//after pop
int minIdx, left, right;
left = root * 2 + 1;
right = root * 2 + 2;
if (left <= (size - 1)) {
if (left == (size - 1))
minIdx = left;
else {
minIdx = ((heap[left] <= heap[right]) ? left : right);
}
if (heap[root] > heap[minIdx]) {
swap(heap[root], heap[minIdx]);
reHeapDown(minIdx);
}
}
}
void reHeapUp(int root, int bottom) {
//bottom: 위로 올릴 노드 idx
//after push
if (root > bottom) {
int parent = (bottom - 1) / 2;
if (heap[parent] < heap[bottom]) {
swap(heap[parent], heap[bottom]);
reHeapUp(root, parent);
}
}
}
public:
TopKHeap(int _k) : k(_k), size(0) {
if (_k <= 0) abort();
heap = new T[_k];
memset(heap, 0, sizeof(heap));
}
~TopKHeap() {
if (heap) delete[] heap;
}
public:
void push(T elem) {
if (size < k) {
heap[size++] = elem;
}
else {
if (elem < heap[0]) {
//less than minimum
}
else {
pop();
heap[size++] = elem;
}
}
reHeapUp(0, size - 1);
}
T pop() {
if (size <= 0)
abort();
else if (size == 1) {
T ret = heap[--size];
return ret;
}
else {
T ret = heap[0];
heap[0] = heap[--size];
reHeapDown(0);
return ret;
}
}
};
\ No newline at end of file
......