View Javadoc
1   package org.opentrafficsim.road.gtu.generator;
2   
3   import java.util.ArrayList;
4   import java.util.LinkedHashMap;
5   import java.util.LinkedHashSet;
6   import java.util.List;
7   import java.util.Locale;
8   import java.util.Map;
9   import java.util.Set;
10  
11  import org.djutils.exceptions.Throw;
12  
13  import nl.tudelft.simulation.jstats.streams.StreamInterface;
14  
15  /**
16   * Markov Chain functionality using state auto-correlations. Rather than specifying a Transition Matrix, this matrix is
17   * calculated from a steady-state and from given auto-correlations. Auto-correlation increases the probability that state S
18   * returns after state S. Correlation between states can be captured with grouping several states under a super state. This
19   * creates a Transition Matrix within a cell of a Transition Matrix. To the super matrix, this is simply a single
20   * state-supplying element, applicable to all previous states that are concerned within the element.<br>
21   * <br>
22   * This class is oblivious to intensity data of the states, in the sense that it must be provided to draw the next state. This
23   * class actually only remembers state auto-correlations. Together with input intensities, a part of the Transition Matrix is
24   * calculated on the fly. This flexibility over a fixed Transition Matrix allows 1) dynamic intensities, and 2) a single object
25   * of this class to be used for multiple processes in which intensities differ, but correlations are equal. This class is
26   * therefore also fairly flexible in terms of which states are concerned. Only states with correlation need to be added. For any
27   * state included in the input, for which no correlation is defined, a correlation of 0 is assumed. Reversely, states that are
28   * included in the class, but that are not part of the input states, are ignored.
29   * <p>
30   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
31   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
32   * <p>
33   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 18 dec. 2017 <br>
34   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
35   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
36   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
37   * @param <S> state type
38   * @param <I> intensity type
39   */
40  public class MarkovCorrelation<S, I extends Number>
41  {
42  
43      /** Leaf node for each state present in the Markov Chain, including in all sub-groups. */
44      private Map<S, FixedState<S, I>> leaves = new LinkedHashMap<>();
45  
46      /** Transition Matrix for each super state, i.e. the matrix within which states are put that have the given super state. */
47      private Map<S, TransitionMatrix<S, I>> superMatrices = new LinkedHashMap<>();
48  
49      /** Matrix in which each state is located. */
50      private Map<S, TransitionMatrix<S, I>> containingMatrices = new LinkedHashMap<>();
51  
52      /** Root matrix. */
53      private TransitionMatrix<S, I> root = new TransitionMatrix<>(null, 0.0); // input not important, it's for sub-groups
54  
55      /**
56       * Adds a state to the root group of the Markov Chain. The correlation increases the chance that the state will occur after
57       * itself. We have: <br>
58       * 
59       * <pre>
60       * p_ii = ss_i + (1 - ss_i) * c_i {eq. 1}
61       * </pre>
62       * 
63       * where,
64       * 
65       * <pre>
66       *   p_ii: the probability state i returns after state i
67       *   ss_i: the steady-state (overall mean) probability of state i
68       *   c_i:  correlation of state i
69       * </pre>
70       * 
71       * Effective correlations of states depend on correlations of other states as well, so the given correlation is not
72       * guaranteed to result. One can easily see this from a system with 2 states: A and B. Suppose B has correlation. In order
73       * to maintain the same overall steady-state (occurrence proportion of A and B), it follows that state A also must be seen
74       * more frequently to follow itself.
75       * 
76       * <pre>
77       * not correlated:  A B B A A A B A A A B A A B B
78       * very correlated: A A A A A A A A A B B B B B B (all B's grouped together, hence all A's grouped together and correlated)
79       * </pre>
80       * 
81       * The effective correlation <i>c_i</i> of any state <i>i</i> can be calculated by reversing equation {@code eq. 1} using
82       * for <i>p_ii</i> the effective value after all correlations are applied. The procedure to derive the various probabilities
83       * from state <i>i</i> to state <i>j</i> (<i>p_ij</i>) is explained below. The procedure is based on the transition matrix
84       * <i>T</i>, in which each value gives the probability that the state changes from <i>i</i> (the row) to state <i>j</i> (the
85       * column). Consequently, the values in each row must sum to 1, as each state will be followed by another state.<br>
86       * <br>
87       * It is important that the transition matrix <i>T</i> results in a steady-state as provided. In particular we have for
88       * steady state <i>S</i> that <b><i>S</i>*<i>T</i> &#61; <i>S</i></b> should hold. Suppose we have <i>S</i> &#61; [0.7, 0.2,
89       * 0.1] for states A, B and C. Without any correlation this would give the base transition matrix:
90       * 
91       * <pre>
92       *     | p_11  p_12  p_13 |   | 0.70  0.20  0.10 |
93       * T = | p_21  p_22  p_23 | = | 0.70  0.20  0.10 | 
94       *     | p_31  p_32  p_33 |   | 0.70  0.20  0.10 |
95       * </pre>
96       * 
97       * Our steady-state results as for whatever the previous state was, the steady-state probabilities are applied. Now suppose
98       * that state C has a correlation of 0.4. This would give that <i>p_33</i> :&#61; <i>p_33</i> + (1 - <i>p_33</i>) * <i>c</i>
99       * &#61; 0.46. With this increased value, the probabilities of row 3 no longer add up to 1. Hence, <i>p_31</i> and
100      * <i>p_32</i> should be reduced. However, we require that the same steady-state <i>S</i> is maintained. This will remain
101      * the case for as long as <i>T</i> remains a <i>reversible</i> Markov Chain. This means that each state has as much input
102      * probability, as it has output probability. A matrix where, except for the values on the diagonal, all column values are
103      * equal, is reversible. So the base <i>T</i> without correlation is reversible, and we only need to maintain reversibility.
104      * A method to maintain reversibility is to <i>scale symmetric pairs</i>. Hence, if we reduce <i>p_32</i>, we should reduce
105      * <i>p_23</i> by the same <i>factor</i>. Forcing row 3 to sum to 1, and scaling <i>p_31</i>, <i>p_13</i>, <i>p_32</i> and
106      * <i>p_23</i> by the same factor 0.6 we obtain the third matrix below.
107      * 
108      * <pre>
109      *      | 0.70  0.20  0.10 |    | 0.70  0.20  0.10 |    | 0.70  0.20  0.06 |    | 0.74  0.20  0.06 |
110      * T =&gt; | 0.70  0.20  0.10 | =&gt; | 0.70  0.20  0.10 | =&gt; | 0.70  0.20  0.06 | =&gt; | 0.70  0.24  0.06 |
111      *      | 0.70  0.20  0.10 |    | 0.70  0.20  0.46 |    | 0.42  0.12  0.46 |    | 0.42  0.12  0.46 |
112      * </pre>
113      * 
114      * As we reduce <i>p_13</i> and <i>p_23</i>, we also reduce the probability sums of rows 1 and 2. These reductions can be
115      * compensated by increasing the values on the diagonals, as is done in the fourth matrix. Note that changing the diagonal
116      * values does not affect reversibility. For example, 0.7*0.74 + 0.2*0.70 + 0.1*0.42 &#61; 0.7 for the first column.<br>
117      * <br>
118      * Changing the diagonal values <i>p_11</i> and <i>p_22</i> as the result of correlation for state C, shows that correlation
119      * of one state automatically introduces correlation at other states, as should also intuitively occur from the A-B example.
120      * The procedure can be started with the base <i>T</i> from steady-state <i>S</i> and can be repeated for each state
121      * <i>i</i> with correlation:
122      * <ol>
123      * <li>Increase <i>p_ii</i> to <i>p_ii</i> + (1-<i>p_ii</i>) * <i>c_i</i>.</li>
124      * <li>Reduce <i>p_ij</i> for all <i>j</i> unequal to <i>i</i> such that row <i>i</i> sums to 1, use one factor <i>f</i> for
125      * all.</li>
126      * <li>Reduce <i>p_ji</i> for all <i>j</i> unequal to <i>i</i> to maintain reversibility. Use factor <i>f</i> again.</li>
127      * <li>Set all <i>p_jj</i> for all <i>j</i> unequal to <i>i</i> such that row <i>j</i> sums to 1.</li>
128      * </ol>
129      * Knowing that each value <i>p_ij</i> gets reduced for correlation of state <i>i</i> and <i>j</i>, and realizing that the
130      * reduction factors <i>f</i> equal (1 - <i>c_i</i>) and (1 - <i>c_j</i>) respectively, the effective correlation can be
131      * calculated by adding all reductions in a row to the diagonal value <i>p_ii</i> and using {@code eq. 1}.<br>
132      * <br>
133      * See also "Construction of Transition Matrices of Reversible Markov Chains" by Qian Jiang.
134      * @param state S; state
135      * @param correlation double; correlation
136      * @throws IllegalArgumentException if correlation is not within the range (-1 ... 1), or the state is already defined
137      * @throws NullPointerException if state is null
138      */
139     public synchronized void addState(final S state, final double correlation)
140     {
141         Throw.whenNull(state, "State may not be null.");
142         Throw.when(this.leaves.containsKey(state), IllegalArgumentException.class, "State %s already defined.", state);
143         Throw.when(correlation <= -1.0 || correlation >= 1.0, IllegalArgumentException.class,
144                 "Correlation at root level need to be in the range (-1 ... 1).");
145         FixedState<S, I> node = new FixedState<>(state, correlation);
146         this.root.addNode(state, node);
147         this.containingMatrices.put(state, this.root);
148         this.leaves.put(state, node);
149     }
150 
151     /**
152      * Adds a state to the group of the Markov Chain indicated by a super state. If the super state is not yet placed in a
153      * sub-group, the sub-group is created. Grouping is useful to let a set of states correlate to any other of the states in
154      * the set. For example, after state A, both states A and B can occur with some correlation, while state C is not correlated
155      * to states A and B. The same correlation is applied when the previous state was B, as it is also part of the same group.
156      * <br>
157      * <br>
158      * To explain sub-groups, suppose we have the following 3-state matrix in which the super state <i>s_2</i> is located (this
159      * can be the root matrix, or any sub-matrix). In this matrix, state <i>s_2</i> is replaced by a matrix <i>S_2</i>.
160      * 
161      * <pre>
162      *       s_1   s_2   s_3             s_1   S_2   s_3
163      * s_1 | p_11  p_12  p_13 |    s_1 | p_11  p_12  p_13 |
164      * s_2 | p_21  p_22  p_23 | =&gt; S_2 | p_21  p_22  p_23 |
165      * s_3 | p_31  p_32  p_33 |    s_3 | p_31  p_32  p_33 |
166      * </pre>
167      * 
168      * From the level of this matrix, nothing changes. Whenever the prior state was any of those inside <i>S_2</i>, row 2 is
169      * applied to determine the next state. If the next state is matrix <i>S_2</i>, the state is further specified by
170      * <i>S_2</i>. Matrix <i>S_2</i> itself will be:
171      * 
172      * <pre>
173      *       s_2   s_4
174      * s_2 | p_22' p_24 |
175      * s_4 | p_42  p_44 |
176      * </pre>
177      * 
178      * It will thus result in either state <i>s_2</i> or state <i>s_4</i>. More states can now be added to <i>S_2</i>, using the
179      * same super state <i>s_2</i>. In case the prior state was either <i>s_1</i> or <i>s_3</i>, i.e. no state included in the
180      * sub-group, the matrix of the sub-group defaults to fractions based on the steady-state only. Correlations are then also
181      * ignored.<br>
182      * <br>
183      * Correlation of <i>s_2</i> is applied to the whole group, and all other states of the group can be seen as sub-types of
184      * the group's super state. Correlations as considered inside the group (<i>c'_2</i> and <i>c'_4</i>) are mapped from the
185      * range <i>c_2</i> to 1. So, <i>c'_2</i> = 0, meaning that within the group there is no further correlation for the super
186      * state. Sub states, who are required to have an equal or larger correlation than the super state of the group, map
187      * linearly between <i>c_2</i> and 1.<br>
188      * <br>
189      * If the super state is only a virtual layer that should not in itself be a valid state of the system, it can simply be
190      * excluded from obtaining a new state using {@code getState()}.<br>
191      * <br>
192      * @param superState S; state of group
193      * @param state S; state to add
194      * @param correlation double; correlation
195      * @throws IllegalArgumentException if correlation is not within the range (0 ... 1), the state is already defined, or
196      *             superState is not yet a state
197      * @throws NullPointerException if an input is null
198      */
199     public synchronized void addState(final S superState, final S state, final double correlation)
200     {
201         Throw.whenNull(superState, "Super-state may not be null.");
202         Throw.whenNull(state, "State may not be null.");
203         Throw.when(this.leaves.containsKey(state), IllegalArgumentException.class, "State %s already defined.", state);
204         Throw.when(correlation < 0.0 || correlation >= 1.0, IllegalArgumentException.class,
205                 "Correlation at root level need to be in the range (-1 ... 1).");
206         if (!this.superMatrices.containsKey(superState))
207         {
208             // branch the super state in to a matrix
209             FixedState<S, I> superOriginal = this.leaves.get(superState);
210             // remove original from it's matrix
211             TransitionMatrix<S, I> superMatrix = this.containingMatrices.get(superState);
212             Throw.when(superMatrix == null, IllegalArgumentException.class, "No state has been defined for super-state %s.",
213                     superState);
214             superMatrix.removeNode(superState);
215             // replace with matrix
216             TransitionMatrix<S, I> matrix = new TransitionMatrix<>(superState, superOriginal.getCorrelation());
217             superMatrix.addNode(superState, matrix);
218             this.superMatrices.put(superState, matrix);
219             // add original node to that matrix
220             superOriginal.clearCorrelation();
221             matrix.addNode(superState, superOriginal);
222             this.containingMatrices.put(superState, matrix);
223         }
224         // add node
225         TransitionMatrix<S, I> superMatrix = this.superMatrices.get(superState);
226         Throw.when(correlation < superMatrix.getCorrelation(), IllegalArgumentException.class,
227                 "Sub states in a group can not have a lower correlation than the super state of the group.");
228         FixedState<S, I> node =
229                 new FixedState<>(state, (correlation - superMatrix.getCorrelation()) / (1.0 - superMatrix.getCorrelation()));
230         superMatrix.addNode(state, node);
231         this.containingMatrices.put(state, superMatrix);
232         this.leaves.put(state, node);
233         // register state as part of matrix node
234         this.root.registerInGroup(superState, state);
235         for (TransitionMatrix<S, I> matrix : this.superMatrices.values())
236         {
237             if (matrix.getState() != superState)
238             {
239                 matrix.registerInGroup(superState, state);
240             }
241         }
242     }
243 
244     /**
245      * Draws a next state from this Markov Chain process, with predefined state correlations, but dynamic intensities. Any
246      * states that are present in the underlying Transition Matrix, but not present in the given states, are ignored. States
247      * that are not present in the underlying Transition Matrix, are added to it with a correlation of 0.
248      * @param previousState S; previous state
249      * @param states S[]; set of states to consider
250      * @param steadyState I[]; current steady-state intensities of the states
251      * @param stream StreamInterface; to draw random numbers
252      * @return S; next state
253      * @throws IllegalArgumentException if number of states is not the same as the stead-state length
254      * @throws NullPointerException if states, steadyState or stream is null
255      */
256     public synchronized S drawState(final S previousState, final S[] states, final I[] steadyState,
257             final StreamInterface stream)
258     {
259         Throw.whenNull(states, "States may not be null.");
260         Throw.whenNull(steadyState, "Steady-state may not be null.");
261         Throw.whenNull(stream, "Stream for random numbers may not be null.");
262         Throw.when(states.length != steadyState.length, IllegalArgumentException.class,
263                 "Number of states should match the length of the steady state.");
264         for (FixedState<S, I> node : this.leaves.values())
265         {
266             node.clearIntensity();
267         }
268         int n = states.length;
269         for (int i = 0; i < n; i++)
270         {
271             S state = states[i];
272             FixedState<S, I> leaf = this.leaves.get(state);
273             if (leaf == null)
274             {
275                 addState(state, 0.0);
276                 leaf = this.leaves.get(state);
277             }
278             leaf.setIntensity(steadyState[i]);
279         }
280         return this.root.drawState(previousState, stream);
281     }
282 
283     /** {@inheritDoc} */
284     @Override
285     public String toString()
286     {
287         return "MarkovCorrelation [ " + this.root + " ]";
288     }
289 
290     /**
291      * Base class for elements inside a Markov {@code TransitionMatrix}.
292      * <p>
293      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
294      * <br>
295      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
296      * <p>
297      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 19 dec. 2017 <br>
298      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
299      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
300      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
301      * @param <S> state type
302      * @param <I> intensity type
303      */
304     private abstract static class MarkovNode<S, I extends Number>
305     {
306 
307         /** State. */
308         private final S state;
309 
310         /** Correlation. */
311         private double correlation;
312 
313         /**
314          * Constructor.
315          * @param state S; state
316          * @param correlation double; correlation
317          */
318         MarkovNode(final S state, final double correlation)
319         {
320             this.state = state;
321             this.correlation = correlation;
322         }
323 
324         /**
325          * Returns the encapsulated state, which is either a fixed state, or the super-state of a group.
326          * @return S; encapsulated state, which is either a fixed state, or the super-state of a group
327          */
328         final S getState()
329         {
330             return this.state;
331         }
332 
333         /**
334          * Returns the correlation.
335          * @return double; correlation
336          */
337         final double getCorrelation()
338         {
339             return this.correlation;
340         }
341 
342         /**
343          * Clears the correlation.
344          */
345         protected final void clearCorrelation()
346         {
347             this.correlation = 0.0;
348         }
349 
350         /**
351          * Returns the current intensity, used for the Markov Chain process.
352          * @return current intensity, used for the Markov Chain process, 0.0 if no intensity was provided
353          */
354         abstract double getIntensity();
355 
356         /**
357          * Returns a state from this node, which is either a fixed state, or a randomly drawn state from a sub-group.
358          * @param previousState S; previous state
359          * @param stream StreamInterface; to draw random numbers
360          * @return S; state from this node, which is either a fixed state, or a randomly drawn state from a sub-group
361          */
362         abstract S drawState(S previousState, StreamInterface stream);
363 
364     }
365 
366     /**
367      * Transition matrix with functionality to return a next state, and to entail a set of fixed states mixed with matrices for
368      * sub-groups.
369      * <p>
370      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
371      * <br>
372      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
373      * <p>
374      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 19 dec. 2017 <br>
375      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
376      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
377      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
378      * @param <S> state type
379      * @param <I> intensity type
380      */
381     private static final class TransitionMatrix<S, I extends Number> extends MarkovNode<S, I>
382     {
383 
384         /** List of state-defining states, where sub-groups are defined by a set of state. */
385         private List<Set<S>> states = new ArrayList<>();
386 
387         /** List of nodes (fixed state or sub-group matrix). */
388         private List<MarkovNode<S, I>> nodes = new ArrayList<>();
389 
390         /**
391          * Constructor.
392          * @param state S; super state representing the sub-group, or {@code null} for the root matrix.
393          * @param correlation double; correlation for the sub-group, or any value for the root matrix.
394          */
395         TransitionMatrix(final S state, final double correlation)
396         {
397             super(state, correlation);
398         }
399 
400         /**
401          * Adds a node to the matrix.
402          * @param state S; state of the node
403          * @param node MarkovNode&lt;S, I&gt;; node
404          */
405         void addNode(final S state, final MarkovNode<S, I> node)
406         {
407             Set<S> set = new LinkedHashSet<>();
408             set.add(state);
409             this.states.add(set);
410             this.nodes.add(node);
411         }
412 
413         /**
414          * Registers the state to belong to the group of super-state. This is used such that the matrix knows which previous
415          * states to map to the group.
416          * @param superState S; super-state of the group
417          * @param state S; state inside the group
418          */
419         void registerInGroup(final S superState, final S state)
420         {
421             for (Set<S> set : this.states)
422             {
423                 if (set.contains(superState))
424                 {
425                     set.add(state);
426                     return;
427                 }
428             }
429         }
430 
431         /**
432          * Removes the node from the matrix, including group registration. This is used to replace a state with a group.
433          * @param state S; state to remove
434          */
435         void removeNode(final S state)
436         {
437             int i = -1;
438             for (int j = 0; j < this.states.size(); j++)
439             {
440                 if (this.states.get(j).contains(state))
441                 {
442                     i = j;
443                     break;
444                 }
445             }
446             if (i > -1)
447             {
448                 this.states.remove(i);
449                 this.nodes.remove(i);
450             }
451         }
452 
453         /**
454          * Returns a state from this matrix. This is done by calculating the row of the Markov Transition Chain for the given
455          * previous state, and using those probabilities to draw an output state.
456          * @param previousState S; previous state
457          * @param stream StreamInterface; to draw random numbers
458          * @return S; state from this matrix
459          * @see MarkovCorrelation#addState(Object, double) algorithm for calculating the Transition Matrix
460          */
461         @Override
462         S drawState(final S previousState, final StreamInterface stream)
463         {
464             // figure out whether this matrix contains the previous state, and if so the correlation factor and row number i
465             boolean contains = false;
466             int n = this.states.size();
467             double intensitySum = 0.0;
468             int i = 0;
469             double iFactor = 1.0;
470             for (int j = 0; j < n; j++)
471             {
472                 intensitySum += this.nodes.get(j).getIntensity();
473                 if (this.states.get(j).contains(previousState))
474                 {
475                     i = j;
476                     contains = true;
477                     iFactor = 1.0 - this.nodes.get(i).getCorrelation();
478                 }
479             }
480             // gather probabilities and apply correlation factors
481             double[] p = new double[n];
482             double pSum = 0.0;
483             for (int j = 0; j < n; j++)
484             {
485                 if (i != j || !contains)
486                 {
487                     MarkovNode<S, I> node = this.nodes.get(j);
488                     double jFactor = 1.0;
489                     if (contains)
490                     {
491                         jFactor = 1.0 - node.getCorrelation();
492                     }
493                     p[j] = jFactor * iFactor * node.getIntensity() / intensitySum;
494                     pSum += p[j];
495                 }
496             }
497             // correct to get row sum = 1.0 by changing the diagonal value
498             if (contains)
499             {
500                 p[i] = 1.0 - pSum;
501             }
502             // make probabilities cumulative
503             for (int j = 1; j < n; j++)
504             {
505                 p[j] = p[j - 1] + p[j];
506             }
507             // draw
508             double r = stream.nextDouble();
509             for (int j = 0; j < n; j++)
510             {
511                 if (r < p[j])
512                 {
513                     return this.nodes.get(j).drawState(previousState, stream);
514                 }
515             }
516             throw new RuntimeException("Unexpected error while drawing state from matrix.");
517         }
518 
519         /**
520          * Returns the current intensity, used for the Markov Chain process, as the sum of matrix elements.
521          * @return current intensity, used for the Markov Chain process, 0.0 if no intensity was provided
522          */
523         @Override
524         double getIntensity()
525         {
526             double intensity = 0;
527             for (MarkovNode<S, I> node : this.nodes)
528             {
529                 intensity += node.getIntensity();
530             }
531             return intensity;
532         }
533 
534         /** {@inheritDoc} */
535         @Override
536         public String toString()
537         {
538             String superType = this.getState() == null ? "" : "(" + this.getState() + ")";
539             String statesStr = "";
540             String sep = "";
541             for (MarkovNode<S, I> node : this.nodes)
542             {
543                 statesStr += sep + node;
544                 sep = ", ";
545             }
546             return "T" + superType + "[ " + statesStr + " ]";
547         }
548     }
549 
550     /**
551      * Container for a fixed state. Each state is reflected in a single object of this class. They are grouped in matrices,
552      * possibly all in the root. Subsets of all states may be grouped in a matrix, but no state is present in more than one
553      * matrix.
554      * <p>
555      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
556      * <br>
557      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
558      * <p>
559      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 19 dec. 2017 <br>
560      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
561      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
562      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
563      * @param <S> state type
564      * @param <I> intensity type
565      */
566     private static final class FixedState<S, I extends Number> extends MarkovNode<S, I>
567     {
568 
569         /** Intensity. */
570         private I intensity;
571 
572         /**
573          * Constructor.
574          * @param state S; state
575          * @param correlation double; correlation
576          */
577         FixedState(final S state, final double correlation)
578         {
579             super(state, correlation);
580         }
581 
582         /**
583          * Returns the state.
584          * @param previousState S; previous state
585          * @param stream StreamInterface; to draw random numbers
586          * @return state S; the state
587          */
588         S drawState(final S previousState, final StreamInterface stream)
589         {
590             return getState();
591         }
592 
593         /**
594          * Sets the current intensity.
595          * @param intensity I; intensity
596          */
597         void setIntensity(final I intensity)
598         {
599             this.intensity = intensity;
600         }
601 
602         /**
603          * Clears the intensity.
604          */
605         void clearIntensity()
606         {
607             this.intensity = null;
608         }
609 
610         /** {@inheritDoc} */
611         @Override
612         double getIntensity()
613         {
614             return this.intensity == null ? 0.0 : this.intensity.doubleValue();
615         }
616 
617         /** {@inheritDoc} */
618         @Override
619         public String toString()
620         {
621             return String.format(Locale.US, "%s(%.2f)", getState(), getCorrelation());
622         }
623     }
624 
625 }