001/* An actor that clones a composite actor containing itself into itself. 002 003 Copyright (c) 2003-2015 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.domains.ddf.lib; 029 030import java.io.IOException; 031import java.io.Writer; 032import java.util.Arrays; 033import java.util.HashMap; 034import java.util.Iterator; 035 036import ptolemy.actor.CompositeActor; 037import ptolemy.actor.IOPort; 038import ptolemy.actor.IORelation; 039import ptolemy.actor.QueueReceiver; 040import ptolemy.actor.Receiver; 041import ptolemy.actor.TypedCompositeActor; 042import ptolemy.actor.TypedIOPort; 043import ptolemy.actor.util.DFUtilities; 044import ptolemy.data.ArrayToken; 045import ptolemy.data.IntToken; 046import ptolemy.data.Token; 047import ptolemy.data.expr.Parameter; 048import ptolemy.data.expr.StringParameter; 049import ptolemy.data.expr.Variable; 050import ptolemy.data.type.BaseType; 051import ptolemy.data.type.Type; 052import ptolemy.domains.ddf.kernel.DDFDirector; 053import ptolemy.kernel.ComponentEntity; 054import ptolemy.kernel.CompositeEntity; 055import ptolemy.kernel.Port; 056import ptolemy.kernel.util.Attribute; 057import ptolemy.kernel.util.IllegalActionException; 058import ptolemy.kernel.util.InternalErrorException; 059import ptolemy.kernel.util.NameDuplicationException; 060 061/** 062 This actor performs actor recursion dynamically during execution. 063 Upon firing, it clones the composite actor which contains itself and 064 is referred to by the StringParameter <i>recursionActor</i>. It then 065 places the clone inside itself and connects the corresponding ports of 066 both actors. It uses a local DDFDirector to preinitialize the clone and 067 then transfers all tokens contained by input ports of this actor to the 068 connected opaque ports inside. It again uses the local DDFDirector to 069 initialize all actors contained by this actor and classifies each of them 070 such as their enabling and deferrable status. It then transfers all 071 tokens contained by output ports of this actor to the connected opaque 072 ports outside. It finally merges the local DDFDirector with its executive 073 DDFDirector and then removes the local DDFDirector. Thus during execution 074 this actor is fired at most once, after which the executive director 075 directly controls all actors inside. Since there is no type constraint 076 between input ports and output ports of this actor, users have to 077 manually configure types for all outputs of this actor. 078 079 @author Gang Zhou 080 @version $Id$ 081 @since Ptolemy II 4.1 082 @Pt.ProposedRating Yellow (zgang) 083 @Pt.AcceptedRating Yellow (cxh) 084 */ 085public class ActorRecursion extends TypedCompositeActor { 086 /** Create an ActorRecursion with a name and a container. 087 * The container argument must not be null, or a NullPointerException 088 * will be thrown. This actor will use the workspace of the container 089 * for synchronization and version counts. If the name argument is 090 * null, then the name is set to the empty string. Increment the 091 * version of the workspace. 092 * The actor creates a DDFDirector initially, which will be removed 093 * toward the end of firing this actor, when the director completes 094 * its responsibility of preinitializing and initializing the cloned 095 * composite actor and merging with the outside DDFDirector. 096 * @param container The container actor. 097 * @param name The name of this actor. 098 * @exception IllegalActionException If the container is incompatible 099 * with this actor. 100 * @exception NameDuplicationException If the name coincides with 101 * an actor already in the container. 102 */ 103 public ActorRecursion(CompositeEntity container, String name) 104 throws IllegalActionException, NameDuplicationException { 105 super(container, name); 106 new DDFDirector(this, uniqueName("DDFDirector")); 107 recursionActor = new StringParameter(this, "recursionActor"); 108 } 109 110 /////////////////////////////////////////////////////////////////// 111 //// parameters //// 112 113 /** A StringParameter representing the name of the composite actor 114 * to clone from. The composite actor contains this actor in some 115 * hierarchy. 116 */ 117 public StringParameter recursionActor; 118 119 /////////////////////////////////////////////////////////////////// 120 //// public methods //// 121 122 /** Clone the composite actor referred to by the StringParameter 123 * recursionActor into itself. Use a local DDFDirector to 124 * preinitialize all (just cloned) actors contained by this actor. 125 * Transfer all tokens contained by input ports of this actor to 126 * the connected opaque ports inside. Read rate parameters of 127 * input ports of all actors receiving tokens from this actor and 128 * propagate these parameters back to the connected output ports 129 * of this actor. Use the local DDFDirector to initialize all actors 130 * contained by this actor and classify each of them according to 131 * their enabling and deferrable status. Transfer all tokens contained 132 * by output ports of this actor to the connected opaque ports 133 * outside. Merge the local DDFDirector with the outside DDFDirector 134 * and finally remove local DDFDirector. 135 * @exception IllegalActionException If any called method throws 136 * IllegalActionException. 137 */ 138 @Override 139 public void fire() throws IllegalActionException { 140 // Don't call super.fire() here. It does not follow what a regular 141 // composite actor would do. 142 try { 143 // Disable redoing type resolution because type compatibility 144 // has been guaranteed during initialization. 145 ((DDFDirector) getExecutiveDirector()).disableTypeResolution(true); 146 ((DDFDirector) getDirector()).disableTypeResolution(true); 147 148 try { 149 _cloneRecursionActor(); 150 } catch (CloneNotSupportedException ex) { 151 throw new IllegalActionException(this, ex, "The actor " 152 + recursionActor.stringValue() + " cannot be cloned."); 153 } 154 155 getDirector().preinitialize(); 156 _transferInputs(); 157 _setOutputPortRate(); 158 getDirector().initialize(); 159 _transferOutputs(); 160 ((DDFDirector) getExecutiveDirector()) 161 .merge((DDFDirector) getDirector()); 162 163 try { 164 // get rid of the local director. 165 getDirector().setContainer(null); 166 } catch (NameDuplicationException ex) { 167 // should not happen. 168 throw new InternalErrorException(this, ex, null); 169 } 170 } finally { 171 ((DDFDirector) getExecutiveDirector()).disableTypeResolution(false); 172 } 173 } 174 175 /** Initialize this actor. First find the composite actor to be 176 * cloned, which is the first containing actor up in the hierarchy 177 * with the name referred to by the StringParameter recursionActor. 178 * Then check the compatibility of the found composite actor with 179 * this actor. It is only done once due to the recursive 180 * nature of this actor. 181 * @exception IllegalActionException If no actor is found with 182 * the given name or the found actor is not compatible. 183 */ 184 @Override 185 public void initialize() throws IllegalActionException { 186 _searchRecursionActor(); 187 188 if (!_isCompatibilityChecked) { 189 _checkCompatibility(); 190 } 191 } 192 193 /** Override the base class to return false. Upon seeing the return 194 * value, its executive director disables this actor and only fires 195 * all inside actors next time. 196 * @return false. 197 * @exception IllegalActionException Not thrown in this base class. 198 */ 199 @Override 200 public boolean postfire() throws IllegalActionException { 201 return false; 202 } 203 204 /** Write a MoML description of the contents of this object. 205 * Override the base class to describe contained ports and 206 * attributes, but not inside entities, links and relations 207 * created during execution. 208 * @param output The output to write to. 209 * @param depth The depth in the hierarchy, to determine indenting. 210 * @exception IOException If an I/O error occurs. 211 */ 212 @Override 213 protected void _exportMoMLContents(Writer output, int depth) 214 throws IOException { 215 Iterator attributes = attributeList().iterator(); 216 217 while (attributes.hasNext()) { 218 Attribute attribute = (Attribute) attributes.next(); 219 attribute.exportMoML(output, depth); 220 } 221 222 Iterator ports = portList().iterator(); 223 224 while (ports.hasNext()) { 225 Port port = (Port) ports.next(); 226 port.exportMoML(output, depth); 227 } 228 } 229 230 /** Notify this actor that the given entity has been added inside it. 231 * Override the base class to do nothing. This will prevent it from 232 * calling requestInitialization(Actor) to the cloned composite actor. 233 * The preinitialization and initialization have already been done in 234 * the fire() method. 235 */ 236 @Override 237 protected void _finishedAddEntity(ComponentEntity entity) { 238 } 239 240 /////////////////////////////////////////////////////////////////// 241 //// private methods //// 242 243 /** Check the compatibility of the to-be-cloned composite actor with 244 * this actor. The compatibility criteria are self-evident in the code. 245 * Basically two actors should look the same from outside and it should 246 * not violate type constraints by placing the cloned composite actor 247 * inside this actor and connecting the corresponding ports. 248 * @exception IllegalActionException If the composite actor to be 249 * cloned is not compatible with this actor. 250 */ 251 private void _checkCompatibility() throws IllegalActionException { 252 if (!(getExecutiveDirector() instanceof DDFDirector)) { 253 throw new IllegalActionException(this, 254 "The executive Director must be a DDFDirector."); 255 } 256 257 if (_recursionActor.inputPortList().size() != inputPortList().size() 258 || _recursionActor.outputPortList().size() != outputPortList() 259 .size()) { 260 throw new IllegalActionException(this, 261 "The recursionActor " + recursionActor.stringValue() 262 + " must have the same number of input ports and " 263 + "same number of output ports as this actor."); 264 } 265 266 Iterator ports = portList().iterator(); 267 268 while (ports.hasNext()) { 269 TypedIOPort port = (TypedIOPort) ports.next(); 270 Object matching = _recursionActor.getPort(port.getName()); 271 272 if (matching == null) { 273 throw new IllegalActionException(this, 274 "Each port of this actor must have the same name as " 275 + "the matching port of the recursionActor " 276 + recursionActor.stringValue() 277 + ". However, the port " + port.getFullName() 278 + " does not have a matching " 279 + "port with the same name."); 280 } 281 282 TypedIOPort matchingPort = (TypedIOPort) matching; 283 284 if (port.getWidth() != matchingPort.getWidth()) { 285 throw new IllegalActionException(this, 286 "The matching ports: " + port.getFullName() + " and " 287 + matchingPort.getFullName() 288 + " must have the same width. Port " 289 + port.getFullName() + "'s width " 290 + port.getWidth() + " is not equal to " 291 + matchingPort.getFullName() + "'s width " 292 + matchingPort.getWidth() + "."); 293 } 294 295 if (port.isInput() && !matchingPort.isInput() 296 || port.isOutput() && !matchingPort.isOutput()) { 297 throw new IllegalActionException(this, 298 "The matching ports: " + port.getFullName() + " and " 299 + matchingPort.getFullName() 300 + " must be both input ports or output ports."); 301 } 302 303 Type portType = port.getType(); 304 Type matchingPortType = matchingPort.getType(); 305 306 if (port.isInput() && !matchingPortType.isCompatible(portType)) { 307 throw new IllegalActionException(this, 308 "The type of the port " + port.getName() 309 + " of the actor " + getName() 310 + " must be equal to or less than " 311 + "that of the matching port."); 312 } 313 314 if (port.isOutput() && !portType.isCompatible(matchingPortType)) { 315 throw new IllegalActionException(this, 316 "The type of the port " + port.getName() 317 + " of the actor " + getName() 318 + " must be equal to or greater than " 319 + "that of the matching port."); 320 } 321 } 322 323 _isCompatibilityChecked = true; 324 } 325 326 /** Clone the composite actor into the same workspace as this actor. 327 * Set its container to this actor. Store all tokens contained by 328 * input ports of this actor. Connect the corresponding ports of 329 * this actor and the cloned composite actor. 330 * @exception IllegalActionException If any called method throws 331 * IllegalActionException, or NameDuplicationException is caught 332 * in this method. 333 * @exception CloneNotSupportedException If the CompositeActor cannot 334 * be cloned. 335 */ 336 private void _cloneRecursionActor() 337 throws IllegalActionException, CloneNotSupportedException { 338 try { 339 // Clone the composite actor. 340 CompositeActor clone = (CompositeActor) _recursionActor 341 .clone(workspace()); 342 343 // Place the clone inside this actor. 344 clone.setContainer(this); 345 346 // i is used to generate different names for new relations. 347 int i = 0; 348 Iterator ports = portList().iterator(); 349 350 _inputTokensHolder.clear(); 351 352 while (ports.hasNext()) { 353 IOPort port = (IOPort) ports.next(); 354 355 // Store all tokens contained by input ports of this actor 356 // because connecting ports will result in creating receivers 357 // again and all tokens in the original receivers will be lost. 358 if (port.isInput()) { 359 int width = port.getWidth(); 360 Receiver[][] receivers = port.getReceivers(); 361 Token[][] tokens = new Token[width][0]; 362 363 for (int channel = 0; channel < width; channel++) { 364 int size = ((QueueReceiver) receivers[channel][0]) 365 .size(); 366 tokens[channel] = new Token[size]; 367 368 for (int count = 0; count < size; count++) { 369 tokens[channel][count] = port.get(channel); 370 } 371 } 372 373 _inputTokensHolder.put(port, tokens); 374 } 375 376 // Connect the corresponding ports of both actors. 377 IOPort matchingPort = (IOPort) clone.getPort(port.getName()); 378 IORelation relation = (IORelation) newRelation("r_" + i++); 379 port.link(relation); 380 matchingPort.link(relation); 381 382 if (port.isMultiport()) { 383 relation.setWidth(port.getWidth()); 384 } 385 } 386 } catch (NameDuplicationException ex) { 387 throw new IllegalActionException(this, "name duplication."); 388 } 389 } 390 391 /** Get token consumption rate for the given receiver. If the port 392 * containing the receiver is an input port, return the consumption 393 * rate for that receiver. If the port containing the receiver is 394 * an opaque output port, return the production rate for that receiver. 395 * @param receiver The receiver to get token consumption rate. 396 * @return The token consumption rate of the given receiver. 397 * @exception IllegalActionException If any called method throws 398 * IllegalActionException. 399 */ 400 private int _getTokenConsumptionRate(Receiver receiver) 401 throws IllegalActionException { 402 int tokenConsumptionRate; 403 404 IOPort port = receiver.getContainer(); 405 Variable rateVariable = null; 406 Token token = null; 407 Receiver[][] portReceivers = null; 408 409 // If DDF domain is inside another domain and the 410 // receiver is contained by an opaque output port... 411 // The default production rate is -1 which means all 412 // tokens in the receiver are transferred to the outside. 413 if (port.isOutput()) { 414 rateVariable = DFUtilities.getRateVariable(port, 415 "tokenProductionRate"); 416 portReceivers = port.getInsideReceivers(); 417 418 if (rateVariable == null) { 419 return -1; 420 } else { 421 token = rateVariable.getToken(); 422 423 if (token == null) { 424 return -1; 425 } 426 } 427 } 428 429 if (port.isInput()) { 430 rateVariable = DFUtilities.getRateVariable(port, 431 "tokenConsumptionRate"); 432 portReceivers = port.getReceivers(); 433 434 if (rateVariable == null) { 435 return 1; 436 } else { 437 token = rateVariable.getToken(); 438 439 if (token == null) { 440 return 1; 441 } 442 } 443 } 444 445 if (token instanceof ArrayToken) { 446 Token[] tokens = ((ArrayToken) token).arrayValue(); 447 448 // Scan the contained receivers of the port to find 449 // out channel index. 450 int channelIndex = 0; 451 foundChannelIndex: for (int m = 0; m < portReceivers.length; m++) { 452 for (int n = 0; n < portReceivers[m].length; n++) { 453 if (receiver == portReceivers[m][n]) { 454 channelIndex = m; 455 break foundChannelIndex; 456 } 457 } 458 } 459 460 tokenConsumptionRate = ((IntToken) tokens[channelIndex]).intValue(); 461 } else { 462 tokenConsumptionRate = ((IntToken) token).intValue(); 463 } 464 465 return tokenConsumptionRate; 466 } 467 468 /** Get the to-be-cloned composite actor's name from StringParameter 469 * recursionActor. Go up in hierarchy and find the first container 470 * with matching name. 471 * @exception IllegalActionException If no actor is found with 472 * the given name. 473 */ 474 private void _searchRecursionActor() throws IllegalActionException { 475 String recursionActorValue = recursionActor.stringValue(); 476 CompositeActor container = (CompositeActor) getContainer(); 477 478 while (container != null) { 479 if (recursionActorValue.equals(container.getName())) { 480 _recursionActor = container; 481 return; 482 } else { 483 container = (CompositeActor) container.getContainer(); 484 } 485 } 486 487 throw new IllegalActionException(this, 488 "Can not find a container with name " + recursionActorValue); 489 } 490 491 /** Read the rate parameters of the input ports of all actors receiving 492 * tokens from this actor and propagate these parameters back to the 493 * connected output ports of this actor. This is needed because during 494 * the initialization of the local director, the contained actors only 495 * see the opaque output ports of this actor instead of the connected 496 * opaque ports on the outside after the local director is removed. 497 * To determine the deferrability (see DDFDirector for its definition) 498 * of the contained actors, which happens during the initialization of 499 * the local director, we need to propagate these rate parameters back 500 * to the connected output ports of this actor. 501 * @exception IllegalActionException If any called method throws 502 * IllegalActionException. 503 */ 504 private void _setOutputPortRate() throws IllegalActionException { 505 Iterator outputPorts = outputPortList().iterator(); 506 507 while (outputPorts.hasNext()) { 508 IOPort outputPort = (IOPort) outputPorts.next(); 509 int[] productionRate = new int[outputPort.getWidthInside()]; 510 511 // If there are more inside channels than outside channels, 512 // it sets default rates of these extra inside channels to 513 // be -1 which then won't cause an upstream actor to be 514 // deferrable because any tokens on these extra channels 515 // are discarded. 516 Arrays.fill(productionRate, -1); 517 518 Receiver[][] farReceivers = outputPort.getRemoteReceivers(); 519 520 for (int i = 0; i < farReceivers.length; i++) { 521 if (i < outputPort.getWidthInside()) { 522 for (int j = 0; j < farReceivers[i].length; j++) { 523 QueueReceiver farReceiver = (QueueReceiver) farReceivers[i][j]; 524 int rate = _getTokenConsumptionRate(farReceiver); 525 526 // According to the definition of deferrability, 527 // we need to find the minimum rate associated with 528 // this channel. -1 is actually the largest rate in 529 // some sense. 530 if (productionRate[i] < 0) { 531 productionRate[i] = rate; 532 } else if (rate >= 0 && rate < productionRate[i]) { 533 productionRate[i] = rate; 534 } 535 } 536 } 537 } 538 539 IntToken[] productionRateToken = new IntToken[outputPort 540 .getWidthInside()]; 541 542 for (int i = 0; i < outputPort.getWidthInside(); i++) { 543 productionRateToken[i] = new IntToken(productionRate[i]); 544 } 545 546 // Since this is output port, we look for token production rate 547 // instead of token consumption rate. 548 Variable rateVariable = DFUtilities.getRateVariable(outputPort, 549 "tokenProductionRate"); 550 551 if (rateVariable == null) { 552 try { 553 rateVariable = new Parameter(outputPort, 554 "tokenProductionRate"); 555 } catch (NameDuplicationException ex) { 556 //should not happen. 557 throw new InternalErrorException(this, ex, null); 558 } 559 } 560 561 rateVariable.setToken( 562 new ArrayToken(BaseType.INT, productionRateToken)); 563 } 564 } 565 566 /** Transfer all tokens contained by input ports of this actor and 567 * stored by an internal variable to the connected opaque ports inside. 568 * We cannot use transferInputs(IOPort) of the local director for two 569 * reason. One is that tokens are now stored by an internal variable. 570 * The other is that we have to transfer <i>all</i> tokens instead of 571 * those specified by rate parameters because all input ports will 572 * become transparent after this firing. 573 * @exception IllegalActionException If conversion to the type of 574 * the destination port cannot be done. 575 */ 576 private void _transferInputs() throws IllegalActionException { 577 Iterator inputPorts = inputPortList().iterator(); 578 579 while (inputPorts.hasNext()) { 580 IOPort inputPort = (IOPort) inputPorts.next(); 581 Token[][] tokens = (Token[][]) _inputTokensHolder.get(inputPort); 582 583 for (int channel = 0; channel < inputPort.getWidth(); channel++) { 584 for (int j = 0; j < tokens[channel].length; j++) { 585 inputPort.sendInside(channel, tokens[channel][j]); 586 } 587 } 588 } 589 } 590 591 /** Transfer all tokens contained by output ports of this actor to the 592 * connected opaque ports outside.We cannot use transferOutputs(IOPort) 593 * of the local director because we have to transfer <i>all</i> tokens 594 * instead of those specified by rate parameters because all output 595 * ports will become transparent after this firing. 596 * @exception IllegalActionException If conversion to the type of 597 * the destination port cannot be done. 598 */ 599 private void _transferOutputs() throws IllegalActionException { 600 Iterator outputPorts = outputPortList().iterator(); 601 602 while (outputPorts.hasNext()) { 603 IOPort outputPort = (IOPort) outputPorts.next(); 604 605 for (int i = 0; i < outputPort.getWidthInside(); i++) { 606 while (outputPort.hasNewTokenInside(i)) { 607 outputPort.send(i, outputPort.getInside(i)); 608 } 609 } 610 } 611 } 612 613 /////////////////////////////////////////////////////////////////// 614 //// private variables //// 615 616 /** The composite actor to be cloned. 617 */ 618 private CompositeActor _recursionActor = null; 619 620 /** A flag indicating if the compatibility of the to-be-cloned composite 621 * actor has been checked. It is set to true after checking so that 622 * checking is performed only once during the execution of the model. 623 */ 624 private boolean _isCompatibilityChecked = false; 625 626 /** A HashMap to store tokens of the input ports. 627 */ 628 private HashMap _inputTokensHolder = new HashMap(); 629}