001/* Interface representing a dependency between ports of a composite actor. 002 003 Copyright (c) 2008-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.actor.util; 029 030import java.util.Collection; 031import java.util.Collections; 032import java.util.Comparator; 033import java.util.HashMap; 034import java.util.HashSet; 035import java.util.Iterator; 036import java.util.LinkedList; 037import java.util.List; 038import java.util.Map; 039import java.util.Set; 040 041import ptolemy.actor.Actor; 042import ptolemy.actor.IOPort; 043import ptolemy.actor.parameters.ParameterPort; 044import ptolemy.kernel.CompositeEntity; 045import ptolemy.kernel.Entity; 046import ptolemy.kernel.util.IllegalActionException; 047import ptolemy.kernel.util.InternalErrorException; 048import ptolemy.kernel.util.NamedObj; 049 050/////////////////////////////////////////////////////////////////// 051//// CausalityInterfaceForComposites 052 053/** 054 This class elaborates its base class by providing an algorithm for inferring 055 the causality interface of a composite actor from the causality interfaces 056 of its component actors and their interconnection topology. 057 058 @author Edward A. Lee 059 @version $Id$ 060 @since Ptolemy II 8.0 061 @Pt.ProposedRating Yellow (eal) 062 @Pt.AcceptedRating Red (eal) 063 */ 064public class CausalityInterfaceForComposites extends DefaultCausalityInterface { 065 066 /** Construct a causality interface for the specified actor. 067 * @param actor The actor for which this is a causality interface. 068 * This is required to be an instance of CompositeEntity. 069 * @param defaultDependency The default dependency of an output 070 * port on an input port. 071 * @exception IllegalArgumentException If the actor parameter is not 072 * an instance of CompositeEntity. 073 */ 074 public CausalityInterfaceForComposites(Actor actor, 075 Dependency defaultDependency) throws IllegalArgumentException { 076 super(actor, defaultDependency); 077 if (!(actor instanceof CompositeEntity)) { 078 throw new IllegalArgumentException("Cannot create an instance of " 079 + "CausalityInterfaceForComposites for " 080 + actor.getFullName() 081 + ", which is not a CompositeEntity."); 082 } 083 } 084 085 /////////////////////////////////////////////////////////////////// 086 //// public methods //// 087 088 /** Check the associated composite actor for causality cycles. 089 * If a cycle is found, throw an exception. This method has as 090 * a side effect computing the depths of actors and ports 091 * within the composite, so subsequent queries for those depths 092 * will be low cost. 093 * @exception IllegalActionException If a cycle is found. 094 */ 095 public void checkForCycles() throws IllegalActionException { 096 _computeActorDepth(); 097 } 098 099 /** Return a collection of the ports in the associated actor that depend on 100 * or are depended on by the specified port. A port X depends 101 * on a port Y if X is an output and Y is an input and 102 * getDependency(X,Y) returns oTimesIdentity() of the default 103 * dependency specified in the constructor. 104 * <p> 105 * This class presumes (but does not check) that the 106 * argument is a port contained by the associated actor. 107 * If the actor is an input, then it returns a collection of 108 * all the outputs. If the actor is output, then it returns 109 * a collection of all the inputs. 110 * <p> 111 * Derived classes may override this, but they may need to 112 * also override {@link #getDependency(IOPort, IOPort)} 113 * and {@link #equivalentPorts(IOPort)} to be consistent. 114 * @param port The port to find the dependents of. 115 * @return a collection of ports that depend on or are depended on 116 * by the specified port. 117 * @exception IllegalActionException Not thrown in this base class. 118 */ 119 @Override 120 public Collection<IOPort> dependentPorts(IOPort port) 121 throws IllegalActionException { 122 // FIXME: This does not support ports that are both input and output. 123 // Should it? 124 HashSet<IOPort> result = new HashSet<IOPort>(); 125 if (port.isOutput()) { 126 List<IOPort> inputs = _actor.inputPortList(); 127 if (inputs.size() != 0) { 128 // Make sure _dependency is computed. 129 getDependency(inputs.get(0), port); 130 Map<IOPort, Dependency> map = _reverseDependencies.get(port); 131 if (map != null) { 132 result.addAll(map.keySet()); 133 } 134 } 135 } else { 136 List<IOPort> outputs = _actor.outputPortList(); 137 if (outputs.size() != 0) { 138 // Make sure _dependency is computed. 139 getDependency(port, outputs.get(0)); 140 Map<IOPort, Dependency> map = _forwardDependencies.get(port); 141 if (map != null) { 142 result.addAll(map.keySet()); 143 } 144 } 145 } 146 return result; 147 } 148 149 /** Return a string that describes the depths of actors and their ports. 150 * @return s string that describes the depths of actors and their ports. 151 * @exception IllegalActionException If there is a causality loop. 152 * @see #getDepthOfActor(Actor) 153 * @see #getDepthOfPort(IOPort) 154 */ 155 public String describeDepths() throws IllegalActionException { 156 _computeActorDepth(); 157 StringBuffer result = new StringBuffer(); 158 List<Actor> actors = ((CompositeEntity) _actor).deepEntityList(); 159 for (Actor actor : actors) { 160 result.append(actor.getFullName()); 161 result.append(": "); 162 result.append(_actorToDepth.get(actor)); 163 result.append("\n"); 164 List<IOPort> ports = ((Entity) actor).portList(); 165 for (IOPort port : ports) { 166 result.append(" "); 167 result.append(port.getName()); 168 result.append(": "); 169 result.append(_portToDepth.get(port)); 170 result.append("\n"); 171 } 172 } 173 return result.toString(); 174 } 175 176 /** Return a set of the input ports in this actor that are 177 * in an equivalence class with the specified input. 178 * The returned result includes the specified input port. 179 * <p> 180 * An equivalence class is defined as follows. 181 * If input ports X and Y each have a dependency equal to 182 * oTimesIdentity() on any common port 183 * or on two equivalent ports 184 * or on the state of the associated actor, then they 185 * are in an equivalence class. That is, 186 * there is a causal dependency. They are also in 187 * the same equivalence class if there is a port Z 188 * in an equivalence class with X and in an equivalence 189 * class with Y. Moreover, if the actor has any instance 190 * of ParameterPort among its input ports, then all input 191 * ports are in an equivalence class. 192 * Otherwise, they are not in the same 193 * equivalence class. 194 * In this base class, we assume the actor has no 195 * state and return the equivalence classes determined 196 * only by the common dependence of output ports. 197 * @param input The port to find the equivalence class of. 198 * @return set of the input ports in this actor that are 199 * in an equivalence class with the specified input. 200 * @exception IllegalActionException If the argument is not 201 * contained by the associated actor. 202 */ 203 @Override 204 public Collection<IOPort> equivalentPorts(IOPort input) 205 throws IllegalActionException { 206 if (input.getContainer() != _actor || !input.isInput()) { 207 throw new IllegalActionException(input, _actor, 208 "equivalentPort() called with argument " 209 + input.getFullName() 210 + " which is not an input port of " 211 + _actor.getFullName()); 212 } 213 // Make sure the data structures are up to date. 214 getDependency(input, null); 215 // The following must include at least the specified input port. 216 return _equivalenceClasses.get(input); 217 } 218 219 /** Return the dependency between the specified input port 220 * and the specified output port. This is done by traversing 221 * the network of actors from the input ports. For each output 222 * port reachable from an input port, its dependency on the 223 * input port is determined by composing the dependencies along 224 * all paths from the input port to the output port using 225 * oPlus() and oTimes() operators of the dependencies. 226 * For any output port that is not reachable from an input 227 * port, the dependency on that input port is set to 228 * the oPlusIdentity() of the default dependency given 229 * in the constructor. 230 * <p> 231 * When called for the first time since a change in the model 232 * structure, this method performs the complete analysis of 233 * the graph and caches the result. Subsequent calls just 234 * look up the result. Note that the complete analysis 235 * can be quite expensive. For each input port, it traverses 236 * the graph to find all ports reachable from that input port, 237 * and tracks the dependencies. In the worst case, the 238 * complexity can be N*M^2, where N is the number of 239 * input ports and M is the total number of ports in the 240 * composite (including the ports of all contained actors). 241 * The algorithm used, however, is optimized for typical 242 * Ptolemy II models, so in most cases the algorithm completes 243 * in time on the order of N*D, where D is the length of 244 * the longest chain of ports from an input port to an 245 * output port. 246 * @param input The input port. 247 * @param output The output port, or null to update the 248 * dependencies (and record equivalence classes) without 249 * requiring there to be an output port. 250 * @return The dependency between the specified input port 251 * and the specified output port, or null if a null output 252 * is port specified. 253 * @exception IllegalActionException Not thrown in this base class. 254 */ 255 @Override 256 public Dependency getDependency(IOPort input, IOPort output) 257 throws IllegalActionException { 258 // Cast is safe because this is checked in the constructor 259 CompositeEntity actor = (CompositeEntity) _actor; 260 261 // If the dependency is not up-to-date, then update it. 262 long workspaceVersion = actor.workspace().getVersion(); 263 if (_dependencyVersion != workspaceVersion) { 264 // Need to update dependencies. The cached version 265 // is obsolete. 266 try { 267 actor.workspace().getReadAccess(); 268 _reverseDependencies = new HashMap<IOPort, Map<IOPort, Dependency>>(); 269 _forwardDependencies = new HashMap<IOPort, Map<IOPort, Dependency>>(); 270 _equivalenceClasses = new HashMap<IOPort, Collection<IOPort>>(); 271 // The following map keeps track for each port in the model which input 272 // ports of the associated actor it depends on. This is used to build 273 // the equivalence classes. 274 Map<IOPort, Collection<IOPort>> dependsOnInputsMap = new HashMap<IOPort, Collection<IOPort>>(); 275 Iterator inputPorts = _actor.inputPortList().iterator(); 276 boolean hasPortParameter = false; 277 while (inputPorts.hasNext()) { 278 IOPort inputPort = (IOPort) inputPorts.next(); 279 if (inputPort instanceof ParameterPort) { 280 hasPortParameter = true; 281 } 282 // Make sure that equivalentPorts() always returns at least a set 283 // with one element. 284 Set<IOPort> justTheInput = new HashSet<IOPort>(); 285 justTheInput.add(inputPort); 286 _equivalenceClasses.put(inputPort, justTheInput); 287 288 // Construct a map of dependencies from this inputPort 289 // to all reachable ports. 290 Map<IOPort, Dependency> map = new HashMap<IOPort, Dependency>(); 291 Collection<IOPort> portsToProcess = inputPort 292 .insideSinkPortList(); 293 // Set the initial dependency of all the portsToProcess. 294 Iterator ports = portsToProcess.iterator(); 295 while (ports.hasNext()) { 296 IOPort port = (IOPort) ports.next(); 297 map.put(port, _defaultDependency.oTimesIdentity()); 298 } 299 if (!portsToProcess.isEmpty()) { 300 _setDependency(inputPort, map, portsToProcess, 301 dependsOnInputsMap); 302 } 303 } 304 // Adjust the equivalent ports list if any ParameterPort was found. 305 if (hasPortParameter) { 306 List<IOPort> allInputs = _actor.inputPortList(); 307 for (IOPort inputPort : allInputs) { 308 _equivalenceClasses.put(inputPort, allInputs); 309 } 310 } 311 } finally { 312 actor.workspace().doneReading(); 313 } 314 _dependencyVersion = workspaceVersion; 315 } 316 if (output == null) { 317 return null; 318 } 319 Map<IOPort, Dependency> inputMap = _forwardDependencies.get(input); 320 if (inputMap != null) { 321 Dependency result = inputMap.get(output); 322 if (result != null) { 323 return result; 324 } 325 } 326 // If there is no recorded dependency, then reply 327 // with the additive identity (which indicates no 328 // dependency). 329 return _defaultDependency.oPlusIdentity(); 330 } 331 332 /** Return the depth of an actor contained (deeply) by the 333 * associated composite actor. 334 * The depth of an actor is the minimum depth of the output ports. 335 * If there are no output ports, then the depth of 336 * the actor is the maximum depth of the input ports. 337 * If there are no input ports or output ports, the depth is zero. 338 * @see #getDepthOfPort(IOPort) 339 * @param actor An actor whose depth is requested. 340 * @return An integer indicating the depth of the given actor. 341 * @exception IllegalActionException If the actor is not within 342 * the associated actor. 343 */ 344 public int getDepthOfActor(Actor actor) throws IllegalActionException { 345 _computeActorDepth(); 346 Integer depth = _actorToDepth.get(actor); 347 if (depth != null) { 348 return depth.intValue(); 349 } 350 throw new IllegalActionException(_actor, 351 "Attempt to get depth of actor " + actor.getFullName() 352 + " that was not sorted. It is probably not" 353 + " contained by " + _actor.getFullName()); 354 } 355 356 /** Return the depth of a port of the associated actor 357 * or an actor contained by it. 358 * The depth of an output port is 359 * the maximum of the depth of all the input ports 360 * it directly depends on, or zero if there are no such 361 * input ports. 362 * The depth of an input port is maximum of the 363 * "source depths" of all the input ports in the 364 * same equivalence class with the specified port. 365 * The "source depth" of an input port is one plus the maximum 366 * depth of all output ports that directly source data 367 * to it, or zero if there are no such ports. 368 * @param ioPort A port whose depth is requested. 369 * @return An integer representing the depth of the specified ioPort. 370 * @exception IllegalActionException If the ioPort does not have 371 * a depth (this should not occur if the ioPort is under the control 372 * of this director). 373 * @see #getDepthOfActor(Actor) 374 */ 375 public int getDepthOfPort(IOPort ioPort) throws IllegalActionException { 376 _computeActorDepth(); 377 Integer depth = _portToDepth.get(ioPort); 378 379 if (depth != null) { 380 return depth.intValue(); 381 } 382 throw new IllegalActionException("Attempt to get depth of ioPort " 383 + ((NamedObj) ioPort).getFullName() + " that was not sorted."); 384 } 385 386 /** Indicate that the cached causality information is invalid. 387 */ 388 public void invalidate() { 389 _actorDepthVersion = -1; 390 _dependencyVersion = -1; 391 } 392 393 /** Remove the dependency that the specified output port has 394 * on the specified input port. Specifically, calling this 395 * method ensures that subsequent calls to 396 * getDependency(inputPort, outputPort) 397 * will return defaultDependency.oPlusIdentity(). 398 * It also adjusts what is returned by 399 * {@link #equivalentPorts(IOPort)} and 400 * {@link #dependentPorts(IOPort)}. 401 * @see #getDependency(IOPort, IOPort) 402 * @param inputPort The input port. 403 * @param outputPort The output port that does not depend on the 404 * input port. 405 */ 406 @Override 407 public void removeDependency(IOPort inputPort, IOPort outputPort) { 408 // First ensure that all dependencies are calculated. 409 try { 410 getDependency(inputPort, outputPort); 411 } catch (IllegalActionException e) { 412 throw new InternalErrorException(e); 413 } 414 Map<IOPort, Dependency> outputPorts = _forwardDependencies 415 .get(inputPort); 416 if (outputPorts != null) { 417 outputPorts.remove(outputPort); 418 } 419 Map<IOPort, Dependency> inputPorts = _reverseDependencies 420 .get(outputPort); 421 if (inputPorts != null) { 422 inputPorts.remove(inputPort); 423 } 424 } 425 426 /** Return a list of the actors deeply contained within 427 * the associated composite actor sorted by actor depth. 428 * For actors that have the same depth, the ordering is 429 * the order returned by 430 * {@link CompositeEntity#deepEntityList()}. 431 * This method always creates a new list. 432 * @return A sorted list of actors. 433 * @exception IllegalActionException If a cycle is found. 434 */ 435 public List<Actor> topologicalSort() throws IllegalActionException { 436 // Ensure the cache is up to date and check for cycles. 437 checkForCycles(); 438 List<Actor> actors = ((CompositeEntity) _actor).deepEntityList(); 439 // Create a copy to sort. 440 // FIXME: Would it be more efficient to just create a TreeSet? 441 List<Actor> sorted = new LinkedList<Actor>(actors); 442 ActorComparator comparator = new ActorComparator(); 443 Collections.sort(sorted, comparator); 444 return sorted; 445 } 446 447 /////////////////////////////////////////////////////////////////// 448 //// protected variables //// 449 450 /** Workspace version when actor depth was last computed. */ 451 protected long _actorDepthVersion = -1; 452 453 /** A table giving the depths of actors. */ 454 protected Map<Actor, Integer> _actorToDepth = null; 455 456 /** The workspace version where the dependency was last updated. */ 457 protected long _dependencyVersion; 458 459 /** Computed equivalence classes of input ports. */ 460 protected Map<IOPort, Collection<IOPort>> _equivalenceClasses; 461 462 /** Computed dependencies between input ports and output ports of the associated actor. */ 463 protected Map<IOPort, Map<IOPort, Dependency>> _forwardDependencies; 464 465 /** Computed reverse dependencies (the key is now an output port). */ 466 protected Map<IOPort, Map<IOPort, Dependency>> _reverseDependencies; 467 468 /////////////////////////////////////////////////////////////////// 469 //// protected methods //// 470 471 /** Compute the depth of ports and actors. 472 * The actor depth is typically used to prioritize firings in response 473 * to pure events (fireAt() calls). The port depth is used 474 * to prioritize firings due to input events. Lower depths 475 * translate into higher priorities, with the lowest 476 * value being zero. 477 * The depth of an actor is the minimum depth of the output ports. 478 * This typically causes the actor to fire as early as possible 479 * to produce an output on those output ports in response 480 * to a pure event, but no earlier than the firings of 481 * actors that may source it data that the firing may 482 * depend on. If there are no output ports, then the depth of 483 * the actor it is the maximum depth of the input ports. 484 * This typically delays the response to fireAt() until all input 485 * events with the same tag have arrived. 486 * If there are no input ports or output ports, the depth is zero. 487 * @exception IllegalActionException If a zero-delay loop is found. 488 */ 489 protected void _computeActorDepth() throws IllegalActionException { 490 if (_actorDepthVersion == ((NamedObj) _actor).workspace() 491 .getVersion()) { 492 return; 493 } 494 _portToDepth = new HashMap<IOPort, Integer>(); 495 _actorToDepth = new HashMap<Actor, Integer>(); 496 497 // To ensure that the associated actor has the lowest priority, 498 // assign it the largest integer. 499 _actorToDepth.put(_actor, Integer.MAX_VALUE); 500 501 // First iterate over all actors, ensuring that their 502 // ports all have depths. Note that each time an actor 503 // is visited, the depths of all upstream ports will be 504 // set, so by the time we get to the end of the list 505 // of actors, there will be nothing to do on them. 506 // But we have to visit all of them to support disconnected 507 // graphs. If the actors happen to be ordered in topological 508 // sort order, then this will be quite efficient. 509 // Otherwise, many actors will be visited twice. 510 List<Actor> actors = ((CompositeEntity) _actor).deepEntityList(); 511 for (Actor actor : actors) { 512 // If the actor already has a depth, skip it. 513 Integer actorDepth = _actorToDepth.get(actor); 514 if (actorDepth != null) { 515 continue; 516 } 517 // First compute the depth of the input ports, 518 // recording the maximum value. 519 Integer maximumInputDepth = Integer.valueOf(0); 520 Iterator inputPorts = actor.inputPortList().iterator(); 521 while (inputPorts.hasNext()) { 522 IOPort inputPort = (IOPort) inputPorts.next(); 523 Integer inputPortDepth = _portToDepth.get(inputPort); 524 if (inputPortDepth == null) { 525 // Keep track of ports that have been visited in 526 // to detect causality loops. Have to separately 527 // keep track of inputs and outputs because a port 528 // may be both, but the output may not depend on the 529 // input (an example is the ChannelPort in a wireless channel). 530 Set<IOPort> visitedInputs = new HashSet<IOPort>(); 531 Set<IOPort> visitedOutputs = new HashSet<IOPort>(); 532 _computeInputDepth(inputPort, visitedInputs, 533 visitedOutputs); 534 inputPortDepth = _portToDepth.get(inputPort); 535 } 536 // Record the maximum and continue to the next port. 537 if (inputPortDepth == null) { 538 throw new InternalErrorException(inputPort, null, 539 "inputPortDepth is null?"); 540 } 541 if (inputPortDepth.compareTo(maximumInputDepth) > 0) { 542 maximumInputDepth = inputPortDepth; 543 } 544 } 545 546 // Next set the depth of the output ports. 547 Integer minimumOutputDepth = null; 548 Iterator outputPorts = actor.outputPortList().iterator(); 549 while (outputPorts.hasNext()) { 550 IOPort outputPort = (IOPort) outputPorts.next(); 551 Integer outputPortDepth = _portToDepth.get(outputPort); 552 if (outputPortDepth == null) { 553 // Keep track of ports that have been visited in 554 // to detect causality loops. Have to separately 555 // keep track of inputs and outputs because a port 556 // may be both, but the output may not depend on the 557 // input (an example is the ChannelPort in a wireless channel). 558 Set<IOPort> visitedInputs = new HashSet<IOPort>(); 559 Set<IOPort> visitedOutputs = new HashSet<IOPort>(); 560 _computeOutputPortDepth(outputPort, visitedInputs, 561 visitedOutputs); 562 outputPortDepth = _portToDepth.get(outputPort); 563 } 564 // Record the minimum and continue to the next port. 565 if (minimumOutputDepth == null 566 || outputPortDepth.compareTo(minimumOutputDepth) < 0) { 567 minimumOutputDepth = outputPortDepth; 568 } 569 } 570 571 // Finally, set the depth of the actor. 572 if (minimumOutputDepth != null) { 573 // There are output ports. 574 _actorToDepth.put(actor, minimumOutputDepth); 575 } else { 576 _actorToDepth.put(actor, maximumInputDepth); 577 } 578 } 579 // Next need to set the depth of all output ports of the 580 // actor. 581 List<IOPort> outputPorts = _actor.outputPortList(); 582 for (IOPort outputPort : outputPorts) { 583 // The depth of each output port is one plus the maximum 584 // depth of all the output ports (or input ports) that source 585 // it data, or zero if there are no such ports. 586 int depth = 0; 587 List<IOPort> sourcePorts = outputPort.insideSourcePortList(); 588 for (IOPort sourcePort : sourcePorts) { 589 Integer sourceDepth = _portToDepth.get(sourcePort); 590 if (sourceDepth == null) { 591 if (!sourcePort.isOutput()) { 592 // This is another external input. 593 // Should this be checked? 594 sourceDepth = Integer.valueOf(0); 595 _portToDepth.put(sourcePort, sourceDepth); 596 } else { 597 // Source port is an output. Have to separately 598 // keep track of inputs and outputs because a port 599 // may be both, but the output may not depend on the 600 // input (an example is the ChannelPort in a wireless channel). 601 Set<IOPort> visitedInputs = new HashSet<IOPort>(); 602 Set<IOPort> visitedOutputs = new HashSet<IOPort>(); 603 _computeOutputPortDepth(sourcePort, visitedInputs, 604 visitedOutputs); 605 sourceDepth = _portToDepth.get(sourcePort); 606 if (sourceDepth == null) { 607 throw new InternalErrorException( 608 "Failed to compute port depth for " 609 + sourcePort.getFullName()); 610 } 611 } 612 } 613 int newDepth = sourceDepth.intValue() + 1; 614 if (newDepth > depth) { 615 depth = newDepth; 616 } 617 } 618 _portToDepth.put(outputPort, Integer.valueOf(depth)); 619 } 620 _actorDepthVersion = ((NamedObj) _actor).workspace().getVersion(); 621 } 622 623 /////////////////////////////////////////////////////////////////// 624 //// private methods //// 625 626 /** Compute the depth of the specified input port. 627 * The depth of an input port is maximum of the 628 * "source depths" of all the input ports in the 629 * same equivalence class with the specified port. 630 * An equivalence class is defined as follows. 631 * If ports X and Y each have a dependency not equal to the 632 * default dependency's oPlusIdentity(), then they 633 * are in an equivalence class. That is, 634 * there is a causal dependency. They are also in 635 * the same equivalence class if there is a port Z 636 * in an equivalence class with X and in an equivalence 637 * class with Y. Otherwise, they are not in the same 638 * equivalence class. If there are no 639 * output ports, then all the input ports 640 * are in a single equivalence class. 641 * <p> 642 * The "source depth" of an input port is one plus the maximum 643 * depth of all output ports that directly source data 644 * to it, or zero if there are no such ports. 645 * This delays the firing of this actor until 646 * all source actors have fired. As a side 647 * effect, this method also sets the depth of all the 648 * input ports in the same equivalence class, as well 649 * as all upstream ports up to a break with non-causal 650 * relationship. It also detects and reports dependency 651 * cycles. The entry point 652 * to this method is _computeActorDepth(). 653 * @see #_computeActorDepth() 654 * @param inputPort The port to compute the depth of. 655 * @param visitedInputs The set of input ports that have been visited 656 * in this round. 657 * @param visitedOutputs The set of output ports that have been visited 658 * in this round. 659 * @exception IllegalActionException If a zero-delay loop is found. 660 */ 661 private void _computeInputDepth(IOPort inputPort, Set<IOPort> visitedInputs, 662 Set<IOPort> visitedOutputs) throws IllegalActionException { 663 int depth = 0; 664 // Iterate over all the ports in the equivalence class. 665 Actor actor = (Actor) inputPort.getContainer(); 666 CausalityInterface causality = actor.getCausalityInterface(); 667 Collection<IOPort> equivalentPorts = causality 668 .equivalentPorts(inputPort); 669 for (IOPort equivalentPort : equivalentPorts) { 670 visitedInputs.add(equivalentPort); 671 // Iterate over the source ports to compute the source depths. 672 List<IOPort> sourcePorts = equivalentPort.sourcePortList(); 673 for (IOPort sourcePort : sourcePorts) { 674 // NOTE: source port may be an input port, in which case 675 // the port is an external input and should have depth 0. 676 // The input port depends directly on this output port. 677 // Find the depth of the output port. 678 Integer sourcePortDepth = _portToDepth.get(sourcePort); 679 if (sourcePortDepth == null) { 680 // Have to compute the depth of the output port. 681 if (visitedOutputs.contains(sourcePort)) { 682 // Found a causality loop. 683 throw new IllegalActionException(_actor, actor, 684 "Found a zero delay loop containing " 685 + actor.getFullName()); 686 } 687 if (!sourcePort.isOutput()) { 688 // This had better be an external input. 689 // Should this be checked? 690 // Set the depth to zero. 691 sourcePortDepth = Integer.valueOf(0); 692 _portToDepth.put(sourcePort, sourcePortDepth); 693 } else { 694 // Source port is an output (it may also be 695 // an input). 696 _computeOutputPortDepth(sourcePort, visitedInputs, 697 visitedOutputs); 698 sourcePortDepth = _portToDepth.get(sourcePort); 699 if (sourcePortDepth == null) { 700 throw new InternalErrorException( 701 "Failed to compute port depth for " 702 + sourcePort.getFullName()); 703 } 704 } 705 } 706 // The depth needs to be one greater than any 707 // output port it depends on. 708 int newDepth = sourcePortDepth.intValue() + 1; 709 if (depth < newDepth) { 710 depth = newDepth; 711 } 712 } 713 } 714 // We have now found the depth for the equivalence class. 715 // Set it. 716 for (IOPort equivalentPort : equivalentPorts) { 717 _portToDepth.put(equivalentPort, Integer.valueOf(depth)); 718 } 719 } 720 721 /** Compute the depth of the specified output port. 722 * The depth of an output port is 723 * the maximum of the depth of all the input ports 724 * it directly depends on, or zero if there are no such 725 * input ports. The entry point 726 * to this method is _computeActorDepth(). 727 * @see #_computeActorDepth() 728 * @param outputPort The actor to compute the depth of. 729 * @param visitedInputs The set of input ports that have been visited 730 * in this round. 731 * @param visitedOutputs The set of output ports that have been visited 732 * in this round. 733 * @exception IllegalActionException If a zero-delay loop is found. 734 */ 735 private void _computeOutputPortDepth(IOPort outputPort, 736 Set<IOPort> visitedInputs, Set<IOPort> visitedOutputs) 737 throws IllegalActionException { 738 visitedOutputs.add(outputPort); 739 int depth = 0; 740 // Iterate over the input ports of the same actor that 741 // this output directly depends on. 742 Actor actor = (Actor) outputPort.getContainer(); 743 CausalityInterface causality = actor.getCausalityInterface(); 744 for (IOPort inputPort : causality.dependentPorts(outputPort)) { 745 // if outputPort is an IO port then inputPort can be an output 746 // port as well. Check here and ignore if inputPort is actually 747 // an output port. 748 if (inputPort.isInput()) { 749 750 // The output port depends directly on this input port. 751 // Find the depth of the input port. 752 Integer inputPortDepth = _portToDepth.get(inputPort); 753 if (inputPortDepth == null) { 754 // Have to compute the depth of the input port. 755 if (visitedInputs.contains(inputPort)) { 756 // Found a causality loop. 757 throw new IllegalActionException(_actor, actor, 758 "Found a zero delay loop containing " 759 + actor.getFullName()); 760 } 761 // The following computes only the source depth, 762 // which may not be the final depth. As a consequence, 763 // _computeActorDepth(), the output depth that 764 // we calculate here may need to be modified. 765 _computeInputDepth(inputPort, visitedInputs, 766 visitedOutputs); 767 inputPortDepth = _portToDepth.get(inputPort); 768 if (inputPortDepth == null) { 769 throw new InternalErrorException( 770 "Failed to compute port depth for " 771 + inputPort.getFullName()); 772 } 773 } 774 775 int newDepth = inputPortDepth.intValue(); 776 if (depth < newDepth) { 777 depth = newDepth; 778 } 779 } 780 } 781 _portToDepth.put(outputPort, Integer.valueOf(depth)); 782 } 783 784 /** Record a dependency of the specified port on the specified 785 * input port in the specified map. The map records all the 786 * dependencies for this particular input port. 787 * If there was a prior dependency already 788 * that was less than this one, then update the dependency 789 * using its oPlus() method. If the dependency is equal 790 * to the oPlusIdentity(), then do not record it and return false. 791 * Return true if the dependency was newly set or modified from 792 * a previously recorded dependency. Return false if no change 793 * was made to a previous dependency. 794 * @param inputPort The source port. 795 * @param port The destination port, which may be an output 796 * port of the associated actor, or any port in a contained 797 * actor. 798 * @param map The map in which to record the dependency. 799 * @param dependency The dependency map for ports reachable from the input port. 800 * @param dependsOnInputsMap The map from ports in the model to input ports 801 * that they depend on, used to construct the equivalence classes. 802 * @return True if the dependency was changed. 803 */ 804 private boolean _recordDependency(IOPort inputPort, IOPort port, 805 Map<IOPort, Dependency> map, Dependency dependency, 806 Map<IOPort, Collection<IOPort>> dependsOnInputsMap) 807 throws IllegalActionException { 808 if (dependency.equals(_defaultDependency.oPlusIdentity())) { 809 return false; 810 } 811 // First, update the equivalence classes. 812 // Construct a set that merges all known input port dependencies 813 // for this port with any known equivalents of the input port. 814 Collection<IOPort> merged = _equivalenceClasses.get(inputPort); 815 if (merged == null) { 816 merged = new HashSet<IOPort>(); 817 merged.add(inputPort); 818 _equivalenceClasses.put(inputPort, merged); 819 } 820 821 // If the port is not already entered in the dependsOnInputsMap, 822 // then enter it. The entry will eventually be 823 // all the actor input ports that this port depends on. 824 Collection<IOPort> dependsOn = dependsOnInputsMap.get(port); 825 if (dependsOn != null) { 826 // Make sure to include any previously found dependencies. 827 merged.addAll(dependsOn); 828 } 829 // If this port has equivalents, and those have dependencies, 830 // then those dependencies need to be added. It can only have 831 // equivalents if it is an input port. 832 if (port.isInput()) { 833 Collection<IOPort> equivalents = ((Actor) port.getContainer()) 834 .getCausalityInterface().equivalentPorts(port); 835 for (IOPort equivalent : equivalents) { 836 // This is guaranteed to include port. 837 Collection<IOPort> otherInputs = dependsOnInputsMap 838 .get(equivalent); 839 if (otherInputs != null) { 840 merged.addAll(otherInputs); 841 // For each of the other inputs, it may have 842 // equivalents. Add those. 843 for (IOPort dependentInputPort : otherInputs) { 844 // Get the equivalence class for another port depended on. 845 Collection<IOPort> equivalenceClass = _equivalenceClasses 846 .get(dependentInputPort); 847 if (equivalenceClass != null) { 848 merged.addAll(equivalenceClass); 849 } 850 } 851 } 852 } 853 } 854 855 // For every input port in the merged set, record the equivalence 856 // to the merged set. 857 for (IOPort mergedInput : merged) { 858 _equivalenceClasses.put(mergedInput, merged); 859 } 860 dependsOnInputsMap.put(port, merged); 861 862 // Next update the forward and reverse dependencies. 863 // If the port belongs to the associated actor, 864 // make a permanent record. 865 Map<IOPort, Dependency> forward = null; 866 Map<IOPort, Dependency> reverse = null; 867 if (port.getContainer() == _actor) { 868 forward = _forwardDependencies.get(inputPort); 869 if (forward == null) { 870 forward = new HashMap<IOPort, Dependency>(); 871 _forwardDependencies.put(inputPort, forward); 872 } 873 forward.put(port, dependency); 874 875 reverse = _reverseDependencies.get(port); 876 if (reverse == null) { 877 reverse = new HashMap<IOPort, Dependency>(); 878 _reverseDependencies.put(port, reverse); 879 } 880 reverse.put(inputPort, dependency); 881 } 882 Dependency priorDependency = map.get(port); 883 if (priorDependency == null) { 884 map.put(port, dependency); 885 return true; 886 } 887 // There is a prior dependency. 888 Dependency newDependency = priorDependency.oPlus(dependency); 889 if (!newDependency.equals(priorDependency)) { 890 // Update the dependency. 891 map.put(port, newDependency); 892 if (port.getContainer() == _actor) { 893 // Have to also change the forward and reverse dependencies. 894 reverse.put(inputPort, newDependency); 895 forward.put(port, newDependency); 896 } 897 return true; 898 } 899 // No change made to the dependency. 900 return false; 901 } 902 903 /** Set the dependency from the specified inputPort to all 904 * ports that are reachable via the portsToProcess ports. 905 * The results are stored in the specified map. 906 * @param inputPort An input port of this actor. 907 * @param map A map of dependencies from this input port to all reachable ports, 908 * built by this method. The map is required to contain all ports in portsToProcess 909 * on entry. 910 * @param portsToProcess Ports connected to the input port directly or indirectly. 911 * @param dependsOnInputsMap The map from ports in the model to input ports 912 * that they depend on, used to construct the equivalence classes. 913 * @exception IllegalActionException Not thrown in this base class. 914 */ 915 private void _setDependency(IOPort inputPort, Map<IOPort, Dependency> map, 916 Collection<IOPort> portsToProcess, 917 Map<IOPort, Collection<IOPort>> dependsOnInputsMap) 918 throws IllegalActionException { 919 Set<IOPort> portsToProcessNext = new HashSet<IOPort>(); 920 for (IOPort port : portsToProcess) { 921 // The argument map is required to contain this dependency. 922 Dependency dependency = map.get(port); 923 // Next, check whether we have gotten to an output port of this actor. 924 if (port.getContainer() == _actor) { 925 // Port is owned by this actor. If it is 926 // output port, then it is dependent on this 927 // input port by the given dependency. It should 928 // not normally be an input port, but we tolerate 929 // that here in case some domain uses it someday. 930 // In that latter case, there is no dependency. 931 if (port.isOutput()) { 932 // We have a path from an input to an output. 933 // Record the dependency. 934 _recordDependency(inputPort, port, map, dependency, 935 dependsOnInputsMap); 936 } 937 } else { 938 // The port presumably belongs to an actor inside this actor. 939 _recordDependency(inputPort, port, map, dependency, 940 dependsOnInputsMap); 941 // Next record the dependency that all output ports of 942 // the actor containing the port have on the input port. 943 Actor actor = (Actor) port.getContainer(); 944 CausalityInterface causality = actor.getCausalityInterface(); 945 Iterator outputPorts = actor.outputPortList().iterator(); 946 while (outputPorts.hasNext()) { 947 IOPort outputPort = (IOPort) outputPorts.next(); 948 Dependency actorDependency = causality.getDependency(port, 949 outputPort); 950 Dependency newDependency = dependency 951 .oTimes(actorDependency); 952 if (_recordDependency(inputPort, outputPort, map, 953 newDependency, dependsOnInputsMap)) { 954 // Dependency of this output port has been set or 955 // changed. Add ports to the set of ports to be 956 // processed next. 957 Collection sinkPorts = outputPort.sinkPortList(); 958 Iterator sinkPortsIterator = sinkPorts.iterator(); 959 while (sinkPortsIterator.hasNext()) { 960 IOPort sinkPort = (IOPort) sinkPortsIterator.next(); 961 _recordDependency(inputPort, sinkPort, map, 962 newDependency, dependsOnInputsMap); 963 if (sinkPort.getContainer() != _actor) { 964 // Port is not owned by this actor. 965 // Further processing will be needed. 966 portsToProcessNext.add(sinkPort); 967 } 968 } 969 } 970 } 971 } 972 } 973 if (!portsToProcessNext.isEmpty()) { 974 _setDependency(inputPort, map, portsToProcessNext, 975 dependsOnInputsMap); 976 } 977 } 978 979 /////////////////////////////////////////////////////////////////// 980 //// private variables //// 981 982 /** A table giving the depths of ports. */ 983 private Map<IOPort, Integer> _portToDepth = null; 984 985 /////////////////////////////////////////////////////////////////// 986 //// inner classes //// 987 988 /** Comparator used to sort the actors. */ 989 private class ActorComparator implements Comparator<Actor> { 990 /** Compare the depths of two actors. 991 * NOTE: This method assumes and does not check that the 992 * depth cache is up to date and contains both specified actors. 993 */ 994 @Override 995 public int compare(Actor actor1, Actor actor2) { 996 Integer level1 = _actorToDepth.get(actor1); 997 Integer level2 = _actorToDepth.get(actor2); 998 return level1.compareTo(level2); 999 } 1000 } 1001}