001/* An analysis that finds the constant variables in a ptolemy model 002 003 Copyright (c) 2003-2013 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 */ 027package ptolemy.actor.util; 028 029import java.util.Collections; 030import java.util.HashMap; 031import java.util.HashSet; 032import java.util.Iterator; 033import java.util.LinkedList; 034import java.util.List; 035import java.util.Map; 036import java.util.Set; 037 038import ptolemy.actor.CompositeActor; 039import ptolemy.actor.Manager; 040import ptolemy.actor.parameters.ParameterPort; 041import ptolemy.actor.parameters.PortParameter; 042import ptolemy.data.Token; 043import ptolemy.data.expr.ModelScope; 044import ptolemy.data.expr.Variable; 045import ptolemy.graph.DirectedGraph; 046import ptolemy.graph.Edge; 047import ptolemy.graph.Node; 048import ptolemy.kernel.CompositeEntity; 049import ptolemy.kernel.Entity; 050import ptolemy.kernel.Port; 051import ptolemy.kernel.util.Attribute; 052import ptolemy.kernel.util.IllegalActionException; 053import ptolemy.kernel.util.NamedObj; 054 055/////////////////////////////////////////////////////////////////// 056//// ConstVariableModelAnalysis 057 058/** 059 An analysis that traverses a model to determine all the constant 060 variables in a hierarchical model. Basically, a constant variable in 061 a particular model is any variable in the model that is defined by a 062 expression of constants, or any variable that is defined by an 063 expression of constants and identifiers that reference other constant 064 variables. 065 066 <p> This class computes the set of constant variables by computing the 067 set of variables that are not constant and then performing the 068 complement. This is somewhat easier to compute. The computation is 069 performed in two passes, the first of which extracts the set of 070 variables which must be not-constant either by not being evaluatable, 071 by inclusion in an initial set, by virtue of being a PortParameter 072 with an external connection, or by assignment from within a modal 073 model. The second pass collects all the variables which are not 074 constant because they depend on other variables which are not 075 constant. This class also recognizes dependence declarations 076 represented by the {@link DependencyDeclaration} class. 077 078 <p> This class also determines the "least change context" of each 079 dynamic variable. The least change context of a variable is 080 typically an actor that contains that variable. During a firing of 081 the least change context, the variable's value is guaranteed to not 082 change. This analysis is important for supporting parameter changes 083 in the context of domains that perform scheduling based on parameter 084 values, like SDF and PSDF. The least change context of a 085 PortParameter with an external connection must be a container of the 086 PortParameter. The least change context of a variable assigned by a 087 finite state machine in a modal model must be a container of the 088 finite state machine. The change context of asserted not constant 089 variables and variables with no expression are assumed to be the 090 toplevel of the model. Note that in some cases (typically when a 091 variable is modified from multiple sources which are not 092 hierarchically related), no least change context may exist. 093 094 @author Stephen Neuendorffer 095 @version $Id$ 096 @since Ptolemy II 4.0 097 @Pt.ProposedRating Yellow (neuendor) 098 @Pt.AcceptedRating Yellow (neuendor) 099 */ 100public class ConstVariableModelAnalysis { 101 /** Create a dummy analysis for actors that are not contained 102 * in a model. 103 */ 104 public ConstVariableModelAnalysis() { 105 _variableToChangeContext = new HashMap(); 106 } 107 108 /** Analyze the given model to determine which variables must be 109 * constants and which variables may change dynamically during 110 * execution. In addition, store the intermediate results for 111 * contained actors so they can be retrieved by the 112 * getConstVariables() method. 113 * @param model The model to be analyzed. 114 * @exception IllegalActionException If an exception occurs 115 * during analysis. 116 */ 117 public ConstVariableModelAnalysis(Entity model) 118 throws IllegalActionException { 119 this(model, Collections.EMPTY_SET); 120 } 121 122 /** Analyze the given model to determine which variables must be 123 * constants and which variables may change dynamically during 124 * execution, given that all variables in the given set may 125 * change dynamically. In addition, store the intermediate 126 * results for contained actors so they can be retrieved by the 127 * getConstVariables() method. 128 * @param model The model to be analyzed. 129 * @param variableSet The set to be analyzed. 130 * @exception IllegalActionException If an exception occurs 131 * during analysis. 132 */ 133 public ConstVariableModelAnalysis(Entity model, Set variableSet) 134 throws IllegalActionException { 135 _variableToChangeContext = new HashMap(); 136 137 for (Iterator variables = variableSet.iterator(); variables 138 .hasNext();) { 139 Variable variable = (Variable) variables.next(); 140 _variableToChangeContext.put(variable, model); 141 } 142 143 _dependencyGraph = new DirectedGraph(); 144 145 _collectConstraints(model); 146 _analyzeAllVariables(); 147 } 148 149 /////////////////////////////////////////////////////////////////// 150 //// public methods //// 151 152 /** Add the information in the given dependency declaration to the 153 * dependence graph of this analysis. This method can be called 154 * by users of this class to update the analysis without 155 * recomputing all of the information from scratch. 156 * @param declaration The given dependency declaration. 157 */ 158 public void addDependencyDeclaration(DependencyDeclaration declaration) { 159 _addDependencyDeclaration(declaration); 160 _analyzeAllVariables(); 161 } 162 163 /** Return the analysis that is active for the given object. 164 * @param object The given object. 165 * @return The active analysis for the given object. 166 * @exception IllegalActionException If an exception occurs during 167 * analysis. 168 */ 169 public static ConstVariableModelAnalysis getAnalysis(NamedObj object) 170 throws IllegalActionException { 171 if (object.toplevel() instanceof CompositeActor) { 172 CompositeActor toplevel = (CompositeActor) object.toplevel(); 173 Manager manager = toplevel.getManager(); 174 175 if (manager == null) { 176 throw new IllegalActionException(object, "No Manager found?"); 177 } 178 ConstVariableModelAnalysis analysis = (ConstVariableModelAnalysis) manager 179 .getAnalysis("ConstVariableModelAnalysis"); 180 181 if (analysis == null) { 182 analysis = new ConstVariableModelAnalysis(toplevel); 183 manager.addAnalysis("ConstVariableModelAnalysis", analysis); 184 } 185 186 return analysis; 187 } else { 188 return new ConstVariableModelAnalysis(); 189 } 190 } 191 192 /** Return the change context of the given variable. This an 193 * actor containing the variable, such that the variable is 194 * guaranteed not to change values during a firing of the actor. 195 * If the variable is constant, or no change context exists, then 196 * return null. 197 * @param variable The given variable. 198 * @return The change context of the given variable. 199 */ 200 public Entity getChangeContext(Variable variable) { 201 return (Entity) _variableToChangeContext.get(variable); 202 } 203 204 /** Return the constant value of the given parameter, if the 205 * parameter is actually constant. 206 * @param variable The given variable. 207 * @return The constant value of the given variable. 208 * @exception IllegalActionException If the given parameter is 209 * not a constant parameter, as determined by this analysis. 210 */ 211 public Token getConstantValue(Variable variable) 212 throws IllegalActionException { 213 if (!isConstant(variable)) { 214 throw new IllegalActionException(variable, 215 "This variable does not have a constant value."); 216 } 217 218 return variable.getToken(); 219 } 220 221 /** Return the computed constant variables for the given container. 222 * @param container The given container. 223 * @return The computed constant variables. 224 * @exception RuntimeException If the constant variables for the 225 * container have not already been computed. 226 */ 227 public Set getConstVariables(NamedObj container) { 228 List variables = container.attributeList(Variable.class); 229 variables.removeAll(_variableToChangeContext.keySet()); 230 return new HashSet(variables); 231 } 232 233 /** Return the parameter dependency graph constructed through this 234 * analysis. 235 * @return The parameter dependency graph. 236 */ 237 public DirectedGraph getDependencyGraph() { 238 return _dependencyGraph; 239 } 240 241 /** Return the computed not constant variables for the given container. 242 * @param container The given container. 243 * @return The computed not constant variables. 244 * @exception RuntimeException If the constant variables for the 245 * container have not already been computed. 246 */ 247 public Set getNotConstVariables(NamedObj container) { 248 List variables = container.attributeList(Variable.class); 249 variables.removeAll(getConstVariables(container)); 250 return new HashSet(variables); 251 } 252 253 /** Return the set of variables anywhere in the model that have 254 * the given container as least change context. 255 * @param container The given container. 256 * @return The set of variables anywhere in the model that have 257 * the given container as least change context. 258 */ 259 public Set getVariablesWithChangeContext(NamedObj container) { 260 Set variableSet = new HashSet(); 261 262 for (Iterator i = _variableToChangeContext.keySet().iterator(); i 263 .hasNext();) { 264 Object key = i.next(); 265 Object value = _variableToChangeContext.get(key); 266 267 if (value == container) { 268 variableSet.add(key); 269 } 270 } 271 272 return variableSet; 273 } 274 275 /** Return true if the given variable is not reconfigured in the 276 * model. The variable is assumed to be contained by the model 277 * this analysis was created with. 278 * @param variable The given variable. 279 * @return True If the given variable is not reconfigured in the model. 280 */ 281 public boolean isConstant(Variable variable) { 282 return !_variableToChangeContext.keySet().contains(variable); 283 } 284 285 /** Return true if the variable has been analyzed by this analysis 286 * and it depends on no other parameters. 287 * @param variable The given variable. 288 * @return True If the variable has been analyzed by this analysis 289 * and it depends on no other parameters 290 */ 291 public boolean isIndependent(Variable variable) { 292 if (_dependencyGraph 293 .backwardReachableNodes(_dependencyGraph.node(variable)) 294 .size() > 0) { 295 return false; 296 } else { 297 return true; 298 } 299 } 300 301 /////////////////////////////////////////////////////////////////// 302 //// private methods //// 303 // Add the dependence information from the given attribute to the 304 // dependence graph. 305 private void _addDependencyDeclaration(DependencyDeclaration declaration) { 306 Variable variable = (Variable) declaration.getContainer(); 307 Node targetNode = _getNode(variable); 308 309 for (Iterator dependents = declaration.getDependents() 310 .iterator(); dependents.hasNext();) { 311 Variable dependent = (Variable) dependents.next(); 312 Node dependentNode = _getNode(dependent); 313 314 // if (!_dependencyGraph.edgeExists(node, targetNode)) { 315 _dependencyGraph.addEdge(dependentNode, targetNode); 316 317 //} 318 } 319 } 320 321 // Collect all of the constraints from the given variable. 322 private void _collectVariableConstraints(Variable variable) { 323 Node targetNode = _getNode(variable); 324 325 // compute the variables. 326 try { 327 Set freeIdentifiers = variable.getFreeIdentifiers(); 328 329 for (Iterator names = freeIdentifiers.iterator(); names 330 .hasNext();) { 331 String name = (String) names.next(); 332 Variable dependent = ModelScope.getScopedVariable(variable, 333 variable, name); 334 335 if (dependent != null) { 336 Node dependentNode = _getNode(dependent); 337 _dependencyGraph.addEdge(dependentNode, targetNode); 338 } 339 } 340 } catch (IllegalActionException ex) { 341 // Assume that this will be changed later... 342 // i.e. input_isPresent in FSM. 343 // Note that this also traps expressions that 344 // have no value as being variable... 345 _updateChangeContext(variable, (Entity) variable.toplevel()); 346 } 347 } 348 349 // Collect the set of variables in the given entity which might 350 // change during execution. This method adds an entry in the 351 // given map from each dynamic parameter deeply contained in the 352 // given entity to the change context of that parameter. 353 private void _collectConstraints(NamedObj container) 354 throws IllegalActionException { 355 if (container instanceof Variable) { 356 Variable variable = (Variable) container; 357 _collectVariableConstraints(variable); 358 } 359 360 if (container instanceof DependencyDeclaration) { 361 DependencyDeclaration declaration = (DependencyDeclaration) container; 362 _addDependencyDeclaration(declaration); 363 } 364 365 if (container instanceof PortParameter) { 366 PortParameter parameter = (PortParameter) container; 367 ParameterPort port = parameter.getPort(); 368 369 // Under what conditions is a PortParameter not associated 370 // with a port? 371 if (port != null && port.isOutsideConnected()) { 372 _updateChangeContext(parameter, 373 (Entity) parameter.getContainer()); 374 } 375 } 376 377 if (container instanceof ExplicitChangeContext) { 378 List list = ((ExplicitChangeContext) container) 379 .getModifiedVariables(); 380 381 for (Iterator variables = list.iterator(); variables.hasNext();) { 382 Variable variable = (Variable) variables.next(); 383 _updateChangeContext(variable, 384 ((ExplicitChangeContext) container).getContext()); 385 } 386 } 387 388 // Recurse through the whole model. 389 for (Iterator attributes = container.attributeList() 390 .iterator(); attributes.hasNext();) { 391 Attribute attribute = (Attribute) attributes.next(); 392 _collectConstraints(attribute); 393 } 394 395 if (container instanceof CompositeEntity) { 396 CompositeEntity composite = (CompositeEntity) container; 397 398 for (Iterator entities = composite.entityList().iterator(); entities 399 .hasNext();) { 400 _collectConstraints((Entity) entities.next()); 401 } 402 } 403 404 if (container instanceof Entity) { 405 for (Iterator ports = ((Entity) container).portList() 406 .iterator(); ports.hasNext();) { 407 Port port = (Port) ports.next(); 408 _collectConstraints(port); 409 } 410 } 411 } 412 413 // Recursively compute the set of constant variables for all actors 414 // deeply contained in the given model. 415 private void _analyzeAllVariables() { 416 // Sets of variables used to track the fixed point iteration. 417 LinkedList workList = new LinkedList(_variableToChangeContext.keySet()); 418 419 while (!workList.isEmpty()) { 420 Variable variable = (Variable) workList.removeFirst(); 421 Node node = _dependencyGraph.node(variable); 422 Entity changeContext = (Entity) _variableToChangeContext 423 .get(variable); 424 425 for (Iterator outputEdges = _dependencyGraph.outputEdges(node) 426 .iterator(); outputEdges.hasNext();) { 427 Node sinkNode = ((Edge) outputEdges.next()).sink(); 428 Variable targetVariable = (Variable) sinkNode.getWeight(); 429 430 if (_updateChangeContext(targetVariable, changeContext) 431 && !workList.contains(targetVariable)) { 432 workList.addLast(targetVariable); 433 } 434 } 435 } 436 } 437 438 // Get the node for the given variable, adding it to the graph if 439 // necessary. 440 private Node _getNode(Variable variable) { 441 if (_dependencyGraph.containsNodeWeight(variable)) { 442 return _dependencyGraph.node(variable); 443 } else { 444 return _dependencyGraph.addNodeWeight(variable); 445 } 446 } 447 448 // Update the change context associated with the given variable to 449 // be at least the given change context. 450 // return true if a change occurred 451 private final boolean _updateChangeContext(Variable variable, 452 Entity changeContext) { 453 if (_variableToChangeContext.keySet().contains(variable)) { 454 Entity oldChangeContext = (Entity) _variableToChangeContext 455 .get(variable); 456 457 // System.out.println("variable = " + variable); 458 // System.out.println("oldChangeContext = " + oldChangeContext); 459 // System.out.println("updatedChangeContext = " + changeContext); 460 Entity newChangeContext = _computeBound(changeContext, 461 oldChangeContext); 462 463 // System.out.println("newChangeContext = " + newChangeContext); 464 if (newChangeContext != oldChangeContext) { 465 _variableToChangeContext.put(variable, newChangeContext); 466 467 return true; 468 } 469 return false; 470 } else { 471 _variableToChangeContext.put(variable, changeContext); 472 return true; 473 } 474 } 475 476 // Return the entity which is contained by the other in the ptolemy 477 // hierarchy. If neither contains the other, then throw an 478 // exception. If either entity is null (corresponding to a change context 479 // which doesn't exist, then return null. 480 private final Entity _computeBound(Entity entity1, Entity entity2) { 481 if (entity1 == null || entity2 == null) { 482 return null; 483 } 484 if (entity2.equals(entity1)) { 485 return entity1; 486 } 487 if (entity2.deepContains(entity1)) { 488 return entity1; 489 } else if (entity1.deepContains(entity2)) { 490 return entity2; 491 } else { 492 return null; 493 } 494 } 495 496 /////////////////////////////////////////////////////////////////// 497 //// private variables //// 498 499 private DirectedGraph _dependencyGraph; 500 501 private Map _variableToChangeContext; 502}