ELVE  1
ELVE Logic Visualization Explorer
/home/travis/build/stdgregwar/elve/Core/Graph.cpp
00001 #include "Graph.h"
00002 #include <string>
00003 #include "Node.h"
00004 
00005 #include <algorithm>
00006 #include <QDebug>
00007 #include <QJsonArray>
00008 #include "utils.h"
00009 
00010 namespace Elve {
00011 
00012 using namespace std;
00013 
00014 bool operator==(const Pin& a,const Pin& b) {
00015     return a.id == b.id && ((a.index == b.index) || a.index == -1 || b.index == -1);
00016 }
00017 
00018 void Aliases::reserve(size_t size) {
00019     inputs.reserve(size);
00020     outputs.reserve(size);
00021 }
00022 
00023 size_t Aliases::size() const {
00024     return inputs.size(); //pretty arbitrary, TODO think of a better solution
00025 }
00026 
00027 Pin::Pin(const NodeID& id) : id(id),index(-1){}
00028 Pin::Pin(const NodeID &id,const Index& index) : id(id), index(index){}
00029 
00030 Graph::Graph(const SharedData &data) : mData(data), mLastId(0)
00031 {
00032     for(const NodeData& d : data->nodeDatas()) {
00033         addNode(d);
00034     }
00035     for(const NodeData& d : data->nodeDatas()) {
00036         for(const Dependency& dep : d.dependencies()) {
00037             addEdge(dep.id,dep.from,d.id(),dep.to);
00038         }
00039     }
00040 }
00041 
00042 Graph::Graph(const SharedData& data, const SparseData &extraData, const Aliases &aliases, const NodeIDSet& excluded)
00043     : mData(data), mExtraData(extraData), mAliases(aliases), mExcluded(excluded), mLastId(0)
00044 {
00045     //Build groups data
00046     using pair_type = SparseData::value_type;
00047     for(const pair_type& p : mExtraData) {
00048         if(!mExcluded.count(p.first)) {
00049             addNode(p.second);
00050         }
00051     }
00052 
00053     for(const NodeData& d : data->nodeDatas()) {
00054         if(!mExcluded.count(d.id())) {
00055             addNode(d);
00056         }
00057     }
00058 
00059     for(const NodeData& d : data->nodeDatas()) {
00060         for(const Dependency& dep : d.dependencies()) {
00061             const Pin cpin = inputAlias({d.id(),dep.to});
00062             if(mExcluded.count(cpin.id)) {
00063                 continue;
00064             }
00065             const Pin dpin = outputAlias({dep.id,dep.from});
00066             if(!mExcluded.count(dpin.id)) {
00067                 addEdge(dpin.id,dpin.index,cpin.id,cpin.index);
00068             }
00069         }
00070     }
00071 }
00072 
00073 NodeID Graph::newID() const {
00074     return ++mLastId;
00075 }
00076 
00077 const QString& Graph::filename() const {
00078     return mData->mFilename;
00079 }
00080 
00081 const NodesByID& Graph::nodes() const {
00082     return mNodes;
00083 }
00084 
00085 size_t Graph::nodeCount() const {
00086     return mNodes.size();
00087 }
00088 
00089 const Pin Graph::inputAlias(const Pin &id) const {
00090     auto it = mAliases.inputs.find(id);
00091     if(it != mAliases.inputs.end())
00092         return inputAlias(it->second);
00093     return id;
00094 }
00095 
00096 const Pin Graph::outputAlias(const Pin &id) const {
00097     auto it = mAliases.outputs.find(id);
00098     if(it != mAliases.outputs.end())
00099         return outputAlias(it->second);
00100     return id;
00101 }
00102 
00103 Node* Graph::addNode(const NodeData& d) {
00104     auto pi = mNodes.emplace(d.id(),d);
00105 
00106     Node* n = &pi.first->second;
00107     if(d.type() == NodeType::OUTPUT or d.type() == NodeType::OUTPUT_CLUSTER) {
00108         mOutputs.push_back(n);
00109     } else if (d.type() == NodeType::INPUT or d.type() == NodeType::INPUT_CLUSTER) {
00110         mInputs.push_back(n);
00111     }
00112     if(n->id() > mLastId) mLastId = n->id();
00113     return n;
00114 }
00115 
00116 void Graph::addEdge(const NodeID& from, Index fi, const NodeID& to, Index ti) {
00117     if(from != to) {
00118         mNodes.at(from).addChild(&mNodes.at(to),fi,ti);
00119     } else {
00120         qDebug() << "will not make edge from" << from << fi << "to" << to << ti;
00121     }
00122 }
00123 
00124 const NodeData& Graph::data(const NodeID& id) const {
00125     if(id < mData->nodeDatas().size()) {
00126         return mData->nodeDatas().at(id);
00127     } else {
00128         return mExtraData.at(id);
00129     }
00130 }
00131 
00133 
00134 void _descend(const Node *n, vector<NodeIDSet> &sets, NodeIDSet &mainSet);
00135 
00136 void _collectClusterables(const Node* n, NodeIDSet& set, vector<NodeIDSet>& sets, NodeIDSet& mainSet) {
00137     set.insert(n->id());
00138     mainSet.insert(n->id());
00139     vector<const Node*> collected; collected.reserve(n->ancestors().size());
00140     for(const Node* cn : n->ancestors()) {
00141         if(std::all_of(cn->children().begin(),
00142                        cn->children().end(),
00143                        [&set](const Node* an){return set.count(an->id());})
00144                 and !cn->isInput() and !cn->isOutput() and !mainSet.count(cn->id())) {
00145             set.insert(cn->id()); //insert if all ancestors are in accumulator
00146             collected.push_back(cn);
00147             mainSet.insert(cn->id());
00148         } else {
00149             _descend(cn,sets,mainSet);
00150         }
00151     }
00152     for(const Node* cn : collected) { //doing breadth first visit
00153          _collectClusterables(cn,set,sets,mainSet);
00154     }
00155 }
00156 
00157 void _descend(const Node* n, vector<NodeIDSet>& sets, NodeIDSet& mainSet) {
00158     if(!mainSet.count(n->id())) {
00159         NodeIDSet collect; collect.reserve(20);
00160         _collectClusterables(n,collect,sets,mainSet);
00161         sets.push_back(collect);
00162     }
00163 }
00164 
00165 SharedGraph Graph::clusterize(size_t level) {
00166     if(level == 0) {
00167         return shared_from_this();
00168     }
00169     vector<NodeIDSet> sets;
00170     NodeIDSet mainSet; mainSet.reserve(nodeCount());
00171     for(Node* out : outputs()) {
00172         //qDebug() << "output" << out->name().c_str();
00173         NodeIDSet collect;
00174         _collectClusterables(out,collect,sets,mainSet);
00175         collect.erase(out->id());
00176         sets.push_back(collect);
00177     }
00178     SharedGraph g = shared_from_this();
00179     int i = 0;
00180     g = g->fastGroup(sets,"cluster");
00181     qDebug() << "shrinked a" << nodeCount() << "nodes graph to a" << g->nodeCount() << "one";
00182     return g->clusterize(level-1); //Does not do anything...
00183 }
00184 
00186 
00187 NodeName Graph::uniqueName(const NodeName& base) const {
00188     NodeName current = base;
00189     unsigned suffix = 1;
00190     repeat:
00191     for(const auto& p : mNodes) {
00192         const NodeName& id = p.second.name();
00193         if(id == current) {
00194            current = base + "_" + std::to_string(suffix++);
00195            goto repeat;
00196         }
00197     }
00198     return current;
00199 }
00200 
00201 const Node& Graph::node(const NodeID& id) const {
00202     return mNodes.at(id);
00203 }
00204 
00205 SharedGraph Graph::ungroup(const NodeID &cluster) {
00206     if(!mExtraData.count(cluster)) {
00207         return shared_from_this();
00208     }
00209     SparseData extras(mExtraData);
00210     NodeIDSet excl = excludedWithout(extras.at(cluster).dependencies());
00211     extras.erase(cluster);
00212     return make_shared<Graph>(mData,extras,aliasesWithout(cluster),excl);
00213 }
00214 
00215 SharedGraph Graph::fastGroup(const vector<NodeIDSet>& groups, const NodeName& basename) {
00216     //Gather stats
00217     size_t contentSize = 0;
00218     for(const NodeIDSet& group : groups) {
00219         contentSize += group.size();
00220     }
00221 
00222     SparseData extra; extra.reserve(mExtraData.size()+groups.size());
00223 
00224     using pair_type = SparseData::value_type;
00225     for(const pair_type& p : mExtraData) {
00226         extra.emplace(p.first,p.second);
00227     }
00228 
00229     Aliases aliases(mAliases); aliases.reserve(aliases.size()+contentSize);
00230     NodeIDSet excluded(mExcluded); excluded.reserve(mExcluded.size()+contentSize);
00231 
00232 
00233     for(const NodeIDSet& group : groups) {
00234         NodeID i = newID();
00235         if(group.size() < 2) { //Ignore groups of one or less elements
00236             continue;
00237         }
00238         vector<Name> inputs;
00239         vector<Name> outputs;
00240         Dependencies deps; deps.reserve(group.size());
00241         deps.insert(deps.end(),group.begin(),group.end());
00242 
00243         float av_index = 0;
00244         for(const NodeID& id : group) {
00245             const Node& n = node(id);
00246             const NodeData& nd = n.data();
00247             for(const Node::Connexion& c : n.fanIn()) {
00248                 if(!group.count(c.node->id())) { //Ancestor is outside of the group
00249                     //Add input to node
00250                     int inputNum = lazy_add(inputs,n.name());
00251                     aliases.inputs.emplace(Pin(id,c.to),Pin(i,inputNum));
00252                 }
00253             }
00254             for(const Node::Connexion& c : n.fanOut()) {
00255                 if(!group.count(c.node->id())) { //Ancestor is outside of the group
00256                     //Add input to node
00257                     int outputNum = lazy_add(outputs,n.name());
00258                     aliases.outputs.emplace(Pin(id,c.from),Pin(i,outputNum));
00259                 }
00260             }
00261             //aliases.inputs.emplace(id,i); //TODO : fix this uglyness
00262             //aliases.outputs.emplace(id,i);
00263             excluded.insert(id);
00264             av_index += data(id).ioIndex();
00265         }
00266         av_index /= group.size();
00267         Names inputNames; inputNames.reserve(inputs.size());
00268         Names outputNames; outputNames.reserve(outputs.size());
00269         inputNames.insert(inputNames.begin(),inputs.begin(),inputs.end());
00270         outputNames.insert(outputNames.begin(),outputs.begin(),outputs.end());
00271 
00272         const NodeData& first = data(*group.begin());
00273         NodeType t = CLUSTER;
00274         switch (first.type()) {
00275         case INPUT:
00276         case INPUT_CLUSTER:
00277             t = INPUT_CLUSTER;
00278             break;
00279         case OUTPUT:
00280         case OUTPUT_CLUSTER:
00281             t = OUTPUT_CLUSTER;
00282             break;
00283         default:
00284             break;
00285         }
00286 
00287         extra.emplace(i,NodeData(i,basename + to_string(i),deps,t,{},av_index,inputs.size(),outputs.size(),inputNames,outputNames));
00288         //i++;
00289     }
00290     return make_shared<Graph>(mData,extra,aliases,excluded);
00291 }
00292 
00293 SharedGraph Graph::group(const NodeIDSet &toGroup, const NodeID& i,const NodeName &groupName) {
00294     if(toGroup.size() < 2) {
00295         return shared_from_this();
00296     }
00297 
00298     return fastGroup({toGroup},groupName);
00299 }
00300 
00301 const NodePtrs& Graph::inputs() {
00302     return mInputs;
00303 }
00304 
00305 const NodePtrs& Graph::outputs() {
00306     return mOutputs;
00307 }
00308 
00309 const SharedData& Graph::datas() const {
00310     return mData;
00311 }
00312 const SparseData& Graph::extraData() const {
00313     return mExtraData;
00314 }
00315 const Aliases& Graph::aliases() const {
00316     return mAliases;
00317 }
00318 
00319 const NodeIDSet Graph::excluded() const {
00320     return mExcluded;
00321 }
00322 
00323 size_t Graph::inputCount() const
00324 {
00325     return mInputs.size();
00326 }
00327 
00328 size_t Graph::outputCount() const
00329 {
00330     return mOutputs.size();
00331 }
00332 
00333 size_t Graph::maxInputIndex() const
00334 {
00335     size_t i = 0;
00336     for(const Node* in : mInputs) {
00337         if(in->IOIndex() > i) {
00338             i = in->IOIndex();
00339         }
00340     }
00341     return i == 0 ? 1 : i;
00342 }
00343 
00344 size_t Graph::maxOutputIndex() const
00345 {
00346     size_t i = 0;
00347     for(const Node* in : mOutputs) {
00348         if(in->IOIndex() > i) {
00349             i = in->IOIndex();
00350         }
00351     }
00352     return i == 0 ? 1 : i;
00353 }
00354 
00355 QJsonObject Graph::json() const
00356 {
00357     QJsonObject main;
00358     main.insert("graph_data",mData->json());
00359 
00360     //Todo insert clustering details here
00361 
00362     {
00363         QJsonArray extra;
00364         using pair_type = SparseData::value_type;
00365         for(const pair_type& p : mExtraData) {
00366             extra.append(p.second.json());
00367         }
00368         main.insert("extra_data",extra);
00369     }
00370     {
00371         {
00372             QJsonArray als;
00373             using pair_type = AliasesMap::value_type;
00374             for(const pair_type& p : mAliases.inputs) {
00375                 als.append(QJsonArray{(int)p.first.id,(int)p.first.index,(int)p.second.id,(int)p.second.index});
00376             }
00377             main.insert("inputAliases",als);
00378         }
00379         {
00380             QJsonArray als;
00381             using pair_type = AliasesMap::value_type;
00382             for(const pair_type& p : mAliases.outputs) {
00383                 als.append(QJsonArray{(int)p.first.id,(int)p.first.index,(int)p.second.id,(int)p.second.index});
00384             }
00385             main.insert("outputAliases",als);
00386         }
00387 
00388     }
00389     QJsonArray excl;
00390     for(const NodeID& id : mExcluded) {
00391         excl.append((int)id);
00392     }
00393     main.insert("excluded",excl);
00394     return main;
00395 }
00396 
00397 NodeLevel Graph::highestLevel() const {
00398     using pair_type = NodesByID::value_type;
00399 
00400     if(mNodes.empty()) return 0;
00401     return max_element(mNodes.begin(),mNodes.end(),
00402         [](const pair_type& a, const pair_type& b)->bool {
00403             return a.second.level() < b.second.level();
00404         })->second.level();
00405 }
00406 
00407 SharedGraph Graph::fromJson(const QJsonObject& obj)
00408 {
00409     SharedData sdata = make_shared<GraphData>(obj.value("graph_data").toObject());
00410     //Todo find clustering primitives here
00411     SparseData extra;
00412     QJsonArray jextr = obj.value("extra_data").toArray();
00413     extra.reserve(jextr.size());
00414     for(const QJsonValue& v : jextr) {
00415         QJsonObject obj = v.toObject();
00416         extra.emplace(obj.value("id").toInt(),obj);
00417     }
00418     Aliases als;
00419     { //Handle input aliases
00420         QJsonArray jals = obj.value("inputAliases").toArray();
00421         als.inputs.reserve(jals.size());
00422         for(const QJsonValue& v : jals) {
00423             QJsonArray pair = v.toArray();
00424             als.inputs.emplace(Pin(pair.at(0).toInt(-1),pair.at(1).toInt(-1)),
00425                         Pin(pair.at(2).toInt(-1),pair.at(3).toInt(-1)));
00426         }
00427     }
00428     { //Handle output aliases
00429         QJsonArray jals = obj.value("outputAliases").toArray();
00430         als.outputs.reserve(jals.size());
00431         for(const QJsonValue& v : jals) {
00432             QJsonArray pair = v.toArray();
00433             als.outputs.emplace(Pin(pair.at(0).toInt(-1),pair.at(1).toInt(-1)),
00434                         Pin(pair.at(2).toInt(-1),pair.at(3).toInt(-1)));
00435         }
00436     }
00437     NodeIDSet excl;
00438     QJsonArray jexcl = obj.value("excluded").toArray();
00439     excl.reserve(jexcl.size());
00440     for(const QJsonValue& v : jexcl) {
00441         excl.insert(v.toInt());
00442     }
00443     return make_shared<Graph>(sdata,extra,als,excl);
00444 }
00445 
00446 Aliases Graph::aliasesWithout(const NodeID& repl) const {
00447     Aliases als;
00448     for(const auto& p : mAliases.inputs) {
00449         if(p.second.id != repl) {
00450             als.inputs.insert(p);
00451         }
00452     }
00453     for(const auto& p : mAliases.outputs) {
00454         if(p.second.id != repl) {
00455             als.outputs.insert(p);
00456         }
00457     }
00458     return als;
00459 }
00460 
00461 NodeIDSet Graph::excludedWithout(const Dependencies& ids) const {
00462     NodeIDSet excl(mExcluded);
00463     for(const Dependency& dep : ids) {
00464         excl.erase(dep.id);
00465     }
00466     return excl;
00467 }
00468 
00469 }
 All Classes Functions