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}