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.simulation;
17  
18  import org.jamesii.core.math.random.distributions.NormalDistribution;
19  import org.jamesii.core.math.random.generators.IRandom;
20  
21  import p3j.misc.Misc;
22  import p3j.misc.math.Matrix2D;
23  import p3j.pppm.parameters.ParameterAssignment;
24  import p3j.pppm.parameters.ParameterType;
25  import p3j.simulation.calculation.deterministic.Constants;
26  
27  /**
28   * Calculates the deviation within a single {@link ParameterAssignment}. The
29   * degree of deviation is queried by {@link ParameterAssignment#getDeviation()}.
30   * If it is 0, the values queried by
31   * {@link ParameterAssignment#getMatrixValue()} are returned as is.
32   * 
33   * Otherwise, a normal distribution with mean 1.0 and standard deviation as
34   * prescribed by the {@link ParameterAssignment} is chosen. It is used to
35   * calculate an error for each year of the projection horizon. The error term is
36   * then multiplied by the data of each corresponding years.
37   * 
38   * 
39   * @see ParameterAssignment
40   * @see SingleExecution
41   * 
42   * @author Christina Bohk
43   * @author Roland Ewald
44   * 
45   */
46  final class DeviationCalculator {
47  
48    /**
49     * Deviation calculator should not be instantiated.
50     */
51    private DeviationCalculator() {
52    }
53  
54    /**
55     * Calculates the (internal) deviation of the given assignment. See class
56     * documentation for details.
57     * 
58     * @param assignment
59     *          the parameter assignment
60     * @param random
61     *          the random number generator used to calculate the deviation
62     * @return the deviated matrix values, ready to be used for calculation
63     */
64    static Matrix2D calculateAssignmentDeviation(ParameterAssignment assignment,
65        IRandom random) {
66  
67      Matrix2D originalValues = assignment.getMatrixValue();
68      double deviation = assignment.getDeviation();
69  
70      // Nothing to do if everything is fixed...
71      if (Double.compare(deviation, 0) == 0) {
72        return originalValues;
73      }
74  
75      Matrix2D deviatedValues;
76  
77      // TODO: This should be replaced by checking against an enumeration.
78      if (assignment.getParamInstance().getParameter().getName()
79          .contains(ParameterType.SURVIVORS_AGE_X)) {
80        deviatedValues = mortalityDeviation(random, originalValues, deviation);
81      } else {
82        deviatedValues = stochasticDeviation(random, originalValues, deviation);
83      }
84  
85      return deviatedValues;
86    }
87  
88    /**
89     * Calculating the deviation for mortality parameters needs an extra
90     * conversion step. The data is transformed to logarithmic mortality
91     * probabilities, and than reverse-transformed after the deviations are
92     * applied.
93     * 
94     * @param random
95     *          the random number generator
96     * @param originalValues
97     *          the original values
98     * @param deviation
99     *          the deviation
100    * 
101    * @return the matrix containing the deviated values
102    */
103   private static Matrix2D mortalityDeviation(IRandom random,
104       Matrix2D originalValues, double deviation) {
105     Matrix2D lnQx = transformMortalityMatrix(originalValues);
106     Matrix2D deviatedValues = stochasticDeviation(random, lnQx, deviation);
107     return reverseTransformation(deviatedValues);
108   }
109 
110   /**
111    * Transforms mortality matrix from amounts of surviving people (L_x) to
112    * natural logarithms of mortality probabilities.
113    * 
114    * @param originalValues
115    *          the original L_x values
116    * 
117    * @return the transformed ln(Q_x)
118    */
119   private static Matrix2D transformMortalityMatrix(Matrix2D originalValues) {
120 
121     Matrix2D lnQx = new Matrix2D(originalValues.rows(),
122         originalValues.columns());
123 
124     for (int year = 0; year < originalValues.rows(); year++) {
125       for (int age = 0; age < originalValues.columns() - 1; age++) {
126         double survProb = originalValues.getQuick(year, age + 1)
127             / originalValues.getQuick(year, age);
128         lnQx.setQuick(year, age, Math.log(1 - survProb));
129       }
130     }
131     return lnQx;
132   }
133 
134   /**
135    * Reverts the transformation.
136    * 
137    * @param deviatedLnQx
138    *          the deviated ln(Q_x)
139    * 
140    * @return the resulting L_x matrix
141    */
142   private static Matrix2D reverseTransformation(Matrix2D deviatedLnQx) {
143 
144     Matrix2D deviatedLx = new Matrix2D(deviatedLnQx.rows(),
145         deviatedLnQx.columns());
146 
147     for (int year = 0; year < deviatedLnQx.rows(); year++) {
148       deviatedLx.setQuick(year, 0, Constants.BASELINE_AMOUNT_MORT_MATRICES);
149       for (int age = 1; age < deviatedLnQx.columns(); age++) {
150         double survProb = (1 - Math.exp(deviatedLnQx.getQuick(year, age - 1)));
151         double remainingPop = deviatedLx.getQuick(year, age - 1) * survProb;
152         deviatedLx.setQuick(year, age, remainingPop);
153       }
154     }
155     return deviatedLx;
156   }
157 
158   /**
159    * Carries out a simple stochastic transformation, the extent of which is
160    * determined by the deviation.
161    * 
162    * @param random
163    *          the random number generator
164    * @param originalValues
165    *          the original values
166    * @param deviation
167    *          the deviation
168    * 
169    * @return the matrix containing the deviating values
170    */
171   private static Matrix2D stochasticDeviation(IRandom random,
172       Matrix2D originalValues, double deviation) {
173     Matrix2D deviatedValues = originalValues.copy();
174     double[] errors = generateErrors(random, deviation, deviatedValues.rows());
175     applyErrors(deviatedValues, errors);
176     return deviatedValues;
177   }
178 
179   /**
180    * Generates the array containing the errors. Its first element contain '1',
181    * as no deviation is expected in the jump-off year.
182    * 
183    * @param random
184    *          the random number generator to be used
185    * @param deviation
186    *          the standard deviation
187    * @param years
188    *          the number of years, i.e. thje size of the returned array
189    * 
190    * @return the double[]
191    */
192   private static double[] generateErrors(IRandom random, double deviation,
193       int years) {
194     double[] errors = new double[years];
195     NormalDistribution normDist = new NormalDistribution(random, 1, deviation);
196     errors[0] = 1.;
197     for (int i = 1; i < years; i++) {
198       errors[i] = errors[i - 1]
199           * Math.max(Misc.EPSILON, normDist.getRandomNumber());
200     }
201     return errors;
202   }
203 
204   /**
205    * Applies errors to matrix.
206    * 
207    * @param deviatedValues
208    *          the deviated values
209    * @param errors
210    *          the errors
211    */
212   private static void applyErrors(Matrix2D deviatedValues, double[] errors) {
213     for (int year = 1; year < deviatedValues.rows(); year++) {
214       for (int age = 0; age < deviatedValues.columns(); age++) {
215         deviatedValues.setQuick(year, age, deviatedValues.getQuick(year, age)
216             * errors[year]);
217       }
218     }
219   }
220 }