001/* Class for representing a solution.
002
003 Copyright (c) 2003-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_3
025 COPYRIGHTENDKEY
026 */
027package ptolemy.moml.unit;
028
029import java.awt.Color;
030import java.text.DecimalFormat;
031import java.util.Iterator;
032import java.util.Vector;
033
034import ptolemy.actor.IOPort;
035import ptolemy.actor.IORelation;
036import ptolemy.actor.TypedCompositeActor;
037import ptolemy.actor.TypedIOPort;
038import ptolemy.kernel.ComponentEntity;
039import ptolemy.kernel.util.Attribute;
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.NamedObj;
042import ptolemy.moml.MoMLChangeRequest;
043
044///////////////////////////////////////////////////////////////////
045//// Solution
046
047/** An instance of this class contains a "solution" of Unit constraints.
048 In essence, the solution represents the constraints between a set of Unit
049 variables, and a set of Units.
050 The table below illustrates this.
051 <table border = "1">
052 <caption>Unit Constraints</caption>
053 <TR>
054 <TH></TH>
055 <TH>V1</TH>
056 <TH>V2</TH>
057 <TH>...</TH>
058 <TH>Vl</TH>
059 <TH></TH>
060 </TR>
061
062 <TR>
063 <TD>C1</TD>
064 <TD>P11</TD>
065 <TD>P12</TD>
066 <TD>...</TD>
067 <TD>P1l</TD>
068 <TD>U1</TD>
069 </TR>
070
071 <TR>
072 <TD>C2</TD>
073 <TD>P21</TD>
074 <TD>P22</TD>
075 <TD>...</TD>
076 <TD>P2l</TD>
077 <TD>U2</TD>
078 </TR>
079
080 <TR>
081 <TD>:</TD>
082 <TD>:</TD>
083 <TD>:</TD>
084 <TD>...</TD>
085 <TD>:</TD>
086 <TD>:</TD>
087 </TR>
088
089 <TR>
090 <TD>Ck</TD>
091 <TD>Pk1</TD>
092 <TD>Pk2</TD>
093 <TD>...</TD>
094 <TD>Pkl</TD>
095 <TD>Uk</TD>
096 </TR>
097 </TABLE>
098 Here, the columns V1, V2, ..., Vl represent l variables.
099 The columns C1, C2, ..., Ck represent k constraints.
100 The U1, U2, ..., Uk on the right represent Units.
101 The meaning of the ith row is that V1^Pi1 + V2^pi2 + .. +Vk^pik = Uk.
102 <p>
103 Generally, this class is used by creating an instance that is derived from
104 the Units specifications of a model.
105 Then, a method is invoked that results in other instances being created that
106 are transformations of the original instance. These transformed instances
107 are equivalent, in a sense, to the original instance. The difference is that
108 they provide a different perspective than that of the original instance. In
109 particular, some of the transformed instances can be used to highlight
110 inconsistencies not apparent in the original instance.
111 @author Rowland R Johnson
112 @version $Id$
113 @since Ptolemy II 8.0
114 @Pt.ProposedRating Red (rowland)
115 @Pt.AcceptedRating Red (rowland)
116 */
117public class Solution {
118    /** Construct an empty solution.
119     * This constructor is used only by the copy() method, and, can therefore,
120     * be made private.
121     *
122     */
123    private Solution() {
124    }
125
126    /**
127     * Construct a Solution from a set of variables, and a set of constraints.
128     *
129     * @param model The model that is the source of the variables and
130     * constraints.
131     * @param vLabels The variables.
132     * @param constraints The constraints.
133     * @exception IllegalActionException If there are problems with transforming
134     * a constraint to canonical form.
135     */
136    public Solution(TypedCompositeActor model, String[] vLabels,
137            Vector constraints) throws IllegalActionException {
138        _constraints = constraints;
139        _numConstraints = constraints.size();
140        _variables = vLabels;
141        _model = model;
142        _numVariables = _variables.length;
143        _vectorA = new Unit[_numConstraints];
144        _source = new NamedObj[_numConstraints];
145        _done = new boolean[_numConstraints];
146        _arrayP = new double[_numConstraints][];
147
148        for (int i = 0; i < _numConstraints; i++) {
149            _arrayP[i] = new double[_numVariables];
150            _done[i] = false;
151        }
152
153        for (int constraintNum = 0; constraintNum < _numConstraints; constraintNum++) {
154            UnitEquation constraint = (UnitEquation) constraints
155                    .elementAt(constraintNum);
156            UnitEquation canonicalEquation = constraint.canonicalize();
157            Vector rightUTerms = canonicalEquation.getRhs().getUTerms();
158
159            if (rightUTerms.size() != 1) {
160                throw new IllegalActionException("canonicalEquation "
161                        + canonicalEquation + " has nonsingular RHS");
162            }
163
164            UnitTerm rhsUterm = (UnitTerm) rightUTerms.elementAt(0);
165
166            if (!rhsUterm.isUnit()) {
167                throw new IllegalActionException("canonicalEquation "
168                        + canonicalEquation + " has nonUnit RHS");
169            }
170
171            _vectorA[constraintNum] = rhsUterm.getUnit();
172            _source[constraintNum] = constraint.getSource();
173
174            Vector leftUTerms = canonicalEquation.getLhs().getUTerms();
175
176            for (int i = 0; i < leftUTerms.size(); i++) {
177                UnitTerm leftUTerm = (UnitTerm) leftUTerms.elementAt(i);
178
179                if (leftUTerm == null) {
180                    throw new IllegalActionException("canonicalEquation "
181                            + canonicalEquation + " has nonVar LHS");
182                }
183
184                String variableLabel = leftUTerm.getVariable();
185                double exponent = leftUTerm.getExponent();
186                int varIndex = -1;
187
188                for (int j = 0; j < _variables.length; j++) {
189                    if (_variables[j].equals(variableLabel)) {
190                        varIndex = j;
191                        break;
192                    }
193                }
194
195                _arrayP[constraintNum][varIndex] = exponent;
196            }
197        }
198    }
199
200    ///////////////////////////////////////////////////////////////////
201    ////                         public methods                    ////
202
203    /**
204     * Annotates the model so that when it is displayed it will be color coded
205     * and have tooltips that will convey various aspects of the solution.
206     */
207    public void annotateGraph() {
208        if (_debug) {
209            trace();
210        }
211
212        String colorString;
213        StringBuffer moml = new StringBuffer();
214
215        for (int j = 0; j < _numVariables; j++) {
216            String explanation = _varBindings[j];
217            Color colorValue = null;
218
219            if (_varState[j] == _CONSISTENT) {
220                colorValue = Color.GREEN;
221            } else if (_varState[j] == _INCONSISTENT) {
222                colorValue = Color.RED;
223            }
224
225            colorString = _getColorString(colorValue);
226            moml.append("<port name=\"" + _variables[j] + "\">"
227                    + " <property name=\"_color\" "
228                    + "class = \"ptolemy.actor.gui.ColorAttribute\" "
229                    + "value = \"" + colorString + "\"/>"
230                    + "<property name=\"_explanation\" "
231                    + "class = \"ptolemy.kernel.util.StringAttribute\" "
232                    + "value = \"" + explanation + "\"/>" + "</port>");
233        }
234
235        for (int constraintNum = 0; constraintNum < _numConstraints; constraintNum++) {
236            NamedObj source = _source[constraintNum];
237            String expression = _constraintExplanations[constraintNum];
238            Color colorValue = null;
239
240            if (_constraintState[constraintNum] == _CONSISTENT) {
241                colorValue = Color.GREEN;
242            } else if (_constraintState[constraintNum] == _INCONSISTENT) {
243                colorValue = Color.RED;
244            }
245
246            colorString = _getColorString(colorValue);
247            if (source instanceof IOPort) {
248                IOPort port = (IOPort) source;
249                ComponentEntity actor = (ComponentEntity) port.getContainer();
250                moml.append("<entity name=\"" + actor.getName() + "\">"
251                        + _momlAnnotate(port, colorString, expression)
252                        + "</entity>");
253            } else if (source instanceof IORelation) {
254                IORelation relation = (IORelation) source;
255                moml.append(_momlAnnotate(relation, colorString, expression));
256            } else if (source instanceof ComponentEntity) {
257                ComponentEntity componentEntity = (ComponentEntity) source;
258                moml.append(_momlAnnotate(componentEntity, colorString,
259                        expression));
260            }
261        }
262
263        if (moml.length() > 0) {
264            String momlUpdate = "<group>" + moml.toString() + "</group>";
265            MoMLChangeRequest request = new MoMLChangeRequest(this, _model,
266                    momlUpdate);
267            request.setUndoable(true);
268            request.setPersistent(false);
269            _debug("Solver.annotateGraph moml " + momlUpdate);
270            _model.requestChange(request);
271        }
272    }
273
274    /** Search for a complete solution.
275     * @return The solution.
276     */
277    public Solution completeSolution() {
278        _debug("Solver.completeSolution.initial " + headerInfo() + stateInfo());
279
280        Index g;
281
282        while ((g = _findG()) != null) {
283            _eliminate(g);
284            _debug("Solver.completeSolution " + stateInfo());
285        }
286
287        for (int i = 0; i < _numConstraints; i++) {
288            _done[i] = true;
289        }
290
291        _analyzeState();
292
293        if (_debug) {
294            trace();
295        }
296
297        return this;
298    }
299
300    /** Make a copy of this solution.
301     * @return The copy.
302     */
303    public Solution copy() {
304        Solution retv = new Solution();
305        retv._numConstraints = _numConstraints;
306        retv._variables = _variables;
307        retv._model = _model;
308        retv._numVariables = _numVariables;
309        retv._source = _source;
310        retv._debug = _debug;
311        retv._constraints = _constraints;
312        retv._vectorA = new Unit[_numConstraints];
313        retv._done = new boolean[_numConstraints];
314        retv._arrayP = new double[_numConstraints][];
315
316        for (int i = 0; i < _numConstraints; i++) {
317            retv._arrayP[i] = new double[_numVariables];
318
319            for (int j = 0; j < _numVariables; j++) {
320                retv._arrayP[i][j] = _arrayP[i][j];
321            }
322
323            retv._done[i] = _done[i];
324            retv._vectorA[i] = _vectorA[i].copy();
325        }
326
327        retv._upper = this;
328        return retv;
329    }
330
331    /** Get the state of the solution.
332     * @return The state of the solution.
333     */
334    public String getShortStateDesc() {
335        switch (_solveState) {
336        case _UNKNOWN:
337            return "UnKnown";
338
339        case _NONUNIQUE:
340            return "No Unique Solution";
341
342        case _INCONSISTENT:
343            return "Inconsistent";
344
345        case _CONSISTENT:
346            return "Consistent";
347        }
348
349        return "Unknown";
350    }
351
352    /** Get the state of the solution.
353     * @return The state of the solution.
354     */
355    public String getStateDesc() {
356        return _stateDescription;
357    }
358
359    /**
360     * Create a human readable presentation of the parts of the solution that
361     * won't change as a result of
362     * the operations necessary to carry out the Gaussian elimination.
363     * I.e. the variable names, and the constraints.
364     * @return A StringBuffer with a human readable presentation of the
365     * invariant parts of the solution.
366     */
367    public StringBuffer headerInfo() {
368        StringBuffer retv = new StringBuffer();
369        retv.append("Header\nVariables\n");
370
371        for (int j = 0; j < _numVariables; j++) {
372            retv.append(
373                    "   " + _vNumFormat.format(j) + " " + _variables[j] + "\n");
374        }
375
376        retv.append("\n");
377        retv.append("ConstrNum  Source\n");
378
379        for (int i = 0; i < _numConstraints; i++) {
380            NamedObj source = _source[i];
381            retv.append(
382                    "" + _vNumFormat.format(i) + "         " + source.toString()
383                            + " " + ((UnitEquation) _constraints.elementAt(i))
384                                    .descriptiveForm()
385                            + "\n");
386        }
387
388        retv.append("\\Header\n");
389        return retv;
390    }
391
392    /** Produce all of the minimal span solutions that can be generated from
393     * this instance. A minimal span solution is one in which a minimal number
394     * of constraints that are connected yield an inconsistency.
395     * @return The vector of minimal span solutions.
396     */
397    public Vector minimalSpanSolutions() {
398        Vector solutions = new Vector();
399        _debug("Solver.minimalSpanSolutions " + headerInfo() + " initial\n"
400                + stateInfo());
401
402        Solution root = copy();
403
404        // First eliminate all the singletons. These are due to statically bound
405        // ports.
406        Iterator allG = root._findAllG().iterator();
407
408        while (allG.hasNext()) {
409            Index g = (Index) allG.next();
410            root._eliminate(g);
411        }
412
413        // The solution may already be inconsistent. This would be the case if
414        // two statically bound ports are connected but have different units.
415        root._checkForInConsistency();
416
417        if (root._solveState == _INCONSISTENT) {
418            root._analyzeState();
419            solutions.add(root);
420        } else {
421            // The root solution is consistent. Now use the root as the starting
422            // point to generate all possible minimally spanning solutions.
423            root._branchPoint = null;
424            _debug("Solver.solve root\n" + root.stateInfo());
425            root._branchPoints = root._findAllG();
426
427            if (root._branchPoints.size() > 0) {
428                for (int i = 0; i < root._branchPoints.size(); i++) {
429                    Solution s = root.copy();
430                    Vector results = s._partialSolveRecursively(1,
431                            (Index) root._branchPoints.elementAt(i));
432                    solutions.addAll(results);
433                }
434            } else {
435                root._analyzeState();
436                solutions.add(root);
437            }
438        }
439
440        if (_debug) {
441            for (int i = 0; i < solutions.size(); i++) {
442                Solution solution = (Solution) solutions.elementAt(i);
443                System.out.println("A Solution\n" + solution.stateInfo());
444            }
445        }
446
447        return solutions;
448    }
449
450    /**
451     * The current state of the solver. A StringBuffer is produced that shows
452     * the variables, done vector, P array, and A vector in a human readable
453     * arrangement.
454     *
455     * @return A StringBuffer with the state of the solver.
456     */
457    public StringBuffer stateInfo() {
458        StringBuffer retv = new StringBuffer();
459        retv.append("State\n");
460        retv.append("BranchPoints " + _branchPoints + "\n    ");
461
462        for (int j = 0; j < _numVariables; j++) {
463            retv.append(" " + _vNumFormat.format(j));
464        }
465
466        retv.append("\n");
467
468        for (int i = 0; i < _numConstraints; i++) {
469            if (_done[i]) {
470                retv.append("T ");
471            } else {
472                retv.append("F ");
473            }
474
475            retv.append("" + _vNumFormat.format(i) + " ");
476
477            for (int j = 0; j < _numVariables; j++) {
478                retv.append("" + _pFormat.format(_arrayP[i][j]) + " ");
479            }
480
481            retv.append("" + _vectorA[i] + " " + _vectorA[i].descriptiveForm());
482            retv.append(" " + ((UnitEquation) _constraints.elementAt(i))
483                    .descriptiveForm());
484            retv.append("\n");
485        }
486
487        if (_branchPoint == null) {
488            retv.append("BranchPoint = null\n");
489        } else {
490            retv.append("BranchPoint = " + _branchPoint.toString() + "\n");
491        }
492
493        retv.append("Solution: " + getStateDesc());
494        retv.append("\n\\State\n");
495        return retv;
496    }
497
498    public void trace() {
499        System.out.print("Solver.trace\n");
500
501        Solution s = this;
502
503        while (s != null) {
504            System.out.print(s.stateInfo());
505            s = s._upper;
506        }
507
508        System.out.print(headerInfo());
509    }
510
511    ///////////////////////////////////////////////////////////////////
512    /////                        private methods                 //////
513    private void _analyzeState() {
514        _varBindings = new String[_numVariables];
515        _constraintExplanations = new String[_numConstraints];
516        _varState = new int[_numVariables];
517        _constraintState = new int[_numConstraints];
518
519        StringBuffer inconsistencyDesc = new StringBuffer();
520
521        for (int i = 0; i < _numConstraints; i++) {
522            _constraintState[i] = _UNKNOWN;
523            _constraintExplanations[i] = "";
524
525            if (_done[i]) {
526                int numNonZeroP = 0;
527
528                for (int j = 0; j < _numVariables; j++) {
529                    if (_arrayP[i][j] != 0) {
530                        numNonZeroP++;
531                    }
532                }
533
534                if (numNonZeroP == 0
535                        && !_vectorA[i].equals(UnitLibrary.Identity)) {
536                    Unit factor = _vectorA[i].invert();
537                    String uString = factor.descriptiveForm();
538                    _constraintState[i] = _INCONSISTENT;
539                    _constraintExplanations[i] = uString;
540                } else if (numNonZeroP > 1
541                        && _vectorA[i].equals(UnitLibrary.Identity)) {
542                    _constraintState[i] = _NONUNIQUE;
543                } else {
544                    String uString = _vectorA[i].descriptiveForm();
545                    _constraintState[i] = _CONSISTENT;
546                    _constraintExplanations[i] = uString;
547                }
548            }
549        }
550
551        for (int j = 0; j < _numVariables; j++) {
552            _varBindings[j] = "";
553
554            int numNonZeroP = 0;
555
556            for (int i = 0; i < _numConstraints; i++) {
557                if (_done[i] && _arrayP[i][j] != 0) {
558                    Unit U = _vectorA[i].pow(1.0 / _arrayP[i][j]);
559
560                    if (numNonZeroP > 0) {
561                        _varBindings[j] += ";";
562                    }
563
564                    _varBindings[j] += U.descriptiveForm();
565                    numNonZeroP++;
566                }
567            }
568
569            if (numNonZeroP > 1) {
570                _varBindings[j] = "*AMBIGUOUS* " + _varBindings[j];
571                _varState[j] = _INCONSISTENT;
572            }
573
574            if (numNonZeroP == 1) {
575                _varState[j] = _CONSISTENT;
576            } else {
577                _varBindings[j] = "<Unbound>";
578                _varState[j] = _NONUNIQUE;
579            }
580        }
581
582        boolean stateInconsistent = false;
583        boolean stateNonUnique = false;
584        _solveState = _CONSISTENT;
585
586        for (int i = 0; i < _numConstraints; i++) {
587            switch (_constraintState[i]) {
588            case _INCONSISTENT: {
589                stateInconsistent = true;
590
591                NamedObj source = _source[i];
592                String sourceName = "NoSource";
593
594                if (source instanceof IORelation) {
595                    sourceName = ((IORelation) source).getName();
596                } else if (source instanceof ComponentEntity) {
597                    sourceName = ((ComponentEntity) source).getName();
598                } else if (source instanceof TypedIOPort) {
599                    sourceName = source
600                            .getName(source.getContainer().getContainer());
601                }
602
603                inconsistencyDesc.append(
604                        " " + sourceName + " " + _constraintExplanations[i]);
605                break;
606            }
607
608            case _NONUNIQUE: {
609                stateNonUnique = true;
610                break;
611            }
612            }
613        }
614
615        if (stateInconsistent && stateNonUnique) {
616            _debug("State is both Inconsistent and NonUnique");
617        }
618
619        for (int j = 0; j < _numVariables; j++) {
620            if (_varState[j] == _INCONSISTENT) {
621                stateInconsistent = true;
622                inconsistencyDesc
623                        .append(" " + _variables[j] + "=" + _varBindings[j]);
624            }
625        }
626
627        if (stateInconsistent) {
628            _solveState = _INCONSISTENT;
629        } else if (stateNonUnique) {
630            _solveState = _NONUNIQUE;
631        } else {
632            _solveState = _CONSISTENT;
633        }
634
635        switch (_solveState) {
636        case _UNKNOWN: {
637            _stateDescription = "UnKnown";
638            break;
639        }
640
641        case _NONUNIQUE: {
642            _stateDescription = "No Unique Solution";
643            break;
644        }
645
646        case _INCONSISTENT: {
647            _stateDescription = "Inconsistent" + inconsistencyDesc;
648            break;
649        }
650
651        case _CONSISTENT: {
652            _stateDescription = "Consistent";
653            break;
654        }
655        }
656    }
657
658    private int[] _branchesFrom(Index g) {
659        int k = g.getK();
660        int l = g.getL();
661        int num = 0;
662
663        for (int i = 0; i < _numConstraints; i++) {
664            if (i != k && _arrayP[i][l] != 0) {
665                num++;
666            }
667        }
668
669        int[] retv = new int[num];
670        int index = 0;
671
672        for (int i = 0; i < _numConstraints; i++) {
673            if (i != k && _arrayP[i][l] != 0) {
674                retv[index++] = i;
675            }
676        }
677
678        return retv;
679    }
680
681    private void _checkForInConsistency() {
682        for (int i = 0; i < _numConstraints; i++) {
683            if (!_vectorA[i].equals(UnitLibrary.Identity)) {
684                boolean inconsistent = true;
685
686                for (int j = 0; j < _numVariables; j++) {
687                    if (_arrayP[i][j] != 0) {
688                        inconsistent = false;
689                    }
690                }
691
692                if (inconsistent) {
693                    _done[i] = true;
694                    _solveState = _INCONSISTENT;
695                }
696            }
697        }
698    }
699
700    private void _debug(String msg) {
701        if (_debug) {
702            System.out.println(msg);
703        }
704    }
705
706    /**
707     * @param g
708     */
709    private void _eliminate(Index g) {
710        int k = g.getK();
711        int l = g.getL();
712        _debug("Eliminating (" + k + ", " + l + ")");
713
714        Unit U = _vectorA[k].pow(1.0 / _arrayP[k][l]);
715        _vectorA[k] = U;
716        _arrayP[k][l] = 1;
717
718        for (int i = 0; i < _numConstraints; i++) {
719            if (i != k && !_done[i] && _arrayP[i][l] != 0) {
720                _vectorA[i] = _vectorA[i].divideBy(U.pow(_arrayP[i][l]));
721                _arrayP[i][l] = 0;
722            }
723        }
724
725        _branchPoint = g;
726        _done[k] = true;
727    }
728
729    /**
730     * Finds all Index(i, l) such that P[i][l] != 0 and 1) all other P in row i
731     * is equal to 0
732     *
733     * @return Index(i, l)
734     */
735    private Vector _findAllG() {
736        Vector retv = new Vector();
737
738        for (int i = 0; i < _numConstraints; i++) {
739            if (!_done[i]) {
740                int l = -1;
741                boolean possible = false;
742
743                for (int j = 0; j < _numVariables; j++) {
744                    if (_arrayP[i][j] != 0) {
745                        if (l == -1) {
746                            possible = true;
747                            l = j;
748                        } else {
749                            possible = false;
750                            break;
751                        }
752                    }
753                }
754
755                if (possible) {
756                    retv.add(new Index(i, l));
757                    continue;
758                }
759            }
760        }
761
762        return retv;
763    }
764
765    /**
766     * @param rows
767     * @return A vector of Indexes.
768     */
769    private Vector _findAllGInRows(int[] rows) {
770        Vector retv = new Vector();
771
772        for (int k : rows) {
773            Index g = _findGInRow(k);
774
775            if (g != null) {
776                retv.add(g);
777            }
778        }
779
780        return retv;
781    }
782
783    /**
784     * Finds an Index(i, l) such that P[i][l] != 0 and 1) all other P in row i
785     * is equal to 0
786     *
787     * @return Index(i, l)
788     */
789    private Index _findG() {
790        for (int i = 0; i < _numConstraints; i++) {
791            if (!_done[i]) {
792                int l = -1;
793                boolean possible = false;
794
795                for (int j = 0; j < _numVariables; j++) {
796                    if (_arrayP[i][j] != 0) {
797                        if (l == -1) {
798                            possible = true;
799                            l = j;
800                        } else {
801                            possible = false;
802                            break;
803                        }
804                    }
805                }
806
807                if (possible) {
808                    return new Index(i, l);
809                }
810            }
811        }
812
813        return null;
814    }
815
816    private Index _findGInRow(int k) {
817        int l = -1;
818
819        for (int j = 0; j < _numVariables; j++) {
820            if (_arrayP[k][j] != 0) {
821                if (l == -1) {
822                    l = j;
823                } else {
824                    return null;
825                }
826            }
827        }
828
829        if (l == -1) {
830            return null;
831        }
832
833        return new Index(k, l);
834    }
835
836    /** Return the string representation of the color value.
837     *  @param colorValue The input color value.
838     *  @return The string representation of the color as a Ptolemy expression
839     *   string array of double values.
840     */
841    private String _getColorString(Color colorValue) {
842        if (colorValue == null) {
843            return "";
844        } else {
845            float[] colorArray = colorValue.getRGBComponents(null);
846            return "{ " + colorArray[0] + ", " + colorArray[1] + ", "
847                    + colorArray[2] + ", " + colorArray[3] + " }";
848        }
849    }
850
851    private String _momlAnnotate(NamedObj entity, String color,
852            String expression) {
853        String colorProperty = null;
854        // We don't use ptolemy.actor.gui.ColorAttribute here
855        // because we don't want to add a dependency between
856        // moml.unit and actor.gui
857        Attribute currentColor = entity.getAttribute("_color");
858
859        if (currentColor != null && color == null) {
860            colorProperty = "<deleteProperty _name=_color/>";
861        } else if (color != null) {
862            colorProperty = "<property name=\"_color\" "
863                    + "class = \"ptolemy.actor.gui.ColorAttribute\" "
864                    + "value = \"" + color + "\"/>";
865        }
866
867        return "<" + entity.getElementName() + " name=\"" + entity.getName()
868                + "\" class=\"" + entity.getClassName() + "\">" + colorProperty
869                + "<property name=\"_explanation\" "
870                + "class = \"ptolemy.kernel.util.StringAttribute\" "
871                + "value = \"" + expression + "\"/></" + entity.getElementName()
872                + ">";
873    }
874
875    private Vector _partialSolveRecursively(int level, Index g) {
876        Vector retv = new Vector();
877        _debug("\nSolver._eliminateRecursively level " + level + " BrancPoint "
878                + g + "\n" + stateInfo());
879
880        int[] rows = _branchesFrom(g);
881        _eliminate(g);
882        _checkForInConsistency();
883        _branchPoints = _findAllGInRows(rows);
884
885        if (_solveState != _INCONSISTENT && _branchPoints.size() > 0) {
886            if (_debug) {
887                System.out.print("Branch Rows at level " + level + " for " + g);
888
889                for (int row : rows) {
890                    System.out.print(" " + row);
891                }
892
893                System.out.print("\nRemaining BranchPoints");
894
895                for (int a = 0; a < _branchPoints.size(); a++) {
896                    System.out.print(" " + _branchPoints.elementAt(a));
897                }
898
899                System.out.print("\n");
900            }
901
902            for (int gi = 0; gi < _branchPoints.size(); gi++) {
903                Solution s = copy();
904                Vector results = s._partialSolveRecursively(level + 1,
905                        (Index) _branchPoints.elementAt(gi));
906
907                if (results != null) {
908                    retv.addAll(results);
909                }
910            }
911        } else {
912            _analyzeState();
913
914            if (_debug) {
915                trace();
916            }
917
918            retv.add(this);
919        }
920
921        return retv;
922    }
923
924    ///////////////////////////////////////////////////////////////////
925    ////                         private variables                 ////
926    private static final int _UNKNOWN = -1;
927
928    private static final int _CONSISTENT = 0;
929
930    private static final int _INCONSISTENT = 1;
931
932    private static final int _NONUNIQUE = 2;
933
934    double[][] _arrayP;
935
936    Index _branchPoint = null;
937
938    Vector _branchPoints = null;
939
940    Vector _constraints = null;
941
942    String[] _constraintExplanations = null;
943
944    int[] _constraintState = null;
945
946    boolean _debug = false;
947
948    boolean[] _done;
949
950    TypedCompositeActor _model;
951
952    int _numConstraints = 0;
953
954    int _numVariables = 0;
955
956    private static final DecimalFormat _pFormat = new DecimalFormat(" 0;-0");
957
958    private int _solveState = _UNKNOWN;
959
960    NamedObj[] _source;
961
962    String _stateDescription = "No description";
963
964    Solution _upper = null;
965
966    String[] _varBindings = null;
967
968    String[] _variables;
969
970    int[] _varState = null;
971
972    Unit[] _vectorA;
973
974    private static final DecimalFormat _vNumFormat = new DecimalFormat("00");
975
976    ///////////////////////////////////////////////////////////////////
977    ////                         inner class                       ////
978
979    /**
980     * Class that represents an index in the P array.
981     *
982     */
983    private static class Index {
984        // FindBugs suggests making this class static so as to decrease
985        // the size of instances and avoid dangling references.
986        int k;
987
988        int l;
989
990        private Index(int k1, int l1) {
991            k = k1;
992            l = l1;
993        }
994
995        public int getK() {
996            return k;
997        }
998
999        public int getL() {
1000            return l;
1001        }
1002
1003        @Override
1004        public String toString() {
1005            return "(" + k + "," + l + ")";
1006        }
1007    }
1008}