MATSIM
NewScoreAssignerImpl.java
Go to the documentation of this file.
1 
2 /* *********************************************************************** *
3  * project: org.matsim.*
4  * NewScoreAssignerImpl.java
5  * *
6  * *********************************************************************** *
7  * *
8  * copyright : (C) 2019 by the members listed in the COPYING, *
9  * LICENSE and WARRANTY file. *
10  * email : info at matsim dot org *
11  * *
12  * *********************************************************************** *
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * See also COPYING, LICENSE and WARRANTY file *
19  * *
20  * *********************************************************************** */
21 
22  package org.matsim.core.scoring;
23 
24 
25 import org.apache.logging.log4j.LogManager;
26 import org.apache.logging.log4j.Logger;
32 
33 import jakarta.inject.Inject;
34 import java.util.HashMap;
35 import java.util.Map;
36 
37 class NewScoreAssignerImpl implements NewScoreAssigner {
38 
39  static private final Logger log = LogManager.getLogger(NewScoreAssignerImpl.class);
40 
41  private Map<Plan,Integer> msaContributions = new HashMap<>() ;
42  private Integer scoreMSAstartsAtIteration;
43  private final double learningRate;
44  private final boolean explainScores;
45  private double scoreSum = 0.0;
46  private long scoreCount = 0;
47 
48  @Inject
49  NewScoreAssignerImpl(ScoringConfigGroup scoringConfigGroup, ControllerConfigGroup controllerConfigGroup) {
50  if (scoringConfigGroup.getFractionOfIterationsToStartScoreMSA()!=null ) {
51  final int diff = controllerConfigGroup.getLastIteration() - controllerConfigGroup.getFirstIteration();
52  this.scoreMSAstartsAtIteration = (int) (diff
53  * scoringConfigGroup.getFractionOfIterationsToStartScoreMSA() + controllerConfigGroup.getFirstIteration());
54  }
55  learningRate = scoringConfigGroup.getLearningRate();
56  explainScores = scoringConfigGroup.isWriteScoreExplanations();
57  }
58 
59  public void assignNewScores(int iteration, ScoringFunctionsForPopulation scoringFunctionsForPopulation, Population population) {
60  log.info("it: " + iteration + " msaStart: " + this.scoreMSAstartsAtIteration );
61 
62  StringBuilder explanation = new StringBuilder();
63 
64  for (Person person : population.getPersons().values()) {
65  ScoringFunction sf = scoringFunctionsForPopulation.getScoringFunctionForAgent(person.getId());
66  double score = sf.getScore();
67  Plan plan = person.getSelectedPlan();
68  Double oldScore = plan.getScore();
69 
70  if (explainScores) {
71  explanation.setLength(0);
72  sf.explainScore(explanation);
73  plan.getAttributes().putAttribute(ScoringFunction.SCORE_EXPLANATION_ATTR, explanation.toString());
74  }
75 
76  if (oldScore == null) {
77  plan.setScore(score);
78  if ( plan.getScore().isNaN() ) {
79  log.warn("score is NaN; plan:" + plan.toString() );
80  }
81  } else {
82  if ( this.scoreMSAstartsAtIteration == null || iteration < this.scoreMSAstartsAtIteration ) {
83  final double newScore = this.learningRate * score + (1 - this.learningRate) * oldScore;
84  if ( log.isTraceEnabled() ) {
85  log.trace( " lrn: " + this.learningRate + " oldScore: " + oldScore + " simScore: " + score + " newScore: " + newScore );
86  }
87  plan.setScore(newScore);
88  if ( plan.getScore().isNaN() ) {
89  log.warn("score is NaN; plan:" + plan.toString()+" with lrn: " + this.learningRate + " oldScore: " + oldScore + " simScore: " + score + " newScore: " + newScore );
90  }
91  } else {
92 // double alpha = 1./(this.iteration - this.scoreMSAstartsAtIteration + 1) ;
93 // alpha *= scenario.getConfig().strategy().getMaxAgentPlanMemorySize() ; //(**)
94 // if ( alpha>1 ) {
95 // alpha = 1. ;
96 // }
97 
98  Integer msaContribs = this.msaContributions.get(plan) ;
99  if ( msaContribs==null ) {
100  msaContribs = 0 ;
101  }
102  this.msaContributions.put(plan,msaContribs+1) ;
103  double alpha = 1./(msaContribs+1) ;
104 
105  final double newScore = alpha * score + (1.-alpha) * oldScore;
106  if ( log.isTraceEnabled() ) {
107  log.trace( " alpha: " + alpha + " oldScore: " + oldScore + " simScore: " + score + " newScore: " + newScore );
108  }
109  plan.setScore( newScore ) ;
110  if ( plan.getScore().isNaN() ) {
111  log.warn("score is NaN; plan:" + plan.toString() );
112  }
113  /*
114  // the above is some variant of MSA (method of successive
115  // averages). It is not the same as MSA since
116  // a plan is typically not scored in every iteration.
117  // However, plans are called with rates, for example
118  // only every 10th iteration. Yet, something like 1/(10x)
119  // still diverges in the same way as 1/x
120  // when integrated, so MSA should still converge to the
121  // correct result. kai, oct'12
122  // The above argument may be theoretically correct. But something 9/10*old+1/10*new is too slow in practice. Now
123  // multiplying with number of plans (**) in hope that it is better. (Where is the theory department?) kai, nov'13
124  * Looks to me like this is now truly MSA. kai, apr'15
125  // yyyy this has never been tested with scenarios :-( . At least there is a test case. kai, oct'12
126  // (In the meantime, I have used it in certain of my own 1% runs, e.g. Ivory Coast.)
127  */
128  }
129  }
130 
131  this.scoreSum += score;
132  this.scoreCount++;
133  }
134  }
135 
136 
137 }