001/* Causality interface where all outputs depend on all inputs.
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.HashMap;
032import java.util.HashSet;
033import java.util.Iterator;
034import java.util.LinkedList;
035import java.util.List;
036import java.util.Map;
037import java.util.Set;
038
039import ptolemy.actor.Actor;
040import ptolemy.actor.IOPort;
041import ptolemy.actor.parameters.ParameterPort;
042import ptolemy.kernel.util.IllegalActionException;
043
044///////////////////////////////////////////////////////////////////
045//// DefaultCausalityInterface
046
047/**
048 This class provides causality interfaces for actor networks as described
049 in the paper "Causality Interfaces for Actor Networks" by Ye Zhou and
050 Edward A. Lee, ACM Transactions on Embedded Computing Systems (TECS),
051 April 2008, as available as <a href="http://www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-148.pdf">
052 Technical Report No. UCB/EECS-2006-148</a>,
053 November 16, 2006.  Specifically, this class represents a simple
054 default causality interface where every output port depends on every
055 input port, unless {@link #removeDependency(IOPort, IOPort)} has
056 been called to prune the dependencies. In the latter case, the dependencies
057 are pruned to not include the ones that were specified in that call.
058 <p>
059 In all cases, if there is any PortParameter in the actor, then all
060 input ports become members of the same equivalence class, regardless
061 of what dependencies have been pruned using
062 {@link #removeDependency(IOPort, IOPort)}. The reason for this is
063 that any output, present or future, may depend on the values at
064 such port parameters. In particular, it is necessary for inputs
065 on these port parameters to be present when any other input is
066 processed because it affects the parameters of the actor.
067 <p>
068 Causality interfaces represent dependencies between input and output ports
069 of an actor and can be used to perform scheduling or static analysis
070 on actor models.
071
072 @author Edward A. Lee
073 @version $Id$
074 @since Ptolemy II 8.0
075 @Pt.ProposedRating Yellow (eal)
076 @Pt.AcceptedRating Red (eal)
077 */
078public class DefaultCausalityInterface implements CausalityInterface {
079
080    /** Construct a causality interface for the specified actor.
081     *  @param actor The actor for which this is a causality interface.
082     *  @param defaultDependency The default dependency of an output
083     *   port on an input port.
084     */
085    public DefaultCausalityInterface(Actor actor,
086            Dependency defaultDependency) {
087        _actor = actor;
088        _defaultDependency = defaultDependency;
089    }
090
091    ///////////////////////////////////////////////////////////////////
092    ////                         public methods                    ////
093
094    /** Set the dependency that the specified output port has
095     *  on the specified input port to represent a time
096     *  delay with the specified value and superdense time index.
097     *  This method is adaptive to the type
098     *  of Dependency provided in the constructor. For example, if the
099     *  constructor provides a BooleanDependency, then
100     *  this method defines the dependency to be TRUE if
101     *  the timeDelay is 0.0 and the index is 0, and otherwise it should
102     *  be FALSE. If the Dependency is a RealDependency, then it uses
103     *  the <i>timeDelay</i> argument to define the dependency.
104     *  If it is a SuperdenseDependency, then it uses both the <i>timeDelay</i>
105     *  and the <i>index</i>.
106     *  @param input The input port.
107     *  @param output The output port with a time delay dependency on the
108     *   input port.
109     *  @param timeDelay The time delay.
110     *  @param index The superdense time index.
111     */
112    @Override
113    public void declareDelayDependency(IOPort input, IOPort output,
114            double timeDelay, int index) {
115        if (_defaultDependency instanceof SuperdenseDependency) {
116            if (timeDelay != 0.0 || index != 0) {
117                _removeDependency(input, output);
118            }
119            // Store the time delay.
120            Dependency dependency = SuperdenseDependency.valueOf(timeDelay,
121                    index);
122            if (_delayDependencies == null) {
123                _delayDependencies = new HashMap<IOPort, Map<IOPort, Dependency>>();
124            }
125            Map<IOPort, Dependency> entry = new HashMap<IOPort, Dependency>();
126            entry.put(output, dependency);
127            _delayDependencies.put(input, entry);
128        } else if (_defaultDependency instanceof RealDependency) {
129            if (timeDelay != 0.0) {
130                _removeDependency(input, output);
131            }
132            // Store the time delay.
133            Dependency dependency = RealDependency.valueOf(timeDelay);
134            if (_delayDependencies == null) {
135                _delayDependencies = new HashMap<IOPort, Map<IOPort, Dependency>>();
136            }
137            Map<IOPort, Dependency> entry = new HashMap<IOPort, Dependency>();
138            entry.put(output, dependency);
139            _delayDependencies.put(input, entry);
140        } else { // the dependency is a BooleanDependency.
141            _removeDependency(input, output);
142        }
143    }
144
145    /** Return a collection of the ports in this actor that depend on
146     *  or are depended on by the specified port. A port X depends
147     *  on a port Y if X is an output and Y is an input and
148     *  getDependency(X,Y) returns oTimesIdentity()
149     *  of the default dependency specified in the constructor.
150     *  <p>
151     *  This base class presumes (but does not check) that the
152     *  argument is a port contained by the associated actor.
153     *  By default,
154     *  if the actor is an input, then it returns a collection of
155     *  all the outputs. If the actor is output, then it returns
156     *  a collection of all the inputs.
157     *  However, if {@link #removeDependency(IOPort, IOPort)}
158     *  has been called, then it prunes the results to not include
159     *  ports where the dependency has been explicitly removed.
160     *  <p>
161     *  Derived classes may override this, but they may need to
162     *  also override {@link #getDependency(IOPort, IOPort)}
163     *  and {@link #equivalentPorts(IOPort)} to be consistent.
164     *  @param port The port to find the dependents of.
165     *  @return a collection of ports that depend on or are depended on
166     *  by the specified port.
167     *  @exception IllegalActionException Not thrown in this base class.
168     */
169    @Override
170    public Collection<IOPort> dependentPorts(IOPort port)
171            throws IllegalActionException {
172        if (_forwardPrunedDependencies == null) {
173            // removeDependency() has not been called, so this is the simple case.
174            if (port.isOutput()) {
175                if (port.isInput()) {
176                    // Port is both input and output.
177                    HashSet<IOPort> result = new HashSet<IOPort>();
178                    result.addAll(_actor.inputPortList());
179                    result.addAll(_actor.outputPortList());
180                    return result;
181                }
182                // Port is output and not input.
183                return _actor.inputPortList();
184            } else if (port.isInput()) {
185                // Port is input and not output.
186                return _actor.outputPortList();
187            } else {
188                // Port is neither input nor output.
189                return _EMPTY_COLLECTION;
190            }
191        }
192        // removeDependency() has been called.  Prune results.
193        Collection<IOPort> result;
194        if (port.isOutput()) {
195            if (port.isInput()) {
196                // Port is both input and output.
197                result = new HashSet<IOPort>();
198                result.addAll(_actor.inputPortList());
199                result.addAll(_actor.outputPortList());
200                Set<IOPort> inputPorts = _backwardPrunedDependencies.get(port);
201                if (inputPorts != null) {
202                    result.removeAll(inputPorts);
203                }
204                Set<IOPort> outputPorts = _forwardPrunedDependencies.get(port);
205                if (outputPorts != null) {
206                    result.removeAll(outputPorts);
207                }
208            } else {
209                // Port is output and not input.
210                Set<IOPort> inputPorts = _backwardPrunedDependencies.get(port);
211                if (inputPorts == null) {
212                    // No dependencies have been pruned for this output port.
213                    result = _actor.inputPortList();
214                } else {
215                    result = new HashSet<IOPort>();
216                    result.addAll(_actor.inputPortList());
217                    result.removeAll(inputPorts);
218                }
219            }
220        } else if (port.isInput()) {
221            // Port is input and not output.
222            Set<IOPort> outputPorts = _forwardPrunedDependencies.get(port);
223            if (outputPorts == null) {
224                // No dependencies have been pruned for this input port.
225                result = _actor.outputPortList();
226            } else {
227                result = new HashSet<IOPort>();
228                result.addAll(_actor.outputPortList());
229                result.removeAll(outputPorts);
230            }
231        } else {
232            result = new HashSet<IOPort>();
233        }
234        return result;
235    }
236
237    /** Return a collection of the input ports in this actor that are
238     *  in the same equivalence class with the specified input
239     *  port. This base class returns a collection of all
240     *  the input ports of the associated actor, unless
241     *  removeDependencies() has been called. In the latter
242     *  case, it constructs the equivalence class based on
243     *  the remaining dependencies on output ports, unless
244     *  there is an instance of PortParameter in the actor.
245     *  In that case, it again returns a collection of all
246     *  the input ports.
247     *  <p>
248     *  If derived classes override this, they may also
249     *  need to override {@link #getDependency(IOPort,IOPort)}
250     *  and {@link #dependentPorts(IOPort)} to be consistent.
251     *  The returned result should always include the specified input port.
252     *  <p>
253     *  An equivalence class is defined as follows.
254     *  If input ports X and Y each have a dependency on any
255     *  common port or on two equivalent ports, or they each can
256     *  affect the state of the associated actor, then they
257     *  are in an equivalence class. That is,
258     *  there is a causal dependency. They are also in
259     *  the same equivalence class if there is a port Z
260     *  in an equivalence class with X and in an equivalence
261     *  class with Y. If the actor has any instance of
262     *  ParameterPort, then all input ports are in the same
263     *  equivalence class. Otherwise, they are not in the same
264     *  equivalence class.
265     *  @param input The port to find the equivalence class of.
266     *  @return set of the input ports in this actor that are
267     *  in an equivalence class with the specified input.
268     *  @exception IllegalArgumentException If the argument is not
269     *   contained by the associated actor.
270     *  @exception IllegalActionException Not thrown in this base class.
271     */
272    @Override
273    public Collection<IOPort> equivalentPorts(IOPort input)
274            throws IllegalActionException {
275        if (input.getContainer() != _actor || !input.isInput()) {
276            throw new IllegalArgumentException(
277                    "equivalentPort() called with argument "
278                            + input.getFullName()
279                            + " that is not an input port for "
280                            + _actor.getFullName());
281        }
282        if (_forwardPrunedDependencies == null) {
283            // removeDependencies() has not been called, so this is the
284            // simple case.
285            return _actor.inputPortList();
286        }
287        // FIXME: Should the result be cached?
288        // If removeDependencies() has been called, finding the equivalent
289        // ports is rather complicated. Presumably, this can only change
290        // if ports are added or removed from the actor, or if
291        // removeDependency() is called.
292        List<IOPort> inputs = _actor.inputPortList();
293        // If there is an instance of ParameterPort, then return the
294        // whole collection of input ports.
295        for (IOPort actorInput : inputs) {
296            if (actorInput instanceof ParameterPort) {
297                return _actor.inputPortList();
298            }
299        }
300        List<IOPort> outputs = _actor.outputPortList();
301        // Deal with simple cases first.
302        if (inputs.size() == 1 || outputs.size() == 0) {
303            // There is only one input port, or there are
304            // no output ports, so we are done.
305            return inputs;
306        }
307        HashSet<IOPort> result = new HashSet<IOPort>();
308        HashSet<IOPort> dependents = new HashSet<IOPort>();
309        _growDependencies(input, result, dependents);
310        return result;
311    }
312
313    /** Return the actor for which this is a dependency.
314     *  @return The actor for which this is a dependency.
315     */
316    @Override
317    public Actor getActor() {
318        return _actor;
319    }
320
321    /** Return the default dependency specified in the constructor.
322     *  @return The default dependency.
323     */
324    @Override
325    public Dependency getDefaultDependency() {
326        return _defaultDependency;
327    }
328
329    /** Return the dependency between the specified input port
330     *  and the specified output port.  This base class returns
331     *  the default dependency if the first port is an input
332     *  port owned by this actor and the second one is an output
333     *  port owned by this actor. Otherwise, it returns the
334     *  additive identity of the dependency. Also, if
335     *  {@link #removeDependency(IOPort, IOPort)} has been
336     *  called with the same two specified ports, then
337     *  this method will return the additive identity.
338     *  <p>
339     *  Derived classes should override this method to provide
340     *  actor-specific dependency information. If they do so,
341     *  then they may also need to override {@link #equivalentPorts(IOPort)}
342     *  and {@link #dependentPorts(IOPort)} to be consistent.
343     *  @param input The input port.
344     *  @param output The output port.
345     *  @return The dependency between the specified input port
346     *   and the specified output port.
347     *  @exception IllegalActionException Not thrown in this base class.
348     */
349    @Override
350    public Dependency getDependency(IOPort input, IOPort output)
351            throws IllegalActionException {
352        // If there is a recorded delay dependency, return that.
353        if (_delayDependencies != null) {
354            Map<IOPort, Dependency> entry = _delayDependencies.get(input);
355            if (entry != null) {
356                Dependency result = entry.get(output);
357                if (result != null) {
358                    return result;
359                }
360            }
361        }
362        if (input.isInput() && input.getContainer() == _actor
363                && output.isOutput() && output.getContainer() == _actor) {
364            if (_forwardPrunedDependencies != null) {
365                Set<IOPort> outputPorts = _forwardPrunedDependencies.get(input);
366                if (outputPorts != null && outputPorts.contains(output)) {
367                    // This dependency has been pruned.
368                    return _defaultDependency.oPlusIdentity();
369                }
370            }
371            return _defaultDependency;
372        }
373        return _defaultDependency.oPlusIdentity();
374    }
375
376    /** Return a description of the causality interfaces.
377     *  @return A description of the causality interfaces.
378     */
379    @Override
380    public String toString() {
381        StringBuffer result = new StringBuffer();
382        Iterator inputPorts = _actor.inputPortList().iterator();
383        while (inputPorts.hasNext()) {
384            IOPort inputPort = (IOPort) inputPorts.next();
385            result.append(inputPort.getName());
386            result.append(" has output dependencies as follows:\n");
387            Iterator outputPorts = _actor.outputPortList().iterator();
388            while (outputPorts.hasNext()) {
389                IOPort outputPort = (IOPort) outputPorts.next();
390                result.append("   ");
391                result.append(outputPort.getName());
392                result.append(": ");
393                try {
394                    result.append(getDependency(inputPort, outputPort));
395                } catch (IllegalActionException e) {
396                    result.append("EXCEPTION: ");
397                    result.append(e);
398                }
399                result.append("\n");
400            }
401        }
402        return result.toString();
403    }
404
405    /** Remove the dependency that the specified output port has
406     *  on the specified input port. Specifically, calling this
407     *  method ensures that getDependency(inputPort, outputPort)
408     *  will return defaultDependency.oPlusIdentity() instead
409     *  of the default defaultDependency.oTimesIdentity().
410     *  It also adjusts what is returned by
411     *  {@link #equivalentPorts(IOPort)} and
412     *  {@link #dependentPorts(IOPort)}.
413     *  @see #getDependency(IOPort, IOPort)
414     *  @param inputPort The input port.
415     *  @param outputPort The output port that does not depend on the
416     *   input port.
417     */
418    @Override
419    public void removeDependency(IOPort inputPort, IOPort outputPort) {
420        if (_defaultDependency instanceof SuperdenseDependency) {
421            // Set time delay to infinity.
422            Dependency dependency = SuperdenseDependency.OPLUS_IDENTITY;
423            if (_delayDependencies == null) {
424                _delayDependencies = new HashMap<IOPort, Map<IOPort, Dependency>>();
425            }
426            Map<IOPort, Dependency> entry = new HashMap<IOPort, Dependency>();
427            entry.put(outputPort, dependency);
428            _delayDependencies.put(inputPort, entry);
429        }
430        if (_defaultDependency instanceof RealDependency) {
431            // Set time delay to infinity.
432            Dependency dependency = RealDependency.OPLUS_IDENTITY;
433            if (_delayDependencies == null) {
434                _delayDependencies = new HashMap<IOPort, Map<IOPort, Dependency>>();
435            }
436            Map<IOPort, Dependency> entry = new HashMap<IOPort, Dependency>();
437            entry.put(outputPort, dependency);
438            _delayDependencies.put(inputPort, entry);
439        }
440        _removeDependency(inputPort, outputPort);
441    }
442
443    ///////////////////////////////////////////////////////////////////
444    ////                         protected methods                 ////
445
446    /** If the input port is already in the inputs set, do nothing
447     *  and return. Otherwise, add the input port to the inputs set,
448     *  and its output dependents to the outputs set. If any of those
449     *  output dependents were not already in the outputs set,
450     *  add them, and then recursively invoke this same method
451     *  on all input ports that depend on those outputs.
452     *  @param input The input port.
453     *  @param inputs The set of inputs to which input is added if
454     *  it is not already present.
455     *  @param outputs The set of output dependents to which the output
456     *  dependents are added if input was not in the inputs set.
457     *  @exception IllegalActionException Not thrown in this base class.
458     */
459    protected void _growDependencies(IOPort input, Set<IOPort> inputs,
460            Set<IOPort> outputs) throws IllegalActionException {
461        if (!inputs.contains(input)) {
462            inputs.add(input);
463            for (IOPort output : dependentPorts(input)) {
464                // If the output has already been handled, skip it.
465                if (!outputs.contains(output)) {
466                    outputs.add(output);
467                    for (IOPort anotherInput : dependentPorts(output)) {
468                        _growDependencies(anotherInput, inputs, outputs);
469                    }
470                }
471            }
472        }
473    }
474
475    ///////////////////////////////////////////////////////////////////
476    ////                         protected variables               ////
477
478    /** The associated actor. */
479    protected Actor _actor;
480
481    /** A record of removed dependencies from output to input, if any.
482     *  In this case, if the dependency between the ports are anything
483     *  other than the oTimesIdentity, then the dependency is removed. */
484    protected Map<IOPort, Set<IOPort>> _backwardPrunedDependencies;
485
486    /** Empty collection for use by dependentPort(). */
487    protected static final Collection<IOPort> _EMPTY_COLLECTION = new LinkedList<IOPort>();
488
489    /** The default dependency of an output port on an input port. */
490    protected Dependency _defaultDependency;
491
492    /** A record of delay dependencies from input to output, if any. */
493    protected Map<IOPort, Map<IOPort, Dependency>> _delayDependencies;
494
495    /** A record of removed dependencies from input to output, if any.
496     *  In this case, if the dependency between the ports are anything
497     *  other than the oTimesIdentity, then the dependency is removed. */
498    protected Map<IOPort, Set<IOPort>> _forwardPrunedDependencies;
499
500    ///////////////////////////////////////////////////////////////////
501    ////                         private methods                   ////
502
503    /** For the benefit of the dependentPorts() and equivalentPorts()
504     *  methods, remove the dependency that the input has on the output.
505     *  @param inputPort The input.
506     *  @param outputPort The output.
507     */
508    private void _removeDependency(IOPort inputPort, IOPort outputPort) {
509        if (_forwardPrunedDependencies == null) {
510            _forwardPrunedDependencies = new HashMap<IOPort, Set<IOPort>>();
511            _backwardPrunedDependencies = new HashMap<IOPort, Set<IOPort>>();
512        }
513        Set<IOPort> outputPorts = _forwardPrunedDependencies.get(inputPort);
514        if (outputPorts == null) {
515            outputPorts = new HashSet<IOPort>();
516            _forwardPrunedDependencies.put(inputPort, outputPorts);
517        }
518        outputPorts.add(outputPort);
519
520        Set<IOPort> inputPorts = _backwardPrunedDependencies.get(outputPort);
521        if (inputPorts == null) {
522            inputPorts = new HashSet<IOPort>();
523            _backwardPrunedDependencies.put(outputPort, inputPorts);
524        }
525        inputPorts.add(inputPort);
526    }
527}