View Javadoc

1   /*
2    * Copyright 2006 - 2012 Christina Bohk and Roland Ewald
3    *  
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License. 
6    * You may obtain a copy of the License at 
7    *  
8    *  http://www.apache.org/licenses/LICENSE-2.0
9    *  
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License. 
15   */
16  package p3j.experiment.results;
17  
18  import java.util.ArrayList;
19  import java.util.Collections;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import p3j.misc.Misc;
25  import p3j.misc.math.Matrix2D;
26  import p3j.pppm.SubPopulation;
27  import p3j.pppm.SubPopulationModel;
28  
29  /**
30   * Simple helper class to define the result aggregation.
31   * 
32   * Use only for a *single* export!
33   * 
34   * @author Christina Bohk
35   * @author Roland Ewald
36   */
37  public class ResultAggregation {
38  
39    /** The number of generations. */
40    private final int generations;
41  
42    /** The number of years. */
43    private final int numOfYears;
44  
45    /** The sub-population model. */
46    private final SubPopulationModel subPopulationModel;
47  
48    /**
49     * Map to easily access selectors for different sub-populations. Map format:
50     * Sub-Population => ( Output Variable => List[Generation_0, ...,
51     * Generation_n] )
52     */
53    private final Map<ISubPopulationSelector, Map<IOutputVariableSelector, List<AbstractAggregationSelector>>> subPopSelectorMap;
54  
55    /** Stores sub-population selectors (their names are unique). */
56    private final Map<SubPopulation, ISubPopulationSelector> subPopulationSelectors;
57  
58    // Several default selectors, for further usage:
59  
60    /**
61     * All male sub-populations to be added for total population.
62     */
63    private final List<AbstractAggregationSelector> allMaleAdditions = new ArrayList<>();
64  
65    /**
66     * All male sub-populations to be added for total population.
67     */
68    private final List<AbstractAggregationSelector> allFemaleAdditions = new ArrayList<>();
69  
70    /**
71     * All male sub-populations to be subtracted for total population.
72     */
73    private final List<AbstractAggregationSelector> allMaleSubtractions = new ArrayList<>();
74  
75    /**
76     * All female sub-populations to be subtracted for total population.
77     */
78    private final List<AbstractAggregationSelector> allFemaleSubtractions = new ArrayList<>();
79  
80    /** All sub-populations to be added for total population. */
81    private final List<AbstractAggregationSelector> allTotalAdditions = new ArrayList<>();
82  
83    /** All sub-populations to be subtracted for total population. */
84    private final List<AbstractAggregationSelector> allTotalSubtractions = new ArrayList<>();
85  
86    /**
87     * Instantiates a new result aggregation helper.
88     * 
89     * @param numberOfGenerations
90     *          the number of generations
91     * @param numberOfYears
92     *          the number of years
93     * @param subPopModel
94     *          the sub pop model
95     */
96    public ResultAggregation(int numberOfGenerations, int numberOfYears,
97        SubPopulationModel subPopModel) {
98  
99      // Define general structure
100     generations = numberOfGenerations;
101     numOfYears = numberOfYears;
102     subPopulationModel = subPopModel;
103 
104     // Create and manage sub-population selectors
105     subPopulationSelectors = createSubPopSelectors();
106     subPopSelectorMap = initSubPopSelectorMap();
107 
108     // Create lists of relevant sub-population selectors
109     for (SubPopulation subPopulation : subPopulationModel.getSubPopulations()) {
110       ISubPopulationSelector subPopSelector = subPopulationSelectors
111           .get(subPopulation);
112       if (subPopulation.isAdditive()) {
113         allMaleAdditions.addAll(subPopSelectorMap.get(subPopSelector).get(
114             END_X_M));
115         allFemaleAdditions.addAll(subPopSelectorMap.get(subPopSelector).get(
116             END_X_F));
117         allTotalAdditions.addAll(getAllForSubPop(subPopSelector));
118       } else {
119         allMaleSubtractions.addAll(subPopSelectorMap.get(subPopSelector).get(
120             END_X_M));
121         allFemaleSubtractions.addAll(subPopSelectorMap.get(subPopSelector).get(
122             END_X_F));
123         allTotalSubtractions.addAll(getAllForSubPop(subPopSelector));
124       }
125     }
126   }
127 
128   /**
129    * Creates the sub-population selectors.
130    * 
131    * @return the map sub-population => sub-population selector
132    */
133   private Map<SubPopulation, ISubPopulationSelector> createSubPopSelectors() {
134     Map<SubPopulation, ISubPopulationSelector> result = new HashMap<>();
135     for (SubPopulation subPopulation : subPopulationModel.getSubPopulations()) {
136       result.put(
137           subPopulation,
138           new SubPopulationSelector(subPopulation.getName(), subPopulation
139               .getSimplifiedName()));
140     }
141     return Collections.unmodifiableMap(result);
142   }
143 
144   /**
145    * Initializes the sub-population selector map.
146    * 
147    * @return map of sub-population selectors
148    * @see ResultAggregation#subPopSelectorMap
149    */
150   private Map<ISubPopulationSelector, Map<IOutputVariableSelector, List<AbstractAggregationSelector>>> initSubPopSelectorMap() {
151     Map<ISubPopulationSelector, Map<IOutputVariableSelector, List<AbstractAggregationSelector>>> resultMap = new HashMap<>();
152     for (SubPopulation subPopulation : subPopulationModel.getSubPopulations()) {
153 
154       ISubPopulationSelector subPopSelector = subPopulationSelectors
155           .get(subPopulation);
156 
157       Map<IOutputVariableSelector, List<AbstractAggregationSelector>> outputMap = new HashMap<>();
158       outputMap.put(END_X_M, new ArrayList<AbstractAggregationSelector>());
159       outputMap.put(END_X_F, new ArrayList<AbstractAggregationSelector>());
160       resultMap.put(subPopSelector, outputMap);
161 
162       if (subPopulation.isConsistingOfDescendantGenerations())
163         for (int i = 0; i < generations; i++) {
164           resultMap.get(subPopSelector).get(END_X_M)
165               .add(new SumOverAgesSelector(END_X_M, subPopSelector, i));
166           resultMap.get(subPopSelector).get(END_X_F)
167               .add(new SumOverAgesSelector(END_X_F, subPopSelector, i));
168         }
169       else {
170         resultMap.get(subPopSelector).get(END_X_M)
171             .add(new SumOverAgesSelector(END_X_M, subPopSelector, -1));
172         resultMap.get(subPopSelector).get(END_X_F)
173             .add(new SumOverAgesSelector(END_X_F, subPopSelector, -1));
174       }
175     }
176     return Collections.unmodifiableMap(resultMap);
177   }
178 
179   /** The selector for male year-end population. */
180   private static final IOutputVariableSelector END_X_M = new IOutputVariableSelector() {
181     @Override
182     public String getSuffix() {
183       return "end_x_m";
184     }
185 
186     @Override
187     public Matrix2D select(BasicResults basicResults) {
188       return basicResults.getEndXm();
189     }
190   };
191 
192   /** The selector for female year-end population. */
193   private static final IOutputVariableSelector END_X_F = new IOutputVariableSelector() {
194     @Override
195     public String getSuffix() {
196       return "end_x_f";
197     }
198 
199     @Override
200     public Matrix2D select(BasicResults basicResults) {
201       return basicResults.getEndXf();
202     }
203   };
204 
205   /** The selector for male mean population. */
206   private static final IOutputVariableSelector MEAN_X_M = new IOutputVariableSelector() {
207     @Override
208     public String getSuffix() {
209       return "mean_x_m";
210     }
211 
212     @Override
213     public Matrix2D select(BasicResults basicResults) {
214       return basicResults.getMeanXm();
215     }
216   };
217 
218   /** The selector for female mean population. */
219   private static final IOutputVariableSelector MEAN_X_F = new IOutputVariableSelector() {
220     @Override
221     public String getSuffix() {
222       return "mean_x_f";
223     }
224 
225     @Override
226     public Matrix2D select(BasicResults basicResults) {
227       return basicResults.getMeanXf();
228     }
229   };
230 
231   /**
232    * Generic selector for sub-populations.
233    * 
234    * Created on 29.07.2013
235    * 
236    * @author Christina Bohk
237    * @author Roland Ewald
238    */
239   class SubPopulationSelector implements ISubPopulationSelector {
240 
241     /** The sub pop name. */
242     private final String subPopName;
243 
244     /**
245      * The prefix to be used in the file system (this places adds restrictions
246      * regarding the permitted characters, hence not identical to
247      * {@link SubPopulationSelector#subPopName}).
248      */
249     private final String prefix;
250 
251     /**
252      * Instantiates a new sub population selector.
253      * 
254      * @param subPopulationName
255      *          the sub population name
256      * @param thePrefix
257      *          the prefix (to be used as part of a file name, i.e. should only
258      *          contain permitted characters)
259      */
260     SubPopulationSelector(String subPopulationName, String thePrefix) {
261       subPopName = subPopulationName;
262       prefix = thePrefix;
263     }
264 
265     @Override
266     public BasicResults select(ResultsOfTrial resultsOfTrial, int generation) {
267       // TODO: this is inefficient; improve if necessary
268       for (BasicResults result : resultsOfTrial.getSubPopulationResults())
269         // The (gen == -1 & resu.gen==0) check accounts for an incompatibility
270         // between results and aggregation selectors: 0 is the default
271         // generation index for the former, but the latter use -1 if there is no
272         // generation
273         if (result.getSubPopName().equals(subPopName)
274             && (result.getGeneration() == generation || generation == -1
275                 && result.getGeneration() == 0))
276           return result;
277       throw new IllegalArgumentException(
278           "Could not find result for generation '" + generation
279               + "' of sub-population '" + subPopName + "'");
280     }
281 
282     @Override
283     public String getPrefix() {
284       return prefix;
285     }
286 
287   }
288 
289   /** The array of result-type selectors. */
290   private static final IOutputVariableSelector[] RESULT_TYPES = { END_X_M,
291       END_X_F, MEAN_X_M, MEAN_X_F };
292 
293   /**
294    * Gets the selectors to be used for report generation.
295    * 
296    * @return the selectors
297    */
298   public IAggregationSelector[] getSelectorsForReport() {
299     List<IAggregationSelector> selectors = defineSubPopMergingSelectors();
300 
301     for (SubPopulation subPopulation : subPopulationModel.getSubPopulations()) {
302       ISubPopulationSelector subPopSelector = subPopulationSelectors
303           .get(subPopulation);
304       if (subPopulation.isConsistingOfDescendantGenerations())
305         for (int i = 0; i < generations; i++)
306           selectors.addAll(createSelectorsPerGeneration(subPopSelector, i));
307       else
308         selectors.addAll(createSelectorsPerGeneration(subPopSelector, -1));
309     }
310 
311     return selectors.toArray(new IAggregationSelector[0]);
312   }
313 
314   /**
315    * Creates the selectors for a sub-population selector and a specific
316    * generation.
317    * 
318    * @param subPopSelector
319    *          the sub pop selector
320    * @param generation
321    *          the generation, use -1 for sub-populations without descendants
322    * @return list of generation-specific selectors
323    */
324   private List<IAggregationSelector> createSelectorsPerGeneration(
325       ISubPopulationSelector subPopSelector, int generation) {
326     List<IAggregationSelector> result = new ArrayList<>();
327     for (IOutputVariableSelector resultType : new IOutputVariableSelector[] {
328         END_X_M, END_X_F }) {
329       result.add(new ChooseAgesForSingleYearSelector(resultType,
330           subPopSelector, generation, 0));
331       result.add(new ChooseAgesForSingleYearSelector(resultType,
332           subPopSelector, generation, numOfYears - 1));
333     }
334     for (IOutputVariableSelector resultType : RESULT_TYPES) {
335       result
336           .add(new SumOverAgesSelector(resultType, subPopSelector, generation));
337     }
338     return result;
339   }
340 
341   /**
342    * Gets the selectors for aggregated data export.
343    * 
344    * @return the selectors for aggregated data export
345    */
346   public IAggregationSelector[] getSelectorsForAggregatedDataExport() {
347     List<IAggregationSelector> selectors = new ArrayList<IAggregationSelector>();
348 
349     selectors.add(new MergeSubPopOldAgeDependencyRatioSelector(
350         allTotalAdditions, allTotalSubtractions, "oadr_total_end_mf"));
351 
352     for (int i = 0; i < numOfYears; i++) {
353       selectors.add(new YearlyAgeTrialMatrixSelector(allMaleAdditions,
354           allMaleSubtractions, "age_trial_matrix_m_for_year_", i));
355       selectors.add(new YearlyAgeTrialMatrixSelector(allFemaleAdditions,
356           allFemaleSubtractions, "age_trial_matrix_f_for_year_", i));
357     }
358 
359     return selectors.toArray(new IAggregationSelector[0]);
360   }
361 
362   /**
363    * Generate selectors that merge certain sub-populations (e.g. all immigrants,
364    * all females, all descendant immigrants, both males and females, etc.).
365    * 
366    * @return the list of defined selectors
367    */
368   private List<IAggregationSelector> defineSubPopMergingSelectors() {
369 
370     // Total population for natives (m+f)
371     List<IAggregationSelector> selectors = new ArrayList<IAggregationSelector>();
372 
373     selectors.addAll(defineMFSelectorsForAllGenerations());
374 
375     // Specifying the total male/female/male+female population
376     selectors.addAll(defineTotalMaleFemaleCombinedSelectors());
377 
378     // Specifying emigrant/immigrant total population
379     selectors
380         .addAll(defineSelectorsForSubPopsWithGenerations(subPopSelectorMap));
381 
382     return selectors;
383   }
384 
385   /**
386    * Specify selectors for total migrant populations.
387    * 
388    * @param subPopSelectorMap
389    *          the sub-population selector map
390    * @return the list
391    */
392   @SuppressWarnings("unchecked")
393   private List<IAggregationSelector> defineSelectorsForSubPopsWithGenerations(
394       Map<ISubPopulationSelector, Map<IOutputVariableSelector, List<AbstractAggregationSelector>>> subPopSelectorMap) {
395 
396     List<IAggregationSelector> selectors = new ArrayList<IAggregationSelector>();
397 
398     for (SubPopulation subPopulation : subPopulationModel.getSubPopulations()) {
399 
400       if (!subPopulation.isConsistingOfDescendantGenerations())
401         continue;
402 
403       ISubPopulationSelector subPopSelector = subPopulationSelectors
404           .get(subPopulation);
405       String prefix = subPopSelector.getPrefix();
406 
407       // Add merging selectors
408       selectors.addAll(defineSingleYearSelectors(
409           new MergeSubPopSumOverAgesSelector(subPopSelectorMap.get(
410               subPopSelector).get(END_X_M), prefix + "_end_x_m"), numOfYears));
411       selectors.addAll(defineSingleYearSelectors(
412           new MergeSubPopSumOverAgesSelector(subPopSelectorMap.get(
413               subPopSelector).get(END_X_F), prefix + "_end_x_f"), numOfYears));
414       selectors.add(new MergeSubPopSumOverAgesSelector(
415           getAllForSubPop(subPopSelector), prefix + "_end_x_mf"));
416 
417       // Define selectors for male and female descendants
418       List<AbstractAggregationSelector> allMaleDescendants = new ArrayList<>(
419           subPopSelectorMap.get(subPopSelector).get(END_X_M));
420       allMaleDescendants.remove(0);
421       List<AbstractAggregationSelector> allFemaleDescendants = new ArrayList<>(
422           subPopSelectorMap.get(subPopSelector).get(END_X_F));
423       allFemaleDescendants.remove(0);
424 
425       // Add merging selectors for descendants
426       selectors.addAll(defineSingleYearSelectors(
427           new MergeSubPopSumOverAgesSelector(allMaleDescendants, null, prefix
428               + "_desc_end_x_m"), numOfYears));
429       selectors.addAll(defineSingleYearSelectors(
430           new MergeSubPopSumOverAgesSelector(allFemaleDescendants, null, prefix
431               + "_desc_end_x_f"), numOfYears));
432       selectors.add(new MergeSubPopSumOverAgesSelector(Misc
433           .<AbstractAggregationSelector> mergeList(allMaleDescendants,
434               allFemaleDescendants), null, prefix + "_desc_end_x_mf"));
435     }
436 
437     return selectors;
438   }
439 
440   /**
441    * Defines selectors that focus on total male/female population.
442    * 
443    * @return list of total male/female population selectors
444    */
445   private List<IAggregationSelector> defineTotalMaleFemaleCombinedSelectors() {
446     List<IAggregationSelector> selectors = new ArrayList<IAggregationSelector>();
447     selectors.addAll(defineSingleYearSelectors(
448         new MergeSubPopSumOverAgesSelector(allMaleAdditions,
449             allMaleSubtractions, "total_end_x_m"), numOfYears));
450     selectors.addAll(defineSingleYearSelectors(
451         new MergeSubPopSumOverAgesSelector(allFemaleAdditions,
452             allFemaleSubtractions, "total_end_x_f"), numOfYears));
453     selectors.addAll(defineSingleYearSelectors(
454         new MergeSubPopSumOverAgesSelector(allTotalAdditions,
455             allTotalSubtractions, "total_end_x_mf"), numOfYears));
456     return selectors;
457   }
458 
459   /**
460    * Defines male+female selectors for all single migrant generations.
461    * 
462    * @return list of all end_x_mf selectors, for all generations and migrant
463    *         populations
464    */
465   private List<IAggregationSelector> defineMFSelectorsForAllGenerations() {
466     List<IAggregationSelector> selectors = new ArrayList<IAggregationSelector>();
467 
468     for (SubPopulation subPopulation : subPopulationModel.getSubPopulations()) {
469       ISubPopulationSelector subPopSelector = subPopulationSelectors
470           .get(subPopulation);
471 
472       if (subPopulation.isConsistingOfDescendantGenerations()) {
473         for (int i = 0; i < generations; i++) {
474           selectors.add(new MergeSubPopSumOverAgesSelector(
475               new AbstractAggregationSelector[] {
476                   subPopSelectorMap.get(subPopSelector).get(END_X_M).get(i),
477                   subPopSelectorMap.get(subPopSelector).get(END_X_F).get(i) },
478               new AbstractAggregationSelector[0], subPopSelector.getPrefix()
479                   + "_gen_" + i + "_end_x_mf"));
480         }
481       } else {
482         selectors.add(new MergeSubPopSumOverAgesSelector(
483             new AbstractAggregationSelector[] {
484                 subPopSelectorMap.get(subPopSelector).get(END_X_M).get(0),
485                 subPopSelectorMap.get(subPopSelector).get(END_X_F).get(0) },
486             subPopSelector.getPrefix() + "_end_x_mf"));
487       }
488     }
489     return selectors;
490   }
491 
492   /**
493    * Adds the given selector and defines two selectors for first/last year for
494    * it.
495    * 
496    * @param mergeSubPopSumOverAgesSelector
497    *          the merge sub pop sum over ages selector
498    * @param numOfYears
499    *          the number of years
500    * @return the list
501    */
502   private static List<IAggregationSelector> defineSingleYearSelectors(
503       MergeSubPopSumOverAgesSelector mergeSubPopSumOverAgesSelector,
504       int numOfYears) {
505     List<IAggregationSelector> selectors = new ArrayList<IAggregationSelector>();
506     selectors.add(mergeSubPopSumOverAgesSelector);
507 
508     // Define selector for first year
509     selectors.add(new MergeSubPopChooseAgesSingleYearSelector(
510         mergeSubPopSumOverAgesSelector, 0));
511 
512     // Define selector for last year
513     selectors.add(new MergeSubPopChooseAgesSingleYearSelector(
514         mergeSubPopSumOverAgesSelector, numOfYears - 1));
515 
516     return selectors;
517   }
518 
519   /**
520    * Gets all selectors for a sub-population selector.
521    * 
522    * @param subPopSelector
523    *          the sub-population selector
524    * 
525    * @return all selectors for the specified sub-population
526    */
527   private List<AbstractAggregationSelector> getAllForSubPop(
528       ISubPopulationSelector subPopSelector) {
529     List<AbstractAggregationSelector> result = new ArrayList<AbstractAggregationSelector>();
530     for (List<AbstractAggregationSelector> selectorList : subPopSelectorMap
531         .get(subPopSelector).values()) {
532       result.addAll(selectorList);
533     }
534     return result;
535   }
536 
537 }