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