MATSIM
ScoringFunctionsForPopulation.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * ScoringFunctionsForPopulation.java
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2013 by the members listed in the COPYING, *
8  * LICENSE and WARRANTY file. *
9  * email : info at matsim dot org *
10  * *
11  * *********************************************************************** *
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * See also COPYING, LICENSE and WARRANTY file *
18  * *
19  * *********************************************************************** */
20 
21 package org.matsim.core.scoring;
22 
23 import com.google.inject.Inject;
24 import gnu.trove.TDoubleCollection;
25 import gnu.trove.iterator.TDoubleIterator;
26 import gnu.trove.list.array.TDoubleArrayList;
27 import org.matsim.api.core.v01.Id;
28 import org.matsim.api.core.v01.IdMap;
51 import org.matsim.core.config.Config;
61 import org.matsim.core.utils.io.IOUtils;
62 import org.matsim.vehicles.Vehicle;
63 
64 import java.io.BufferedWriter;
65 import java.io.IOException;
66 import java.util.List;
67 import java.util.Map.Entry;
68 import java.util.concurrent.atomic.AtomicReference;
69 
71 
80  final class ScoringFunctionsForPopulation implements BasicEventHandler {
81 
82  private final Population population;
83  private final ScoringFunctionFactory scoringFunctionFactory;
84 
85  private final EventsToLegs legsDelegate;
86  private final EventsToActivities actsDelegate;
87 
88  private final IdMap<Person, ScoringFunction> agentScorers = new IdMap<>(Person.class);
89  private final IdMap<Person, TDoubleCollection> partialScores = new IdMap<>(Person.class);
90  private final AtomicReference<Throwable> exception = new AtomicReference<>();
91  private final IdMap<Person, Plan> tripRecords = new IdMap<>(Person.class);
92 
93  private final Vehicle2DriverEventHandler vehicles2Drivers = new Vehicle2DriverEventHandler();
94 
95  @Inject
96  ScoringFunctionsForPopulation(ControlerListenerManager controlerListenerManager, EventsManager eventsManager, EventsToActivities eventsToActivities, EventsToLegs eventsToLegs,
97  Population population, ScoringFunctionFactory scoringFunctionFactory, Config config) {
98  ControllerConfigGroup controllerConfigGroup = config.controller();
99 
100  if (controllerConfigGroup.getEventTypeToCreateScoringFunctions() == ControllerConfigGroup.EventTypeToCreateScoringFunctions.IterationStarts) {
101  controlerListenerManager.addControlerListener((IterationStartsListener) event -> init());
102  } else if (controllerConfigGroup.getEventTypeToCreateScoringFunctions() == ControllerConfigGroup.EventTypeToCreateScoringFunctions.BeforeMobsim) {
103  controlerListenerManager.addControlerListener((BeforeMobsimListener) event -> init());
104  } else {
105  throw new RuntimeException("Unknown approach when to create the scoring functions for population. Aborting...");
106  }
107 
108  this.population = population;
109  this.legsDelegate = eventsToLegs;
110  this.actsDelegate = eventsToActivities;
111  this.scoringFunctionFactory = scoringFunctionFactory;
112 
113  eventsManager.addHandler(this);
114  eventsToActivities.addActivityHandler(this::handleActivity);
115  eventsToLegs.addLegHandler(this::handleLeg);
116  }
117 
118  private void init() {
119  for (Person person : this.population.getPersons().values()) {
120  this.agentScorers.put(person.getId(), this.scoringFunctionFactory.createNewScoringFunction(person ) );
121  this.partialScores.put(person.getId(), new TDoubleArrayList());
122  this.tripRecords.put(person.getId(), PopulationUtils.createPlan());
123  }
124  }
125 
126  @Override
127  public void handleEvent(Event o) {
128  // this is for the stuff that is directly based on events. note that this passes on _all_ person events, even those which are
129  // aggregated into legs and activities. for the time being, not all PersonEvents may "implement HasPersonId". link enter/leave events
130  // are NOT passed on, for performance reasons. kai/dominik, dec'12
131  if (o instanceof HasPersonId) {
132  ScoringFunction scoringFunction = getScoringFunctionForAgent(((HasPersonId) o).getPersonId());
133  if (scoringFunction != null) {
134  if (o instanceof PersonStuckEvent) {
135  scoringFunction.agentStuck(o.getTime());
136  } else if (o instanceof PersonMoneyEvent) {
137  scoringFunction.addMoney(((PersonMoneyEvent) o).getAmount());
138  // yy looking at this, I am a bit skeptic if it truly makes sense to not pass this additionally into the general events handling function below.
139  // A use case might be different utilities of money by money transaction type (e.g. toll, fare, reimbursement, ...). kai, mar'17
140  } else if (o instanceof PersonScoreEvent) {
141  scoringFunction.addScore(((PersonScoreEvent) o).getAmount());
142  }
143  scoringFunction.handleEvent(o);
144  // passing this on in any case, see comment above. kai, mar'17
145  }
146  }
147 
148  // Establish and end connection between driver and vehicle
149  if (o instanceof VehicleEntersTrafficEvent) {
150  this.vehicles2Drivers.handleEvent((VehicleEntersTrafficEvent) o);
151  }
152  if (o instanceof VehicleLeavesTrafficEvent) {
153  this.vehicles2Drivers.handleEvent((VehicleLeavesTrafficEvent) o);
154  }
155 
156  // Pass LinkEnterEvent to person scoring, required e.g. for bicycle where link attributes are observed in scoring
157  /*
158  * (This shouldn't really be more expensive than passing the link events to the router: here, we have a map lookup
159  * for agentId, there we have a map lookup for linkId. Should be somewhat similar in terms of average
160  * computational complexity. In BetaTravelTest, 194sec w/ "false", 193sec w/ "true". However, the experienced
161  * plans service in fact does the same thing, so we should be able to get away without having to do this twice.
162  * kai, mar'17)
163  */
164  if (o instanceof LinkEnterEvent) {
165  Id<Vehicle> vehicleId = ((LinkEnterEvent)o).getVehicleId();
166  Id<Person> driverId = this.vehicles2Drivers.getDriverOfVehicle(vehicleId);
167  ScoringFunction scoringFunction = getScoringFunctionForAgent( driverId );
168  // (this will NOT do the scoring function lookup twice since LinkEnterEvent is not an instance of HasPersonId. kai, mar'17)
169  if (scoringFunction != null) {
170  scoringFunction.handleEvent(o);
171  }
172  }
173 
174  /* Now also handle events for eventsToLegs and eventsToActivities.
175  * This class deliberately only implements BasicEventHandler and not the individual event handlers required
176  * by EventsToLegs and EventsToActivities to better control the order in which events are passed to scoring
177  * functions. By handling the delegation here *after* having the events passed to scoringFunction.handleEvent()
178  * makes sure that the corresponding event was already seen by a scoring function when the call to handleActivity(),
179  * handleLeg() or handleTrip() is done.
180  */
181  if (o instanceof ActivityStartEvent) this.handleActivityStart((ActivityStartEvent) o);
182  if (o instanceof ActivityEndEvent) this.actsDelegate.handleEvent((ActivityEndEvent) o);
183 
184  if (o instanceof PersonDepartureEvent) this.legsDelegate.handleEvent((PersonDepartureEvent) o);
185  if (o instanceof PersonArrivalEvent) this.legsDelegate.handleEvent((PersonArrivalEvent) o);
186  if (o instanceof LinkEnterEvent) this.legsDelegate.handleEvent((LinkEnterEvent) o);
187  if (o instanceof TeleportationArrivalEvent) this.legsDelegate.handleEvent((TeleportationArrivalEvent) o);
188  if (o instanceof TransitDriverStartsEvent) this.legsDelegate.handleEvent((TransitDriverStartsEvent) o);
189  if (o instanceof PersonEntersVehicleEvent) this.legsDelegate.handleEvent((PersonEntersVehicleEvent) o);
190  if (o instanceof VehicleArrivesAtFacilityEvent) this.legsDelegate.handleEvent((VehicleArrivesAtFacilityEvent) o);
191  if (o instanceof VehicleEntersTrafficEvent) this.legsDelegate.handleEvent((VehicleEntersTrafficEvent) o);
192  if (o instanceof VehicleLeavesTrafficEvent) this.legsDelegate.handleEvent((VehicleLeavesTrafficEvent) o);
193  }
194 
195  private void handleActivityStart(ActivityStartEvent event) {
196  this.actsDelegate.handleEvent(event);
197  if (!StageActivityTypeIdentifier.isStageActivity( event.getActType() ) ) {
198  this.callTripScoring(event);
199  }
200  }
201 
202  private void callTripScoring(ActivityStartEvent event) {
203  Plan plan = this.tripRecords.get(event.getPersonId()); // as container for trip
204  if (plan != null) {
205  // we are at a real activity, which is not the first one we see for this agent. output the trip ...
206  Activity activity = PopulationUtils.createActivityFromLinkId(event.getActType(), event.getLinkId());
207  activity.setStartTime(event.getTime());
208  plan.addActivity(activity);
209  final List<Trip> trips = TripStructureUtils.getTrips(plan);
210  // yyyyyy should in principle only return one trip. There are, however, situations where it returns two trips, in particular
211  // in conjunction with the minibus raptor. Possibly something that has to do with not alternating between acts and legs.
212  // (To make matters worse, it passes on my local machine, but fails in jenkins. Possibly, the byte buffer memory management
213  // in the minibus raptor implementation has issues--???) kai, sep'18
214 
215  ScoringFunction scoringFunction = ScoringFunctionsForPopulation.this.getScoringFunctionForAgent(event.getPersonId());
216  for (Trip trip : trips) {
217  if (trip != null) {
218  scoringFunction.handleTrip(trip);
219  }
220  }
221 
222  // ... and clean out the intermediate plan (which will remain in tripRecords).
223  plan.getPlanElements().clear();
224  }
225  }
226 
227  void handleLeg(PersonExperiencedLeg o) {
228  Id<Person> agentId = o.getAgentId();
229  Leg leg = o.getLeg();
230  ScoringFunction scoringFunction = ScoringFunctionsForPopulation.this.getScoringFunctionForAgent(agentId);
231  if (scoringFunction != null) {
232  scoringFunction.handleLeg(leg);
233  TDoubleCollection partialScoresForAgent = this.partialScores.get(agentId);
234  partialScoresForAgent.add(scoringFunction.getScore());
235  }
236  Plan plan = this.tripRecords.get( agentId ) ; // as container for trip
237  if ( plan!=null ) {
238  plan.addLeg( leg );
239  }
240  }
241 
242  void handleActivity(PersonExperiencedActivity o) {
243  Id<Person> agentId = o.getAgentId();
244  Activity activity = o.getActivity();
245  ScoringFunction scoringFunction = ScoringFunctionsForPopulation.this.getScoringFunctionForAgent(agentId);
246  if (scoringFunction != null) {
247  scoringFunction.handleActivity(activity);
248  TDoubleCollection partialScoresForAgent = this.partialScores.get(agentId);
249  partialScoresForAgent.add(scoringFunction.getScore());
250  }
251 
252  Plan plan = this.tripRecords.get( agentId ); // as container for trip
253  if ( plan!= null ) {
254  plan.addActivity( activity );
255  }
256  }
257 
268  ScoringFunction getScoringFunctionForAgent(final Id<Person> agentId) {
269  return this.agentScorers.get(agentId);
270  }
271 
272  void finishScoringFunctions() {
273  // Rethrow an exception in a scoring function (user code) if there was one.
274  Throwable throwable = this.exception.get();
275  if (throwable != null) {
276  if (throwable instanceof RuntimeException) {
277  throw ((RuntimeException) throwable);
278  } else {
279  throw new RuntimeException(throwable);
280  }
281  }
282  for (ScoringFunction sf : this.agentScorers.values()) {
283  sf.finish();
284  }
285  for (Entry<Id<Person>, TDoubleCollection> entry : this.partialScores.entrySet()) {
286  entry.getValue().add(this.getScoringFunctionForAgent(entry.getKey()).getScore());
287  }
288  }
289 
290  void writePartialScores(String iterationFilename) {
291  try ( BufferedWriter out = IOUtils.getBufferedWriter(iterationFilename) ) {
292  for (Entry<Id<Person>, TDoubleCollection> entry : this.partialScores.entrySet()) {
293  out.write(entry.getKey().toString());
294  TDoubleIterator iterator = entry.getValue().iterator();
295  while (iterator.hasNext()) {
296  out.write('\t');
297  out.write(String.valueOf(iterator.next()));
298  }
299  out.write(IOUtils.NATIVE_NEWLINE);
300  }
301  } catch (IOException e) {
302  throw new RuntimeException(e);
303  }
304  }
305 
306  @Override
307  public void reset(int iteration) {
308  this.legsDelegate.reset(iteration);
309  this.actsDelegate.reset(iteration);
310  }
311 
312 }