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}