/* ---- This file is part of SECONDO. Copyright (C) 2017, University in Hagen, Faculty of Mathematics and Computer Science, Database Systems for New Applications. SECONDO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. SECONDO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with SECONDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- //[_] [\_] */ #ifndef MGRAPHCOMMON_H #define MGRAPHCOMMON_H #include "MainMemoryExt.h" #include "Algebras/Relation-C++/RelationAlgebra.h" #include "NestedList.h" #include "SecondoCatalog.h" #include "SecondoSystem.h" #include "MEdge.h" #include "StopWatch.h" #include #include #include #include //#include namespace mm2algebra{ /* 1 Class ~shortCutInfo~ This is an auxiliary class for graph contraction. */ class shortCutInfo{ public: shortCutInfo(size_t _source, size_t _target, double _cost, size_t _middle): source(_source), target(_target), cost(_cost), middle(_middle){} size_t source; size_t target; double cost; size_t middle; }; /* 2 Class ~MGraphCommon~ This class represents a graph object. */ class MGraphCommon : public MemoryObject{ protected: typedef std::pair,std::list > alist; public: /* 2.1 constructor */ MGraphCommon(const bool _flob, const std::string& _database, const std::string& _type, const int _sourcePos, const int _targetPos, const int _costPos): MemoryObject(_flob,_database,_type), sourcePos(_sourcePos), targetPos(_targetPos), costPos(_costPos){ ListExpr k; if(!nl->ReadFromString(_type,k)){ std::cerr << "Invalid type description " << _type << endl; assert(false); } // remove mem(mgraphX ... k = nl->Second(nl->Second(k)); SecondoCatalog* ctlg = SecondoSystem::GetCatalog(); k = ctlg->NumericType(k); tt = new TupleType(k); memSize += tt->GetTotalSize(); } /* 2.2 Destructor */ ~MGraphCommon(){ tt->DeleteIfAllowed(); memSize = 0; } /* 2.3 ~numSuccessors~ Returns the number of successors of a node. */ size_t numSuccessor(size_t vertex)const{ if(vertex>=graph.size()){ return 0; } return graph[vertex].first.size(); } /* 2.4 ~numPredecessors~ Returns the number of predecessors of a node. */ size_t numPredecessor(size_t vertex) const{ if(vertex>=graph.size()){ return 0; } return graph[vertex].second.size(); } /* 2.5 ~insertGraphEdge~ Inserts an edge to this graph. Source, Target and costs are taken from the argument. If one of these valus is undefined or outside the allowed range, false is returned and the graph is keepts as before. Otherwise, the edge is inserted into the graph. */ bool insertGraphEdge(Tuple* ginfo){ CcInt* Source = (CcInt*) ginfo->GetAttribute(sourcePos); CcInt* Target = (CcInt*) ginfo->GetAttribute(targetPos); CcReal* Costs = (CcReal*) ginfo->GetAttribute(costPos); if(!Source->IsDefined() || !Target->IsDefined() || !Costs->IsDefined()){ return false; } int source = Source->GetValue(); int target = Target->GetValue(); double costs = Costs->GetValue(); if(source < 0 || (size_t)source>=graph.size()) return false; if(target < 0 || (size_t)target>=graph.size()) return false; if(costs<0) return false; if(flob) ginfo->bringToMemory(); MEdge e(source, target, costs, ginfo); graph[source].first.push_back(e); graph[target].second.push_back(e); memSize += ginfo->GetSize(); return true; } /* 2.6 ~inserts~ Inserts an edge to this graph. */ void insert(MEdge& e){ int source = e.source; int target = e.target; assert(source>=0); assert(target>=0); assert((size_t)sourcegraph.end()){ if(eit!=eite){ Tuple* res = eit->info; eit++; res->IncReference(); return res; } vit++; if(vit != g->graph.end()){ eit = vit->first.begin(); eite = vit->first.end(); } } return 0; } private: std::vector::iterator vit; std::list::iterator eit; std::list::iterator eite; MGraphCommon* g; edgeIterator(MGraphCommon* _g){ g = _g; vit = g->graph.begin(); if(vit != g->graph.end()){ eit = vit->first.begin(); eite = vit->first.end(); } } }; // end of class edgeIterator /* 2.9 ~getEdgeIt~ Returns an iterator over all edges of this graph. */ edgeIterator* getEdgeIt(){ return new edgeIterator(this); } /* 2.10 Auxiliary class ~singleNodeIterator~ */ class singleNodeIterator{ friend class MGraphCommon; public: virtual Tuple* next(){ if(it!=list->end()){ Tuple* res = it->info; res->IncReference(); it++; return res; } return 0; } virtual ~singleNodeIterator(){} private: std::list* list; std::list::iterator it; singleNodeIterator( std::list* _list){ this->list = _list; if(list!=nullptr){ it = list->begin(); } } }; // end of class singleNodeIterator class singleNodeDeleteIterator : public singleNodeIterator{ friend class MGraphCommon; public: virtual Tuple* next(){ if(vlist.empty()){ return nullptr; } MEdge e = vlist.front(); vlist.pop_front(); if(e.info){ e.info->IncReference(); } return e.info; } private: std::list vlist; singleNodeDeleteIterator( int _vertex, MGraphCommon* _graph, bool _successors):singleNodeIterator(0){ if(_successors){ std::swap(vlist,_graph->graph[_vertex].first); } else { std::swap(vlist,_graph->graph[_vertex].second); } delBack(vlist,_graph,_successors,_vertex); } void delBack(std::list& l, MGraphCommon* g, bool succ, int v){ std::list::iterator it; for(it = l.begin();it!=l.end(); it++){ MEdge e = *it; if(succ){ MGraphCommon::removeSource(g->graph[e.target].second,v); } else { removeTarget(g->graph[e.source].first,v); } } } }; // end of class singleNodeDeleteIterator /* 2.11 ~getSuccessors~ Returns an iterator over all edges of this graph having ~v~ as source. */ singleNodeIterator* getSuccessors(int v){ if(v<0 || (size_t)v >= graph.size()){ return 0; } return new singleNodeIterator(&(graph[v].first)); } singleNodeIterator* getSuccessors(int v, bool delOption){ if(v<0 || (size_t)v >= graph.size()){ return 0; } if(!delOption){ return new singleNodeIterator(&(graph[v].first)); } return new singleNodeDeleteIterator(v,this,true); } /* 2.12 ~getPredecessors~ Returns an iterator over all edges of this graph having ~v~ as target. */ singleNodeIterator* getPredecessors(int v){ if(v<0 || (size_t)v >= graph.size()){ return 0; } return new singleNodeIterator(&(graph[v].second)); } singleNodeIterator* getPredecessors(int v, bool delOption){ if(v<0 || (size_t)v >= graph.size()){ return 0; } if(!delOption){ return new singleNodeIterator(&(graph[v].second)); } return new singleNodeDeleteIterator(v,this,false); } /* 2.13 ~getSuccList~ Returns the list of successorts of ~vertex~ */ const std::list& getSuccList(int vertex){ return graph[vertex].first; } /* 2.14 ~succCount~ Returns the numbers of successors of a node. */ int succCount(int v){ if(v<0 || (size_t)v >= graph.size()){ return -1; } return graph[v].first.size(); } /* 2.16 ~predCount~ Returns the number of predecessors of a node. */ int predCount(int v){ if(v<0 || (size_t)v >= graph.size()){ return -1; } return graph[v].second.size(); } /* 2.17 ~removeAllEdges~ Removes all Edges from Source to target. */ void removeAllEdges(int source, int target){ if(source< 0 || source >= (int) graph.size()) return; if(target< 0 || target >= (int) graph.size()) return; removeTarget(graph[source].first,target); removeSource(graph[target].second,source); memSize -= sizeof(MEdge); } /* 2.17. ~disconnect~ Removes all edges from and to a specified node. */ bool disconnect(int vertex){ if(vertex<0 || (size_t)vertex >=graph.size()){ return false; } alist& vlist = graph[vertex]; // remove vertex from the predecessors list of its successors std::list::iterator it; for(it = vlist.first.begin(); it!=vlist.first.end();it++){ MEdge& edge = *it; //assert(edge.source==vertex); removeSource(graph[edge.target].second,vertex); } // remove vertex from successors list of its predecessors for(it = vlist.second.begin(); it!=vlist.second.end();it++){ MEdge& edge = *it; //assert(edge.target==vertex); removeTarget(graph[edge.source].first,vertex); memSize -= sizeof(MEdge); } // clean lists of vertex vlist.first.clear(); vlist.second.clear(); return true; } /* 2.18 ~components~ Assigns a component number to each node. */ void components(std::vector& v){ v.clear(); std::vector v2; tarjan(v2); for(size_t i=0;i& allShortCuts, int variant, size_t skipRecalculate, const size_t maxEdges){ std::vector finished(graph.size(),0); uint64_t finishedMark = 1; if(variant == 1){ return simpleContraction1(maxPrio, minBlockSize, maxHopsF, maxHopsB, allShortCuts, skipRecalculate, maxEdges, &finished, finishedMark); } else { return simpleContraction2(maxPrio, minBlockSize, maxHopsF, maxHopsB, allShortCuts, skipRecalculate, maxEdges, &finished, finishedMark); } } /* 2.20 ~pathCosts~ Computes the length of the minimum path between ~source~ and ~target~. The search is restricted to a certain number of hops from source and from target. The forbidden node is not used. If the maximum costs are reached, the search terminates as well. Using negative number for the number of hops and the forbidden node, then the search spans the whole graph. */ double pathCosts(const int source, const int target, const int maxHopsForward, const int maxHopsBackward, const int forbidden, double maxCosts = std::numeric_limits::max()){ minPathCosts mpc; if(maxCosts<=0){ maxCosts = std::numeric_limits::max(); } return mpc(this,source, target, maxHopsForward, maxHopsBackward, maxCosts, forbidden, false, std::numeric_limits::max(),0,0); } struct ddsgedge{ ddsgedge(const size_t _target, const size_t _cost, const size_t _dir): target(_target), cost(_cost),dir(_dir){} size_t target; size_t cost; size_t dir; // 0 both, 1 forward, 2 backward void print(std::ostream& o, size_t source){ o << source << " " << target << " " << cost << " " << dir << std::endl; } }; bool exportDDSG(std::string& filename, const double scaleCost){ std::ofstream out(filename.c_str(), std::ios::out | std::ios::trunc); if(!out){ return false; } std::vector > edges; size_t noEdges = fillddsg(edges, scaleCost); out << "d" << endl; out << edges.size() << " " << noEdges << endl; for(size_t i=0;i& v = edges[i]; for(size_t j=0;j >& result, double scale){ result.clear(); for(size_t i=0;i v; result.push_back(v); } uint32_t noEdges = 0; for(size_t i=0;i successors = graph[i].first; std::list::iterator it; for( it = successors.begin(); it!=successors.end(); it++){ size_t source = i; MEdge e = *it; size_t target = e.target; if(source != target){ size_t cost = (size_t)((e.costs*scale)+0.5); size_t dir = 1; if(source > target){ std::swap(source,target); dir = 2; } std::vector& v = result[source]; bool found = false; for(size_t e=0;e cost){ d.cost = cost; } if(d.dir!=dir){ d.dir = 0; } } } if(!found){ v.push_back(ddsgedge(target,cost,dir)); noEdges++; } } } } return noEdges; } std::ostream& print(std::ostream& out, std::vector* names, bool printBackward){ out << "Graph " << endl; if(names != nullptr){ out << "sourcePos : " << sourcePos << endl; out << "targetPos : " << targetPos << endl; out << "costPost : " << costPos << endl; } for(size_t node = 0; node < graph.size() ; node++){ printNode(out, node, graph[node], names, printBackward); } return out; } protected: std::vector graph; // the graph representation TupleType* tt; int sourcePos; // positions within the tuples int targetPos; int costPos; std::vector nodeOrder; // for contraction static void printNode(std::ostream& out, size_t nodeNumber, alist& edges, std::vector* names, bool backward){ out << " --- Node " << nodeNumber << " --- " << endl; auto fit = edges.first.begin(); if(backward){ out << " --- outgoing edges " << endl; } while( fit != edges.first.end()){ fit->print(out, names); fit++; out << endl; } if(backward){ out << endl; out << " --- incoming edges " << endl; auto bit = edges.second.begin(); while(bit != edges.second.end()){ bit->print(out,names); bit++; out << endl; } } out << endl; } /* 2.21 ~setPositions~ Sets the attribute positions for the edge tuples where to find the source, the target and the costs. */ void setPositions(int s, int t, int c){ sourcePos = s; targetPos = t; costPos = c; } /* 2.22 Auxiliary class ~tarjanInfo~ This class helps to compute the strongly connected components. */ class tarjanInfo{ public: tarjanInfo(): seen(false), index(0), lowlink(0), inStack(false), compNo(-1){} bool seen; int index; int lowlink; bool inStack; int compNo; }; /* 2.23 ~tarjan~ This function computes the connected components. Each node is assigned to a component number. */ void tarjan(std::vector& tarjanVector) { tarjanVector.clear(); for(size_t i = 0; i< graph.size();i++){ tarjanInfo ti; tarjanVector.push_back(ti); } std::stack tarjanstack; int index = 0; int compNo = 1; for(size_t i=0;i& tarjanVector, int& index, std::stack& stack, int& compNo){ // pair of vertex number and iterator in successor list typedef std::pair::iterator > stackentry; std::stack rstack; // recursion stack rstack.push(stackentry(vertex,graph[vertex].first.begin())); while(!rstack.empty()){ stackentry e = rstack.top(); rstack.pop(); vertex = e.first; std::list::iterator pos = e.second; if(!tarjanVector[vertex].seen || pos != graph[vertex].first.begin()){ if(!tarjanVector[vertex].seen ){ // first time visited tarjanVector[vertex].index = index; tarjanVector[vertex].lowlink = index; index++; tarjanVector[vertex].inStack = true; tarjanVector[vertex].seen = true; stack.push(vertex); } if(pos != graph[vertex].first.end()){ // not the end of vertex's successors int w = pos->target; pos++; rstack.push(stackentry(vertex,pos)); if(!tarjanVector[w].seen){ rstack.push(stackentry(w,graph[w].first.begin())); } else if(tarjanVector[w].inStack) { tarjanVector[vertex].lowlink = std::min( tarjanVector[vertex].lowlink, tarjanVector[w].index); } } else { // end of successors std::list::iterator sit; for(sit=graph[vertex].first.begin(); sit!=graph[vertex].first.end(); sit++){ int w = sit->target; if(tarjanVector[w].compNo<0){ tarjanVector[vertex].lowlink = std::min( tarjanVector[vertex].lowlink, tarjanVector[w].lowlink); } } if(tarjanVector[vertex].lowlink == tarjanVector[vertex].index){ assert(tarjanVector[vertex].compNo<0); tarjanVector[vertex].compNo = compNo; while(true){ int w = stack.top(); stack.pop(); tarjanVector[w].inStack=false; if(w==vertex) break; assert(tarjanVector[w].compNo<0); tarjanVector[w].compNo = compNo; } compNo++; } } } } // while } // tarjan private: /* 2.28 ~removeSource~ Removes all edges from ~l~ with source = ~s~. */ static void removeSource(std::list& l, int s){ std::list nl; std::list::iterator it; for(it = l.begin();it!=l.end();it++){ if(it->source!=s){ nl.push_back(*it); } } //std::swap(l,nl); l = nl; } /* 2.29 ~removeTarget~ Removes all edges from ~l~ with target = ~s~. */ static void removeTarget(std::list& l, int s){ std::list nl; std::list::iterator it; for(it = l.begin();it!=l.end();it++){ if(it->target!=s){ nl.push_back(*it); } } //std::swap(l,nl); l = nl; } /* 2.30 ~createEdge~ Creates a new MEdge from the given infos. */ static MEdge createEdge(Tuple* templ, int source, int target, double cost){ /* Tuple* t = new Tuple(tt); for(int i=0;iGetNoAttributes();i++){ Attribute* a; if(i==sourcePos){ a = new CcInt(true,source); } else if(i==targetPos){ a = new CcInt(true,target); } else if(i==costPos){ a = new CcReal(true,cost); } else { a = templ->GetAttribute(i)->Clone(); a->SetDefined(false); } t->PutAttribute(i,a); } */ Tuple* t = 0; MEdge e(source, target,cost,t); //t->DeleteIfAllowed(); return e; } /* 2.31 ~struct queueentry~ Structure supporting dijkstra algorithm. */ // computes the costs for all targets that can be reached within maxHops hops // without using the node forbidden. struct queueentry{ queueentry(size_t _node, double _cost, size_t _depth): node(_node), cost(_cost), depth(_depth){ } bool operator<(const queueentry& e) const{ return cost > e.cost; } size_t node; double cost; size_t depth; }; /* 2.32 queueentryComp This is a comparator class for queueentry. */ class queueentryComp{ public: bool operator()(const queueentry& f, const queueentry& s) const { return f.cost < s.cost; } }; //typedef mmheap::mmheap queue_t; typedef std::priority_queue queue_t; typedef mmheap::mmheap queue_t2; /* 2.33 ~processNode~ Auxiliary function for computing multi-target-path-costs. */ template void processNode(queueentry e, size_t forbidden, std::map& targets, size_t maxHops, size_t reached, std::vector* finished, uint64_t& finishedMark, Q& front, size_t& processedEdges, const size_t maxEdges ){ // check wether node has already been processed if( (*finished)[e.node] == finishedMark){ return; } // mark node as finished (*finished)[e.node] = finishedMark; // check whether node is one of the target nodes std::map::iterator it = targets.find(e.node); if(it!=targets.end()){ it->second = e.cost; reached--; if(reached==0) return; } // check whether maxHops has been reached if(e.depth >= maxHops){ return; } // process edges if(processedEdges >= maxEdges){ // early stop return; } std::list& succs = graph[e.node].first; std::list::iterator sit; for(sit = succs.begin(); (sit!=succs.end()) && (processedEdges < maxEdges); sit++){ MEdge& me = *sit; size_t t = me.target; if( (t!=forbidden) && ((*finished)[t] != finishedMark)){ processedEdges++; queueentry et(t, e.cost + me.costs,e.depth+1); front.push(et); } } } /* 2.34 ~conputeCosts~ Computes the costs from source to all targets in the graph. The search stops if the number of hops, the maximum costs, or the maximum number of edges is reached. Unreached targets will have maximum costs. */ void computeCosts(size_t source, size_t forbidden, std::map& targets, double maxCost, size_t maxHops, size_t maxEdges, std::vector* finished, uint64_t finishedMark){ queueentry e(source,0,0); queue_t2 front; //std::set finished; front.push(e); size_t edges = 0; size_t reached = targets.size(); while(!front.empty() && reached>0){ queueentry e = front.top(); front.pop(); if(e.cost > maxCost){ return; } processNode(e, forbidden, targets, maxHops, reached, finished,finishedMark,front, edges, maxEdges); } } /* 2.35 ~computeShortCuts1~ This operator computes the shortcuts if a given node would be contracted. This variant computes the costs from a source to all targets in a single step. */ void computeShortCuts1(size_t node, size_t maxHops, std::vector& result, const size_t maxEdges, std::vector* finished, uint64_t& finishedMark){ // collect all target nodes in a set result.clear(); std::list& preds = graph[node].second; std::list& succs = graph[node].first; typedef std::list::iterator edgeIt; std::map > cand; std::map maxCost; size_t candsize = 0; // collect all candidates and there costs for(edgeIt predIt = preds.begin(); predIt!=preds.end(); predIt++){ MEdge& inEdge = *predIt; size_t source = inEdge.source; double sc = inEdge.costs; std::map cmap; double max = 0; for(edgeIt succit = succs.begin();succit!=succs.end();succit++){ MEdge& outEdge = *succit; size_t target = outEdge.target; if(source != target){ std::map::iterator it = cmap.find(target); double cost = sc + outEdge.costs; if(cost>max){ max = cost; } if(it==cmap.end()){ cmap[target] = cost; } else if(it->second > cost){ it->second = cost; } } } if(cmap.size()>0){ cand[source] = cmap; maxCost[source] = max; } candsize += cmap.size(); } // all potential shortcuts are collected in cand Tuple* tuple = 0; if(cand.size()>0){ tuple = graph[node].first.begin()->info; } std::map >::iterator sit; for(sit= cand.begin(); sit!=cand.end();sit++){ int source = sit->first; double costs = maxCost[source]; std::map::iterator tit; std::map targets; for(tit = sit->second.begin(); tit!=sit->second.end(); tit++){ targets[tit->first] = std::numeric_limits::max(); } // store minPathCost in targets computeCosts(source, node, targets, costs, maxHops, maxEdges, finished, finishedMark); finishedMark++; if(finishedMark==std::numeric_limits::max()){ for(size_t i=0;isize();i++){ (*finished)[i] =0; } finishedMark = 1; } // for(tit = sit->second.begin(); tit!=sit->second.end(); tit++){ if(tit->second < targets[tit->first]){ // shortcut is shorter than another way result.push_back(createEdge(tuple, source, tit->first, tit->second)); } } } } /* 2.36 computeShortCuts2 This function computes the shortcuts to be inserted into the graph if the node ~node~ is contracted. This variant computes the shortest path costs for all combinations of source and target using a bidirectional dijkstra restricted by hops and maxEdges. */ void computeShortCuts2(size_t node, size_t maxHopsF, size_t maxHopsB, std::vector& result, const size_t maxEdges, std::vector* finished, uint64_t& finishedMark){ result.clear(); std::list& preds1 = graph[node].second; std::list& succs1 = graph[node].first; static minPathCosts mpc; // remove duplicates from both lists, keep only such with minimum costs std::map preds; std::list::iterator itp1; for(itp1 = preds1.begin(); itp1!=preds1.end(); itp1++){ std::map::iterator i1 = preds.find(itp1->source); if(i1==preds.end()){ preds[itp1->source] = (*itp1); } else { if(i1->second.costs > itp1->costs){ preds[itp1->source] = *itp1; } } } std::map succs; std::list::iterator itp2; for(itp2 = succs1.begin(); itp2!=succs1.end(); itp2++){ std::map::iterator i2 = succs.find(itp2->target); if(i2==succs.end()){ succs[itp2->target] = *itp2; } else { if(i2->second.costs > itp2->costs){ succs[itp2->target] = *itp2; } } } std::map::iterator itp; for(itp = preds.begin(); itp != preds.end();itp++){ MEdge& pedge = itp->second; double c1 = pedge.costs; int s = pedge.source; std::map::iterator its; for(its=succs.begin();its!=succs.end();its++){ MEdge& sedge = its->second; int t = sedge.target; double c = c1 + sedge.costs; if(s!=t){ double spc = mpc(this,s,t,maxHopsF, maxHopsB,c, node,true, maxEdges, finished, finishedMark); finishedMark++; if(finishedMark==std::numeric_limits::max()){ for(size_t i=0;isize();i++){ (*finished)[i] =0; } finishedMark = 1; } if(spc > c){ // path longer than going over node result.push_back(createEdge(pedge.info, s, t, c)); } } } } } /* 2.40 ~insertShortCuts~ Inserts all edges in ~edges~ into this graph. */ void insertShortCuts(std::vector & edges){ for(size_t i = 0; i< edges.size();i++){ insert(edges[i]); } } /* 2.41 Auxiliary class ~simplePrioEntry~ This class represents a queue entry for contraction. */ // simple contraction as done in Script class simplePrioEntry{ public: simplePrioEntry(size_t _node): node(_node), prio(0.0) , reinsertions(0){} simplePrioEntry(size_t _node, double _prio): node(_node), prio(_prio),reinsertions(0){} bool operator<(const simplePrioEntry& e) const{ return prio > e.prio; } size_t node; double prio; size_t reinsertions; }; /* 2.42 ~simplePrioEntryComp~ Comparator class for simplePrioEntry. */ class simplePrioEntryComp{ public: bool operator()(const simplePrioEntry& f, const simplePrioEntry& s) const { return f.prio > s.prio; } }; /* 2.42 ~edgeDifferenceA~ Computes the edgeDifference in a simple way. It ignores common source and target nodes, i.e., it also counts edges having the same source and target. */ int edgeDifferenceA(int node) const{ const std::list& inL = graph[node].second; const std::list& outL = graph[node].first; int in = inL.size(); int out = outL.size(); return in*out - (in + out); } /* 2.43 ~edgeDifference~ Computes the maximum edgeDifference without counting loops of a single node. */ int edgeDifference(int node) const{ const std::list& inL = graph[node].second; const std::list& outL = graph[node].first; if(inL.empty()) return -outL.size(); if(outL.empty()) return -inL.size(); static std::set inS; inS.clear(); static std::list::const_iterator it; for(it = inL.begin();it!=inL.end();it++){ inS.insert(it->source); } static std::set outS; outS.clear(); for(it = outL.begin();it!=outL.end();it++){ outS.insert(it->target); } int in=0; int out=0; int inout = 0; static std::set::iterator it1; it1=inS.begin(); static std::set::iterator it2; it2 = outS.begin(); while(it1!=inS.end() && it2!=outS.end()){ if(*it1<*it2){ in++; it1++; } else if(*it1>*it2){ out++; it2++; } else { inout++; it1++; it2++; } } while(it1!=inS.end()){ in++; it1++;} while(it2!=outS.end()){ out++; it2++;} int create = (in+inout) * (out+inout) - inout; int remove = in + out + 2*inout; int prio = create - remove; return prio; } /* 2.44 simpleContraction1 This method contracts this graph using a two-step priority computation. */ size_t simpleContraction1(int maxPrio, size_t minBlockSize, int maxHopsF, int maxHopsB, std::vector& allShortCuts, size_t skipReinsert, const size_t maxEdges, std::vector* finished, uint64_t& finishedMark){ //std::cout << "called simple contraction" << std::endl; // initialize priority queue with all nodes and prio 0.0 // typedef mmheap::mmheap queue_t; typedef std::priority_queue queue_t; queue_t queue; for(size_t i=0;i shortcuts; allShortCuts.clear(); size_t removedEdges = 0; size_t reinits = 0; while(!queue.empty()){ size_t s = queue.size(); simplePrioEntry e = queue.top(); queue.pop(); size_t node = e.node; double prio = e.prio; int in = numPredecessor(node); int out = numSuccessor(node); if((in*out > prio) && (s >= skipReinsert)){ // reinsert level 1 e.prio = in*out; queue.push(e); } else { if(maxHopsB<=0){ computeShortCuts1(node, maxHopsF, shortcuts, maxEdges, finished, finishedMark); } else { computeShortCuts2(node, maxHopsF, maxHopsB,shortcuts, maxEdges, finished, finishedMark); } if( (in*out + (shortcuts.size() - (in + out)) > prio) && (shortcuts.size() > (size_t)(in+out)) && (s>=skipReinsert)){ // reinsert level 2 e.prio = (in*out + shortcuts.size()) - (in + out); queue.push(e); } else { // this is either the next node to contract or the node will // simplify the graph // do contraction insertShortCuts(shortcuts); disconnect(node); nodeOrder.push_back(node); removedEdges += (in + out); /* if(shortcuts.size() > (size_t)(in + out)){ cout << "insert " << shortcuts.size() << " shortcuts " << endl; cout << "removed " << in + out << " edges " << endl; } */ for(size_t i=0;imaxPrio && blockCount > minBlockSize){ reinits++; blockCount = 0; queue_t tmp; while(!queue.empty()){ simplePrioEntry e = queue.top(); queue.pop(); e.prio = 0; tmp.push(e); } std::swap(tmp,queue); //queue.swap(tmp); } p2++; // some progress counter if(p2==prog){ p2=0; cout << "."; cout.flush(); } } } } cout << "processed " << nodeOrder.size() << "nodes" << endl; cout << "shortcut edges " << allShortCuts.size() << endl; cout << "removed edges " << removedEdges << endl; cout << "number of reinitializations " << reinits << endl; return cs; } /* 2.45 ~simpleContraction2~ Contract this graph using edgeDifference and number of contracted neighbors as priority. */ size_t simpleContraction2(int maxPrio, size_t minBlockSize, int maxHopsF, int maxHopsB, std::vector& allShortCuts, size_t skipReinit, const size_t maxEdges, std::vector* finished, uint64_t& finishedMark){ //std::cout << "called simple contraction" << std::endl; // initialize priority queue with all nodes and prio 0.0 //typedef mmheap::mmheap queue_t; typedef std::priority_queue queue_t; // initialize a vector with number of contracted neighbors std::vector contractedNeighbors(graph.size(),0); int weight_EdgeDiff = 5; int weight_contractedNeighbors = 1; cout << "init prio" << endl; int minPrio = 1000000; int maxPrioQ = -1000000; queue_t queue; for(size_t i=0;i p) minPrio=p; if(maxPrioQ < p) maxPrioQ = p; } cout << "prio initialized" << endl; cout << "minPrio = " << minPrio; cout << "maxPrio = " << maxPrioQ; nodeOrder.clear(); size_t blockCount = 0; size_t cs = 0; // number of contraction edges size_t prog = queue.size() / 100; if(prog <2) prog = 2; size_t p2 = 0; std::cout << "start with queue of size " << queue.size() << std::endl; std::cout << "write a dot each " << prog << " processed nodes" << endl; std::vector shortcuts; allShortCuts.clear(); size_t removedEdges = 0; size_t reinits = 0; size_t maxShortcutsPerNode = 0; size_t sumreinsertions=0; size_t maxreinsertions=0; while(queue.size() > 1){ simplePrioEntry e = queue.top(); queue.pop(); size_t node = e.node; double prio = e.prio; int in = numPredecessor(node); int out = numSuccessor(node); int extra = edgeDifference(node); // Edge difference int nprio = weight_EdgeDiff*extra + weight_contractedNeighbors*contractedNeighbors[node]; if((nprio > prio) && (queue.size() >= skipReinit)){ e.prio = nprio; e.reinsertions++; queue.push(e); } else { // do contraction sumreinsertions += e.reinsertions; if(maxreinsertions < e.reinsertions){ maxreinsertions = e.reinsertions; assert(maxreinsertions < 400); // pure debug code } if(maxHopsB<=0){ computeShortCuts1(node, maxHopsF, shortcuts, maxEdges, finished, finishedMark); } else { computeShortCuts2(node, maxHopsF, maxHopsB,shortcuts, maxEdges, finished, finishedMark); } insertShortCuts(shortcuts); // update number of contracted neighbors static std::set neighbors; neighbors.clear(); static std::list::iterator it; for(it = graph[node].first.begin(); it!=graph[node].first.end(); it++){ int n = it->target; if(neighbors.find(n)==neighbors.end()){ neighbors.insert(n); contractedNeighbors[n]++; } } for(it = graph[node].second.begin(); it!=graph[node].second.end(); it++){ int n = it->source; if(neighbors.find(n)==neighbors.end()){ neighbors.insert(n); contractedNeighbors[n]++; } } disconnect(node); nodeOrder.push_back(node); removedEdges += (in + out); for(size_t i=0;imaxPrio) && (blockCount > minBlockSize)){ // reinit queue reinits++; blockCount = 0; queue_t tmp; while(!queue.empty()){ simplePrioEntry e = queue.top(); queue.pop(); e.prio = weight_EdgeDiff * edgeDifference(e.node) +weight_contractedNeighbors*contractedNeighbors[e.node]; tmp.push(e); } std::swap(tmp,queue); } p2++; // some progress counter if(p2==prog){ p2=0; cout << "."; cout.flush(); } } } if(queue.size()>0){ // process the last node simplePrioEntry e = queue.top(); // insert into node order nodeOrder.push_back(e.node); // remove edges graph[e.node].first.clear(); graph[e.node].second.clear(); } cout << "processed " << nodeOrder.size() << "nodes" << endl; cout << "shortcut edges " << allShortCuts.size() << endl; cout << "removed edges " << removedEdges << endl; cout << "reinitializations " << reinits << endl; cout << "maximum shortcuts per node " << maxShortcutsPerNode << endl; cout << "sum reinsertions " << sumreinsertions << endl; cout << "max reinsertions " << maxreinsertions << endl; return cs; } /* 2.46 Auxiliary class ~minPathCosts~ This class can be used to compute the costs between two nodes using a bidirectional dijkstra. */ class minPathCosts{ public: minPathCosts(){ } double operator()(MGraphCommon* _g, const int _source, const int _target, const int _maxHopsForward, const int _maxHopsBackward, const double _maxCosts, const int _forbidden, const bool _skipIfSmaller, const size_t _maxEdges, std::vector* finished, uint64_t finishedMark){ g = _g; source = _source; target = _target; maxHopsForward = _maxHopsForward; maxHopsBackward = _maxHopsBackward; maxCosts = _maxCosts; forbidden = _forbidden; skipIfSmaller = _skipIfSmaller; maxEdges = _maxEdges; double costs = std::numeric_limits::max(); if(_source < 0 || source>= g->graph.size()){ return costs; } if(_target<0 || target>=g->graph.size()){ return costs; } if(source == target){ return 0; } // clear all structures processedForward.clear(); processedBackward.clear(); frontForward.clear(); frontBackward.clear(); edges = 0; return compute(); } private: size_t source; size_t target; int maxHopsForward; int maxHopsBackward; double maxCosts; int forbidden; std::map processedForward; std::map processedBackward; //typedef std::priority_queue queue_t; typedef mmheap::mmheap queue_t; queue_t frontForward; queue_t frontBackward; bool skipIfSmaller; size_t edges; size_t maxEdges; MGraphCommon* g; double compute(){ queueentry ef(source,0,0); frontForward.push(ef); queueentry eb(target,0,0); frontBackward.push(eb); bool done = false; double costs = std::numeric_limits::max(); while((!frontForward.empty() || !frontBackward.empty()) && !done){ bool forward; queueentry e(0,0,0); if(frontForward.empty()){ e = frontBackward.top(); frontBackward.pop(); forward = false; } else if(frontBackward.empty()){ e = frontForward.top(); frontForward.pop(); forward = true; } else { queueentry f = frontForward.top(); queueentry b = frontBackward.top(); if(b.cost > f.cost){ e = f; frontForward.pop(); forward = true; } else { e = b; frontBackward.pop(); forward = false; } } if(skipIfSmaller){ if(costs < maxCosts){ // found a path shorter than maxcosts done = true; } } if(done || e.cost > maxCosts || e.cost > costs){ // maxcosts reached or current path cannot shortened done = true; } else { processNode(e, forward, costs, done); } } return costs; } void processNode(queueentry& e, bool forward, double& costs, bool& done){ int node = e.node; std::map* processed = forward?&processedForward :&processedBackward; if(processed->find(node) != processed->end()){ return; // already processed } (*processed)[node] = e.cost; // check whether we hit a processed node of the other direction std::map* processedReverse = !forward ?&processedForward :&processedBackward; std::map::iterator it = processedReverse->find(e.node); if(it != processedReverse->end()){ double x = it->second + e.cost; if(x < costs){ costs = x; } } // check whether maxHops are reached int maxHops = forward?maxHopsForward:maxHopsBackward; if((int)e.depth >= maxHops){ return; } // process edges if(edges >= maxEdges){ // maximum number of edges reached return; } std::list& nextNodes = forward?g->graph[node].first :g->graph[node].second; std::list::iterator itN; for(itN=nextNodes.begin(); (itN!=nextNodes.end()) && (edges<=maxEdges); itN++){ processEdge(*itN,forward,e); edges++; } } void processEdge(MEdge& edge, bool forward, queueentry& e){ int target = forward?edge.target:edge.source; if(target==forbidden) return; // do not use forbidden node std::map& processed = forward?processedForward :processedBackward; if(processed.find(target)!=processed.end()){ return; } queue_t& front = forward?frontForward :frontBackward; queueentry ne(target, e.cost + edge.costs, e.depth+1); front.push(ne); } }; // end of class minPathCosts }; } // end of namespace mm2algebra #endif