View Javadoc
1   package org.opentrafficsim.core.gtu.behavioralcharacteristics;
2   
3   import java.io.Serializable;
4   import java.util.HashMap;
5   import java.util.Iterator;
6   import java.util.LinkedHashMap;
7   import java.util.LinkedHashSet;
8   import java.util.Map;
9   import java.util.Set;
10  
11  import org.djunits.unit.Unit;
12  import org.djunits.value.vdouble.scalar.AbstractDoubleScalarRel;
13  import org.djunits.value.vdouble.scalar.DoubleScalarInterface;
14  import org.opentrafficsim.base.parameters.ParameterException;
15  import org.opentrafficsim.base.parameters.ParameterType;
16  import org.opentrafficsim.base.parameters.ParameterTypeDouble;
17  import org.opentrafficsim.base.parameters.ParameterTypeNumeric;
18  import org.opentrafficsim.base.parameters.Parameters;
19  import org.opentrafficsim.core.gtu.GTUType;
20  import org.opentrafficsim.core.units.distributions.ContinuousDistDoubleScalar;
21  
22  import nl.tudelft.simulation.jstats.distributions.DistContinuous;
23  
24  /**
25   * Sets parameter values based on the the GTU type. This includes stochastic parameters. Parameters may also be defined for all
26   * GTU types. Similarly, correlations between two parameters can be determined, for all or a specific GTU type.
27   * <p>
28   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
29   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
30   * <p>
31   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 18 nov. 2016 <br>
32   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
33   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
34   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
35   */
36  public class ParameterFactoryByType implements ParameterFactory
37  {
38  
39      /** Parameters. */
40      private final Map<GTUType, Set<ParameterEntry<?>>> map = new HashMap<>();
41  
42      /** Map of correlations. */
43      private Map<GTUType, Map<ParameterType<?>, Map<ParameterType<?>, Correlation<?, ?>>>> correlations = new HashMap<>();
44  
45      /** {@inheritDoc} */
46      @Override
47      public void setValues(final Parameters parameters, final GTUType gtuType)
48      {
49          // set of parameters that this class is going to set
50          Map<ParameterType<?>, ParameterEntry<?>> setType = new LinkedHashMap<>();
51          for (GTUType type : new GTUType[] { null, gtuType })
52          {
53              if (this.map.containsKey(type))
54              {
55                  for (ParameterEntry<?> entry : this.map.get(type))
56                  {
57                      setType.put(entry.getParameterType(), entry);
58                  }
59              }
60          }
61  
62          /* {@formatter:off}
63           * Based on all given correlations we create two maps: 
64           * - remainingCorrelations, keys are ParameterTypes that depend on values that this class still needs to set 
65           * - allCorrelations, correlations combined from all and the specific GTU type, used to actually alter the values set
66           * The map remainingCorrelations will only contain correlations to parameters also defined in this class. For other 
67           * correlations the independent parameter should already be present in the input Parameters set. If it's not, an 
68           * exception follows as the parameter could not be retrieved. Circular dependencies are recognized as a loop has not 
69           * done anything, while remainingCorrelations is not empty (i.e. some parameters should still be set, but can't as they 
70           * all depend on parameters not yet set). {@formatter:on}
71           */
72          Map<ParameterType<?>, Map<ParameterType<?>, Correlation<?, ?>>> remainingCorrelations = new LinkedHashMap<>();
73          Map<ParameterType<?>, Map<ParameterType<?>, Correlation<?, ?>>> allCorrelations = new LinkedHashMap<>();
74          for (GTUType type : new GTUType[] { null, gtuType }) // null first, so specific type overwrites
75          {
76              if (this.correlations.containsKey(type))
77              {
78                  Map<ParameterType<?>, Map<ParameterType<?>, Correlation<?, ?>>> map1 = this.correlations.get(type);
79                  for (ParameterType<?> then : map1.keySet())
80                  {
81                      Map<ParameterType<?>, Correlation<?, ?>> map2a = map1.get(then);
82                      Map<ParameterType<?>, Correlation<?, ?>> map2b = new LinkedHashMap<>(map2a); // safe copy
83                      // retain only independent correlation parameters this class will set
84                      map2b.keySet().retainAll(setType.keySet());
85                      if (!map2b.isEmpty())
86                      {
87                          Map<ParameterType<?>, Correlation<?, ?>> map3 = remainingCorrelations.get(then);
88                          if (map3 == null)
89                          {
90                              map3 = new LinkedHashMap<>();
91                              remainingCorrelations.put(then, map3);
92                          }
93                          map3.putAll(map2b);
94                      }
95                      if (!map2a.isEmpty())
96                      {
97                          Map<ParameterType<?>, Correlation<?, ?>> map3 = allCorrelations.get(then);
98                          if (map3 == null)
99                          {
100                             map3 = new LinkedHashMap<>();
101                             allCorrelations.put(then, map3);
102                         }
103                         map3.putAll(map2a);
104                     }
105                 }
106             }
107         }
108 
109         // loop and set parameters that do not correlate to parameters not yet set
110         boolean altered = true;
111         while (altered)
112         {
113             altered = false;
114 
115             Iterator<ParameterType<?>> iterator = setType.keySet().iterator();
116             while (iterator.hasNext())
117             {
118                 ParameterType<?> parameterType = iterator.next();
119                 ParameterEntry<?> entry = setType.get(parameterType);
120 
121                 if (!remainingCorrelations.containsKey(parameterType))
122                 {
123                     altered = true;
124                     iterator.remove();
125                     Object value = entry.getValue();
126                     setParameter(parameterType, value, parameters, allCorrelations.get(parameterType));
127                     // remove the set parameter from correlations that need to be considered
128                     Iterator<ParameterType<?>> it = remainingCorrelations.keySet().iterator();
129                     while (it.hasNext())
130                     {
131                         Map<ParameterType<?>, Correlation<?, ?>> remMap = remainingCorrelations.get(it.next());
132                         remMap.remove(parameterType);
133                         if (remMap.isEmpty())
134                         {
135                             it.remove(); // all independent parameters were set, remove correlation to consider
136                         }
137                     }
138                 }
139             }
140         }
141         if (!altered && !remainingCorrelations.isEmpty())
142         {
143             throw new RuntimeException("Circular correlation between parameters.");
144         }
145 
146     }
147 
148     /**
149      * Sets a parameter including type casting.
150      * @param parameterType ParameterType&lt;?&gt;; parameter type
151      * @param value Object; value
152      * @param parameters Parameters; parameters to set in
153      * @param correls Map&lt;ParameterType&lt;?&gt;, Correlation&lt;?, ?&gt;&gt;; correlations
154      * @param <C> parameter value type of first parameter
155      * @param <T> parameter value type of then parameter
156      */
157     @SuppressWarnings("unchecked")
158     private <C, T> void setParameter(final ParameterType<?> parameterType, final Object value, final Parameters parameters,
159             final Map<ParameterType<?>, Correlation<?, ?>> correls)
160     {
161         T val = (T) value;
162         try
163         {
164             if (correls != null)
165             {
166                 for (ParameterType<?> param : correls.keySet())
167                 {
168                     Correlation<C, T> correlation = (Correlation<C, T>) correls.get(param);
169                     if (param == null)
170                     {
171                         val = correlation.correlate(null, val);
172                     }
173                     else
174                     {
175                         val = correlation.correlate(parameters.getParameter((ParameterType<C>) param), val);
176                     }
177                 }
178             }
179             parameters.setParameter((ParameterType<T>) parameterType, val);
180         }
181         catch (ParameterException exception)
182         {
183             throw new RuntimeException("Value out of bounds or dependent parameter not present.", exception);
184         }
185     }
186 
187     /**
188      * @param gtuType GTUType; the gtu type
189      * @param parameterType ParameterType&lt;T&gt;; the parameter type
190      * @param value T; the value of the parameter
191      * @param <T> parameter value type
192      */
193     public <T extends DoubleScalarInterface> void addParameter(final GTUType gtuType, final ParameterType<T> parameterType,
194             final T value)
195     {
196         assureTypeInMap(gtuType);
197         this.map.get(gtuType).add(new FixedEntry<>(parameterType, value));
198     }
199 
200     /**
201      * @param gtuType GTUType; the gtu type
202      * @param parameterType ParameterTypeDouble; the parameter type
203      * @param value double; the value of the parameter
204      */
205     public void addParameter(final GTUType gtuType, final ParameterTypeDouble parameterType, final double value)
206     {
207         assureTypeInMap(gtuType);
208         this.map.get(gtuType).add(new FixedEntryDouble(parameterType, value));
209     }
210 
211     /**
212      * @param gtuType GTUType; the gtu type
213      * @param parameterType ParameterTypeNumeric&lt;T&gt;; the parameter type
214      * @param distribution ContinuousDistDoubleScalar.Rel&lt;T,U&gt;; the distribution of the parameter
215      * @param <U> unit type
216      * @param <T> parameter value type
217      */
218     public <U extends Unit<U>, T extends AbstractDoubleScalarRel<U, T>> void addParameter(final GTUType gtuType,
219             final ParameterTypeNumeric<T> parameterType, final ContinuousDistDoubleScalar.Rel<T, U> distribution)
220     {
221         assureTypeInMap(gtuType);
222         this.map.get(gtuType).add(new DistributedEntry<>(parameterType, distribution));
223     }
224 
225     /**
226      * @param gtuType GTUType; the gtu type
227      * @param parameterType ParameterTypeDouble; the parameter type
228      * @param distribution DistContinuous; the distribution of the parameter
229      */
230     public void addParameter(final GTUType gtuType, final ParameterTypeDouble parameterType, final DistContinuous distribution)
231     {
232         assureTypeInMap(gtuType);
233         this.map.get(gtuType).add(new DistributedEntryDouble(parameterType, distribution));
234     }
235 
236     /**
237      * Add parameter for all GTU types.
238      * @param parameterType ParameterType&lt;T&gt;; the parameter type
239      * @param value T; the value of the parameter
240      * @param <T> type
241      */
242     public <T extends DoubleScalarInterface> void addParameter(final ParameterType<T> parameterType, final T value)
243     {
244         addParameter(null, parameterType, value);
245     }
246 
247     /**
248      * Add parameter for all GTU types.
249      * @param parameterType ParameterTypeDouble; the parameter type
250      * @param value double; the value of the parameter
251      */
252     public void addParameter(final ParameterTypeDouble parameterType, final double value)
253     {
254         addParameter(null, parameterType, value);
255     }
256 
257     /**
258      * Add parameter for all GTU types.
259      * @param parameterType ParameterTypeNumeric&lt;T&gt;; the parameter type
260      * @param distribution ContinuousDistDoubleScalar.Rel&lt;T,U&gt;; the distribution of the parameter
261      * @param <U> unit type
262      * @param <T> parameter value type
263      */
264     public <U extends Unit<U>, T extends AbstractDoubleScalarRel<U, T>> void addParameter(
265             final ParameterTypeNumeric<T> parameterType, final ContinuousDistDoubleScalar.Rel<T, U> distribution)
266     {
267         addParameter(null, parameterType, distribution);
268     }
269 
270     /**
271      * Add parameter for all GTU types.
272      * @param parameterType ParameterTypeDouble; the parameter type
273      * @param distribution DistContinuous; the distribution of the parameter
274      */
275     public void addParameter(final ParameterTypeDouble parameterType, final DistContinuous distribution)
276     {
277         addParameter(null, parameterType, distribution);
278     }
279 
280     /**
281      * Correlates one parameter to another. The parameter 'first' may also be {@code null}, in which case the parameter can be
282      * correlated to an external source.
283      * @param gtuType GTUType; GTU type
284      * @param first ParameterType&lt;C&gt;; independent parameter
285      * @param then ParameterType&lt;T&gt;; dependent parameter
286      * @param correlation Correlation&lt;C, T&gt;; correlation
287      * @param <C> parameter value type of first parameter
288      * @param <T> parameter value type of then parameter
289      */
290     public <C, T> void addCorrelation(final GTUType gtuType, final ParameterType<C> first, final ParameterType<T> then,
291             final Correlation<C, T> correlation)
292     {
293         assureTypeInMap(gtuType);
294         Map<ParameterType<?>, Map<ParameterType<?>, Correlation<?, ?>>> map1 = this.correlations.get(gtuType);
295         Map<ParameterType<?>, Correlation<?, ?>> map2 = map1.get(then);
296         if (map2 == null)
297         {
298             map2 = new LinkedHashMap<>();
299             map1.put(then, map2);
300         }
301         map2.put(first, correlation);
302     }
303 
304     /**
305      * Correlates one parameter to another for all GTU types.
306      * @param first ParameterType&lt;C&gt;; independent parameter
307      * @param then ParameterType&lt;T&gt;; dependent parameter
308      * @param correlation Correlation&lt;C, T&gt;; correlation
309      * @param <C> parameter value type of first parameter
310      * @param <T> parameter value type of then parameter
311      */
312     public <C, T> void addCorrelation(final ParameterType<C> first, final ParameterType<T> then,
313             final Correlation<C, T> correlation)
314     {
315         addCorrelation(null, first, then, correlation);
316     }
317 
318     /**
319      * Assures the gtu type is in the map.
320      * @param gtuType GTUType; the gtu type
321      */
322     private void assureTypeInMap(final GTUType gtuType)
323     {
324         if (!this.map.containsKey(gtuType))
325         {
326             this.map.put(gtuType, new LinkedHashSet<>());
327             this.correlations.put(gtuType, new LinkedHashMap<>());
328         }
329     }
330 
331     /** {@inheritDoc} */
332     @Override
333     public String toString()
334     {
335         return "ParameterFactoryByType [map=" + this.map + "]";
336     }
337 
338     /**
339      * Local storage interface for parameters.
340      * <p>
341      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
342      * <br>
343      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
344      * <p>
345      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 19 nov. 2016 <br>
346      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
347      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
348      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
349      * @param <T> value type
350      */
351     private interface ParameterEntry<T>
352     {
353         /**
354          * Returns the value for parameter.
355          * @return T; value for the parameter
356          */
357         T getValue();
358 
359         /**
360          * Returns the parameter type.
361          * @return ParameterType; parameter type
362          */
363         ParameterType<T> getParameterType();
364     }
365 
366     /**
367      * Fixed parameter.
368      * <p>
369      * Copyright (c) 2013-2019 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 nov. 2016 <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 <T> value type
378      */
379     private final class FixedEntry<T> implements ParameterEntry<T>, Serializable
380     {
381         /** */
382         private static final long serialVersionUID = 20170400L;
383 
384         /** Parameter type. */
385         private final ParameterType<T> parameterType;
386 
387         /** Value. */
388         private final T value;
389 
390         /**
391          * @param parameterType ParameterType&lt;T&gt;; the parameter type
392          * @param value T; the fixed value
393          */
394         FixedEntry(final ParameterType<T> parameterType, final T value)
395         {
396             this.parameterType = parameterType;
397             this.value = value;
398         }
399 
400         /** {@inheritDoc} */
401         @Override
402         public T getValue()
403         {
404             return this.value;
405         }
406 
407         /** {@inheritDoc} */
408         @Override
409         public ParameterType<T> getParameterType()
410         {
411             return this.parameterType;
412         }
413 
414         /** {@inheritDoc} */
415         @Override
416         public String toString()
417         {
418             return "FixedEntry [parameterType=" + this.parameterType + ", value=" + this.value + "]";
419         }
420 
421     }
422 
423     /**
424      * Fixed double value.
425      * <p>
426      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
427      * <br>
428      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
429      * <p>
430      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 30 nov. 2016 <br>
431      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
432      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
433      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
434      */
435     private final class FixedEntryDouble implements ParameterEntry<Double>, Serializable
436     {
437         /** */
438         private static final long serialVersionUID = 20170400L;
439 
440         /** Parameter type. */
441         private final ParameterTypeDouble parameterType;
442 
443         /** Value. */
444         private final double value;
445 
446         /**
447          * @param parameterType ParameterTypeDouble; the parameter type
448          * @param value double; the fixed value
449          */
450         FixedEntryDouble(final ParameterTypeDouble parameterType, final double value)
451         {
452             this.parameterType = parameterType;
453             this.value = value;
454         }
455 
456         /** {@inheritDoc} */
457         @Override
458         public Double getValue()
459         {
460             return this.value;
461         }
462 
463         /** {@inheritDoc} */
464         @Override
465         public ParameterType<Double> getParameterType()
466         {
467             return this.parameterType;
468         }
469 
470         /** {@inheritDoc} */
471         @Override
472         public String toString()
473         {
474             return "FixedEntryDouble [parameterType=" + this.parameterType + ", value=" + this.value + "]";
475         }
476     }
477 
478     /**
479      * Distributed parameter.
480      * <p>
481      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
482      * <br>
483      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
484      * <p>
485      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 2 mrt. 2018 <br>
486      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
487      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
488      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
489      * @param <U> unit type
490      * @param <T> value type
491      */
492     private final class DistributedEntry<U extends Unit<U>, T extends AbstractDoubleScalarRel<U, T>>
493             implements ParameterEntry<T>, Serializable
494     {
495         /** */
496         private static final long serialVersionUID = 20180203L;
497 
498         /** Parameter type. */
499         private final ParameterType<T> parameterType;
500 
501         /** Distribution of the parameter. */
502         private final ContinuousDistDoubleScalar.Rel<T, U> distribution;
503 
504         /**
505          * @param parameterType ParameterType&lt;T&gt;; the parameter type
506          * @param distribution ContinuousDistDoubleScalar.Rel&lt;T,U&gt;; the distribution of the parameter
507          */
508         DistributedEntry(final ParameterType<T> parameterType, final ContinuousDistDoubleScalar.Rel<T, U> distribution)
509         {
510             this.parameterType = parameterType;
511             this.distribution = distribution;
512         }
513 
514         /** {@inheritDoc} */
515         @Override
516         public T getValue()
517         {
518             return this.distribution.draw();
519         }
520 
521         /** {@inheritDoc} */
522         @Override
523         public ParameterType<T> getParameterType()
524         {
525             return this.parameterType;
526         }
527 
528         /** {@inheritDoc} */
529         @Override
530         public String toString()
531         {
532             return "DistributedEntry [parameterType=" + this.parameterType + ", distribution=" + this.distribution + "]";
533         }
534     }
535 
536     /**
537      * Distributed double value.
538      * <p>
539      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
540      * <br>
541      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
542      * <p>
543      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 30 nov. 2016 <br>
544      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
545      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
546      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
547      */
548     private final class DistributedEntryDouble implements ParameterEntry<Double>, Serializable
549     {
550         /** */
551         private static final long serialVersionUID = 20180203L;
552 
553         /** Parameter type. */
554         private final ParameterTypeDouble parameterType;
555 
556         /** Parameter distribution. */
557         private final DistContinuous distribution;
558 
559         /**
560          * @param parameterType ParameterTypeDouble; the parameter type
561          * @param distribution DistContinuous; parameter distribution
562          */
563         DistributedEntryDouble(final ParameterTypeDouble parameterType, final DistContinuous distribution)
564         {
565             this.parameterType = parameterType;
566             this.distribution = distribution;
567         }
568 
569         /** {@inheritDoc} */
570         @Override
571         public Double getValue()
572         {
573             return this.distribution.draw();
574         }
575 
576         /** {@inheritDoc} */
577         @Override
578         public ParameterType<Double> getParameterType()
579         {
580             return this.parameterType;
581         }
582 
583         /** {@inheritDoc} */
584         @Override
585         public String toString()
586         {
587             return "DistributedEntryDouble [parameterType=" + this.parameterType + ", distribution=" + this.distribution + "]";
588         }
589     }
590 
591     /**
592      * Correlates two parameter values.
593      * <p>
594      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
595      * <br>
596      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
597      * <p>
598      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 3 mrt. 2018 <br>
599      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
600      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
601      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
602      * @param <C> value type of independent parameter
603      * @param <T> value type of dependent parameter
604      */
605     @FunctionalInterface
606     public interface Correlation<C, T>
607     {
608         /**
609          * Returns the correlated value.
610          * @param first C; value of independent parameter
611          * @param then T; pre-determined value
612          * @return correlated value
613          */
614         T correlate(C first, T then);
615     }
616 
617 }