001/* A utility class to infer the width of relations that don't specify the width
002
003Copyright (c) 2008-2015 The Regents of the University of California.
004All rights reserved.
005Permission is hereby granted, without written agreement and without
006license or royalty fees, to use, copy, modify, and distribute this
007software and its documentation for any purpose, provided that the above
008copyright notice and the following two paragraphs appear in all copies
009of this software.
010
011IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015SUCH DAMAGE.
016
017THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022ENHANCEMENTS, OR MODIFICATIONS.
023
024PT_COPYRIGHT_VERSION_2
025COPYRIGHTENDKEY
026
027 */
028
029package ptolemy.actor;
030
031import java.util.Date;
032import java.util.HashSet;
033import java.util.Iterator;
034import java.util.LinkedList;
035import java.util.List;
036import java.util.Set;
037
038import ptolemy.data.BooleanToken;
039import ptolemy.data.Token;
040import ptolemy.data.expr.ModelScope;
041import ptolemy.kernel.ComponentRelation;
042import ptolemy.kernel.util.IllegalActionException;
043import ptolemy.kernel.util.NamedObj;
044
045///////////////////////////////////////////////////////////////////
046////RelationWidthInference
047
048/**
049A class that offers convenience utility methods to infer the widths of
050relations in a composite actor.
051
052
053@author Bert Rodiers
054@version $Id$
055@since Ptolemy II 8.0
056@Pt.ProposedRating Red (rodiers)
057 */
058
059public class RelationWidthInference {
060
061    ///////////////////////////////////////////////////////////////////
062    ////                         public methods                    ////
063
064    /**
065     * Create RelationWidthInference, the algorithm for width inference.
066     * Also set the top level to the value given as argument.
067     * @param topLevel The top level CompositeActor.
068     * @exception IllegalArgumentException If the specified actor is not the
069     *   top level container. That is, its container is not null.
070     */
071    public RelationWidthInference(CompositeActor topLevel) {
072
073        if (topLevel == null) {
074            throw new IllegalArgumentException(
075                    "The toplevel should not be a null pointer.");
076        }
077
078        _topLevel = topLevel;
079    }
080
081    /** Determine whether widths are currently being inferred or not.
082     *  @return True When widths are currently being inferred.
083     */
084    public boolean inferringWidths() {
085        return _inferringWidths;
086    }
087
088    /**
089     *  Infer the width of the relations for which no width has been
090     *  specified yet.
091     *  The specified actor must be the top level container of the model.
092     *  @exception IllegalActionException If the widths of the relations at port are not consistent
093     *                  or if the width cannot be inferred for a relation.
094     */
095    public void inferWidths() throws IllegalActionException {
096        if (_needsWidthInference) {
097            // Extra test for compositeActor != null since when the manager is changed
098            // the old manager gets a null pointer as compositeActor.
099            // Afterwards width inference should not be done anymore on this manager
100            // (this will throw a null pointer exception since _topLevel will be set to null).
101            if (_topLevel.getContainer() instanceof CompositeActor) {
102                throw new IllegalArgumentException(
103                        "Width inference failed: The specified actor is "
104                                + "not the top level container.");
105            }
106            final boolean logTimings = false;
107            boolean checkConsistencyAtMultiport = true;
108
109            {
110                Token checkConsistency = ModelScope.preferenceValue(_topLevel,
111                        "_checkWidthConsistencyAtMultiports");
112                if (checkConsistency instanceof BooleanToken) {
113                    checkConsistencyAtMultiport = ((BooleanToken) checkConsistency)
114                            .booleanValue();
115                }
116            }
117            boolean checkWidthConstraints = true;
118
119            {
120                Token checkConstraints = ModelScope.preferenceValue(_topLevel,
121                        "_checkWidthConstraints");
122                if (checkConstraints instanceof BooleanToken) {
123                    checkWidthConstraints = ((BooleanToken) checkConstraints)
124                            .booleanValue();
125                }
126            }
127
128            long startTime = 0L;
129            // FindBugs: avoid dead local store.
130            if (logTimings) {
131                startTime = new Date().getTime();
132            }
133
134            try {
135                _topLevel.workspace().getWriteAccess();
136                _inferringWidths = true;
137
138                Set<ComponentRelation> relationList = _topLevel
139                        .deepRelationSet();
140                Set<IORelation> workingRelationSet = new HashSet<IORelation>();
141                Set<IOPort> workingPortSet = new HashSet<IOPort>();
142                Set<IOPort> workingDefaultPortSet = new HashSet<IOPort>();
143                HashSet<IORelation> unspecifiedSet = new HashSet<IORelation>();
144                HashSet<IOPort> portsToCheckConsistency = new HashSet<IOPort>();
145                HashSet<IOPort> portsThatCanBeIngnoredForConsistencyCheck = new HashSet<IOPort>();
146
147                for (ComponentRelation componentRelation : relationList) {
148                    if (componentRelation instanceof IORelation) {
149                        IORelation relation = (IORelation) componentRelation;
150                        if (!relation._skipWidthInference()) {
151                            if (relation.needsWidthInference()) {
152                                unspecifiedSet.add(relation);
153                            }
154                            if (!relation.isWidthFixed()
155                                    && relation.needsWidthInference()) {
156                                // If connected to non-multiports => the relation should be 1
157                                List<?> linkedObjects = relation
158                                        .linkedObjectsList();
159                                // If there is no port linked to the relation, we
160                                // declare the width of the relation to be zero.
161                                // FIXME: If there is only one port, the width should
162                                // also be zero. But there seem to be many tests that
163                                // depend on it being non-zero.
164                                if (linkedObjects.isEmpty()) {
165                                    relation._setInferredWidth(0);
166                                    workingRelationSet.add(relation);
167                                } else {
168                                    for (Object object : linkedObjects) {
169                                        if (object instanceof IOPort) {
170                                            IOPort port = (IOPort) object;
171
172                                            if (!port.isMultiport()) {
173                                                relation._setInferredWidth(1);
174                                                workingRelationSet
175                                                        .add(relation);
176                                                break; //Break the for loop.
177                                            } else {
178                                                //Add known outside relations
179                                                for (Object connectedRelationObject : port
180                                                        .linkedRelationList()) {
181                                                    IORelation connectedRelation = (IORelation) connectedRelationObject;
182                                                    if (connectedRelation != null
183                                                            && connectedRelation
184                                                                    .isWidthFixed()) {
185                                                        workingRelationSet.add(
186                                                                connectedRelation);
187                                                    }
188                                                }
189                                                //Add known inside relations
190                                                for (Object connectedRelationObject : port
191                                                        .insideRelationList()) {
192                                                    IORelation connectedRelation = (IORelation) connectedRelationObject;
193                                                    if (connectedRelation != null
194                                                            && connectedRelation
195                                                                    .isWidthFixed()) {
196                                                        workingRelationSet.add(
197                                                                connectedRelation);
198                                                    }
199                                                }
200                                                if (port.hasWidthConstraints()) {
201                                                    workingPortSet.add(port);
202                                                }
203                                                if (port.getDefaultWidth() >= 0) {
204                                                    workingDefaultPortSet
205                                                            .add(port);
206                                                }
207                                            }
208                                        }
209                                    }
210                                }
211                            }
212                        }
213                    }
214                }
215
216                LinkedList<IORelation> workingRelationList = new LinkedList<IORelation>(
217                        workingRelationSet);
218                workingRelationSet = null;
219
220                if (logTimings) {
221                    System.out.println("Width inference - initialization: "
222                            + (System.currentTimeMillis() - startTime)
223                            + " ms.");
224                }
225
226                long afterinit = 0L;
227                // FindBugs: avoid dead local store.
228                if (logTimings) {
229                    afterinit = new Date().getTime();
230                }
231
232                boolean continueInference = true;
233
234                while (continueInference && !unspecifiedSet.isEmpty()
235                        && (!workingPortSet.isEmpty()
236                                || !workingRelationList.isEmpty()
237                                || !workingDefaultPortSet.isEmpty())) {
238
239                    while (!workingRelationList.isEmpty()
240                            && !unspecifiedSet.isEmpty()) {
241
242                        //IORelation relation = workingSet2.pop();
243                        // pop has been added to LinkedList in Java 1.6
244                        // (cfr. http://download.oracle.com/javase/6/docs/api/java/util/LinkedList.html#pop() ).
245                        // Hence we use get an remove for the time being...
246                        IORelation relation = workingRelationList.get(0);
247                        workingRelationList.remove(0);
248
249                        unspecifiedSet.remove(relation);
250                        assert !relation.needsWidthInference();
251                        int width = relation.getWidth();
252                        assert width >= 0;
253
254                        // All relations in the same relation group need to have the same width
255                        for (Object otherRelationObject : relation
256                                .relationGroupList()) {
257                            IORelation otherRelation = (IORelation) otherRelationObject;
258                            if (relation != otherRelation
259                                    && otherRelation.needsWidthInference()) {
260                                otherRelation._setInferredWidth(width);
261                                unspecifiedSet.remove(otherRelation);
262                                // We don't need to add otherRelation to unspecifiedSet since
263                                // we will process all ports linked to the complete relationGroup
264                                // all at once.
265                            }
266                        }
267
268                        // Now we see whether we can determine the widths of relations directly connected
269                        // at the multiports linked to this relation.
270
271                        // linkedPortList() can contain a port more than once. We only want
272                        // them once. We will also only add multiports
273                        HashSet<IOPort> multiports = new HashSet<IOPort>();
274                        for (Object port : relation.linkedPortList()) {
275                            if (((IOPort) port).isMultiport()) {
276                                multiports.add((IOPort) port);
277                            }
278                        }
279                        for (IOPort port : multiports) {
280                            List<IORelation> updatedRelations = new LinkedList<IORelation>();
281                            _updateRelationsFromMultiport(port,
282                                    updatedRelations);
283                            if (checkConsistencyAtMultiport
284                                    && !updatedRelations.isEmpty()) {
285                                // If we have updated relations for this port, it means that it is consistent
286                                // and hence we don't need to check consistency anymore.
287                                portsThatCanBeIngnoredForConsistencyCheck
288                                        .add(port);
289                                for (IORelation updatedRelation : updatedRelations) {
290                                    portsToCheckConsistency
291                                            .addAll(updatedRelation
292                                                    .linkedPortList(port));
293                                }
294                            }
295                            workingRelationList.addAll(updatedRelations);
296                        }
297                    }
298
299                    // Use the width constraints on ports to infer the widths.
300                    if (!workingPortSet.isEmpty()
301                            && !unspecifiedSet.isEmpty()) {
302                        continueInference = false;
303                        LinkedList<IOPort> workingPortList = new LinkedList<IOPort>(
304                                workingPortSet);
305                        for (IOPort port : workingPortList) {
306                            List<IORelation> updatedRelations = new LinkedList<IORelation>();
307                            boolean constraintStillUseful = _updateRelationsFromMultiport(
308                                    port, updatedRelations);
309                            if (!updatedRelations.isEmpty()) {
310                                workingPortSet.remove(port);
311                                continueInference = true;
312                                // We found new information, so we can try to infer
313                                // new relations.
314                            } else if (!constraintStillUseful) {
315                                workingDefaultPortSet.remove(port);
316                            }
317                            if (checkConsistencyAtMultiport
318                                    && !updatedRelations.isEmpty()) {
319                                // If we have updated relations for this port, it means that it is consistent
320                                // and hence we don't need to check consistency anymore.
321                                portsThatCanBeIngnoredForConsistencyCheck
322                                        .add(port);
323                                for (IORelation updatedRelation : updatedRelations) {
324                                    portsToCheckConsistency
325                                            .addAll(updatedRelation
326                                                    .linkedPortList(port));
327                                }
328                            }
329                            workingRelationList.addAll(updatedRelations);
330                        }
331                    }
332
333                    // If we can't infer any widths anymore (workingRelationList.isEmpty())
334                    // we will look whether there are ports that have a default width.
335                    if (!workingDefaultPortSet.isEmpty()
336                            && workingRelationList.isEmpty()) {
337                        LinkedList<IOPort> workingDefaultPortList = new LinkedList<IOPort>(
338                                workingDefaultPortSet);
339                        for (IOPort port : workingDefaultPortList) {
340                            List<IORelation> updatedRelations = new LinkedList<IORelation>();
341                            boolean constraintStillUseful = _updateRelationsFromDefaultWidth(
342                                    port, updatedRelations);
343                            if (!updatedRelations.isEmpty()) {
344                                workingDefaultPortSet.remove(port);
345                                continueInference = true;
346                                // We found new information, so we can try to infer
347                                // new relations.
348                            } else if (!constraintStillUseful) {
349                                workingDefaultPortSet.remove(port);
350                            }
351                            workingRelationList.addAll(updatedRelations);
352                        }
353                    }
354                }
355
356                if (logTimings) {
357                    System.out.println("Actual algorithm: "
358                            + (System.currentTimeMillis() - afterinit)
359                            + " ms.");
360                }
361
362                //Consistency check
363                if (checkConsistencyAtMultiport) {
364
365                    portsToCheckConsistency.removeAll(
366                            portsThatCanBeIngnoredForConsistencyCheck);
367                    for (IOPort port : portsToCheckConsistency) {
368                        _checkConsistency(port);
369                    }
370                }
371                if (checkWidthConstraints) {
372                    for (IOPort port : workingPortSet) {
373                        _checkConsistency(port);
374                        port.checkWidthConstraints();
375                    }
376                }
377
378                while (!unspecifiedSet.isEmpty()) {
379
380                    boolean defaultInferredWidthTo1 = false;
381
382                    Token defaultTo1 = ModelScope.preferenceValue(_topLevel,
383                            "_defaultInferredWidthTo1");
384                    if (defaultTo1 instanceof BooleanToken) {
385                        defaultInferredWidthTo1 = ((BooleanToken) defaultTo1)
386                                .booleanValue();
387                    }
388
389                    if (defaultInferredWidthTo1) {
390                        for (IORelation relation : unspecifiedSet) {
391                            relation._setInferredWidth(1);
392                        }
393                        unspecifiedSet.clear();
394                    } else {
395                        StringBuffer portDetails = new StringBuffer();
396                        IORelation relation = unspecifiedSet.iterator().next();
397                        List<IOPort> linkedPorts = relation
398                                .deepLinkedPortList();
399
400                        // Look for a special case where a relation is linking a multiport
401                        // inside a composite to the inside of one or more multiports that are
402                        // not connected to anything on the outside. In this case, the width
403                        // will be zero. The pattern we look for that no more than one port
404                        // is linked on the outside.
405                        boolean foundOutside = false;
406                        boolean giveUp = false;
407                        for (IOPort port : linkedPorts) {
408                            if (port.isInsideGroupLinked(relation)) {
409                                continue;
410                            } else {
411                                if (foundOutside) {
412                                    // relation links more than one port on the outside,
413                                    // so there is nothing more we can do.
414                                    giveUp = true;
415                                    break;
416                                }
417                                foundOutside = true;
418                            }
419                        }
420                        if (giveUp) {
421                            Iterator deepPorts = linkedPorts.iterator();
422                            while (deepPorts.hasNext()) {
423                                if (portDetails.length() > 0) {
424                                    portDetails.append("\n");
425                                }
426                                portDetails.append(((IOPort) deepPorts.next())
427                                        .getFullName());
428                            }
429
430                            String message1 = "The width of relation "
431                                    + relation.getFullName()
432                                    + " can not be uniquely inferred.\n";
433                            String message2 = "One possible solution is to create a toplevel parameter "
434                                    + "named \"_defaultInferredWidthTo1\" with the boolean "
435                                    + "value true.\n"
436                                    + "Please make the width inference deterministic by"
437                                    + " explicitly specifying the width of this relation."
438                                    + " In the user interface, right click on the "
439                                    + "relation, select Configure and change the width. "
440                                    + " Note that some actors may need to have their "
441                                    + " Java code updated to call setDefaultWidth(1) "
442                                    + "on the output port. "
443                                    + "The relation is deeply connected to these ports:\n"
444                                    + portDetails.toString();
445                            Manager manager = ((CompositeActor) relation
446                                    .toplevel()).getManager();
447                            if (manager != null
448                                    && manager.getState() != Manager.IDLE) {
449                                throw new IllegalActionException(
450                                        relation, message1
451                                                + "The model is not idle, so stopping the model "
452                                                + "might help.\n" + message2);
453                            }
454                            throw new IllegalActionException(relation,
455                                    message1 + message2);
456                        } else {
457                            relation._setInferredWidth(0);
458                            unspecifiedSet.remove(relation);
459                        }
460                    }
461                }
462            } finally {
463                _inferringWidths = false;
464                _topLevel.workspace().doneTemporaryWriting();
465                if (logTimings) {
466                    System.out.println("Time to do width inference: "
467                            + (System.currentTimeMillis() - startTime)
468                            + " ms.");
469                }
470            }
471            _needsWidthInference = false;
472        }
473    }
474
475    /**
476     *  Return whether the current widths of the relation in the model
477     *  are no longer valid anymore and the widths need to be inferred again.
478     *  @return True when width inference needs to be executed again.
479     */
480    public boolean needsWidthInference() {
481        return _needsWidthInference;
482    }
483
484    /**
485     *  Notify the width inference algorithm that the connectivity in the model changed
486     *  (width of relation changed, relations added, linked to different ports, ...).
487     *  This will invalidate the current width inference.
488     */
489    public void notifyConnectivityChange() {
490        if (!_inferringWidths) {
491            _needsWidthInference = true;
492        }
493        // If we are currently inferring widths we ignore connectivity changes,
494        // since evaluating expressions can cause a call of attributesChanged,
495        // which results in notifyConnectivityChange. In this case we aren't
496        // changing the model, but just getting all parameters.
497        // Notice that we use the boolean _inferringWidths without any locking
498        // This is to avoid deadlocks... In case we are inferring widths notifyConnectivityChange
499        // should only be called from the same thread as the one that is inferring
500        // widths and hence there is no issue. When the user is actually changing he model
501        // we shouldn't be doing width inference and hence the parameter should not be
502        // changing.
503    }
504
505    ///////////////////////////////////////////////////////////////////
506    ////                         protected methods                 ////
507
508    /** Filter the relations for which the width still has to be inferred.
509     *  @param  relationList The relations that need to be filtered.
510     *  @return The relations for which the width still has to return.
511     *  @exception IllegalActionException If the expression for the width cannot
512     *   be parsed or cannot be evaluated, or if the result of evaluation
513     *   violates type constraints, or if the result of evaluation is null
514     *   and there are variables that depend on this one.
515     */
516    static protected Set<IORelation> _relationsWithUnspecifiedWidths(
517            List<?> relationList) throws IllegalActionException {
518        Set<IORelation> result = new HashSet<IORelation>();
519        for (Object relation : relationList) {
520            if (relation != null
521                    && ((IORelation) relation).needsWidthInference()) {
522                result.add((IORelation) relation);
523            }
524        }
525        return result;
526    }
527
528    ///////////////////////////////////////////////////////////////////
529    ////                         private methods                   ////
530
531    /**
532     * Check whether the widths at a port are consistent. Consistent means that
533     * the input and output width is either zero or that the input width is equal
534     * to the output width.
535     * @param  port The port which will be checked.
536     * @exception IllegalActionException If the widths of the relations at port
537     *                 are not consistent.
538     */
539    static private void _checkConsistency(IOPort port)
540            throws IllegalActionException {
541        // We check whether the inside and outside widths are consistent. In case
542        // widths are inferred they should be inferred uniquely. We don't want to have
543        // different results depending on where we start in the graph.
544        int insideWidth = port._getInsideWidth(null);
545        int outsideWidth = port._getOutsideWidth(null);
546
547        // Special case.
548        if (port instanceof SubscriptionAggregatorPort) {
549            if (insideWidth != 1) {
550                throw new IllegalActionException(port,
551                        "The inside width is required to be 1. Got "
552                                + insideWidth);
553            }
554            return;
555        }
556
557        if (insideWidth != 0 && outsideWidth != 0
558                && insideWidth != outsideWidth) {
559            throw new IllegalActionException(port, "The inside width ("
560                    + insideWidth + ") and the outside width (" + outsideWidth
561                    + ") of port " + port.getFullName()
562                    + " are not either equal to 0 or not equal to each other and are therefore"
563                    + " inconsistent.\nCan't determine a uniquely defined width for"
564                    + " the connected relations. A possible fix is to right clicking on either the"
565                    + " inside or outside relation and set the width -1.");
566        }
567    }
568
569    /**
570     * Infer the width for the relations connected to the port. If the width can be
571     * inferred, update the width and add the relations for which the width has been
572     * updated.
573     * @param port The port for whose connected relations the width should be inferred.
574     * @param updatedRelations The relations for which the width has been updated.
575     * @return true When this constraint is still useful (can be used to extra more information).
576     * @exception IllegalActionException If the widths of the relations at port are not consistent.
577     */
578    static private boolean _updateRelationsFromMultiport(IOPort port,
579            List<IORelation> updatedRelations) throws IllegalActionException {
580        boolean constraintStillUseful = true;
581        Set<IORelation> outsideUnspecifiedWidths = _relationsWithUnspecifiedWidths(
582                port.linkedRelationList());
583        //port.linkedRelationList() returns the outside relations
584
585        int outsideUnspecifiedWidthsSize = outsideUnspecifiedWidths.size();
586
587        NamedObj namedObject = port.getContainer();
588        if (namedObject == null) {
589            assert false; // not expected
590            return false;
591        }
592        int difference = -1;
593        Set<IORelation> unspecifiedWidths = null;
594        if (namedObject instanceof AtomicActor) {
595
596            assert outsideUnspecifiedWidthsSize >= 0;
597            if (outsideUnspecifiedWidthsSize > 0
598                    && port.hasWidthConstraints()) {
599                difference = port.getWidthFromConstraints();
600                unspecifiedWidths = outsideUnspecifiedWidths;
601                if (difference < 0) {
602                    return true; // Constraints still unknown
603                }
604            } else {
605                return false;
606            }
607        } else {
608
609            Set<IORelation> insideUnspecifiedWidths = _relationsWithUnspecifiedWidths(
610                    port.insideRelationList());
611            int insideUnspecifiedWidthsSize = insideUnspecifiedWidths.size();
612
613            if (insideUnspecifiedWidthsSize > 0
614                    && outsideUnspecifiedWidthsSize > 0) {
615                return true;
616            }
617            if (insideUnspecifiedWidthsSize == 0
618                    && outsideUnspecifiedWidthsSize == 0) {
619                return false;
620            }
621
622            int insideWidth = port._getInsideWidth(null);
623            int outsideWidth = port._getOutsideWidth(null);
624
625            if (insideUnspecifiedWidthsSize > 0) {
626                unspecifiedWidths = insideUnspecifiedWidths;
627                difference = outsideWidth - insideWidth;
628            } else {
629                assert outsideUnspecifiedWidthsSize > 0;
630                unspecifiedWidths = outsideUnspecifiedWidths;
631                difference = insideWidth - outsideWidth;
632            }
633
634            // We expect that the following case does not exist anymore since atomic
635            // actors are handled separately. Hence the assert...
636            // For opaque ports we not always want the same behavior.
637            // For example at an add-subtract actor we only have information
638            // about the outside, and hence we can't infer any widths at this port.
639            // In this case difference < 0.
640            // However in the case of a multimodel, you often have relations on the
641            // inside and width inference needs to happen. In this case difference >=0
642            if (port.isOpaque() && difference < 0) {
643                assert false; // We don't expect to come in this case
644                assert insideWidth == 0;
645                return false; // No width inference possible and necessary at this port.
646            }
647            if (difference < 0) {
648                throw new IllegalActionException(port,
649                        "The inside and outside widths of port "
650                                + port.getFullName()
651                                + " are not consistent.\nThe inferred width of relation "
652                                + unspecifiedWidths.iterator().next()
653                                        .getFullName()
654                                + " would be negative.");
655            }
656        }
657
658        assert unspecifiedWidths != null;
659        int unspecifiedWidthsSize = unspecifiedWidths.size();
660
661        // Put the next test in comments since the condition
662        // difference >= unspecifiedWidthsSize only needs to be fulfilled
663        // in case we don't allow inferred widths to be zero.
664        //
665        // if (difference > 0 && difference < unspecifiedWidthsSize) {
666        //     throw new IllegalActionException(port,
667        //             "The inside and outside widths of port " + port.getFullName()
668        //             + " are not consistent.\n Can't determine a uniquely defined width for relation"
669        //             + unspecifiedWidths.iterator().next().getFullName());
670        // }
671
672        if (unspecifiedWidthsSize == 1 || unspecifiedWidthsSize == difference
673                || difference == 0) {
674            int width = difference / unspecifiedWidthsSize;
675            assert width >= 0;
676            for (IORelation relation : unspecifiedWidths) {
677                relation._setInferredWidth(width);
678                updatedRelations.add(relation);
679            }
680            constraintStillUseful = false;
681        }
682        return constraintStillUseful;
683    }
684
685    /** Infer the width for the relations connected to the port (which should have a default width).
686     *  If the width can be inferred, update the width and add the relations for which the width
687     *  has been updated.
688     *  @param port The port for whose connected relations the width should be inferred.
689     *  @param updatedRelations The relations for which the width has been updated.
690     *  @return true When this constraint is still useful (can be used to extra more information).
691     *  @exception IllegalActionException If the expression for the width cannot
692     *   be parsed or cannot be evaluated, or if the result of evaluation
693     *   violates type constraints, or if the result of evaluation is null
694     *   and there are variables that depend on this one.
695     */
696    static private boolean _updateRelationsFromDefaultWidth(IOPort port,
697            List<IORelation> updatedRelations) throws IllegalActionException {
698        boolean constraintStillUseful = true;
699
700        Set<IORelation> outsideUnspecifiedWidths = _relationsWithUnspecifiedWidths(
701                port.linkedRelationList());
702        //port.linkedRelationList() returns the outside relations
703
704        int outsideUnspecifiedWidthsSize = outsideUnspecifiedWidths.size();
705
706        NamedObj namedObject = port.getContainer();
707        if (namedObject == null) {
708            assert false; // not expected
709            return false;
710        }
711        assert outsideUnspecifiedWidthsSize >= 0;
712        if (outsideUnspecifiedWidthsSize > 0) {
713            int defaultWidth = port.getDefaultWidth();
714            assert defaultWidth >= 0;
715            int unspecifiedWidthsSize = outsideUnspecifiedWidths.size();
716            if (unspecifiedWidthsSize == 1
717                    || unspecifiedWidthsSize % defaultWidth == 0
718                    || defaultWidth == 0) {
719                int width = 0;
720                // Coverity: Avoid a divide by zero error.
721                if (defaultWidth != 0) {
722                    width = unspecifiedWidthsSize / defaultWidth;
723                }
724                assert width >= 0;
725                for (IORelation relation : outsideUnspecifiedWidths) {
726                    relation._setInferredWidth(width);
727                    updatedRelations.add(relation);
728                }
729                constraintStillUseful = false;
730            }
731        } else {
732            constraintStillUseful = false;
733        }
734        return constraintStillUseful;
735    }
736
737    ///////////////////////////////////////////////////////////////////
738    ////                         private variables                 ////
739
740    //True when we are inferring widths
741    private boolean _inferringWidths = false;
742
743    //True when width inference needs to happen again
744    private boolean _needsWidthInference = true;
745
746    //The top level of the model.
747    private CompositeActor _topLevel = null;
748}