Vuo  2.0.0
VuoCompilerGraph.cc
Go to the documentation of this file.
1 
10 #include "VuoCable.hh"
11 #include "VuoCompiler.hh"
12 #include "VuoCompilerGraph.hh"
13 #include "VuoCompilerCable.hh"
14 #include "VuoCompilerChain.hh"
16 #include "VuoCompilerException.hh"
18 #include "VuoCompilerIssue.hh"
19 #include "VuoCompilerNode.hh"
25 #include "VuoComposition.hh"
26 #include "VuoNode.hh"
27 #include "VuoNodeClass.hh"
28 #include "VuoPublishedPort.hh"
29 #include "VuoStringUtilities.hh"
30 #include <sstream>
31 
42 VuoCompilerGraph::VuoCompilerGraph(VuoCompilerComposition *composition, VuoCompiler *compiler, set<VuoCompilerCable *> potentialCables)
43 {
44  VuoCompilerNode *publishedInputNode = nullptr;
45  VuoCompilerNode *publishedOutputNode = nullptr;
46  VuoCompilerNode *publishedInputTriggerNode = nullptr;
47  bool ownsPublishedNodeClasses = false;
48 
49  VuoCompilerNodeClass *triggerNodeClass = nullptr;
50  if (compiler)
51  {
52  triggerNodeClass = compiler->getNodeClass("vuo.event.spinOffEvent2");
53  }
54  else
55  {
56  VuoPortClass *refreshPortClass = (new VuoCompilerInputEventPortClass("refresh"))->getBase();
57  VuoCompilerTriggerPortClass *triggerPortClass = new VuoCompilerTriggerPortClass("spunOff", nullptr);
58  vector<VuoPortClass *> inputPortClasses;
59  vector<VuoPortClass *> outputPortClasses(1, triggerPortClass->getBase());
60  VuoNodeClass *baseNodeClass = new VuoNodeClass("vuo.event.spinOffEvent2", refreshPortClass, inputPortClasses, outputPortClasses);
61  triggerNodeClass = VuoCompilerNodeClass::newNodeClassWithoutImplementation(baseNodeClass)->getCompiler();
62  }
63 
64  vector<VuoPublishedPort *> publishedInputPorts = composition->getBase()->getPublishedInputPorts();
65  vector<VuoPublishedPort *> publishedOutputPorts = composition->getBase()->getPublishedOutputPorts();
66 
67  if (! (publishedInputPorts.empty() && publishedOutputPorts.empty()) )
68  {
69  if (compiler)
70  {
71  publishedInputNode = compiler->createPublishedInputNode(publishedInputPorts)->getCompiler();
72  publishedOutputNode = compiler->createPublishedOutputNode(publishedOutputPorts)->getCompiler();
73 
74  ownsPublishedNodeClasses = false;
75  }
76  else
77  {
78  VuoNodeClass *publishedInputNodeClass = VuoCompilerPublishedInputNodeClass::newNodeClass(publishedInputPorts);
79  VuoNodeClass *publishedOutputNodeClass = VuoCompilerPublishedOutputNodeClass::newNodeClass(publishedOutputPorts);
80  publishedInputNode = publishedInputNodeClass->getCompiler()->newNode()->getCompiler();
81  publishedOutputNode = publishedOutputNodeClass->getCompiler()->newNode()->getCompiler();
82 
83  ownsPublishedNodeClasses = true;
84  }
85 
86  publishedInputTriggerNode = triggerNodeClass->newNode()->getCompiler();
88  }
89 
90  VuoCompilerNode *manuallyFirableTriggerNode = triggerNodeClass->newNode()->getCompiler();
92 
93  initializeInstance(composition, potentialCables, publishedInputNode, publishedOutputNode, publishedInputTriggerNode, ownsPublishedNodeClasses,
94  manuallyFirableTriggerNode);
95 }
96 
119 void VuoCompilerGraph::initializeInstance(VuoCompilerComposition *composition, set<VuoCompilerCable *> potentialCables,
120  VuoCompilerNode *publishedInputNode, VuoCompilerNode *publishedOutputNode,
121  VuoCompilerNode *publishedInputTriggerNode, bool ownsPublishedNodeClasses,
122  VuoCompilerNode *manuallyFirableTriggerNode)
123 {
124  this->publishedInputNode = publishedInputNode;
125  this->publishedOutputNode = publishedOutputNode;
126  this->ownsPublishedNodeClasses = ownsPublishedNodeClasses;
127  this->publishedInputTrigger = nullptr;
128 
129  // Nodes in the composition
130 
131  set<VuoNode *> nodesIncludingInvalid = composition->getBase()->getNodes();
132  set<VuoNode *> nodesToAnalyze;
133  for (VuoNode *node : nodesIncludingInvalid)
134  if (node->hasCompiler()) // Ignore nodes with missing node classes.
135  nodesToAnalyze.insert(node);
136 
137  if (publishedInputNode && publishedOutputNode)
138  {
139  // Published input/output nodes
140 
141  nodesToAnalyze.insert(publishedInputNode->getBase());
142  nodesToAnalyze.insert(publishedOutputNode->getBase());
143 
144  // `Spin Off Event` node for published inputs
145 
146  nodesToAnalyze.insert(publishedInputTriggerNode->getBase());
147 
148  VuoPort *publishedInputTriggerPort = publishedInputTriggerNode->getBase()->getOutputPorts().at(VuoNodeClass::unreservedOutputPortStartIndex);
149  this->publishedInputTrigger = static_cast<VuoCompilerTriggerPort *>(publishedInputTriggerPort->getCompiler());
150  }
151 
152  // `Spin Off Event` node for manually firable trigger
153 
154  nodesToAnalyze.insert(manuallyFirableTriggerNode->getBase());
155 
156  VuoPort *manuallyFirableTriggerPort = manuallyFirableTriggerNode->getBase()->getOutputPorts().at(VuoNodeClass::unreservedOutputPortStartIndex);
157  this->manuallyFirableTrigger = static_cast<VuoCompilerTriggerPort *>(manuallyFirableTriggerPort->getCompiler());
158 
159  // Internal cables in the composition
160 
161  set<VuoPort *> potentialCableInputs;
162  for (VuoCompilerCable *cable : potentialCables)
163  if (cable->carriesData())
164  potentialCableInputs.insert(cable->getBase()->getToPort());
165 
166  set<VuoCable *> cablesIncludingInvalidAndPublished = composition->getBase()->getCables();
167  set<VuoCable *> cablesToAnalyze;
168  for (VuoCable *cable : cablesIncludingInvalidAndPublished)
169  {
170  if ((cable->getFromPort() && cable->getToPort()) // Ignore disconnected cables.
171  && (cable->getFromPort()->hasCompiler() && cable->getToPort()->hasCompiler()) // Ignore cables on nodes with missing node classes.
172  && ! (cable->hasCompiler() && cable->getCompiler()->carriesData() &&
173  potentialCableInputs.find(cable->getToPort()) != potentialCableInputs.end()) ) // Ignore displaced cables.
174  {
175  if (cable->isPublished())
176  publishedCables.insert(cable);
177  else
178  cablesToAnalyze.insert(cable);
179  }
180  }
181 
182  // Potential internal cables
183 
184  for (VuoCompilerCable *cable : potentialCables)
185  {
186  if (cable->getBase()->isPublished())
187  publishedCables.insert(cable->getBase());
188  else
189  cablesToAnalyze.insert(cable->getBase());
190  }
191 
192  // Cables connected to published input/output nodes
193 
194  vector<VuoPublishedPort *> publishedInputPorts = composition->getBase()->getPublishedInputPorts();
195  vector<VuoPublishedPort *> publishedOutputPorts = composition->getBase()->getPublishedOutputPorts();
196  for (VuoCable *cable : publishedCables)
197  {
198  VuoNode *fromNode = nullptr;
199  VuoNode *toNode = nullptr;
200  VuoPort *fromPort = nullptr;
201  VuoPort *toPort = nullptr;
202 
203  if (cable->isPublishedInputCable())
204  {
205  size_t publishedPortIndex = std::distance(publishedInputPorts.begin(), std::find(publishedInputPorts.begin(), publishedInputPorts.end(), cable->getFromPort()));
206 
207  fromNode = publishedInputNode->getBase();
208  fromPort = getOutputPortOnPublishedInputNode(publishedPortIndex);
209  toNode = cable->getToNode();
210  toPort = cable->getToPort();
211  }
212  else
213  {
214  size_t publishedPortIndex = std::distance(publishedOutputPorts.begin(), std::find(publishedOutputPorts.begin(), publishedOutputPorts.end(), cable->getToPort()));
215 
216  fromNode = cable->getFromNode();
217  fromPort = cable->getFromPort();
218  toNode = publishedOutputNode->getBase();
219  toPort = getInputPortOnPublishedOutputNode(publishedPortIndex);
220  }
221 
222  VuoCompilerCable *replacement = new VuoCompilerCable(fromNode->getCompiler(), static_cast<VuoCompilerPort *>(fromPort->getCompiler()),
223  toNode->getCompiler(), static_cast<VuoCompilerPort *>(toPort->getCompiler()), false);
224  replacement->setAlwaysEventOnly(cable->getCompiler()->getAlwaysEventOnly());
225  cablesToAnalyze.insert(replacement->getBase());
226  }
227 
228  // Cable from `Spin Off Event` to published input node
229 
230  if (publishedInputTrigger)
231  {
232  VuoPort *toPort = publishedInputNode->getBase()->getInputPorts().at(0);
233  VuoCompilerCable *spinOffCable = new VuoCompilerCable(publishedInputTriggerNode, publishedInputTrigger,
234  publishedInputNode, static_cast<VuoCompilerPort *>(toPort->getCompiler()), false);
235  cablesToAnalyze.insert(spinOffCable->getBase());
236  }
237 
238  // Cable from `Spin Off Event` to manually firable input port
239 
240  {
241  VuoNode *toNode = composition->getManuallyFirableInputNode();
242  VuoPort *toPort = composition->getManuallyFirableInputPort();
243  if (toNode && toPort)
244  {
245  VuoCompilerCable *spinOffCable = new VuoCompilerCable(manuallyFirableTriggerNode, manuallyFirableTrigger,
246  toNode->getCompiler(), static_cast<VuoCompilerPort *>(toPort->getCompiler()), false);
247  cablesToAnalyze.insert(spinOffCable->getBase());
248  }
249  }
250 
251  for (VuoNode *node : nodesToAnalyze)
252  nodes.insert(node->getCompiler());
253 
254  makeTriggers(nodesToAnalyze);
255  makeVerticesAndEdges(cablesToAnalyze);
256  makeDownstreamVertices();
257  sortVertices();
258  makeVertexDistances();
259  makeDownstreamNodesViaDataOnlyTransmission(nodesToAnalyze, cablesToAnalyze);
260 }
261 
266 {
267  VuoCompilerNode *publishedInputTriggerNode = nodeForTrigger[publishedInputTrigger];
268 
269  if (ownsPublishedNodeClasses)
270  {
271  delete publishedInputNode->getBase()->getNodeClass()->getCompiler();
272  delete publishedOutputNode->getBase()->getNodeClass()->getCompiler();
273  delete publishedInputTriggerNode->getBase()->getNodeClass()->getCompiler();
274  }
275 
276  delete publishedInputNode;
277  delete publishedOutputNode;
278  delete publishedInputTriggerNode;
279 }
280 
284 void VuoCompilerGraph::makeTriggers(set<VuoNode *> nodes)
285 {
286  for (VuoNode *node : nodes)
287  {
288  for (VuoPort *port : node->getOutputPorts())
289  {
290  VuoCompilerTriggerPort *trigger = dynamic_cast<VuoCompilerTriggerPort *>(port->getCompiler());
291  if (trigger)
292  {
293  nodeForTrigger[trigger] = node->getCompiler();
294  triggers.push_back(trigger);
295  }
296  }
297  }
298 }
299 
303 void VuoCompilerGraph::makeVerticesAndEdges(const set<VuoCable *> &cables)
304 {
305  // Create vertices to visit for all cables.
306 
307  set<Vertex> allVertices;
308  for (VuoCable *cable : cables)
309  {
310  VuoCompilerPort *fromPort = cable->getFromPort() ? static_cast<VuoCompilerPort *>(cable->getFromPort()->getCompiler()) : nullptr;
311  VuoCompilerTriggerPort *fromTrigger = dynamic_cast<VuoCompilerTriggerPort *>(fromPort);
312  VuoCompilerNode *fromNode = cable->getFromNode()->getCompiler();
313  VuoCompilerNode *toNode = cable->getToNode()->getCompiler();
314  Vertex vertex = (fromTrigger ? Vertex(fromTrigger, toNode) : Vertex(fromNode, toNode));
315 
316  set<Vertex>::iterator vertexIter = allVertices.find(vertex);
317  if (vertexIter != allVertices.end())
318  {
319  vertex.cableBundle = (*vertexIter).cableBundle;
320  allVertices.erase(vertexIter);
321  }
322  vertex.cableBundle.insert(cable->getCompiler());
323 
324  allVertices.insert(vertex);
325  }
326 
327  // For each trigger, add all vertices reachable from it.
328 
329  for (VuoCompilerTriggerPort *trigger : triggers)
330  {
331  set<Vertex> verticesToVisit;
332  set<Edge> edgesVisited;
333 
334  for (set<Vertex>::iterator j = allVertices.begin(); j != allVertices.end(); ++j)
335  if ((*j).fromTrigger == trigger)
336  verticesToVisit.insert(*j);
337 
338  map<VuoCompilerNode *, set<Vertex> > outgoingVerticesForNode; // Cached data to speed up search for outgoing vertices.
339  for (set<Vertex>::iterator j = allVertices.begin(); j != allVertices.end(); ++j)
340  outgoingVerticesForNode[(*j).fromNode].insert(*j);
341 
342  while (! verticesToVisit.empty())
343  {
344  Vertex vertex = *verticesToVisit.begin();
345  if (find(vertices[trigger].begin(), vertices[trigger].end(), vertex) == vertices[trigger].end())
346  vertices[trigger].push_back(vertex);
347  verticesToVisit.erase(verticesToVisit.begin());
348 
349  set<Vertex> potentialOutgoingVertices = outgoingVerticesForNode[vertex.toNode];
350  for (set<Vertex>::iterator j = potentialOutgoingVertices.begin(); j != potentialOutgoingVertices.end(); ++j)
351  {
352  Vertex outgoingVertex = *j;
353 
354  if (mayTransmit(vertex.cableBundle, outgoingVertex.cableBundle))
355  {
356  Edge outgoingEdge(vertex, outgoingVertex);
357  if (edgesVisited.find(outgoingEdge) == edgesVisited.end()) // Avoid infinite feedback loops.
358  {
359  edges[trigger].insert(outgoingEdge);
360  edgesVisited.insert(outgoingEdge);
361 
362  verticesToVisit.insert(outgoingVertex);
363  }
364  }
365  }
366  }
367  }
368 }
369 
374 void VuoCompilerGraph::makeDownstreamVerticesWithInclusionRule(VuoCompilerTriggerPort *trigger, std::function<bool(Edge)> includeEdge,
375  map<Vertex, set<Vertex> > &_downstreamVertices,
376  set<Vertex> &_repeatedVertices)
377 {
378  list<Vertex> verticesToVisit; // Used as a stack, except for a call to find().
379 
380  for (Vertex vertex : vertices[trigger])
381  if (vertex.fromTrigger == trigger)
382  verticesToVisit.push_back(vertex);
383 
384  map<Vertex, set<Vertex> > outgoingVerticesFromVertex; // Cached data to speed up search for outgoing vertices.
385  for (Edge edge : edges[trigger])
386  if (includeEdge(edge))
387  outgoingVerticesFromVertex[edge.fromVertex].insert(edge.toVertex);
388 
389  while (! verticesToVisit.empty())
390  {
391  // Visit the vertex at the top of the stack.
392  Vertex currentVertex = verticesToVisit.back();
393 
394  set<Vertex> currentDownstreamVertices;
395  bool areDownstreamVerticesComplete = true;
396 
397  // Consider each vertex immediately downstream of this vertex.
398  set<Vertex> outgoingVertices = outgoingVerticesFromVertex[currentVertex];
399  for (set<Vertex>::iterator j = outgoingVertices.begin(); j != outgoingVertices.end(); ++j)
400  {
401  Vertex outgoingVertex = *j;
402  currentDownstreamVertices.insert(outgoingVertex);
403 
404  if (_downstreamVertices.find(outgoingVertex) != _downstreamVertices.end())
405  {
406  // The downstream vertex has already been visited, so add its downstream vertices to this vertex's.
407  set<Vertex> furtherDownstreamVertices = _downstreamVertices[outgoingVertex];
408  currentDownstreamVertices.insert( furtherDownstreamVertices.begin(), furtherDownstreamVertices.end() );
409  }
410  else
411  {
412  if (find(verticesToVisit.begin(), verticesToVisit.end(), outgoingVertex) != verticesToVisit.end())
413  {
414  // The downstream vertex is already on the stack, so it's an infinite feedback loop.
415  _repeatedVertices.insert(outgoingVertex);
416  }
417  else
418  {
419  // The downstream vertex has not yet been visited, so add it to the stack.
420  verticesToVisit.push_back(outgoingVertex);
421  areDownstreamVerticesComplete = false;
422  }
423  }
424  }
425 
426  if (areDownstreamVerticesComplete)
427  {
428  _downstreamVertices[currentVertex] = currentDownstreamVertices;
429  verticesToVisit.pop_back();
430  }
431  }
432 
433  // Clean up repeatedVertices so that it only contains vertices within an infinite feedback loop,
434  // not other outgoing vertices from nodes in the infinite feedback loop.
435  set<Vertex> repeatedVerticesCopy = _repeatedVertices;
436  for (Vertex vertex : repeatedVerticesCopy)
437  if (_downstreamVertices[vertex].find(vertex) == _downstreamVertices[vertex].end())
438  _repeatedVertices.erase(vertex);
439 }
440 
444 void VuoCompilerGraph::makeDownstreamVertices(void)
445 {
446  auto includeAllEdges = [] (Edge edge) { return true; };
447 
448  for (VuoCompilerTriggerPort *trigger : triggers)
449  {
450  makeDownstreamVerticesWithInclusionRule(trigger, includeAllEdges, downstreamVertices[trigger], repeatedVertices[trigger]);
451 
452  if (repeatedVertices[trigger].empty())
453  repeatedVertices.erase(trigger);
454  }
455 
456  // For each trigger, add a cable and vertex from each leaf vertex to the published output node's gather port.
457  // - For the published input trigger and triggers that spin off events from it, this ensures that the
458  // composition doesn't notify its runner (for top-level compositions) or parent composition (for subcompositions)
459  // until the event has finished propagating through the composition.
460  // - For internal triggers that reach a published output, this ensures that published trigger doesn't fire
461  // until the event has finished propagating through the composition.
462 
463  if (publishedOutputNode)
464  {
465  for (VuoCompilerTriggerPort *trigger : triggers)
466  {
467  for (const map<Vertex, set<Vertex> >::value_type &i : downstreamVertices[trigger])
468  {
469  if (i.first.toNode != publishedOutputNode && i.second.empty())
470  {
471  Vertex leafVertex = i.first;
472 
473  VuoPort *toPort = getGatherPortOnPublishedOutputNode();
474  VuoCompilerCable *gather = new VuoCompilerCable(leafVertex.toNode, nullptr,
475  publishedOutputNode, static_cast<VuoCompilerPort *>(toPort->getCompiler()), false);
476 
477  Vertex gatherVertex(leafVertex.toNode, publishedOutputNode);
478  gatherVertex.cableBundle.insert(gather);
479 
480  Edge gatherEdge(leafVertex, gatherVertex);
481 
482  vertices[trigger].push_back(gatherVertex);
483  edges[trigger].insert(gatherEdge);
484  downstreamVertices[trigger][leafVertex].insert(gatherVertex);
485  }
486  }
487  }
488  }
489 }
490 
494 void VuoCompilerGraph::sortVertices(void)
495 {
496  for (map<VuoCompilerTriggerPort *, vector<Vertex> >::iterator i = vertices.begin(); i != vertices.end(); ++i)
497  {
498  VuoCompilerTriggerPort *trigger = i->first;
499  vector<Vertex> verticesToSort = i->second;
500 
501  map<Vertex, set<Vertex> > dependentVertices;
502  list<Vertex> verticesToVisit; // Used as a stack, except for a call to find().
503  map<Vertex, bool> verticesCompleted;
504 
505  for (vector<Vertex>::iterator j = verticesToSort.begin(); j != verticesToSort.end(); ++j)
506  if ((*j).fromTrigger == trigger)
507  verticesToVisit.push_back(*j);
508 
509  map<VuoCompilerNode *, set<Vertex> > outgoingVerticesForNode; // Cached data to speed up topological sort.
510  for (vector<Vertex>::iterator j = verticesToSort.begin(); j != verticesToSort.end(); ++j)
511  outgoingVerticesForNode[(*j).fromNode].insert(*j);
512 
513  while (! verticesToVisit.empty())
514  {
515  // Visit the vertex at the top of the stack.
516  Vertex currentVertex = verticesToVisit.back();
517 
518  set<Vertex> currentDependentVertices;
519  bool areDependentVerticesComplete = true;
520 
521  // Form a list of vertices immediately dependent on this vertex's to-node.
522  // This includes vertices that are not downstream of this vertex because of a wall.
523  // But, if this vertex is at the end of a feedback loop, it doesn't include vertices
524  // beyond the end of the feedback loop.
525  set<Vertex> outgoingVertices;
526  set<Vertex> potentialOutgoingVertices = outgoingVerticesForNode[currentVertex.toNode];
527  for (set<Vertex>::iterator j = potentialOutgoingVertices.begin(); j != potentialOutgoingVertices.end(); ++j)
528  {
529  Vertex outgoingVertex = *j;
530  if (downstreamVertices[trigger][outgoingVertex].find(currentVertex) == downstreamVertices[trigger][outgoingVertex].end())
531  outgoingVertices.insert(outgoingVertex);
532  else
533  {
534  outgoingVertices.clear();
535  break;
536  }
537  }
538 
539  for (set<Vertex>::iterator j = outgoingVertices.begin(); j != outgoingVertices.end(); ++j)
540  {
541  Vertex outgoingVertex = *j;
542  currentDependentVertices.insert(outgoingVertex);
543 
544  if (verticesCompleted[outgoingVertex])
545  {
546  // The dependent vertex has already been visited, so add its dependent vertices to this vertex's.
547  currentDependentVertices.insert( dependentVertices[outgoingVertex].begin(), dependentVertices[outgoingVertex].end() );
548  }
549  else if (find(verticesToVisit.begin(), verticesToVisit.end(), outgoingVertex) == verticesToVisit.end())
550  {
551  // The dependent vertex has not yet been visited, so add it to the stack.
552  verticesToVisit.push_back(outgoingVertex);
553  areDependentVerticesComplete = false;
554  }
555  }
556 
557  if (areDependentVerticesComplete)
558  {
559  dependentVertices[currentVertex] = currentDependentVertices;
560  verticesToVisit.pop_back();
561  verticesCompleted[currentVertex] = true;
562  }
563  }
564 
565  // Put the vertices in descending order of the number of vertices that can't be reached until after the vertex.
566  vector< pair<size_t, Vertex> > verticesAndDependents;
567  for (map<Vertex, set<Vertex> >::iterator j = dependentVertices.begin(); j != dependentVertices.end(); ++j)
568  verticesAndDependents.push_back( make_pair(j->second.size(), j->first) );
569  sort(verticesAndDependents.begin(), verticesAndDependents.end());
570 
571  vector<Vertex> sortedVertices;
572  for (vector< pair<size_t, Vertex> >::reverse_iterator j = verticesAndDependents.rbegin(); j != verticesAndDependents.rend(); ++j)
573  sortedVertices.push_back((*j).second);
574 
575  vertices[trigger] = sortedVertices;
576  }
577 }
578 
582 void VuoCompilerGraph::makeVertexDistances(void)
583 {
584  for (map<VuoCompilerTriggerPort *, vector<Vertex> >::iterator i = vertices.begin(); i != vertices.end(); ++i)
585  {
586  VuoCompilerTriggerPort *trigger = i->first;
587  vector<Vertex> verticesDownstream = i->second;
588 
589  map<VuoCompilerNode *, set<Vertex> > incomingVerticesForNode; // Cached data to speed up search for incoming vertices.
590  for (vector<Vertex>::iterator j = verticesDownstream.begin(); j != verticesDownstream.end(); ++j)
591  incomingVerticesForNode[(*j).toNode].insert(*j);
592 
593  // Handle vertices immediately downstream of the trigger.
594  for (vector<Vertex>::iterator j = verticesDownstream.begin(); j != verticesDownstream.end(); ++j)
595  {
596  Vertex vertex = *j;
597  if (vertex.fromTrigger == trigger)
598  {
599  vertexDistanceFromTrigger[trigger][vertex] = 1;
600  triggerMustTransmitToVertex[trigger][vertex] = true;
601  }
602  }
603 
604  // Handle vertices further downstream.
605  for (vector<Vertex>::iterator j = verticesDownstream.begin(); j != verticesDownstream.end(); ++j)
606  {
607  Vertex vertex = *j;
608  if (vertex.fromTrigger != trigger)
609  {
610  size_t minDistance = verticesDownstream.size();
611  bool anyMustTransmit = false;
612  for (set<Vertex>::iterator k = incomingVerticesForNode[vertex.fromNode].begin(); k != incomingVerticesForNode[vertex.fromNode].end(); ++k)
613  {
614  Vertex upstreamVertex = *k;
615 
616  minDistance = min(vertexDistanceFromTrigger[trigger][upstreamVertex], minDistance);
617  bool currMustTransmit = triggerMustTransmitToVertex[trigger][upstreamVertex] && mustTransmit(upstreamVertex, trigger);
618  anyMustTransmit = currMustTransmit || anyMustTransmit;
619  }
620 
621  vertexDistanceFromTrigger[trigger][vertex] = minDistance + 1;
622  triggerMustTransmitToVertex[trigger][vertex] = anyMustTransmit;
623  }
624  }
625  }
626 }
627 
631 void VuoCompilerGraph::makeDownstreamNodesViaDataOnlyTransmission(set<VuoNode *> nodes, set<VuoCable *> cables)
632 {
633  // @todo Maybe this could be refactored to call makeDownstreamVerticesWithInclusionRule.
634 
635  // Find all nodes that can transmit through their output data cables, and put them in topological order.
636  // (Assumes the nodes don't have walled ports, so they can't have different topological orders for different triggers.)
637 
638  map<VuoCompilerNode *, set<VuoCompilerNode *> > remainingIncomingNodes;
639  map<VuoCompilerNode *, set<VuoCompilerNode *> > remainingOutgoingNodes;
640  for (VuoCable *cable : cables)
641  {
642  VuoCompilerPort *fromPort = cable->getFromPort() ? static_cast<VuoCompilerPort *>(cable->getFromPort()->getCompiler()) : nullptr;
643  VuoCompilerTriggerPort *fromTrigger = dynamic_cast<VuoCompilerTriggerPort *>(fromPort);
644  if (fromTrigger || ! cable->getCompiler()->carriesData())
645  continue; // Ignore cables that can't transmit without an event.
646 
647  VuoCompilerNode *fromNode = cable->getFromNode()->getCompiler();
648  VuoCompilerNode *toNode = cable->getToNode()->getCompiler();
649 
650  if (mayTransmitDataOnly(fromNode))
651  {
652  remainingOutgoingNodes[fromNode].insert(toNode);
653  if (mayTransmitDataOnly(toNode))
654  remainingIncomingNodes[toNode].insert(fromNode);
655  }
656  }
657  map<VuoCompilerNode *, set<VuoCompilerNode *> > outgoingNodes = remainingOutgoingNodes;
658 
659  set<VuoCompilerNode *> nodesToVisit;
660  for (VuoNode *node : nodes)
661  {
662  if (mayTransmitDataOnly(node->getCompiler()) &&
663  remainingIncomingNodes.find(node->getCompiler()) == remainingIncomingNodes.end() &&
664  remainingOutgoingNodes.find(node->getCompiler()) != remainingOutgoingNodes.end())
665  nodesToVisit.insert(node->getCompiler());
666  }
667 
668  vector<VuoCompilerNode *> sortedNodesAllowingDataOnlyTransmission;
669  while (! nodesToVisit.empty())
670  {
671  VuoCompilerNode *node = *nodesToVisit.begin();
672  nodesToVisit.erase(nodesToVisit.begin());
673 
674  sortedNodesAllowingDataOnlyTransmission.push_back(node);
675 
676  set<VuoCompilerNode *> outgoingNodesCopy = remainingOutgoingNodes[node];
677  for (VuoCompilerNode *outgoingNode : outgoingNodesCopy)
678  {
679  remainingIncomingNodes[outgoingNode].erase(node);
680  remainingOutgoingNodes[node].erase(outgoingNode);
681 
682  if (remainingIncomingNodes[outgoingNode].empty())
683  nodesToVisit.insert(outgoingNode);
684  }
685  }
686 
687  // For each of those nodes, find the nodes that are downstream of it via eventless transmission, in topological order.
688 
689  for (int firstIndex = sortedNodesAllowingDataOnlyTransmission.size() - 1; firstIndex >= 0; --firstIndex)
690  {
691  VuoCompilerNode *firstNode = sortedNodesAllowingDataOnlyTransmission[firstIndex];
692 
693  for (size_t possiblyDownstreamIndex = firstIndex + 1; possiblyDownstreamIndex < sortedNodesAllowingDataOnlyTransmission.size(); ++possiblyDownstreamIndex)
694  {
695  VuoCompilerNode *possiblyDownstreamNode = sortedNodesAllowingDataOnlyTransmission[possiblyDownstreamIndex];
696 
697  if (outgoingNodes[firstNode].find(possiblyDownstreamNode) != outgoingNodes[firstNode].end())
698  {
699  vector<VuoCompilerNode *> nodesToAdd = downstreamNodesViaDataOnlyTransmission[possiblyDownstreamNode];
700  nodesToAdd.insert(nodesToAdd.begin(), possiblyDownstreamNode);
701 
702  for (VuoCompilerNode *nodeToAdd : nodesToAdd)
703  if (find(downstreamNodesViaDataOnlyTransmission[firstNode].begin(), downstreamNodesViaDataOnlyTransmission[firstNode].end(), nodeToAdd) == downstreamNodesViaDataOnlyTransmission[firstNode].end())
704  downstreamNodesViaDataOnlyTransmission[firstNode].push_back(nodeToAdd);
705  }
706  }
707  }
708 }
709 
716 bool VuoCompilerGraph::mustTransmit(const set<VuoCompilerCable *> &fromCables, const set<VuoCompilerCable *> &toCables)
717 {
718  bool fromCablesMustTransmit = false;
719  for (VuoCompilerCable *cable : fromCables)
720  {
721  VuoPort *inputPort = cable->getBase()->getToPort();
723  {
724  fromCablesMustTransmit = true;
725  break;
726  }
727  }
728 
729  bool toCablesMayTransmit = false;
730  for (VuoCompilerCable *cable : toCables)
731  {
732  VuoPort *outputPort = cable->getBase()->getFromPort();
733  if (! outputPort || outputPort->getClass()->getPortType() != VuoPortClass::triggerPort)
734  {
735  toCablesMayTransmit = true;
736  break;
737  }
738  }
739 
740  return fromCablesMustTransmit && toCablesMayTransmit;
741 }
742 
746 bool VuoCompilerGraph::mustTransmit(Vertex vertex, VuoCompilerTriggerPort *trigger)
747 {
748  set<VuoCompilerCable *> cablesBetweenFromNodeAndToNode = vertex.cableBundle;
749 
750  set<VuoCompilerCable *> cablesOutOfToNode;
751  for (vector<Vertex>::iterator i = vertices[trigger].begin(); i != vertices[trigger].end(); ++i)
752  {
753  Vertex otherVertex = *i;
754  if (vertex.toNode == otherVertex.fromNode)
755  cablesOutOfToNode.insert(otherVertex.cableBundle.begin(), otherVertex.cableBundle.end());
756  }
757 
758  return mustTransmit(cablesBetweenFromNodeAndToNode, cablesOutOfToNode);
759 }
760 
761 
768 bool VuoCompilerGraph::mayTransmit(const set<VuoCompilerCable *> &fromCables, const set<VuoCompilerCable *> &toCables)
769 {
770  bool fromCablesMayTransmit = false;
771  for (VuoCompilerCable *cable : fromCables)
772  {
773  VuoPort *inputPort = cable->getBase()->getToPort();
775  {
776  fromCablesMayTransmit = true;
777  break;
778  }
779  }
780 
781  bool toCablesMayTransmit = false;
782  for (VuoCompilerCable *cable : toCables)
783  {
784  VuoPort *outputPort = cable->getBase()->getFromPort();
785  if (! outputPort || outputPort->getClass()->getPortType() != VuoPortClass::triggerPort)
786  {
787  toCablesMayTransmit = true;
788  break;
789  }
790  }
791 
792  return fromCablesMayTransmit && toCablesMayTransmit;
793 }
794 
798 bool VuoCompilerGraph::mayTransmit(Vertex vertex, VuoCompilerTriggerPort *trigger)
799 {
800  set<VuoCompilerCable *> cablesBetweenFromNodeAndToNode = vertex.cableBundle;
801 
802  set<VuoCompilerCable *> cablesOutOfToNode;
803  for (vector<Vertex>::iterator i = vertices[trigger].begin(); i != vertices[trigger].end(); ++i)
804  {
805  Vertex otherVertex = *i;
806  if (vertex.toNode == otherVertex.fromNode)
807  cablesOutOfToNode.insert(otherVertex.cableBundle.begin(), otherVertex.cableBundle.end());
808  }
809 
810  return mayTransmit(cablesBetweenFromNodeAndToNode, cablesOutOfToNode);
811 }
812 
817 {
818  set<VuoCompilerCable *> incomingCables;
819  set<VuoCompilerCable *> outgoingCables;
820 
821  for (vector<Vertex>::iterator i = vertices[trigger].begin(); i != vertices[trigger].end(); ++i)
822  {
823  Vertex vertex = *i;
824 
825  if (vertex.toNode == node)
826  incomingCables.insert( vertex.cableBundle.begin(), vertex.cableBundle.end() );
827  if (vertex.fromNode == node)
828  outgoingCables.insert( vertex.cableBundle.begin(), vertex.cableBundle.end() );
829  }
830 
831  return mayTransmit(incomingCables, outgoingCables);
832 }
833 
838 {
839  if (vertexMayTransmit[trigger].empty() && ! vertices[trigger].empty())
840  for (vector<Vertex>::iterator i = vertices[trigger].begin(); i != vertices[trigger].end(); ++i)
841  vertexMayTransmit[trigger][(*i).fromNode][(*i).toNode] = true;
842 
843  return vertexMayTransmit[trigger][fromNode][toNode];
844 }
845 
850 {
851  return node->getBase()->getNodeClass()->isDrawerNodeClass() || node == publishedInputNode;
852 }
853 
857 vector<VuoCompilerTriggerPort *> VuoCompilerGraph::getTriggerPorts(void)
858 {
859  return triggers;
860 }
861 
866 {
867  auto foundIter = nodeForTrigger.find(trigger);
868  if (foundIter != nodeForTrigger.end())
869  return foundIter->second;
870 
871  return NULL;
872 }
873 
878 set<VuoCompilerNode *> VuoCompilerGraph::getNodes(void)
879 {
880  return nodes;
881 }
882 
889 {
890  return publishedInputNode;
891 }
892 
899 {
900  return publishedOutputNode;
901 }
902 
907 {
908  return publishedInputTrigger;
909 }
910 
915 {
916  VuoCompilerPublishedInputNodeClass *publishedInputNodeClass = static_cast<VuoCompilerPublishedInputNodeClass *>(publishedInputNode->getBase()->getNodeClass()->getCompiler());
917  size_t portIndex = publishedInputNodeClass->getInputPortIndexForPublishedInputPort(publishedInputPortIndex);
918  return publishedInputNode->getBase()->getInputPorts().at(portIndex);
919 }
920 
924 VuoPort * VuoCompilerGraph::getOutputPortOnPublishedInputNode(size_t publishedInputPortIndex)
925 {
926  VuoCompilerPublishedInputNodeClass *publishedInputNodeClass = static_cast<VuoCompilerPublishedInputNodeClass *>(publishedInputNode->getBase()->getNodeClass()->getCompiler());
927  size_t portIndex = publishedInputNodeClass->getOutputPortIndexForPublishedInputPort(publishedInputPortIndex);
928  return publishedInputNode->getBase()->getOutputPorts().at(portIndex);
929 }
930 
935 {
936  VuoCompilerPublishedOutputNodeClass *publishedOutputNodeClass = static_cast<VuoCompilerPublishedOutputNodeClass *>(publishedOutputNode->getBase()->getNodeClass()->getCompiler());
937  size_t portIndex = publishedOutputNodeClass->getInputPortIndexForPublishedOutputPort(publishedOutputPortIndex);
938  return publishedOutputNode->getBase()->getInputPorts().at(portIndex);
939 }
940 
944 VuoPort * VuoCompilerGraph::getGatherPortOnPublishedOutputNode(void)
945 {
946  VuoCompilerPublishedOutputNodeClass *publishedOutputNodeClass = static_cast<VuoCompilerPublishedOutputNodeClass *>(publishedOutputNode->getBase()->getNodeClass()->getCompiler());
947  size_t portIndex = publishedOutputNodeClass->getGatherInputPortIndex();
948  return publishedOutputNode->getBase()->getInputPorts().at(portIndex);
949 }
950 
955 {
956  return manuallyFirableTrigger;
957 }
958 
962 size_t VuoCompilerGraph::getNumVerticesWithFromNode(VuoCompilerNode *fromNode, VuoCompilerTriggerPort *trigger)
963 {
964  int numVertices = 0;
965  for (vector<Vertex>::iterator i = vertices[trigger].begin(); i != vertices[trigger].end(); ++i)
966  if ((*i).fromNode == fromNode)
967  ++numVertices;
968 
969  return numVertices;
970 }
971 
975 size_t VuoCompilerGraph::getNumVerticesWithToNode(VuoCompilerNode *toNode, VuoCompilerTriggerPort *trigger)
976 {
977  map<VuoCompilerTriggerPort *, map<VuoCompilerNode *, size_t> >::iterator triggerIter = numVerticesWithToNode.find(trigger);
978  if (triggerIter != numVerticesWithToNode.end())
979  {
980  map<VuoCompilerNode *, size_t>::iterator nodeIter = triggerIter->second.find(toNode);
981  if (nodeIter != triggerIter->second.end())
982  return nodeIter->second;
983  }
984 
985  size_t numVertices = 0;
986  for (vector<Vertex>::iterator i = vertices[trigger].begin(); i != vertices[trigger].end(); ++i)
987  if ((*i).toNode == toNode)
988  ++numVertices;
989 
990  numVerticesWithToNode[trigger][toNode] = numVertices;
991  return numVertices;
992 }
993 
1005 map<VuoCompilerTriggerPort *, vector<VuoCompilerChain *> > VuoCompilerGraph::getChains(void)
1006 {
1007  if (! chains.empty())
1008  return chains;
1009 
1010  for (VuoCompilerTriggerPort *trigger : triggers)
1011  {
1012  // Visit the vertices in topological order, and put the nodes into lists (chains-in-progress).
1013  vector< vector<VuoCompilerNode *> > chainsInProgress;
1014  set<VuoCompilerNode *> nodesAdded;
1015  bool skippedPublishedOutputNode = false;
1016  for (vector<Vertex>::iterator j = vertices[trigger].begin(); j != vertices[trigger].end(); ++j)
1017  {
1018  Vertex vertex = *j;
1019  bool addedToChain = false;
1020 
1021  if (nodesAdded.find(vertex.toNode) != nodesAdded.end())
1022  continue; // Avoid creating duplicate chains for nodes with gathers (multiple incoming vertices).
1023 
1024  if (vertex.toNode == publishedOutputNode)
1025  {
1026  skippedPublishedOutputNode = true;
1027  continue; // Save the published output node for last.
1028  }
1029 
1030  nodesAdded.insert(vertex.toNode);
1031 
1032  if (vertex.fromNode)
1033  {
1034  if (getNumVerticesWithFromNode(vertex.fromNode, trigger) == 1 &&
1035  getNumVerticesWithToNode(vertex.toNode, trigger) == 1)
1036  {
1037  for (int k = 0; k < chainsInProgress.size(); ++k)
1038  {
1039  VuoCompilerNode *lastNodeInChain = chainsInProgress[k].back();
1040  if (lastNodeInChain == vertex.fromNode)
1041  {
1042  // Add vertex.toNode to an existing chain-in-progress.
1043  chainsInProgress[k].push_back(vertex.toNode);
1044  addedToChain = true;
1045  break;
1046  }
1047  }
1048  }
1049  }
1050 
1051  if (! addedToChain)
1052  {
1053  // Create a new chain-in-progress that starts with vertex.toNode.
1054  chainsInProgress.push_back( vector<VuoCompilerNode *>(1, vertex.toNode) );
1055  }
1056  }
1057 
1058  // Turn the chains-in-progress into actual chains.
1059  for (vector< vector<VuoCompilerNode *> >::iterator j = chainsInProgress.begin(); j != chainsInProgress.end(); ++j)
1060  {
1061  vector<VuoCompilerNode *> chainNodes = *j;
1062  VuoCompilerChain *chain = new VuoCompilerChain(chainNodes, false);
1063  chains[trigger].push_back(chain);
1064  }
1065 
1066  // Create a new chain for each node that is the repeated node in a feedback loop.
1067  map<VuoCompilerNode *, bool> nodesSeen;
1068  for (vector<Vertex>::iterator j = vertices[trigger].begin(); j != vertices[trigger].end(); ++j)
1069  {
1070  VuoCompilerNode *node = (*j).toNode;
1071  if (nodesSeen[node])
1072  continue;
1073  nodesSeen[node] = true;
1074 
1075  if (isRepeatedInFeedbackLoop(node, trigger))
1076  {
1077  VuoCompilerChain *chain = new VuoCompilerChain(vector<VuoCompilerNode *>(1, node), true);
1078  chains[trigger].push_back(chain);
1079  }
1080  }
1081 
1082  // Create a new chain for the published output node.
1083  if (skippedPublishedOutputNode)
1084  {
1085  VuoCompilerChain *chain = new VuoCompilerChain(vector<VuoCompilerNode *>(1, publishedOutputNode), false);
1086  chains[trigger].push_back(chain);
1087  }
1088  }
1089 
1090  return chains;
1091 }
1092 
1097 {
1098  set<VuoCompilerNode *> downstreamNodes;
1099  for (vector<Vertex>::iterator i = vertices[trigger].begin(); i != vertices[trigger].end(); ++i)
1100  if ((*i).fromTrigger == trigger)
1101  downstreamNodes.insert((*i).toNode);
1102 
1103  return vector<VuoCompilerNode *>(downstreamNodes.begin(), downstreamNodes.end());
1104 }
1105 
1110 {
1111  set<VuoCompilerNode *> downstreamNodes;
1112  for (vector<Vertex>::iterator i = vertices[trigger].begin(); i != vertices[trigger].end(); ++i)
1113  if ((*i).fromNode == node)
1114  downstreamNodes.insert((*i).toNode);
1115 
1116  return vector<VuoCompilerNode *>(downstreamNodes.begin(), downstreamNodes.end());
1117 }
1118 
1123 {
1124  map<VuoCompilerTriggerPort *, vector<VuoCompilerNode *> >::iterator triggerIter = downstreamNodesForTrigger.find(trigger);
1125  if (triggerIter != downstreamNodesForTrigger.end())
1126  return triggerIter->second;
1127 
1128  set<VuoCompilerNode *> downstreamNodes;
1129  vector<VuoCompilerNode *> immediatelyDownstreamNodes = getNodesImmediatelyDownstream(trigger);
1130  downstreamNodes.insert(immediatelyDownstreamNodes.begin(), immediatelyDownstreamNodes.end());
1131 
1132  for (vector<VuoCompilerNode *>::iterator i = immediatelyDownstreamNodes.begin(); i != immediatelyDownstreamNodes.end(); ++i)
1133  {
1134  vector<VuoCompilerNode *> furtherDownstreamNodes = getNodesDownstream(*i, trigger);
1135  downstreamNodes.insert(furtherDownstreamNodes.begin(), furtherDownstreamNodes.end());
1136  }
1137 
1138  vector<VuoCompilerNode *> downstreamNodesVector(downstreamNodes.begin(), downstreamNodes.end());
1139  downstreamNodesForTrigger[trigger] = downstreamNodesVector;
1140  return downstreamNodesVector;
1141 }
1142 
1147 {
1148  map<VuoCompilerTriggerPort *, map<VuoCompilerNode *, vector<VuoCompilerNode *> > >::iterator triggerIter = downstreamNodesForNode.find(trigger);
1149  if (triggerIter != downstreamNodesForNode.end())
1150  {
1151  map<VuoCompilerNode *, vector<VuoCompilerNode *> >::iterator nodeIter = triggerIter->second.find(node);
1152  if (nodeIter != triggerIter->second.end())
1153  return nodeIter->second;
1154  }
1155 
1156  set<Vertex> verticesWithDownstreamNodes;
1157  for (vector<Vertex>::iterator i = vertices[trigger].begin(); i != vertices[trigger].end(); ++i)
1158  {
1159  Vertex vertex = *i;
1160  if (vertex.fromNode == node)
1161  {
1162  verticesWithDownstreamNodes.insert(vertex);
1163  set<Vertex> downstream = downstreamVertices[trigger][vertex];
1164  verticesWithDownstreamNodes.insert(downstream.begin(), downstream.end());
1165  }
1166  }
1167 
1168  set<VuoCompilerNode *> downstreamNodes;
1169  for (set<Vertex>::iterator i = verticesWithDownstreamNodes.begin(); i != verticesWithDownstreamNodes.end(); ++i)
1170  downstreamNodes.insert((*i).toNode);
1171 
1172  vector<VuoCompilerNode *> downstreamNodesVector(downstreamNodes.begin(), downstreamNodes.end());
1173  downstreamNodesForNode[trigger][node] = downstreamNodesVector;
1174  return downstreamNodesVector;
1175 }
1176 
1182 {
1183  return downstreamNodesViaDataOnlyTransmission[node];
1184 }
1185 
1191 set<VuoCompilerCable *> VuoCompilerGraph::getOutgoingCables(VuoCompilerPort *outputPort)
1192 {
1193  set<VuoCompilerCable *> outgoingCables;
1194 
1195  // Add cables from the original composition.
1196 
1197  for (VuoCable *cable : outputPort->getBase()->getConnectedCables())
1198  if (cable->getToNode() && cable->getToNode()->hasCompiler())
1199  outgoingCables.insert(cable->getCompiler());
1200 
1201  // Add any other cables created for graph analysis.
1202 
1203  for (map<VuoCompilerTriggerPort *, vector<Vertex> >::value_type kv : vertices)
1204  for (Vertex vertex : kv.second)
1205  for (VuoCompilerCable *cable : vertex.cableBundle)
1206  if (cable->getBase()->getFromPort() == outputPort->getBase())
1207  outgoingCables.insert(cable);
1208 
1209  return outgoingCables;
1210 }
1211 
1216 {
1217  set<VuoCompilerNode *> nodesTransmittingDataOnly;
1218  set<VuoCompilerNode *> nodesDownstreamOfAnother;
1219 
1220  for (const map<VuoCompilerNode *, vector<VuoCompilerNode *> >::value_type &i : downstreamNodesViaDataOnlyTransmission)
1221  {
1222  nodesTransmittingDataOnly.insert(i.first);
1223  nodesDownstreamOfAnother.insert(i.second.begin(), i.second.end());
1224  }
1225 
1226  set<VuoCompilerNode *> nodesNotDownstream;
1227  std::set_difference(nodesTransmittingDataOnly.begin(), nodesTransmittingDataOnly.end(),
1228  nodesDownstreamOfAnother.begin(), nodesDownstreamOfAnother.end(),
1229  std::inserter(nodesNotDownstream, nodesNotDownstream.end()));
1230  return nodesNotDownstream;
1231 }
1232 
1237 void VuoCompilerGraph::getWorkerThreadsNeeded(VuoCompilerChain *chain, int &minThreadsNeeded, int &maxThreadsNeeded)
1238 {
1239  // Non-subcomposition nodes need 1 thread.
1240  minThreadsNeeded = 1;
1241  maxThreadsNeeded = 1;
1242 
1243  for (VuoCompilerNode *node : chain->getNodes())
1244  {
1245  VuoCompilerNodeClass *nodeClass = node->getBase()->getNodeClass()->getCompiler();
1246  vector<VuoCompilerTriggerDescription *> triggerDescriptions = nodeClass->getTriggerDescriptions();
1247 
1248  for (vector<VuoCompilerTriggerDescription *>::iterator k = triggerDescriptions.begin(); k != triggerDescriptions.end(); ++k)
1249  {
1250  if ((*k)->getNodeIdentifier() == VuoNodeClass::publishedInputNodeIdentifier)
1251  {
1252  // Subcomposition nodes need 1 thread plus those needed within the subcomposition.
1253  int minThreadsNeededForSubcomposition, maxThreadsNeededForSubcomposition;
1254  (*k)->getWorkerThreadsNeeded(minThreadsNeededForSubcomposition, maxThreadsNeededForSubcomposition);
1255  minThreadsNeeded = max(minThreadsNeeded, minThreadsNeededForSubcomposition + 1);
1256  maxThreadsNeeded = max(maxThreadsNeeded, maxThreadsNeededForSubcomposition + 1);
1257  break;
1258  }
1259  }
1260  }
1261 }
1262 
1268 void VuoCompilerGraph::getWorkerThreadsNeeded(VuoCompilerTriggerPort *trigger, int &minThreadsNeeded, int &maxThreadsNeeded)
1269 {
1270  // Make a data structure of the number of threads needed for each chain.
1271  vector<VuoCompilerChain *> chainsForTrigger = getChains()[trigger];
1272  map<VuoCompilerChain *, pair<int, int> > threadsNeededForChain;
1273  for (vector<VuoCompilerChain *>::iterator i = chainsForTrigger.begin(); i != chainsForTrigger.end(); ++i)
1274  {
1275  VuoCompilerChain *chain = *i;
1276 
1277  int minThreadsNeededForChain, maxThreadsNeededForChain;
1278  getWorkerThreadsNeeded(chain, minThreadsNeededForChain, maxThreadsNeededForChain);
1279 
1280  threadsNeededForChain[chain] = make_pair(minThreadsNeededForChain, maxThreadsNeededForChain);
1281  }
1282 
1283  // Make a data structure of which chains are downstream of which.
1284  map<VuoCompilerChain *, set<VuoCompilerChain *> > chainsDownstream;
1285  for (vector<VuoCompilerChain *>::iterator i = chainsForTrigger.begin(); i != chainsForTrigger.end(); ++i)
1286  {
1287  VuoCompilerChain *chain = *i;
1288  VuoCompilerNode *lastNodeInThisChain = chain->getNodes().back();
1289 
1290  vector<VuoCompilerNode *> nodesDownstream = getNodesDownstream(lastNodeInThisChain, trigger);
1291 
1292  for (vector<VuoCompilerChain *>::iterator j = i+1; j != chainsForTrigger.end(); ++j)
1293  {
1294  VuoCompilerChain *otherChain = *j;
1295  VuoCompilerNode *firstNodeInOtherChain = otherChain->getNodes().front();
1296 
1297  if (find(nodesDownstream.begin(), nodesDownstream.end(), firstNodeInOtherChain) != nodesDownstream.end())
1298  chainsDownstream[chain].insert(otherChain);
1299  }
1300  }
1301 
1302  // Make a data structure of which chains are immediately downstream and upstream of which.
1303  map<VuoCompilerChain *, set<VuoCompilerChain *> > chainsImmediatelyDownstream;
1304  map<VuoCompilerChain *, set<VuoCompilerChain *> > chainsImmediatelyUpstream;
1305  for (vector<VuoCompilerChain *>::iterator i = chainsForTrigger.begin(); i != chainsForTrigger.end(); ++i)
1306  {
1307  VuoCompilerChain *chain = *i;
1308  VuoCompilerNode *lastNodeInThisChain = chain->getNodes().back();
1309 
1310  vector<VuoCompilerNode *> nodesDownstream = getNodesImmediatelyDownstream(lastNodeInThisChain, trigger);
1311 
1312  for (vector<VuoCompilerChain *>::iterator j = i+1; j != chainsForTrigger.end(); ++j)
1313  {
1314  VuoCompilerChain *otherChain = *j;
1315  VuoCompilerNode *firstNodeInOtherChain = otherChain->getNodes().front();
1316 
1317  if (find(nodesDownstream.begin(), nodesDownstream.end(), firstNodeInOtherChain) != nodesDownstream.end())
1318  {
1319  chainsImmediatelyDownstream[chain].insert(otherChain);
1320  chainsImmediatelyUpstream[otherChain].insert(chain);
1321  }
1322  }
1323  }
1324 
1325  // Every trigger worker needs at least 1 thread, since it waits on the trigger node's semaphore.
1326  minThreadsNeeded = 1;
1327  maxThreadsNeeded = 1;
1328 
1329 
1330  // Find the maximum number of threads required — an approximation. Ideally, this is the thread count if
1331  // all chains that can possibly execute concurrently do so. This is approximated by adding up the thread
1332  // count for all scatters, scatters of scatters, etc., without taking into account any gathers. It may
1333  // be an overestimate of the ideal thread count.
1334 
1335  // Collect the chains immediately downstream of the trigger.
1336  vector< pair<VuoCompilerChain *, vector<VuoCompilerChain *> > > potentialScatters;
1337  vector<VuoCompilerChain *> scatterForTrigger;
1338  for (vector<VuoCompilerChain *>::iterator i = chainsForTrigger.begin(); i != chainsForTrigger.end(); ++i)
1339  if (chainsImmediatelyUpstream.find(*i) == chainsImmediatelyUpstream.end())
1340  scatterForTrigger.push_back(*i);
1341  potentialScatters.push_back( make_pair((VuoCompilerChain *)NULL, scatterForTrigger) );
1342 
1343  // Add in the chains immediately downstream of each chain.
1344  for (vector<VuoCompilerChain *>::iterator i = chainsForTrigger.begin(); i != chainsForTrigger.end(); ++i)
1345  {
1346  vector<VuoCompilerChain *> scatterForChain(chainsImmediatelyDownstream[*i].begin(), chainsImmediatelyDownstream[*i].end());
1347  potentialScatters.push_back( make_pair(*i, scatterForChain) );
1348  }
1349 
1350  map<VuoCompilerChain *, int> threadsNeededForScatters;
1351  for (vector< pair<VuoCompilerChain *, vector<VuoCompilerChain *> > >::reverse_iterator i = potentialScatters.rbegin(); i != potentialScatters.rend(); ++i)
1352  {
1353  VuoCompilerChain *chain = (*i).first;
1354  vector<VuoCompilerChain *> scatterChains = (*i).second;
1355 
1356  // Discard any chains that are downstream of another in the collection,
1357  // leaving a group of chains that may all execute concurrently.
1358  for (size_t j = 0; j < scatterChains.size(); ++j)
1359  {
1360  VuoCompilerChain *outer = scatterChains[j];
1361 
1362  for (size_t k = j+1; k < scatterChains.size(); ++k)
1363  {
1364  VuoCompilerChain *inner = scatterChains[k];
1365 
1366  if (chainsDownstream[inner].find(outer) != chainsDownstream[inner].end())
1367  {
1368  scatterChains.erase( scatterChains.begin() + j-- );
1369  break;
1370  }
1371 
1372  if (chainsDownstream[outer].find(inner) != chainsDownstream[outer].end())
1373  scatterChains.erase( scatterChains.begin() + k-- );
1374  }
1375  }
1376 
1377  // Add up the threads needed for the chains in the scatter.
1378  int threadsNeededForScatter = 0;
1379  for (vector<VuoCompilerChain *>::iterator j = scatterChains.begin(); j != scatterChains.end(); ++j)
1380  threadsNeededForScatter += threadsNeededForScatters[*j];
1381 
1382  // Factor in the threads needed for this chain itself.
1383  threadsNeededForScatter = max(threadsNeededForScatter, threadsNeededForChain[chain].second);
1384 
1385  threadsNeededForScatters[chain] = threadsNeededForScatter;
1386  }
1387 
1388  // Find the maximum threads needed for any chain and its downstream scatters.
1389  for (map<VuoCompilerChain *, int>::iterator i = threadsNeededForScatters.begin(); i != threadsNeededForScatters.end(); ++i)
1390  maxThreadsNeeded = max(maxThreadsNeeded, i->second);
1391 
1392 
1393  // Find the minimum number of threads required. This is the thread count if all chains execute sequentially,
1394  // which is the maximum of the minimum threads needed across all chains in the composition. If the trigger
1395  // has no downstream chains, the trigger worker still needs 1 thread to wait on the trigger node's semaphore.
1396 
1397  for (map<VuoCompilerChain *, pair<int, int> >::iterator i = threadsNeededForChain.begin(); i != threadsNeededForChain.end(); ++i)
1398  minThreadsNeeded = max(minThreadsNeeded, i->second.first);
1399 }
1400 
1412 {
1413  vector< pair<VuoCompilerTriggerPort *, size_t> > distancesForMusts;
1414  vector< pair<VuoCompilerTriggerPort *, size_t> > distancesForMays;
1415 
1416  for (map<VuoCompilerTriggerPort *, vector<Vertex> >::iterator i = vertices.begin(); i != vertices.end(); ++i)
1417  {
1418  VuoCompilerTriggerPort *trigger = i->first;
1419  vector<Vertex> verticesDownstream = i->second;
1420 
1421  for (vector<Vertex>::iterator j = verticesDownstream.begin(); j != verticesDownstream.end(); ++j)
1422  {
1423  Vertex vertex = *j;
1424  if (vertex.toNode == node)
1425  {
1426  pair<VuoCompilerTriggerPort *, size_t> p = make_pair(trigger, vertexDistanceFromTrigger[trigger][vertex]);
1427  if (triggerMustTransmitToVertex[trigger][vertex])
1428  distancesForMusts.push_back(p);
1429  else
1430  distancesForMays.push_back(p);
1431  }
1432  }
1433  }
1434 
1435  if (! distancesForMusts.empty())
1436  {
1437  sort(distancesForMusts.begin(), distancesForMusts.end(), compareTriggers);
1438  return distancesForMusts[0].first;
1439  }
1440  else if (! distancesForMays.empty())
1441  {
1442  sort(distancesForMays.begin(), distancesForMays.end(), compareTriggers);
1443  return distancesForMays[0].first;
1444  }
1445  else
1446  return NULL;
1447 }
1448 
1453 bool VuoCompilerGraph::compareTriggers(const pair<VuoCompilerTriggerPort *, size_t> &lhs, const pair<VuoCompilerTriggerPort *, size_t> &rhs)
1454 {
1455  if (lhs.second == rhs.second)
1456  return lhs.first->getIdentifier() < rhs.first->getIdentifier();
1457 
1458  return lhs.second < rhs.second;
1459 }
1460 
1473 {
1474  if (! publishedInputTrigger)
1476 
1477  // If no event-only or data-and-event published output ports, return "none".
1478 
1479  VuoCompilerPublishedOutputNodeClass *publishedOutputNodeClass = static_cast<VuoCompilerPublishedOutputNodeClass *>(publishedOutputNode->getBase()->getNodeClass()->getCompiler());
1480  size_t publishedOutputCount = publishedOutputNodeClass->getGatherInputPortIndex() - VuoNodeClass::unreservedInputPortStartIndex;
1481  set<string> publishedOutputTriggers = getPublishedOutputTriggers();
1482  if (publishedOutputCount - publishedOutputTriggers.size() == 0)
1484 
1485  VuoPort *outputPort = getOutputPortOnPublishedInputNode(publishedInputPortIndex);
1486 
1487  set<Vertex> verticesFromPublishedInputNode;
1488  for (Vertex vertex : vertices[publishedInputTrigger])
1489  if (vertex.fromNode == publishedInputNode)
1490  verticesFromPublishedInputNode.insert(vertex);
1491 
1492  // Find the vertices downstream of the published input node with no intervening doors or walls.
1493 
1494  auto includeNonBlocking = [this] (Edge edge)
1495  {
1496  return (edge.fromVertex.fromTrigger == publishedInputTrigger ||
1497  mustTransmit(edge.fromVertex.cableBundle, edge.toVertex.cableBundle));
1498  };
1499 
1500  if (downstreamVerticesNonBlocking.empty())
1501  {
1502  set<Vertex> unused;
1503  makeDownstreamVerticesWithInclusionRule(publishedInputTrigger, includeNonBlocking, downstreamVerticesNonBlocking, unused);
1504  }
1505 
1506  // Find the vertices downstream of the published input node with no intervening walls.
1507 
1508  auto includeNonBlockingAndDoor = [this] (Edge edge)
1509  {
1510  return (edge.fromVertex.fromTrigger == publishedInputTrigger ||
1511  mayTransmit(edge.fromVertex.cableBundle, edge.toVertex.cableBundle));
1512  };
1513 
1514  if (downstreamVerticesNonBlockingOrDoor.empty())
1515  {
1516  set<Vertex> unused;
1517  makeDownstreamVerticesWithInclusionRule(publishedInputTrigger, includeNonBlockingAndDoor, downstreamVerticesNonBlockingOrDoor, unused);
1518  }
1519 
1520  auto minBlocking = [] (VuoPortClass::EventBlocking b1, VuoPortClass::EventBlocking b2)
1521  {
1522  // Returns the one that lets more events through.
1523  return min(b1, b2);
1524  };
1525 
1526  auto maxBlocking = [] (VuoPortClass::EventBlocking b1, VuoPortClass::EventBlocking b2)
1527  {
1528  // Returns the one that blocks more events.
1529  return max(b1, b2);
1530  };
1531 
1533  vector<Vertex> verticesToPublishedOutputNode;
1534  for (Vertex vertex : verticesFromPublishedInputNode)
1535  {
1537 
1538  // Check the event blocking for the exact cables connected to the output port on the published input node.
1539  for (VuoCompilerCable *cable : vertex.cableBundle)
1540  {
1541  if (cable->getBase()->getFromPort() == outputPort)
1542  {
1543  VuoPortClass::EventBlocking toPortBlocking = cable->getBase()->getToPort()->getClass()->getEventBlocking();
1544  blockingForVertex = minBlocking(blockingForVertex, toPortBlocking);
1545  }
1546  }
1547 
1548  if (blockingForVertex == VuoPortClass::EventBlocking_Wall)
1549  {
1550  // If the event is blocked at the cables from the published input node's output port, it's a wall.
1551  }
1552  else
1553  {
1554  auto isVertexToPublishedOutputNode = [this] (Vertex downstreamVertex) { return downstreamVertex.toNode == publishedOutputNode; };
1555 
1556  vector<Vertex> verticesNonBlocking;
1557  std::copy_if(downstreamVerticesNonBlocking[vertex].begin(), downstreamVerticesNonBlocking[vertex].end(),
1558  std::back_inserter(verticesNonBlocking), isVertexToPublishedOutputNode);
1559 
1560  verticesToPublishedOutputNode.insert(verticesToPublishedOutputNode.end(), verticesNonBlocking.begin(), verticesNonBlocking.end());
1561 
1562  if (! verticesNonBlocking.empty())
1563  {
1564  // If the event can reach the published outputs without any further blocking, it's whatever blocking
1565  // the cables from the published input node's output port have (none or door).
1566  }
1567  else
1568  {
1569  vector<Vertex> verticesNonBlockingOrDoor;
1570  std::copy_if(downstreamVerticesNonBlockingOrDoor[vertex].begin(), downstreamVerticesNonBlockingOrDoor[vertex].end(),
1571  std::back_inserter(verticesNonBlockingOrDoor), isVertexToPublishedOutputNode);
1572 
1573  verticesToPublishedOutputNode.insert(verticesToPublishedOutputNode.end(), verticesNonBlocking.begin(), verticesNonBlocking.end());
1574 
1575  if (! verticesNonBlockingOrDoor.empty())
1576  {
1577  // If the event can reach the published outputs via door blocking, it's a door.
1578  blockingForVertex = VuoPortClass::EventBlocking_Door;
1579  }
1580  else
1581  {
1582  // If the event can't reach the published outputs, it's a wall.
1583  blockingForVertex = VuoPortClass::EventBlocking_Wall;
1584  }
1585  }
1586  }
1587 
1588  blocking = minBlocking(blocking, blockingForVertex);
1589  }
1590 
1591  // If the event can reach some but not all of the non-trigger published outputs, it's a door.
1592 
1593  set<VuoPort *> publishedOutputPortsReached;
1594  for (Vertex vertex : verticesToPublishedOutputNode)
1595  for (VuoCompilerCable *cable : vertex.cableBundle)
1596  if (publishedOutputTriggers.find(cable->getBase()->getToPort()->getClass()->getName()) == publishedOutputTriggers.end())
1597  publishedOutputPortsReached.insert(cable->getBase()->getToPort());
1598 
1599  if (publishedOutputPortsReached.size() < publishedOutputCount - publishedOutputTriggers.size())
1600  blocking = maxBlocking(blocking, VuoPortClass::EventBlocking_Door);
1601 
1602  return blocking;
1603 }
1604 
1610 {
1611  set<string> triggerNames;
1612  for (VuoCompilerTriggerPort *trigger : triggers)
1613  {
1614  set<string> currTriggerNames = getPublishedOutputTriggersDownstream(trigger);
1615  triggerNames.insert(currTriggerNames.begin(), currTriggerNames.end());
1616  }
1617 
1618  return triggerNames;
1619 }
1620 
1626 {
1627  if (trigger == publishedInputTrigger)
1628  return set<string>();
1629 
1630  set<string> outputNames = getPublishedOutputPortsDownstream(trigger);
1631  set<string> outputNamesForPublishedInputTrigger = getPublishedOutputPortsDownstream(publishedInputTrigger);
1632 
1633  set<string> triggerNames;
1634  std::set_difference(outputNames.begin(), outputNames.end(),
1635  outputNamesForPublishedInputTrigger.begin(), outputNamesForPublishedInputTrigger.end(),
1636  std::inserter(triggerNames, triggerNames.begin()));
1637 
1638  return triggerNames;
1639 }
1640 
1645 {
1646  auto iter = publishedOutputNames.find(trigger);
1647  if (iter != publishedOutputNames.end())
1648  return iter->second;
1649 
1650  set<string> names;
1651  for (Vertex vertex : vertices[trigger])
1652  {
1653  if (vertex.toNode == publishedOutputNode)
1654  {
1655  for (VuoCompilerCable *cable : vertex.cableBundle)
1656  {
1657  VuoPort *toPort = cable->getBase()->getToPort();
1658  if (toPort != getGatherPortOnPublishedOutputNode())
1659  names.insert(toPort->getClass()->getName());
1660  }
1661  }
1662  }
1663 
1664  publishedOutputNames[trigger] = names;
1665  return names;
1666 }
1667 
1673 {
1674  for (VuoCompilerTriggerPort *trigger : triggers)
1675  if (! getPublishedOutputPortsDownstream(trigger).empty())
1676  return true;
1677 
1678  return false;
1679 }
1680 
1685 {
1686  vector<VuoCompilerNode *> downstreamNodes = getNodesDownstream(node, trigger);
1687  return find(downstreamNodes.begin(), downstreamNodes.end(), node) != downstreamNodes.end();
1688 }
1689 
1694 {
1695  vector<VuoCompilerNode *> downstreamNodes = getNodesDownstream(node, trigger);
1696  for (vector<VuoCompilerNode *>::iterator i = downstreamNodes.begin(); i != downstreamNodes.end(); ++i)
1697  if (getNumVerticesWithToNode(*i, trigger) > 1)
1698  return true;
1699 
1700  return false;
1701 }
1702 
1708 {
1709  bool isScatterAtTrigger = getNodesImmediatelyDownstream(trigger).size() > 1;
1710  if (isScatterAtTrigger)
1711  {
1712  vector<VuoCompilerNode *> downstreamNodes = getNodesDownstream(trigger);
1713  return areNodesPartiallyOverlappedByAnotherTrigger(downstreamNodes, trigger);
1714  }
1715 
1716  return false;
1717 }
1718 
1724 {
1725  bool isScatterAtNode = getNodesImmediatelyDownstream(node, trigger).size() > 1;
1726  if (isScatterAtNode)
1727  {
1728  vector<VuoCompilerNode *> downstreamNodes = getNodesDownstream(node, trigger);
1729  return areNodesPartiallyOverlappedByAnotherTrigger(downstreamNodes, trigger);
1730  }
1731 
1732  return false;
1733 }
1734 
1738 bool VuoCompilerGraph::areNodesPartiallyOverlappedByAnotherTrigger(const vector<VuoCompilerNode *> &nodes, VuoCompilerTriggerPort *trigger)
1739 {
1740  for (vector<VuoCompilerTriggerPort *>::iterator i = triggers.begin(); i != triggers.end(); ++i)
1741  {
1742  VuoCompilerTriggerPort *otherTrigger = *i;
1743  if (otherTrigger == trigger)
1744  continue;
1745 
1746  vector<VuoCompilerNode *> nodesDownstreamOfOtherTrigger = getNodesDownstream(otherTrigger);
1747  nodesDownstreamOfOtherTrigger.push_back(nodeForTrigger[otherTrigger]);
1748 
1749  bool hasOverlappedNode = false;
1750  bool hasNonOverlappedNode = false;
1751  for (vector<VuoCompilerNode *>::const_iterator j = nodes.begin(); j != nodes.end(); ++j)
1752  {
1753  VuoCompilerNode *downstreamNode = *j;
1754 
1755  if (find(nodesDownstreamOfOtherTrigger.begin(), nodesDownstreamOfOtherTrigger.end(), downstreamNode) != nodesDownstreamOfOtherTrigger.end())
1756  hasOverlappedNode = true;
1757  else
1758  hasNonOverlappedNode = true;
1759  }
1760 
1761  if (hasOverlappedNode && hasNonOverlappedNode)
1762  return true;
1763  }
1764 
1765  return false;
1766 }
1767 
1773 {
1774  vector<VuoCompilerNode *> nodesDownstreamOfTrigger = getNodesDownstream(trigger);
1775  vector<VuoCompilerNode *> nodesDownstreamOfSpinOffs;
1776 
1777  // Collect all nodes downstream of Spin Off nodes downstream of this trigger
1778  // (including Spin Offs downstream of other Spin Offs downstream of this trigger).
1779  vector<VuoCompilerNode *> nodesToCheck = nodesDownstreamOfTrigger;
1780  map<VuoCompilerNode *, bool> nodesChecked;
1781  while (! nodesToCheck.empty())
1782  {
1783  VuoCompilerNode *node = nodesToCheck.back();
1784  nodesToCheck.pop_back();
1785 
1786  if (nodesChecked[node])
1787  continue;
1788  nodesChecked[node] = true;
1789 
1790  if (node == nodeForTrigger[publishedInputTrigger])
1791  continue;
1792 
1793  string nodeClassName = node->getBase()->getNodeClass()->getClassName();
1794  if (VuoStringUtilities::beginsWith(nodeClassName, "vuo.event.spinOff"))
1795  {
1796  VuoPort *spinOffOutput = node->getBase()->getOutputPorts().at(VuoNodeClass::unreservedOutputPortStartIndex);
1797  VuoCompilerTriggerPort *spinOffTrigger = static_cast<VuoCompilerTriggerPort *>( spinOffOutput->getCompiler() );
1798  vector<VuoCompilerNode *> nodesDownstream = getNodesDownstream(spinOffTrigger);
1799 
1800  nodesDownstreamOfSpinOffs.insert(nodesDownstreamOfSpinOffs.end(), nodesDownstream.begin(), nodesDownstream.end());
1801  nodesToCheck.insert(nodesToCheck.end(), nodesDownstream.begin(), nodesDownstream.end());
1802  }
1803  }
1804 
1805  sort(nodesDownstreamOfTrigger.begin(), nodesDownstreamOfTrigger.end());
1806  sort(nodesDownstreamOfSpinOffs.begin(), nodesDownstreamOfSpinOffs.end());
1807 
1808  // Do this trigger and the Spin Offs have any downstream nodes in common?
1809  set<VuoCompilerNode *> nodesDownstreamOfBoth;
1810  set_intersection(nodesDownstreamOfTrigger.begin(), nodesDownstreamOfTrigger.end(),
1811  nodesDownstreamOfSpinOffs.begin(), nodesDownstreamOfSpinOffs.end(),
1812  std::inserter(nodesDownstreamOfBoth, nodesDownstreamOfBoth.begin()));
1813  return ! nodesDownstreamOfBoth.empty();
1814 }
1815 
1826 {
1827  if (! repeatedVertices.empty())
1828  {
1829  // Identify the nodes and cables involved in the infinite feedback loop.
1830  vector< set<VuoNode *> > nodesInLoops;
1831  vector< set<VuoCable *> > cablesInLoops;
1832  for (const auto &i : repeatedVertices)
1833  {
1834  VuoCompilerTriggerPort *trigger = i.first;
1835  for (const Vertex &repeatedVertex : i.second)
1836  {
1837  set<VuoNode *> nodesInLoop;
1838  set<VuoCable *> cablesInLoop;
1839  for (const Vertex &otherVertex : vertices[trigger])
1840  {
1841  if (downstreamVertices[trigger][repeatedVertex].find(otherVertex) != downstreamVertices[trigger][repeatedVertex].end() &&
1842  downstreamVertices[trigger][otherVertex].find(repeatedVertex) != downstreamVertices[trigger][otherVertex].end())
1843  {
1844  nodesInLoop.insert(otherVertex.fromNode->getBase());
1845  nodesInLoop.insert(otherVertex.toNode->getBase());
1846 
1847  for (set<VuoCompilerCable *>::iterator m = otherVertex.cableBundle.begin(); m != otherVertex.cableBundle.end(); ++m)
1848  cablesInLoop.insert((*m)->getBase());
1849  }
1850  }
1851  nodesInLoops.push_back(nodesInLoop);
1852  cablesInLoops.push_back(cablesInLoop);
1853  }
1854  }
1855 
1856  // Coalesce the lists of nodes and cables to avoid reporting the same ones multiple times.
1857  vector< set<VuoNode *> > coalescedNodesInLoops;
1858  vector< set<VuoCable *> > coalescedCablesInLoops;
1859  for (size_t i = 0; i < nodesInLoops.size(); ++i)
1860  {
1861  bool coalesced = false;
1862  for (size_t j = 0; j < coalescedNodesInLoops.size(); ++j)
1863  {
1864  set<VuoNode *> nodesInBoth;
1865  std::set_intersection(nodesInLoops[i].begin(), nodesInLoops[i].end(),
1866  coalescedNodesInLoops[j].begin(), coalescedNodesInLoops[j].end(),
1867  std::inserter(nodesInBoth, nodesInBoth.begin()));
1868 
1869  set<VuoCable *> cablesInBoth;
1870  std::set_intersection(cablesInLoops[i].begin(), cablesInLoops[i].end(),
1871  coalescedCablesInLoops[j].begin(), coalescedCablesInLoops[j].end(),
1872  std::inserter(cablesInBoth, cablesInBoth.begin()));
1873 
1874  if (nodesInBoth.size() == nodesInLoops[i].size() && cablesInBoth.size() == cablesInLoops[i].size())
1875  {
1876  // One of the coalesced items is a superset of nodesInLoops[i] and cablesInLoops[i]. Already taken care of.
1877  coalesced = true;
1878  break;
1879  }
1880  else if (nodesInBoth.size() == coalescedNodesInLoops[j].size() && cablesInBoth.size() == coalescedCablesInLoops[j].size())
1881  {
1882  // One of the coalesced items is a subset of nodesInLoops[i] and cablesInLoops[i]. Merge in the additional nodes and cables.
1883  coalescedNodesInLoops[j] = nodesInLoops[i];
1884  coalescedCablesInLoops[j] = cablesInLoops[i];
1885  coalesced = true;
1886  break;
1887  }
1888  }
1889 
1890  if (! coalesced)
1891  {
1892  // No existing items to coalesce with, so add a new one.
1893  coalescedNodesInLoops.push_back(nodesInLoops[i]);
1894  coalescedCablesInLoops.push_back(cablesInLoops[i]);
1895  }
1896  }
1897 
1898  for (size_t i = 0; i < coalescedNodesInLoops.size(); ++i)
1899  {
1900  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling composition", "",
1901  "Infinite feedback loop",
1902  "An event is not allowed to travel through the same cable more than once.");
1903  issue.setHelpPath("errors-warnings-and-other-issues.html");
1904  issue.setNodes(coalescedNodesInLoops[i]);
1905  issue.setCables(coalescedCablesInLoops[i]);
1906  issues->append(issue);
1907  }
1908 
1909  throw VuoCompilerException(issues, false);
1910  }
1911 }
1912 
1923 {
1924  bool foundIssue = false;
1925  for (VuoCompilerTriggerPort *trigger : triggers)
1926  {
1927  // For each node, find all nodes downstream of it.
1928  map<VuoCompilerNode *, set<VuoCompilerNode *> > downstreamNodes;
1929  for (map<Vertex, set<Vertex> >::iterator j = downstreamVertices[trigger].begin(); j != downstreamVertices[trigger].end(); ++j)
1930  {
1931  Vertex upstreamVertex = j->first;
1932  set<VuoCompilerNode *> downstreamNodesForVertex;
1933  bool isRepeated = false;
1934 
1935  for (set<Vertex>::iterator k = j->second.begin(); k != j->second.end(); ++k)
1936  {
1937  Vertex downstreamVertex = *k;
1938  if (upstreamVertex.toNode == downstreamVertex.toNode)
1939  {
1940  isRepeated = true;
1941  break;
1942  }
1943 
1944  downstreamNodesForVertex.insert(downstreamVertex.toNode);
1945  }
1946 
1947  if (! isRepeated)
1948  downstreamNodes[upstreamVertex.toNode].insert(downstreamNodesForVertex.begin(), downstreamNodesForVertex.end());
1949  }
1950 
1951  // Find any pairs of nodes that are mutually downstream.
1952  set< pair<VuoCompilerNode *, VuoCompilerNode *> > mutuallyDownstreamNodePairs;
1953  for (map<VuoCompilerNode *, set<VuoCompilerNode *> >::iterator j = downstreamNodes.begin(); j != downstreamNodes.end(); ++j)
1954  {
1955  VuoCompilerNode *upstreamNode = j->first;
1956  for (set<VuoCompilerNode *>::iterator k = j->second.begin(); k != j->second.end(); ++k)
1957  {
1958  VuoCompilerNode *downstreamNode = *k;
1959  if (downstreamNodes[downstreamNode].find(upstreamNode) != downstreamNodes[downstreamNode].end() &&
1960  mutuallyDownstreamNodePairs.find( make_pair(downstreamNode, upstreamNode) ) == mutuallyDownstreamNodePairs.end())
1961  {
1962  mutuallyDownstreamNodePairs.insert( make_pair(upstreamNode, downstreamNode) );
1963  }
1964  }
1965  }
1966 
1967  // Report any errors, with all nodes and cables involved in the deadlocked feedback loops.
1968  for (set< pair<VuoCompilerNode *, VuoCompilerNode *> >::iterator j = mutuallyDownstreamNodePairs.begin(); j != mutuallyDownstreamNodePairs.end(); ++j)
1969  {
1970  VuoCompilerNode *firstNode = j->first;
1971  VuoCompilerNode *secondNode = j->second;
1972 
1973  set<VuoNode *> nodesInLoop;
1974  set<VuoCable *> cablesInLoop;
1975  nodesInLoop.insert(firstNode->getBase());
1976  nodesInLoop.insert(secondNode->getBase());
1977 
1978  for (set<VuoCompilerNode *>::iterator k = downstreamNodes[firstNode].begin(); k != downstreamNodes[firstNode].end(); ++k)
1979  {
1980  VuoCompilerNode *otherNode = *k;
1981  if (downstreamNodes[otherNode].find(secondNode) != downstreamNodes[otherNode].end())
1982  nodesInLoop.insert(otherNode->getBase());
1983  }
1984  for (set<VuoCompilerNode *>::iterator k = downstreamNodes[secondNode].begin(); k != downstreamNodes[secondNode].end(); ++k)
1985  {
1986  VuoCompilerNode *otherNode = *k;
1987  if (downstreamNodes[otherNode].find(firstNode) != downstreamNodes[otherNode].end())
1988  nodesInLoop.insert(otherNode->getBase());
1989  }
1990 
1991  for (vector<Vertex>::iterator k = vertices[trigger].begin(); k != vertices[trigger].end(); ++k)
1992  {
1993  Vertex vertex = *k;
1994  if (vertex.fromNode &&
1995  nodesInLoop.find(vertex.fromNode->getBase()) != nodesInLoop.end() &&
1996  nodesInLoop.find(vertex.toNode->getBase()) != nodesInLoop.end())
1997  {
1998  for (set<VuoCompilerCable *>::iterator m = vertex.cableBundle.begin(); m != vertex.cableBundle.end(); ++m)
1999  cablesInLoop.insert((*m)->getBase());
2000  }
2001  }
2002 
2003  VuoCompilerIssue issue(VuoCompilerIssue::Error, "compiling composition", "",
2004  "Deadlocked feedback loop",
2005  "There's more than one possible path for events to travel through this composition. "
2006  "It's unclear which is the correct one.");
2007  issue.setHelpPath("errors-warnings-and-other-issues.html");
2008  issue.setNodes(nodesInLoop);
2009  issue.setCables(cablesInLoop);
2010  issues->append(issue);
2011  foundIssue = true;
2012  }
2013  }
2014 
2015  if (foundIssue)
2016  throw VuoCompilerException(issues, false);
2017 }
2018 
2023 {
2024  ostringstream s;
2025 
2026  // nodes — pointers, identifiers
2027  s << "[";
2028  for (VuoNode *node : composition->getBase()->getNodes())
2029  {
2030  VuoCompilerNode *compilerNode = NULL;
2031  string identifier;
2032  if (node->hasCompiler())
2033  {
2034  compilerNode = node->getCompiler();
2035  identifier = compilerNode->getIdentifier();
2036  }
2037  s << "{" << compilerNode << "," << identifier << "},";
2038  }
2039  s << "]";
2040 
2041  // cables — pointers, node identifiers, port identifiers
2042  s << "[";
2043  set<VuoCable *> cables = composition->getBase()->getCables();
2044  for (set<VuoCable *>::iterator i = cables.begin(); i != cables.end(); ++i)
2045  {
2047  string fromNode = ((*i)->getFromNode() && (*i)->getFromNode()->hasCompiler()) ? (*i)->getFromNode()->getCompiler()->getIdentifier() : "";
2048  string fromPort = (*i)->getFromPort() ? (*i)->getFromPort()->getClass()->getName() : "";
2049  string toNode = ((*i)->getToNode() && (*i)->getToNode()->hasCompiler()) ? (*i)->getToNode()->getCompiler()->getIdentifier() : "";
2050  string toPort = (*i)->getToPort() ? (*i)->getToPort()->getClass()->getName() : "";
2051  s << "{" << (*i)->getCompiler() << "," << fromNode << "," << fromPort << "," << toNode << "," << toPort << "},";
2052  }
2053  s << "]";
2054 
2055  // published ports — pointers, identifiers
2056  s << "[";
2057  vector<VuoPublishedPort *> publishedInputPorts = composition->getBase()->getPublishedInputPorts();
2058  for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
2059  s << "{" << (*i)->getCompiler() << "," << (*i)->getClass()->getName() << "},";
2060  s << "]";
2061  s << "[";
2062  vector<VuoPublishedPort *> publishedOutputPorts = composition->getBase()->getPublishedOutputPorts();
2063  for (vector<VuoPublishedPort *>::iterator i = publishedOutputPorts.begin(); i != publishedOutputPorts.end(); ++i)
2064  s << "{" << (*i)->getCompiler() << "," << (*i)->getClass()->getName() << "},";
2065  s << "]";
2066 
2067  // manually firable input port — pointer, identifier
2068  s << "[";
2069  VuoPort *manuallyFirableInputPort = composition->getManuallyFirableInputPort();
2070  if (manuallyFirableInputPort)
2071  s << "{" << manuallyFirableInputPort->getCompiler() << "," << manuallyFirableInputPort->getClass()->getName() << "},";
2072  s << "]";
2073 
2074  return VuoStringUtilities::hash(s.str());
2075 }
2076 
2081 {
2082  return VuoNodeClass::publishedInputNodeIdentifier + "Trigger";
2083 }
2084 
2089 {
2090  return "ManuallyFirableTrigger";
2091 }
2092 
2096 VuoCompilerGraph::Vertex::Vertex(VuoCompilerTriggerPort *fromTrigger, VuoCompilerNode *toNode)
2097 {
2098  this->fromNode = NULL;
2099  this->fromTrigger = fromTrigger;
2100  this->toNode = toNode;
2101 }
2102 
2106 VuoCompilerGraph::Vertex::Vertex(VuoCompilerNode *fromNode, VuoCompilerNode *toNode)
2107 {
2108  this->fromTrigger = NULL;
2109  this->fromNode = fromNode;
2110  this->toNode = toNode;
2111 }
2112 
2116 VuoCompilerGraph::Vertex::Vertex(void)
2117 {
2118  this->fromNode = NULL;
2119  this->fromTrigger = NULL;
2120  this->toNode = NULL;
2121 }
2122 
2126 VuoCompilerGraph::Edge::Edge(const VuoCompilerGraph::Vertex &fromVertex, const VuoCompilerGraph::Vertex &toVertex)
2127  : fromVertex(fromVertex), toVertex(toVertex)
2128 {
2129 }
2130 
2134 VuoCompilerGraph::Edge::Edge(void)
2135 {
2136 }
2137 
2141 bool operator==(const VuoCompilerGraph::Vertex &lhs, const VuoCompilerGraph::Vertex &rhs)
2142 {
2143  return (lhs.fromTrigger == rhs.fromTrigger && lhs.fromNode == rhs.fromNode && lhs.toNode == rhs.toNode);
2144 }
2145 
2149 bool operator!=(const VuoCompilerGraph::Vertex &lhs, const VuoCompilerGraph::Vertex &rhs)
2150 {
2151  return ! (lhs == rhs);
2152 }
2153 
2157 bool operator<(const VuoCompilerGraph::Vertex &lhs, const VuoCompilerGraph::Vertex &rhs)
2158 {
2159  return (lhs.fromTrigger != rhs.fromTrigger ?
2160  lhs.fromTrigger < rhs.fromTrigger :
2161  (lhs.fromNode != rhs.fromNode ?
2162  lhs.fromNode < rhs.fromNode :
2163  lhs.toNode < rhs.toNode));
2164 }
2165 
2169 bool operator<(const VuoCompilerGraph::Edge &lhs, const VuoCompilerGraph::Edge &rhs)
2170 {
2171  return (lhs.fromVertex != rhs.fromVertex ?
2172  lhs.fromVertex < rhs.fromVertex :
2173  lhs.toVertex < rhs.toVertex);
2174 }
2175 
2179 string VuoCompilerGraph::Vertex::toString(void) const
2180 {
2181  return (fromNode ?
2182  fromNode->getBase()->getTitle() :
2183  fromTrigger->getBase()->getClass()->getName()) +
2184  "->" + toNode->getBase()->getTitle();
2185 }
2186 
2190 string VuoCompilerGraph::Edge::toString(void) const
2191 {
2192  return fromVertex.toString() + " -> " + toVertex.toString();
2193 }