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-2020 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> = <i>S</i></b> should hold. Suppose we have <i>S</i> = [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> := <i>p_33</i> + (1 - <i>p_33</i>) * <i>c</i> 99 * = 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 => | 0.70 0.20 0.10 | => | 0.70 0.20 0.10 | => | 0.70 0.20 0.06 | => | 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 = 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 | => 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-2020 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-2020 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<S, I>; 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-2020 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 }