/* Base class for directors that have fixed point semantics at each iteration. Copyright (c) 2006-2014 The Regents of the University of California. All rights reserved. Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. PT_COPYRIGHT_VERSION_2 COPYRIGHTENDKEY */ package ptolemy.actor.sched; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import ptolemy.actor.Actor; import ptolemy.actor.CompositeActor; import ptolemy.actor.IOPort; import ptolemy.actor.Receiver; import ptolemy.actor.SuperdenseTimeDirector; import ptolemy.actor.parameters.ParameterPort; import ptolemy.actor.util.Time; import ptolemy.data.BooleanToken; import ptolemy.data.IntToken; import ptolemy.data.expr.Parameter; import ptolemy.data.type.BaseType; import ptolemy.kernel.CompositeEntity; import ptolemy.kernel.util.IllegalActionException; import ptolemy.kernel.util.NameDuplicationException; import ptolemy.kernel.util.Nameable; import ptolemy.kernel.util.Workspace; /////////////////////////////////////////////////////////////////// //// FixedPointDirector /** A base class for directors that have fixed point semantics at each iteration. An iteration consists of repeated firings of the actors controlled by this director until a fixed point is reached. An iteration has converged if firing actors will not change signal status any more.
At the beginning of each iteration, the status of all inputs and outputs is unknown. Upon firing an actor, the status of its output signals may become known. Once the status of a signal becomes known, it cannot be changed back to unknown in the iteration. This monotonicity constraint ensures the existence and uniqueness of the fixed point. During an iteration, the prefire() and fire() methods of the controlled actors may be repeatedly invoked, but the postfire() method will be invoked exactly once after the fixed point has been found. The postfire() methods of the contained actors are invoked only in the postfire() method of this director, and they are invoked in arbitrary order.
If the prefire() method of an actor returns false, then this director assumes that all the outputs of the actor are absent. The actor has declined to fire.
Although this director does not require any specific ordering of actor firings, a scheduler is used to choose an efficient ordering.
By default, actors are strict, which means that all their input signals must be known before the actor can be fired. Here, what we mean by "fired" is that prefire() is invoked, and if it returns true, then fire() is invoked. Such actors will be fired only once in an iteration. A non-strict actor can be fired regardless of the status of its inputs, and may be fired repeatedly in an iteration if some of the inputs are unknown. Once an actor is fired with all its inputs known, it will not be fired again in the same iteration. A composite actor containing this director is a non-strict actor.
For an actor to be used under the control of this director, it must either be strict, or if it is non-strict, it must be monotonic. Montonicity implies two constraints on the actor. First, if prefire() ever returns true during an iteration, then it will return true on all subsequent invocations in the same iteration(). Second, if either prefire() or fire() call clear() on an output port, then no subsequent invocation in the same iteration can call put() on the port. If prefire() or fire() call put() on an output port with some token, then no subsequent invocation in the same iteration can call clear() or put() with a token with a different value. These constraints ensure determinacy.
If synchronizeToRealTime is set to true
,
then the postfire() method stalls until the real time elapsed
since the model started matches the current time.
This ensures that the director does not get ahead of real time. However,
of course, this does not ensure that the director keeps up with real time.
Note that this synchronization occurs after actors have been fired,
but before they have been postfired.
This class is based on the original SRDirector, written by Paul Whitaker. @author Haiyang Zheng and Edward A. Lee @version $Id: FixedPointDirector.java 70402 2014-10-23 00:52:20Z cxh $ @since Ptolemy II 5.2 @Pt.ProposedRating Green (hyzheng) @Pt.AcceptedRating Yellow (eal) */ public class FixedPointDirector extends StaticSchedulingDirector implements SuperdenseTimeDirector { /** Construct a director in the default workspace with an empty string * as its name. The director is added to the list of objects in * the workspace. Increment the version number of the workspace. * @exception IllegalActionException If the name has a period in it, or * the director is not compatible with the specified container. * @exception NameDuplicationException If the container already contains * an entity with the specified name. */ public FixedPointDirector() throws IllegalActionException, NameDuplicationException { super(); _init(); } /** Construct a director in the given workspace with an empty name. * The director is added to the list of objects in the workspace. * Increment the version number of the workspace. * @param workspace The workspace for this object. * @exception IllegalActionException If the name has a period in it, or * the director is not compatible with the specified container. * @exception NameDuplicationException If the container already contains * an entity with the specified name. */ public FixedPointDirector(Workspace workspace) throws IllegalActionException, NameDuplicationException { super(workspace); _init(); } /** Construct a director in the given container with the given name. * The container argument must not be null, or a * NullPointerException will be thrown. * If the name argument is null, then the name is set to the * empty string. Increment the version number of the workspace. * @param container Container of the director. * @param name Name of this director. * @exception IllegalActionException If the director is not compatible * with the specified container. * @exception NameDuplicationException If the name collides with an * attribute in the container. */ public FixedPointDirector(CompositeEntity container, String name) throws IllegalActionException, NameDuplicationException { super(container, name); _init(); } /////////////////////////////////////////////////////////////////// //// parameters //// /** The number of times that postfire may be called before it * returns false. The type must be int, and the value * defaults to zero. If the value is less than or equal to zero, * then the execution will never return false in postfire, and * thus the execution can continue forever. */ public Parameter iterations; /** Specify whether the execution should synchronize to the * real time. This parameter has type boolean and defaults * to false. If set to true, then this director stalls in the * prefire() method until the elapsed real real time matches * the current time. If the period parameter has value * 0.0 (the default), then changing this parameter to true * has no effect. Note that in this base class, there is * no period parameter and time is never advanced, * so this will have no effect. It has effect in derived * classes. */ public Parameter synchronizeToRealTime; /////////////////////////////////////////////////////////////////// //// public methods //// /** Clone the director into the specified workspace. This calls the * base class and then sets the attribute public members to refer * to the attributes of the new director. * @param workspace The workspace for the new director. * @return A new director. * @exception CloneNotSupportedException If a derived class contains * an attribute that cannot be cloned. */ @Override public Object clone(Workspace workspace) throws CloneNotSupportedException { FixedPointDirector newObject = (FixedPointDirector) super .clone(workspace); newObject._receivers = new LinkedList(); newObject._actorsAllowedToFire = new HashSet(); newObject._actorsFinishedFiring = new HashSet(); newObject._actorsFired = new HashSet(); newObject._cachedAllInputsKnown = new HashSet(); return newObject; } /** Prefire and fire actors in the order given by the scheduler * until the iteration converges. * An iteration converges when a pass through the schedule does * not change the status of any receiver. * @exception IllegalActionException If an actor violates the * monotonicity constraints, or the prefire() or fire() method * of the actor throws it. */ @Override public void fire() throws IllegalActionException { if (_debugging) { _debug("FixedPointDirector: invoking fire()."); } Schedule schedule = getScheduler().getSchedule(); int iterationCount = 0; do { Iterator firingIterator = schedule.firingIterator(); while (firingIterator.hasNext() && !_stopRequested) { Actor actor = ((Firing) firingIterator.next()).getActor(); // If the actor has previously returned false in postfire(), // do not fire it. if (!_actorsFinishedExecution.contains(actor)) { // check if the actor is ready to fire. if (_isReadyToFire(actor)) { _fireActor(actor); _actorsFired.add(actor); } else { if (_debugging) { if (!_actorsFinishedFiring.contains(actor) && actor.isStrict()) { _debug("Strict actor has uknown inputs: " + actor.getFullName()); } } } } else { // The postfire() method of this actor returned false in // some previous iteration, so here, for the benefit of // connected actors, we need to explicitly call the // send(index, null) method of all of its output ports, // which indicates that a signal is known to be absent. if (_debugging) { _debug("FixedPointDirector: no longer enabled (return false in postfire): " + actor.getFullName()); } _sendAbsentToAllUnknownOutputsOf(actor); } } iterationCount++; } while (!_hasIterationConverged() && !_stopRequested); if (_debugging) { _debug(this.getFullName() + ": Fixed point found after " + iterationCount + " iterations."); } } /** Return the current index of the director. * The current index is a portion of the superdense time. * Superdense time means that time is a real value and an index, * allowing multiple sequential steps to occur at a fixed (real) time. * @return the superdense time index * @see #setIndex(int) * @see ptolemy.actor.SuperdenseTimeDirector */ @Override public int getIndex() { return _index; } /** Return the next time of interest in the model being executed by * this director or the director of any enclosing model up the * hierarchy. If this director is at the top level, then this * default implementation simply returns infinity, indicating * that this director has no interest in any future time. * If this director is not at the top level, then return * whatever the enclosing director returns. *
* This method is useful for domains that perform * speculative execution (such as Continuous itself). * Such a domain in a hierarchical * model (i.e. CT inside DE) uses this method to determine how far * into the future to execute. This is simply an optimization that * reduces the likelihood of having to roll back. *
* The base class implementation in Director is almost right, * but not quite, because at the top level it returns current * time. However, this director should not constrain any director * below it from speculatively executing into the future. * Instead, it assumes that any director below it implements * a strict actor semantics. Note in particular that the * implementation below would block time advancement in * a Continuous in DE in Continuous model because the * top-level model will usually only invoke the DE model * during a zero-step execution, which means that the returned * next iteration time will always be current time, which will * force the inside Continuous director to have a zero step * size always. * @return The next time of interest. * @exception IllegalActionException If creating a Time object fails. * @see #getModelTime() */ @Override public Time getModelNextIterationTime() throws IllegalActionException { if (isEmbedded()) { return super.getModelNextIterationTime(); } return Time.POSITIVE_INFINITY; } /** Return true, indicating that this director assumes and exports * the strict actor semantics, as described in this paper: *
* A. Goderis, C. Brooks, I. Altintas, E. A. Lee, and C. Goble, * "Heterogeneous Composition of Models of Computation," * EECS Department, University of California, Berkeley, * Tech. Rep. UCB/EECS-2007-139, Nov. 2007. * http://www.eecs.berkeley.edu/Pubs/TechRpts/2007/EECS-2007-139.html *
* In particular, a director that implements this interface guarantees
* that it will not invoke the postfire() method of an actor until all
* its inputs are known at the current tag. Moreover, it it will only
* do so in its own postfire() method, and in its prefire() and fire()
* methods, it does not change its own state. Thus, such a director
* can be used within a model of computation that has a fixed-point
* semantics, such as SRDirector and ContinuousDirector.
* @return True.
*/
@Override
public boolean implementsStrictActorSemantics() {
return true;
}
/** Initialize the director and all deeply contained actors by calling
* the super.initialize() method. Reset all private variables.
* @exception IllegalActionException If the superclass throws it.
*/
@Override
public void initialize() throws IllegalActionException {
_currentIteration = 0;
// This variable has to be reset at the very beginning, because
// some actors may call fireAt method to register breakpoints in DE
// and Continuous domains, which depend on the value of _index.
_index = 0;
// This could be getting re-initialized during execution
// (e.g., if we are inside a modal model), in which case,
// if the enclosing director is a superdense time director,
// we should initialize to its microstep, not to our own.
// NOTE: Some (weird) directors pretend they are not embedded even
// if they are (e.g. in Ptides), so we call _isEmbedded() to give
// the subclass the option of pretending it is not embedded.
/* NOTE: No, this doesn't make sense. Initialization should
* reset the microstep to zero, otherwise actors like clocks
* and PeriodicSampler won't work properly.
if (isEmbedded()) {
Nameable container = getContainer();
if (container instanceof CompositeActor) {
Director executiveDirector = ((CompositeActor) container)
.getExecutiveDirector();
if (executiveDirector instanceof SuperdenseTimeDirector) {
_index = ((SuperdenseTimeDirector) executiveDirector)
.getIndex();
}
}
}
*/
_cachedFunctionalProperty = true;
_functionalPropertyVersion = -1L;
super.initialize();
_realStartTime = System.currentTimeMillis();
// NOTE: The following used to be done in prefire(), which is wrong,
// because prefire() can be invoked multiple times in an iteration
// (particularly if this is inside another FixedPointDirector).
_resetAllReceivers();
}
/** Return true if all the controlled actors' isFireFunctional()
* methods return true. Otherwise, return false.
*
* @return True if all controlled actors are functional. Return
* false if there is no container or no actors in the container.
*/
@Override
public boolean isFireFunctional() {
if (workspace().getVersion() == _functionalPropertyVersion) {
return _cachedFunctionalProperty;
}
boolean result = true;
boolean containsActors = false;
CompositeActor container = (CompositeActor) getContainer();
if (container == null) {
return false;
}
Iterator actors = container.deepEntityList().iterator();
while (result && actors.hasNext() && !_stopRequested) {
Actor actor = (Actor) actors.next();
result = actor.isFireFunctional() && result;
containsActors = true;
}
if (!containsActors) {
result = false;
}
_cachedFunctionalProperty = result;
_functionalPropertyVersion = workspace().getVersion();
return result;
}
/** Return false. The transferInputs() method checks whether
* the inputs are known before calling hasToken().
* Thus this director tolerates unknown inputs.
* @return False.
*/
@Override
public boolean isStrict() {
return false;
}
/** Return a new FixedPointReceiver. If a subclass overrides this
* method, the receiver it creates must be a subclass of FixedPointReceiver,
* and it must add the receiver to the _receivers list (a protected
* member of this class).
* @return A new FixedPointReceiver.
*/
@Override
public Receiver newReceiver() {
Receiver receiver = new FixedPointReceiver(this);
_receivers.add(receiver);
return receiver;
}
/** Call postfire() on all contained actors that were fired in the current
* iteration. Return false if the model
* has finished executing, either by reaching the iteration limit, or if
* no actors in the model return true in postfire(), or if stop has
* been requested, or if no actors fired at all in the last iteration.
* This method is called only once for each iteration.
* Note that actors are postfired in arbitrary order.
* @return True if the execution is not finished.
* @exception IllegalActionException If the iterations parameter does
* not have a valid token, or if there still some unknown inputs (which
* indicates a causality loop).
*/
@Override
public boolean postfire() throws IllegalActionException {
if (_debugging) {
_debug("FixedPointDirector: Called postfire().");
}
boolean needMoreIterations = true;
// If no actors were fired, this director used to return
// false in postfire. However, this is not correct because there
// may be actors that are using time to decide whether to fire
// and, in addition, this may be embedded, in which case future
// events will trigger firings.
/*
int numberOfActors = getScheduler().getSchedule().size();
if ((numberOfActors > 0) && (_actorsFired.size() == 0)) {
needMoreIterations = false;
}
*/
// The following used to inexplicably iterate only over
// _actorsFired. Now we iterate over all actors in the order
// of the schedule.
Schedule schedule = getScheduler().getSchedule();
Iterator firingIterator = schedule.firingIterator();
while (firingIterator.hasNext() && !_stopRequested) {
Actor actor = ((Firing) firingIterator.next()).getActor();
// Check for remaining unknown inputs.
// Don't care about actors that have previously returned false from postfire().
if (!_areAllInputsKnown(actor)
&& !_actorsFinishedExecution.contains(actor)) {
// Construct a list of the unknown inputs.
StringBuffer unknownInputs = new StringBuffer();
Iterator inputPorts = actor.inputPortList().iterator();
IOPort firstPort = null;
while (inputPorts.hasNext()) {
IOPort inputPort = (IOPort) inputPorts.next();
if (!inputPort.isKnown()) {
unknownInputs.append(inputPort.getName());
unknownInputs.append("\n");
if (firstPort == null) {
firstPort = inputPort;
}
}
}
throw new IllegalActionException(actor, firstPort,
"Unknown inputs remain. Possible causality loop:\n"
+ unknownInputs);
}
if (_actorsFired.contains(actor)) {
if (!_postfireActor(actor)) {
// postfire() returned false, so prevent the actor
// from iterating again.
_actorsFinishedExecution.add(actor);
}
}
}
if (_debugging) {
_debug(this.getFullName() + ": Iteration " + _currentIteration
+ " is complete.");
}
// NOTE: The following used to be done in prefire(), which is wrong,
// because prefire() can be invoked multiple times in an iteration
// (particularly if this is inside another FixedPointDirector).
_resetAllReceivers();
// In this base class, the superdense time index is the only advancement
// of time, and it advances on every iteration. Derived classes must set
// it to zero in their postfire method if they advance time.
_index++;
// Check whether the current execution has reached its iteration limit.
_currentIteration++;
int numberOfIterations = ((IntToken) iterations.getToken()).intValue();
if (numberOfIterations > 0 && _currentIteration >= numberOfIterations) {
super.postfire();
return false;
}
return super.postfire() && needMoreIterations;
}
/** Return true if the director is ready to fire.
* If synchronizeToRealTime is true, then
* wait for real time elapse to match or exceed model time.
* The return whatever the base class returns.
* @return True.
* @exception IllegalActionException Not thrown in this base class.
*/
@Override
public boolean prefire() throws IllegalActionException {
_synchronizeToRealTime();
_postfireReturns = true;
boolean result = true;
List