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