View Javadoc
1   package org.djutils.stats.summarizers.event;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertNotNull;
5   import static org.junit.Assert.assertTrue;
6   import static org.junit.Assert.fail;
7   
8   import org.djutils.event.Event;
9   import org.djutils.event.EventInterface;
10  import org.djutils.event.EventListenerInterface;
11  import org.djutils.event.EventType;
12  import org.djutils.metadata.MetaData;
13  import org.djutils.metadata.ObjectDescriptor;
14  import org.junit.Test;
15  
16  /**
17   * The EventBasedWeightedTallyTest tests the EventBasedWeightedTally.
18   * <p>
19   * Copyright (c) 2002-2022 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
20   * for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
21   * project is distributed under a three-clause BSD-style license, which can be found at
22   * <a href="https://simulation.tudelft.nl/dsol/3.0/license.html" target="_blank">
23   * https://simulation.tudelft.nl/dsol/3.0/license.html</a>. <br>
24   * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
25   * @since 1.5
26   */
27  public class EventBasedWeightedTallyTest
28  {
29      /** an event to fire. */
30      private static final EventType VALUE_EVENT = new EventType("VALUE_EVENT",
31              new MetaData("WeightAndValue", "Double[] with Double weight and Double value ",
32                      new ObjectDescriptor[] { new ObjectDescriptor("Weight", "Double weight", Double.class),
33                              new ObjectDescriptor("Valuie", "Double value", Double.class) }));
34  
35      /** Test the EventBasedWeightedTally. */
36      @Test
37      public void testEventBasedWeightedTally()
38      {
39          String description = "THIS EVENT BASED WEIGHTED TALLY IS TESTED";
40          EventBasedWeightedTally wt = new EventBasedWeightedTally(description);
41  
42          // check the description
43          assertEquals(description, wt.getDescription());
44          assertTrue(wt.toString().contains(description));
45  
46          // now we check the initial values
47          assertTrue(Double.isNaN(wt.getMin()));
48          assertTrue(Double.isNaN(wt.getMax()));
49          assertTrue(Double.isNaN(wt.getWeightedSampleMean()));
50          assertTrue(Double.isNaN(wt.getWeightedSampleVariance()));
51          assertTrue(Double.isNaN(wt.getWeightedSampleStDev()));
52          assertEquals(0.0, wt.getWeightedSum(), 0.0);
53          assertEquals(0L, wt.getN());
54  
55          wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1, 1.1 }));
56          wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1, 1.2 }));
57          wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1, 1.3 }));
58          wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1, 1.4 }));
59          wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1, 1.5 }));
60          wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1, 1.6 }));
61          wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1, 1.7 }));
62          wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1, 1.8 }));
63          wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1, 1.9 }));
64          wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1, 2.0 }));
65          wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1, 1.0 }));
66  
67          // Now we check the EventBasedWeightedTally
68          assertEquals(2.0, wt.getMax(), 1.0E-6);
69          assertEquals(1.0, wt.getMin(), 1.0E-6);
70          assertEquals(11, wt.getN());
71          assertEquals(1.5 * 0.1 * 11, wt.getWeightedSum(), 1.0E-6);
72          assertEquals(1.5, wt.getWeightedSampleMean(), 1.0E-6);
73          assertEquals(0.316228, wt.getWeightedPopulationStDev(), 1.0E-6); // Computed with Excel sheet
74          assertEquals(0.100000, wt.getWeightedPopulationVariance(), 1.0E-6);
75          assertEquals(0.331662, wt.getWeightedSampleStDev(), 1.0E-6); // Computed with Excel sheet
76          assertEquals(0.110000, wt.getWeightedSampleVariance(), 1.0E-6);
77  
78          // Let's compute the standard deviation
79          double variance = 0;
80          for (int i = 0; i < 11; i++)
81          {
82              variance += Math.pow(1.5 - (1.0 + i / 10.0), 2);
83          }
84          variance = variance / 10.0;
85          double stDev = Math.sqrt(variance);
86  
87          assertEquals(variance, wt.getWeightedSampleVariance(), 1.0E-6);
88          assertEquals(stDev, wt.getWeightedSampleStDev(), 1.0E-6);
89  
90          try
91          {
92              wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { -0.1, 123.456 }));
93              fail("negative weight should have thrown an exception");
94          }
95          catch (IllegalArgumentException iae)
96          {
97              // Ignore expected exception
98          }
99          try
100         {
101             wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", "123"));
102             fail("non Object[] content should have thrown an exception");
103         }
104         catch (IndexOutOfBoundsException iobe)
105         {
106             // Ignore expected exception
107         }
108         try
109         {
110             wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1 }));
111             fail("Object[] with one argument should have thrown an exception");
112         }
113         catch (IndexOutOfBoundsException iae)
114         {
115             // Ignore expected exception
116         }
117         try
118         {
119             wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.1, 0.2, 0.3 }));
120             fail("Object[] with thre arguments should have thrown an exception");
121         }
122         catch (IndexOutOfBoundsException iae)
123         {
124             // Ignore expected exception
125         }
126         try
127         {
128             wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { "bla", 0.2 }));
129             fail("EventBasedWeightedTally should fail on weight !instanceOf Double");
130         }
131         catch (Exception exception)
132         {
133             assertNotNull(exception);
134         }
135         try
136         {
137             wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.3, "bla" }));
138             fail("EventBasedWeightedTally should fail on value !instanceOf Double");
139         }
140         catch (Exception exception)
141         {
142             assertNotNull(exception);
143         }
144 
145     }
146 
147     /** Test the EventBasedWeightedTally on a simple example. */
148     @Test
149     public void testEventBasedWeightedTallySimple()
150     {
151         // From: https://sciencing.com/calculate-time-decimals-5962681.html
152         EventBasedWeightedTally wt = new EventBasedWeightedTally("simple EventBasedWeightedTally statistic");
153         wt.initialize();
154         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 13.0, 86.0 }));
155         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 23.0, 26.0 }));
156         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 4.0, 0.0 }));
157 
158         assertEquals(1716.0, wt.getWeightedSum(), 0.001);
159         assertEquals(42.9, wt.getWeightedSampleMean(), 0.001);
160 
161         // When we shift the times, we should get the same answers
162         wt = new EventBasedWeightedTally("simple EventBasedWeightedTally statistic");
163         wt.initialize();
164         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 13.0, 86.0 }));
165         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 23.0, 26.0 }));
166         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 14.0, 0.0 }));
167 
168         assertEquals(1716.0, wt.getWeightedSum(), 0.001);
169         assertEquals(34.32, wt.getWeightedSampleMean(), 0.001);
170 
171         // When we have observations with duration 0, we should get the same answers
172         wt = new EventBasedWeightedTally("simple EventBasedWeightedTally statistic");
173         wt.initialize();
174         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 13.0, 86.0 }));
175         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.0, 86.0 }));
176         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 23.0, 26.0 }));
177         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 4.0, 0.0 }));
178         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0.0, 0.0 }));
179 
180         assertEquals(1716.0, wt.getWeightedSum(), 0.001);
181         assertEquals(42.9, wt.getWeightedSampleMean(), 0.001);
182 
183         // Example from NIST: https://www.itl.nist.gov/div898/software/dataplot/refman2/ch2/weightsd.pdf
184         wt = new EventBasedWeightedTally("NIST");
185         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 1d, 2d }));
186         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 1d, 3d }));
187         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0d, 5d }));
188         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0d, 7d }));
189         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 4d, 11d }));
190         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 1d, 13d }));
191         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 2d, 17d }));
192         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 1d, 19d }));
193         wt.notify(new Event(VALUE_EVENT, "EventBasedWeightedTallyTest", new Object[] { 0d, 23d }));
194 
195         assertEquals((2 + 3 + 4 * 11 + 13 + 2 * 17 + 19) / 10.0, wt.getWeightedSampleMean(), 0.001);
196         assertEquals(5.82, wt.getWeightedSampleStDev(), 0.01);
197     }
198 
199     /**
200      * Test produced events by EventBasedWeightedTally.
201      */
202     @Test
203     public void testWeightedTallyEventProduction()
204     {
205         EventBasedWeightedTally weightedTally = new EventBasedWeightedTally("testTally");
206         assertEquals(weightedTally, weightedTally.getSourceId());
207         WeightedObservationEventListener woel = new WeightedObservationEventListener();
208         weightedTally.addListener(woel, StatisticsEvents.WEIGHTED_OBSERVATION_ADDED_EVENT);
209         assertEquals(0, woel.getObservationEvents());
210 
211         EventType[] types = new EventType[] { StatisticsEvents.N_EVENT, StatisticsEvents.MIN_EVENT, StatisticsEvents.MAX_EVENT,
212                 StatisticsEvents.WEIGHTED_POPULATION_MEAN_EVENT, StatisticsEvents.WEIGHTED_POPULATION_VARIANCE_EVENT,
213                 StatisticsEvents.WEIGHTED_POPULATION_STDEV_EVENT, StatisticsEvents.WEIGHTED_SUM_EVENT,
214                 StatisticsEvents.WEIGHTED_SAMPLE_MEAN_EVENT, StatisticsEvents.WEIGHTED_SAMPLE_VARIANCE_EVENT,
215                 StatisticsEvents.WEIGHTED_SAMPLE_STDEV_EVENT };
216         LoggingEventListener[] listeners = new LoggingEventListener[types.length];
217         for (int i = 0; i < types.length; i++)
218         {
219             listeners[i] = new LoggingEventListener();
220             weightedTally.addListener(listeners[i], types[i]);
221         }
222 
223         for (int i = 1; i <= 10; i++)
224         {
225             weightedTally.register(1.0 * i, 10.0 * i);
226         }
227 
228         assertEquals(10, woel.getObservationEvents());
229 
230         // values based on formulas from https://www.itl.nist.gov/div898/software/dataplot/refman2/ch2/weightsd.pdf
231         Object[] expectedValues = new Object[] { 10L, 10.0, 100.0, 70.0, 600.0, 24.4949, 3850.0, 70.0, 666.6667, 25.81989 };
232         for (int i = 0; i < types.length; i++)
233         {
234             assertEquals("Number of events for listener " + types[i], 10, listeners[i].getNumberOfEvents());
235             assertEquals("Event sourceId for listener " + types[i], weightedTally, listeners[i].getLastEvent().getSourceId());
236             assertEquals("Event type for listener " + types[i], types[i], listeners[i].getLastEvent().getType());
237             if (expectedValues[i] instanceof Long)
238             {
239                 assertEquals("Final value for listener " + types[i], expectedValues[i],
240                         listeners[i].getLastEvent().getContent());
241             }
242             else
243             {
244                 double e = ((Double) expectedValues[i]).doubleValue();
245                 double c = ((Double) listeners[i].getLastEvent().getContent()).doubleValue();
246                 assertEquals("Final value for listener " + types[i], e, c, 0.001);
247             }
248         }
249     }
250 
251     /** The listener that counts the OBSERVATION_ADDED_EVENT events and checks correctness. */
252     class WeightedObservationEventListener implements EventListenerInterface
253     {
254         /** */
255         private static final long serialVersionUID = 1L;
256 
257         /** counter for the event. */
258         private int observationEvents = 0;
259 
260         @Override
261         public void notify(final EventInterface event)
262         {
263             assertTrue(event.getType().equals(StatisticsEvents.WEIGHTED_OBSERVATION_ADDED_EVENT));
264             assertTrue("Content of the event has a wrong type, not Object[]: " + event.getContent().getClass(),
265                     event.getContent() instanceof Object[]);
266             Object[] c = (Object[]) event.getContent();
267             assertTrue("Content[0] of the event has a wrong type, not double: " + c[0].getClass(), c[0] instanceof Double);
268             assertTrue("Content[1] of the event has a wrong type, not double: " + c[1].getClass(), c[1] instanceof Double);
269             assertTrue("SourceId of the event has a wrong type, not EventBasedWeightedTally: " + event.getSourceId().getClass(),
270                     event.getSourceId() instanceof EventBasedWeightedTally);
271             this.observationEvents++;
272         }
273 
274         /**
275          * @return countEvents
276          */
277         public int getObservationEvents()
278         {
279             return this.observationEvents;
280         }
281     }
282 
283 }