| 1 | package de.uka.ipd.sdq.reliability.solver.pcm2markov; |
| 2 | |
| 3 | import java.io.BufferedWriter; |
| 4 | import java.io.File; |
| 5 | import java.io.FileWriter; |
| 6 | import java.io.IOException; |
| 7 | import java.net.URL; |
| 8 | import java.util.HashMap; |
| 9 | import java.util.List; |
| 10 | import java.util.Map; |
| 11 | |
| 12 | import org.apache.log4j.Logger; |
| 13 | import org.eclipse.core.runtime.FileLocator; |
| 14 | |
| 15 | import de.uka.ipd.sdq.markov.MarkovChain; |
| 16 | import de.uka.ipd.sdq.markov.State; |
| 17 | import de.uka.ipd.sdq.pcm.usagemodel.UsageScenario; |
| 18 | import de.uka.ipd.sdq.pcmsolver.runconfig.PCMSolverWorkflowRunConfiguration; |
| 19 | import de.uka.ipd.sdq.reliability.core.MarkovFailureType; |
| 20 | |
| 21 | /** |
| 22 | * Aggregates the results of a PCM2MarkovTransformation. |
| 23 | * |
| 24 | * @author brosch |
| 25 | * |
| 26 | */ |
| 27 | public class MarkovTransformationResult { |
| 28 | |
| 29 | /** |
| 30 | * A logger to give detailed information about the PCM instance |
| 31 | * transformation. |
| 32 | */ |
| 33 | private static Logger logger = Logger |
| 34 | .getLogger(MarkovTransformationResult.class.getName()); |
| 35 | |
| 36 | /** |
| 37 | * Indicates if an approximation scheme shall be used for printing of |
| 38 | * probabilities. |
| 39 | */ |
| 40 | private boolean approximate; |
| 41 | |
| 42 | /** |
| 43 | * Configuration options of the transformation. |
| 44 | */ |
| 45 | private PCMSolverWorkflowRunConfiguration configuration; |
| 46 | |
| 47 | /** |
| 48 | * Overall failure type probabilities, accumulated over all considered |
| 49 | * physical system states. |
| 50 | */ |
| 51 | private Map<MarkovFailureType, Double> cumulatedFailureTypeProbabilities = new HashMap<MarkovFailureType, Double>(); |
| 52 | |
| 53 | /** |
| 54 | * Overall physical state probability, accumulated over all considered |
| 55 | * physical system states. |
| 56 | */ |
| 57 | private double cumulatedPhysicalStateProbability; |
| 58 | |
| 59 | /** |
| 60 | * Overall success probability, accumulated over all considered physical |
| 61 | * system states. |
| 62 | */ |
| 63 | private double cumulatedSuccessProbability; |
| 64 | |
| 65 | /** |
| 66 | * The list of potential failure types. |
| 67 | */ |
| 68 | private List<MarkovFailureType> failureTypes; |
| 69 | |
| 70 | /** |
| 71 | * A helper class providing functionality for Markov chains. |
| 72 | */ |
| 73 | private MarkovBuilder markovBuilder; |
| 74 | |
| 75 | /** |
| 76 | * Holds state information required during the PCM2MarkovTransformation. |
| 77 | */ |
| 78 | private MarkovTransformationSource markovSource; |
| 79 | |
| 80 | /** |
| 81 | * Indicates the number of physical system states (which is n^2 for n |
| 82 | * resources). |
| 83 | */ |
| 84 | private long numberOfPhysicalSystemStates; |
| 85 | |
| 86 | /** |
| 87 | * Counts the evaluated physical system states. |
| 88 | */ |
| 89 | private long physicalStateEvaluationCount; |
| 90 | |
| 91 | /** |
| 92 | * Holds the Makov chain that results from the transformation. |
| 93 | */ |
| 94 | private MarkovChain resultChain; |
| 95 | |
| 96 | /** |
| 97 | * The usage scenario which has been evaluated. |
| 98 | */ |
| 99 | private UsageScenario scenario; |
| 100 | |
| 101 | /** |
| 102 | * Creates a new Markov results aggregator. |
| 103 | * |
| 104 | * @param configuration |
| 105 | * configuration options of the transformation |
| 106 | * @param markovSource |
| 107 | * the Markov state holder |
| 108 | * @param scenario |
| 109 | * the usage scenario to evaluate |
| 110 | * @param failureTypes |
| 111 | * the list of potential failure types |
| 112 | */ |
| 113 | public MarkovTransformationResult( |
| 114 | final PCMSolverWorkflowRunConfiguration configuration, |
| 115 | final MarkovTransformationSource markovSource, |
| 116 | final UsageScenario scenario, |
| 117 | final List<MarkovFailureType> failureTypes) { |
| 118 | this.configuration = configuration; |
| 119 | this.cumulatedPhysicalStateProbability = 0.0; |
| 120 | this.cumulatedSuccessProbability = 0.0; |
| 121 | this.markovBuilder = new MarkovBuilder(false); |
| 122 | this.markovSource = markovSource; |
| 123 | this.physicalStateEvaluationCount = 0; |
| 124 | this.numberOfPhysicalSystemStates = (long) Math.pow(2, markovSource |
| 125 | .getUnreliableResourceDescriptors().size()); |
| 126 | this.resultChain = null; |
| 127 | this.scenario = scenario; |
| 128 | this.failureTypes = failureTypes; |
| 129 | } |
| 130 | |
| 131 | /** |
| 132 | * Adds the results of the evaluation of a single physical system state |
| 133 | * during the PCM2Markov transformation. |
| 134 | * |
| 135 | * @param resultChain |
| 136 | * the Markov chain resulting from the evaluation of the physical |
| 137 | * system state |
| 138 | * @param markovProbabilityMatrix |
| 139 | * the matrix with the probabilities to get from state i to state |
| 140 | * j in the Markov chain |
| 141 | * @param physicalStateProbability |
| 142 | * the probability of the evaluated physical system state to |
| 143 | * occur |
| 144 | */ |
| 145 | public void addPhysicalStateResults(final MarkovChain resultChain, |
| 146 | final double[][] markovProbabilityMatrix, |
| 147 | final double physicalStateProbability) { |
| 148 | |
| 149 | // Check plausibility of input parameters: |
| 150 | if (physicalStateProbability < 0.0 || physicalStateProbability > 1.0) { |
| 151 | throw new MarkovException("Illegal physical state probability (" |
| 152 | + physicalStateProbability |
| 153 | + "). The value must be in [0,1]."); |
| 154 | } |
| 155 | |
| 156 | // Store the first resulting Markov Chain: |
| 157 | if (physicalStateEvaluationCount == 0) { |
| 158 | this.resultChain = resultChain; |
| 159 | } |
| 160 | |
| 161 | // Get the indices of the Start and Success States: |
| 162 | int indexStart = markovBuilder.indexOf(resultChain, markovBuilder |
| 163 | .getStartState(resultChain)); |
| 164 | int indexSuccess = markovBuilder.indexOf(resultChain, markovBuilder |
| 165 | .getSuccessState(resultChain)); |
| 166 | |
| 167 | // Check plausibility of Markov probabilities: |
| 168 | double successProbability = markovProbabilityMatrix[indexStart][indexSuccess]; |
| 169 | if (successProbability < 0.0 || successProbability > 1.0) { |
| 170 | // It should never happen that the success probability is outside |
| 171 | // [0,1]. |
| 172 | // The only feasible explanation is a rounding error (which has been |
| 173 | // observed for certain sample PCM instances): |
| 174 | double correctedSuccessProbability = (successProbability < 0.5) ? 0.0 |
| 175 | : 1.0; |
| 176 | logger.warn("Illegal success probability " + successProbability |
| 177 | + " outside [0,1]. Assuming a rounding error. " |
| 178 | + "Setting success probability to " |
| 179 | + correctedSuccessProbability); |
| 180 | successProbability = correctedSuccessProbability; |
| 181 | } |
| 182 | |
| 183 | // Accumulate results: |
| 184 | cumulatedPhysicalStateProbability += physicalStateProbability; |
| 185 | cumulatedSuccessProbability += physicalStateProbability |
| 186 | * successProbability; |
| 187 | |
| 188 | // Consider also the failure type probabilities: |
| 189 | List<State> failureStates = markovBuilder.getFailureStates(resultChain); |
| 190 | for (int i = 0; i < failureStates.size(); i++) { |
| 191 | |
| 192 | // Check plausibility of Markov probabilities: |
| 193 | double failureTypeProbability = markovProbabilityMatrix[indexStart][markovBuilder |
| 194 | .indexOf(resultChain, failureStates.get(i))]; |
| 195 | if (failureTypeProbability < 0.0 || failureTypeProbability > 1.0) { |
| 196 | throw new MarkovException("Illegal failure type probability (" |
| 197 | + failureTypeProbability |
| 198 | + "). The value must be in [0,1]."); |
| 199 | } |
| 200 | |
| 201 | // Determine the failure type probability for this physical system |
| 202 | // state: |
| 203 | double failureTypeProbabilityDelta = physicalStateProbability |
| 204 | * failureTypeProbability; |
| 205 | |
| 206 | // Add the failure type probability to the already existing value: |
| 207 | String failureTypeId = markovBuilder.getFailureTypeId(failureStates |
| 208 | .get(i)); |
| 209 | MarkovFailureType failureType = getFailureType(failureTypeId); |
| 210 | |
| 211 | Double failureProbability = cumulatedFailureTypeProbabilities |
| 212 | .get(failureType); |
| 213 | cumulatedFailureTypeProbabilities.put(failureType, |
| 214 | ((failureProbability == null) ? 0.0 : failureProbability) |
| 215 | + failureTypeProbabilityDelta); |
| 216 | } |
| 217 | |
| 218 | // Increase the counter of evaluated physical system states: |
| 219 | physicalStateEvaluationCount++; |
| 220 | |
| 221 | // Do the logging: |
| 222 | if (configuration.isPrintMarkovSingleResults()) { |
| 223 | // yes - write output to log file |
| 224 | BufferedWriter out = null; |
| 225 | String filePath = resolveFile(configuration.getLogFile()); |
| 226 | try { |
| 227 | if (physicalStateEvaluationCount == 1) { |
| 228 | File f = new File(filePath); |
| 229 | // if the file exists, we will delete it and create a new, |
| 230 | // empty one |
| 231 | // (i.e., overwrite the existing file) once, and then |
| 232 | // repeatedly append |
| 233 | // to this file |
| 234 | if (f.exists()) { |
| 235 | f.delete(); // delete current ("old") file |
| 236 | f.createNewFile(); // create a new, empty file |
| 237 | } |
| 238 | out = new BufferedWriter(new FileWriter(filePath, true)); |
| 239 | logger |
| 240 | .info("Logging results of all Markov transformation runs to: " |
| 241 | + filePath); |
| 242 | out.append(getLogHeadings() |
| 243 | + System.getProperty("line.separator")); |
| 244 | out.flush(); |
| 245 | out.close(); |
| 246 | } |
| 247 | out = new BufferedWriter(new FileWriter(filePath, true)); |
| 248 | out.append(getLogSingleResults(successProbability, |
| 249 | physicalStateProbability) |
| 250 | + System.getProperty("line.separator")); |
| 251 | out.flush(); |
| 252 | } catch (IOException e) { |
| 253 | e.printStackTrace(); |
| 254 | } finally { |
| 255 | try { |
| 256 | if (out != null) { |
| 257 | out.flush(); |
| 258 | out.close(); |
| 259 | } |
| 260 | } catch (IOException e) { |
| 261 | e.printStackTrace(); |
| 262 | } |
| 263 | } |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | /** |
| 268 | * Gets the overall failure type probabilities, accumulated over all |
| 269 | * considered physical system states. |
| 270 | * |
| 271 | * @return the overall failure type probabilities |
| 272 | */ |
| 273 | public Map<MarkovFailureType, Double> getCumulatedFailureTypeProbabilities() { |
| 274 | return cumulatedFailureTypeProbabilities; |
| 275 | } |
| 276 | |
| 277 | /** |
| 278 | * Gets the overall physical state probability, accumulated over all |
| 279 | * considered physical system states. |
| 280 | * |
| 281 | * @return the overall physical state probability |
| 282 | */ |
| 283 | public double getCumulatedPhysicalStateProbability() { |
| 284 | return cumulatedPhysicalStateProbability; |
| 285 | } |
| 286 | |
| 287 | /** |
| 288 | * Retrieves the failure type with the given id. |
| 289 | * |
| 290 | * @param failureTypeId |
| 291 | * the failure type id |
| 292 | * @return the failure type |
| 293 | */ |
| 294 | private MarkovFailureType getFailureType(String failureTypeId) { |
| 295 | for (MarkovFailureType failureType : failureTypes) { |
| 296 | if (failureType.getId().equals(failureTypeId)) { |
| 297 | return failureType; |
| 298 | } |
| 299 | } |
| 300 | throw new IllegalArgumentException( |
| 301 | "MarkovTransformationResult: Failure type with ID \"" |
| 302 | + failureTypeId + "\" not found!"); |
| 303 | } |
| 304 | |
| 305 | /** |
| 306 | * Builds the headings string for Markov transformation logging. |
| 307 | * |
| 308 | * @return the log headings string |
| 309 | */ |
| 310 | private String getLogHeadings() { |
| 311 | |
| 312 | // Build a result string: |
| 313 | StringBuilder resultString = new StringBuilder(); |
| 314 | |
| 315 | // Append state number heading: |
| 316 | resultString.append("physical system state number;"); |
| 317 | |
| 318 | // Append resource state headings: |
| 319 | for (ProcessingResourceDescriptor descriptor : markovSource |
| 320 | .getUnreliableResourceDescriptors()) { |
| 321 | resultString.append(descriptor.getResourceContainerName() + " - " |
| 322 | + descriptor.getType().getName() + ";"); |
| 323 | } |
| 324 | |
| 325 | // Append success probability heading: |
| 326 | resultString.append("success probability;"); |
| 327 | |
| 328 | // Append state probability heading: |
| 329 | resultString.append("physical state probability"); |
| 330 | |
| 331 | // Return the result: |
| 332 | return resultString.toString(); |
| 333 | } |
| 334 | |
| 335 | /** |
| 336 | * Builds the results string for Markov transformation logging. |
| 337 | * |
| 338 | * @param successProbability |
| 339 | * success probability of the current physical system state |
| 340 | * @param physicalStateProbability |
| 341 | * occurrence probability of the current physical system state |
| 342 | * @return the results string |
| 343 | */ |
| 344 | private String getLogSingleResults(final double successProbability, |
| 345 | final double physicalStateProbability) { |
| 346 | |
| 347 | // Build a result string: |
| 348 | StringBuilder resultString = new StringBuilder(); |
| 349 | |
| 350 | // Append state number: |
| 351 | resultString.append(physicalStateEvaluationCount + ";"); |
| 352 | |
| 353 | // Append resource states: |
| 354 | for (ProcessingResourceDescriptor descriptor : markovSource |
| 355 | .getUnreliableResourceDescriptors()) { |
| 356 | resultString.append(descriptor.getCurrentState().name() + ";"); |
| 357 | } |
| 358 | |
| 359 | // Append success probability: |
| 360 | resultString.append(successProbability + ";"); |
| 361 | |
| 362 | // Append state probability: |
| 363 | resultString.append(physicalStateProbability); |
| 364 | |
| 365 | // Return the result: |
| 366 | return resultString.toString(); |
| 367 | } |
| 368 | |
| 369 | /** |
| 370 | * Retrieves the number of physical system states. |
| 371 | * |
| 372 | * @return the number of physical system states |
| 373 | */ |
| 374 | public long getNumberOfPhysicalSystemStates() { |
| 375 | return numberOfPhysicalSystemStates; |
| 376 | } |
| 377 | |
| 378 | /** |
| 379 | * Retrieves the current number of evaluated physical system states. |
| 380 | * |
| 381 | * @return the current number of evaluated physical system states |
| 382 | */ |
| 383 | public long getPhysicalStateEvaluationCount() { |
| 384 | return physicalStateEvaluationCount; |
| 385 | } |
| 386 | |
| 387 | /** |
| 388 | * Retrieves the Markov chain that results from the transformation. |
| 389 | * |
| 390 | * @return the Markov chain that results from the transformation |
| 391 | */ |
| 392 | public MarkovChain getResultChain() { |
| 393 | return resultChain; |
| 394 | } |
| 395 | |
| 396 | /** |
| 397 | * Gets the usage scenario which has been evaluated |
| 398 | * |
| 399 | * @return the usage scenario |
| 400 | */ |
| 401 | public UsageScenario getScenario() { |
| 402 | return scenario; |
| 403 | } |
| 404 | |
| 405 | /** |
| 406 | * Retrieves the overall success probability. |
| 407 | * |
| 408 | * @return the success probability |
| 409 | */ |
| 410 | public double getSuccessProbability() { |
| 411 | return cumulatedSuccessProbability; |
| 412 | } |
| 413 | |
| 414 | /** |
| 415 | * Determines if the calculated success probability conforms to a given |
| 416 | * required accuracy. |
| 417 | * |
| 418 | * @param requiredAccuracy |
| 419 | * the required accuracy in decimal places |
| 420 | * @return true if the required accuracy has been reached |
| 421 | */ |
| 422 | public boolean hasRequiredAccuracy(final int requiredAccuracy) { |
| 423 | |
| 424 | // Create an approximation for the accumulated success probability: |
| 425 | MarkovResultApproximation approximation = new MarkovResultApproximation( |
| 426 | cumulatedSuccessProbability, cumulatedSuccessProbability |
| 427 | + (1.0 - cumulatedPhysicalStateProbability)); |
| 428 | |
| 429 | // Check for the required accuracy: |
| 430 | return approximation.hasRequiredAccuracy(requiredAccuracy); |
| 431 | } |
| 432 | |
| 433 | /** |
| 434 | * Indicates if an approximation scheme shall be used for printing of |
| 435 | * probabilities. |
| 436 | * |
| 437 | * @return <code>true</code>, if an approximation scheme shall be used for |
| 438 | * printing probabilities, <code>false</code> otherwise. |
| 439 | */ |
| 440 | public boolean isDoApproximate() { |
| 441 | return (configuration.isIterationOverPhysicalSystemStatesEnabled()) |
| 442 | && approximate |
| 443 | && (physicalStateEvaluationCount < Math.pow(markovSource |
| 444 | .getUnreliableResourceDescriptors().size(), 2)); |
| 445 | } |
| 446 | |
| 447 | /** |
| 448 | * Resolves a file's path in case it starts with "platform:/" and returns |
| 449 | * the entire absolute path to the file, including the file's name. |
| 450 | * |
| 451 | * @param fileURL |
| 452 | * the path to a file, including the file's name (and its |
| 453 | * extension) |
| 454 | * @return the absolute path to the file, including the file's name |
| 455 | */ |
| 456 | private String resolveFile(String fileURL) { |
| 457 | // if this is a platform URL, first resolve it to an absolute path |
| 458 | if (fileURL.startsWith("platform:")) { |
| 459 | try { |
| 460 | URL solvedURL = FileLocator.resolve(new URL(fileURL)); |
| 461 | fileURL = solvedURL.getPath(); |
| 462 | } catch (Exception e) { |
| 463 | e.printStackTrace(); |
| 464 | return ""; |
| 465 | } |
| 466 | } |
| 467 | return fileURL; |
| 468 | } |
| 469 | |
| 470 | /** |
| 471 | * Method for setting a value responsible for telling if an approximation |
| 472 | * scheme shall be used for printing probabilities. |
| 473 | * |
| 474 | * @param approximate |
| 475 | * the value indicating if an approximation scheme shall be used |
| 476 | * for printing probabilities |
| 477 | */ |
| 478 | public void setApproximate(boolean approximate) { |
| 479 | this.approximate = approximate; |
| 480 | } |
| 481 | } |