1 | package de.uka.ipd.sdq.reliability.solver.pcm2markov; |
2 | |
3 | import java.util.ArrayList; |
4 | import java.util.List; |
5 | import java.util.concurrent.TimeUnit; |
6 | |
7 | import org.apache.log4j.Logger; |
8 | |
9 | import de.uka.ipd.sdq.markov.MarkovChain; |
10 | import de.uka.ipd.sdq.pcm.usagemodel.UsageScenario; |
11 | import de.uka.ipd.sdq.pcmsolver.models.PCMInstance; |
12 | import de.uka.ipd.sdq.pcmsolver.runconfig.PCMSolverWorkflowRunConfiguration; |
13 | import de.uka.ipd.sdq.pcmsolver.visitors.UsageModelVisitor; |
14 | import de.uka.ipd.sdq.reliability.core.MarkovEvaluationType; |
15 | import de.uka.ipd.sdq.reliability.core.MarkovFailureType; |
16 | import de.uka.ipd.sdq.reliability.core.helper.MarkovFailureTypeHelper; |
17 | import de.uka.ipd.sdq.reliability.solver.MarkovSolver; |
18 | import de.uka.ipd.sdq.reliability.solver.sensitivity.MarkovSensitivity; |
19 | |
20 | /** |
21 | * This class has the functionality to perform a complete PCM2Markov |
22 | * transformation. |
23 | * |
24 | * @author brosch |
25 | * |
26 | */ |
27 | public class MarkovTransformation { |
28 | |
29 | /** |
30 | * A logger to give detailed information about the PCM instance |
31 | * transformation. |
32 | */ |
33 | private static Logger logger = Logger.getLogger(MarkovTransformation.class |
34 | .getName()); |
35 | |
36 | /** |
37 | * Provides functionality to manage failure types. |
38 | */ |
39 | private MarkovFailureTypeHelper helper = new MarkovFailureTypeHelper(); |
40 | |
41 | /** |
42 | * Checks whether there exists a next permutation based on the current one |
43 | * (which is given by the resources' current states). Generates the next |
44 | * permutation, if it exists, according to Narayana Pandita's method of |
45 | * systematically generating all permutations, and sets the resources' |
46 | * states accordingly. |
47 | * |
48 | * @param descriptors |
49 | * a list containing the processing resource descriptors |
50 | * @return true, if a new permutation was generated; false otherwise |
51 | */ |
52 | private boolean createNextPermutation( |
53 | final List<ProcessingResourceDescriptor> descriptors) { |
54 | int size = descriptors.size(); |
55 | |
56 | int k = -1; // used for step 1 (see below) |
57 | int l; // used for step 2 (see below) |
58 | MarkovResourceState tmp; // used for step 4 (see below) |
59 | int left; // used for step 4 (see below) |
60 | int right; // used for step 4 (see below) |
61 | |
62 | /* |
63 | * Systematic generation of all permutations according to Narayana |
64 | * Pandita. |
65 | */ |
66 | |
67 | // 1. Find the largest index k such that a[k] < a[k + 1]. If no such |
68 | // index exists the permutation |
69 | // is the last permutation. [wikipedia] |
70 | for (int i = 0; i < size - 1; i++) { |
71 | if (descriptors.get(i).getCurrentState() == MarkovResourceState.NA |
72 | && descriptors.get(i + 1).getCurrentState() == MarkovResourceState.OK) { |
73 | k = i; |
74 | } |
75 | } |
76 | if (k == -1) { |
77 | return false; // no such index exists (i.e., there is no next |
78 | // permutation) - return false |
79 | } |
80 | |
81 | // 2. Find the largest index l such that a[k] < a[l]. Since k + 1 is |
82 | // such an index, l is |
83 | // well defined and satisfies k < l. [wikipedia] |
84 | // Situation so far: a[k] = N/A, a[k+1] = OK. |
85 | // Now look for an l such that l > (k+1), and a[l] = OK. |
86 | l = k + 1; // a[k+1] is definitely OK. We may find another one, though: |
87 | for (int i = k + 2; i < size; i++) { |
88 | if (descriptors.get(i).getCurrentState() == MarkovResourceState.OK) { |
89 | l = i; // new such index found, update l |
90 | } |
91 | } |
92 | |
93 | // 3. Swap a[k] with a[l] [wikipedia] |
94 | // I.e., switch their states. |
95 | descriptors.get(k).switchState(); |
96 | descriptors.get(l).switchState(); |
97 | |
98 | // 4. Reverse the sequence from a[k + 1] up to and including the final |
99 | // element a[n]. [wikipedia] |
100 | for (left = k + 1, right = size - 1; left < right; left++, right--) { |
101 | // swap right and left |
102 | tmp = descriptors.get(left).getCurrentState(); |
103 | descriptors.get(left).setCurrentState( |
104 | descriptors.get(right).getCurrentState()); |
105 | descriptors.get(right).setCurrentState(tmp); |
106 | } |
107 | |
108 | return true; // next permutation successfully generated - return true |
109 | } |
110 | |
111 | /** |
112 | * Retrieves the failure types evaluation mode from a given analysis |
113 | * configuration. |
114 | * |
115 | * @param configuration |
116 | * the analysis configuration |
117 | * @return the evaluation mode |
118 | */ |
119 | private MarkovEvaluationType getEvaluationMode( |
120 | PCMSolverWorkflowRunConfiguration configuration) { |
121 | return MarkovEvaluationType.valueOf(configuration |
122 | .getMarkovEvaluationMode()); |
123 | } |
124 | |
125 | /** |
126 | * Sets the leftmost <i>m</i> resources in the descriptors list to N/A. |
127 | * |
128 | * @param descriptors |
129 | * a list containing the processing resource descriptors |
130 | * @param m |
131 | * the number of leftmost resources that will be set to N/A |
132 | */ |
133 | private void initializeResourceStates( |
134 | final List<ProcessingResourceDescriptor> descriptors, final int m) { |
135 | int size = descriptors.size(); |
136 | // set m leftmost resources to N/A |
137 | for (int i = 0; i < m; i++) { |
138 | descriptors.get(i).setCurrentState(MarkovResourceState.NA); |
139 | } |
140 | // set remaining (n-m) resources to OK |
141 | for (int i = m; i < size; i++) { |
142 | descriptors.get(i).setCurrentState(MarkovResourceState.OK); |
143 | } |
144 | } |
145 | |
146 | /** |
147 | * Checks whether a termination condition - possibly specified by the user - |
148 | * holds. |
149 | * |
150 | * @param configuration |
151 | * configuration properties for the reliability solver workflow |
152 | * @param markovResult |
153 | * results of the PCM2MarkovTransformation |
154 | * @param startTimeMs |
155 | * indicates when the process started. This needs to be a value |
156 | * in milliseconds determined by having called |
157 | * System.currentTimeMillis(). |
158 | * @return true, if a termination condition holds; false otherwise |
159 | */ |
160 | private boolean isStopConditionReached( |
161 | final PCMSolverWorkflowRunConfiguration configuration, |
162 | final MarkovTransformationResult markovResult, |
163 | final long startTimeMs) { |
164 | |
165 | // check solving time limit |
166 | if (configuration.isSolvingTimeLimitEnabled()) { |
167 | // solving time limit reached? |
168 | if (System.currentTimeMillis() - startTimeMs >= configuration |
169 | .getSolvingTimeLimit() * 1000) { |
170 | // yes, stop condition holds - return true |
171 | logger.info("Maximal solving time (" |
172 | + configuration.getSolvingTimeLimit() |
173 | + " seconds) reached - stopping!"); |
174 | return true; |
175 | } |
176 | } |
177 | |
178 | // check limit for the number of evaluated physical system states |
179 | if (configuration.isNumberOfEvaluatedSystemStatesEnabled()) { |
180 | // maximum number of system states to be evaluated reached? |
181 | if (markovResult.getPhysicalStateEvaluationCount() == configuration |
182 | .getNumberOfEvaluatedSystemStates()) { |
183 | // yes, stop condition holds - return true |
184 | logger.info("Maximal number of evaluated system states (" |
185 | + configuration.getNumberOfEvaluatedSystemStates() |
186 | + ") reached - stopping!"); |
187 | return true; |
188 | } |
189 | } |
190 | |
191 | // check limit for the number of exact decimal places |
192 | if (configuration.isNumberOfExactDecimalPlacesEnabled()) { |
193 | // required number of exact decimal places reached? |
194 | if (markovResult.hasRequiredAccuracy(configuration |
195 | .getNumberOfExactDecimalPlaces())) { |
196 | // yes, stop condition holds - return true |
197 | logger.info("Required number of exact decimal places (" |
198 | + configuration.getNumberOfExactDecimalPlaces() |
199 | + ") reached - stopping!"); |
200 | return true; |
201 | } |
202 | } |
203 | |
204 | // no stop condition holds - return false |
205 | return false; |
206 | } |
207 | |
208 | /** |
209 | * Solves all parametric dependencies within a given PCM instance. |
210 | * |
211 | * @param markovSource |
212 | * state information required during the PCM2MarkovTransformation |
213 | * @param scenario |
214 | * the usage scenario to evaluate |
215 | */ |
216 | private void runDSolver(final UsageScenario scenario, |
217 | final MarkovTransformationSource markovSource) { |
218 | |
219 | logger.debug("Resolving parametric dependencies."); |
220 | // Record the time consumed for solving parametric dependencies: |
221 | long startTime = System.nanoTime(); |
222 | |
223 | // The parametric dependencies are solved by using a visitor: |
224 | UsageModelVisitor visitor = new UsageModelVisitor(markovSource |
225 | .getModel()); |
226 | |
227 | // Solve the PCM instance using the visitor: |
228 | visitor.doSwitch(scenario.getScenarioBehaviour_UsageScenario()); |
229 | |
230 | // Let the user know about the time consumed: |
231 | long stopTime = System.nanoTime(); |
232 | long duration = TimeUnit.NANOSECONDS.toMillis(stopTime - startTime); |
233 | logger.info("Solved parametric dependencies: " + duration + " ms"); |
234 | } |
235 | |
236 | /** |
237 | * Transforms the given PCM instance into a Markov Chain instance. The PCM |
238 | * instance is assumed to have all parametric dependencies solved. |
239 | * |
240 | * @param configuration |
241 | * configuration properties for the reliability solver workflow |
242 | * @param scenario |
243 | * the UsageScenario to evaluate |
244 | * @param markovSource |
245 | * state information required during the PCM2MarkovTransformation |
246 | * @param markovResult |
247 | * results of the PCM2MarkovTransformation |
248 | * @return true if the transformation has stopped before completion, such |
249 | * that the result has to be approximated |
250 | */ |
251 | private boolean runPcm2Markov( |
252 | final PCMSolverWorkflowRunConfiguration configuration, |
253 | final UsageScenario scenario, |
254 | final MarkovTransformationSource markovSource, |
255 | final MarkovTransformationResult markovResult) { |
256 | |
257 | logger.debug("Transforming PCM model into analysis model."); |
258 | |
259 | // Declare the result variable: |
260 | boolean approximate = false; |
261 | |
262 | // Record the time consumed for creating the Markov Chain instance: |
263 | long startTime = System.nanoTime(); |
264 | |
265 | // Check for the requested state handling strategy: |
266 | if (!configuration.isIterationOverPhysicalSystemStatesEnabled()) { |
267 | // Run a single Markov transformation according to the |
268 | // "always ask" strategy: |
269 | runPcm2MarkovSingle(configuration, scenario, markovSource, |
270 | markovResult); |
271 | } else { |
272 | // Repeat the transformation for all physical system states: |
273 | approximate = runPcm2MarkovIteratively(configuration, scenario, |
274 | markovSource, markovResult); |
275 | } |
276 | |
277 | // Let the user know about the time consumed: |
278 | long stopTime = System.nanoTime(); |
279 | long duration = TimeUnit.NANOSECONDS.toMillis(stopTime - startTime); |
280 | logger.info("Finished Markov transformation: " + duration + " ms"); |
281 | if (configuration.isIterationOverPhysicalSystemStatesEnabled()) { |
282 | logger.info("Number of evaluated physical system states: " |
283 | + markovResult.getPhysicalStateEvaluationCount() |
284 | + " out of " |
285 | + markovResult.getNumberOfPhysicalSystemStates()); |
286 | } |
287 | |
288 | // Return the result: |
289 | return approximate; |
290 | } |
291 | |
292 | /** |
293 | * Runs the PCM 2 Markov transformation for all possible classes, or until |
294 | * at least one of the possible termination conditions holds. |
295 | * |
296 | * @param configuration |
297 | * configuration properties for the reliability solver workflow |
298 | * @param scenario |
299 | * the UsageScenario to evaluate |
300 | * @param markovSource |
301 | * state information required during the PCM2MarkovTransformation |
302 | * @param markovResult |
303 | * results of the PCM2MarkovTransformation |
304 | * @return true if the transformation has stopped before completion, such |
305 | * that the result has to be approximated |
306 | */ |
307 | private boolean runPcm2MarkovIteratively( |
308 | final PCMSolverWorkflowRunConfiguration configuration, |
309 | final UsageScenario scenario, |
310 | final MarkovTransformationSource markovSource, |
311 | final MarkovTransformationResult markovResult) { |
312 | |
313 | // Remember the start time of the transformation: |
314 | long startTimeMs = System.currentTimeMillis(); |
315 | |
316 | // Some short notations: |
317 | List<ProcessingResourceDescriptor> descriptors = markovSource |
318 | .getUnreliableResourceDescriptors(); |
319 | final int size = descriptors.size(); |
320 | |
321 | /* |
322 | * Now, for all n resources: Let m out of n (for m in 0..(n-1)) |
323 | * resources fail in an iteration, considering first the resources that |
324 | * are most likely to fail, moving on to resources that are less likely |
325 | * to fail, finally considering the resources that are least likely to |
326 | * fail. |
327 | */ |
328 | for (int permutationClass = 0; permutationClass <= size; permutationClass++) { |
329 | |
330 | /* |
331 | * Consider the current permutation first, where the m leftmost |
332 | * resources are set to N/A, while the remaining ones are OK. |
333 | */ |
334 | |
335 | // set m leftmost out of n resources total to N/A state |
336 | initializeResourceStates(descriptors, permutationClass); |
337 | |
338 | // markovSource.printCurrentResourceStates(); |
339 | |
340 | // Evaluate the physical system state: |
341 | runPcm2MarkovSingle(configuration, scenario, markovSource, |
342 | markovResult); |
343 | |
344 | // Check if any of the stop conditions is reached: |
345 | if (isStopConditionReached(configuration, markovResult, startTimeMs)) { |
346 | return true; |
347 | } |
348 | |
349 | /* |
350 | * Now generate all other permutations where m out of n |
351 | * ("n choose m") resources are N/A. |
352 | */ |
353 | for (;;) { |
354 | |
355 | // see if there's a next permutation |
356 | if (!createNextPermutation(descriptors)) { |
357 | break; // there's no new permutation - leave inner for loop |
358 | } |
359 | |
360 | // markovSource.printCurrentResourceStates(); |
361 | |
362 | /* |
363 | * At this point, we have a new permutation, i.e. system state. |
364 | * It was generated via the createNextPermutation(descriptors) |
365 | * call above. |
366 | */ |
367 | |
368 | // Evaluate the physical system state: |
369 | runPcm2MarkovSingle(configuration, scenario, markovSource, |
370 | markovResult); |
371 | |
372 | // Check if any of the stop conditions is reached: |
373 | if (isStopConditionReached(configuration, markovResult, |
374 | startTimeMs)) { |
375 | return true; |
376 | } |
377 | } // end for: generation of all "n choose m" permutations |
378 | } // end for: m out of n resources N/A |
379 | |
380 | // The whole transformation has been performed without reaching any stop |
381 | // conditions: |
382 | return false; |
383 | } |
384 | |
385 | /** |
386 | * Evaluates a single physical system state. |
387 | * |
388 | * @param configuration |
389 | * configuration properties for the reliability solver workflow |
390 | * @param scenario |
391 | * the UsageScenario to evaluate |
392 | * @param markovSource |
393 | * state information required during the PCM2MarkovTransformation |
394 | * @param markovResult |
395 | * results of the PCM2MarkovTransformation |
396 | */ |
397 | private void runPcm2MarkovSingle( |
398 | final PCMSolverWorkflowRunConfiguration configuration, |
399 | final UsageScenario scenario, |
400 | final MarkovTransformationSource markovSource, |
401 | final MarkovTransformationResult markovResult) { |
402 | |
403 | // Retrieve the MarkovSolver singleton instance: |
404 | MarkovSolver solver = MarkovSolver.getSingletonInstance(); |
405 | |
406 | // If Markov statistics printing is switched on, do the first physical |
407 | // state evaluation without Markov chain reduction, and count the number |
408 | // of resulting Markov states: |
409 | boolean countStates = configuration.isPrintMarkovStatistics() |
410 | && (markovResult.getPhysicalStateEvaluationCount() == 0); |
411 | |
412 | // calculate current state probability |
413 | double physicalStateProbability = 1.0; |
414 | if (configuration.isIterationOverPhysicalSystemStatesEnabled()) { |
415 | for (ProcessingResourceDescriptor descriptor : markovSource |
416 | .getUnreliableResourceDescriptors()) { |
417 | physicalStateProbability *= descriptor |
418 | .getStateProbability(descriptor.getCurrentState()); |
419 | } |
420 | } |
421 | |
422 | // The Markov Chain instance is created by using a visitor: |
423 | // MarkovUsageModelVisitor visitor = new MarkovUsageModelVisitor( |
424 | // markovSource, !countStates); |
425 | MarkovUsageModelVisitor visitor = new MarkovUsageModelVisitor( |
426 | markovSource, getEvaluationMode(configuration), !configuration |
427 | .isIterationOverPhysicalSystemStatesEnabled(), |
428 | configuration.isMarkovModelReductionEnabled(), configuration |
429 | .isMarkovModelTracesEnabled()); |
430 | |
431 | // Create the Markov Chain instance using the visitor: |
432 | MarkovChain resultChain = (MarkovChain) visitor.doSwitch(scenario |
433 | .getScenarioBehaviour_UsageScenario()); |
434 | |
435 | // Display information to the user: |
436 | if (countStates) { |
437 | logger |
438 | .info("Number of Markov states per evaluated physical system state:\t" |
439 | + resultChain.getStates().size()); |
440 | logger |
441 | .info("Number of Markov transitions per evaluated physical system state:\t" |
442 | + resultChain.getTransitions().size()); |
443 | } |
444 | |
445 | // Solve the Markov Chain, and add the result to the overall success |
446 | // probability, weighted by the probability of the current resource |
447 | // state combination: |
448 | double[][] markovProbabilityMatrix = solver.solve(resultChain); |
449 | |
450 | // Add the results of this evaluation: |
451 | markovResult.addPhysicalStateResults(resultChain, |
452 | markovProbabilityMatrix, physicalStateProbability); |
453 | } |
454 | |
455 | /** |
456 | * Transforms a PCM instance into a Markov Chain instance. |
457 | * |
458 | * The transformation is performed in two steps. In the first step, |
459 | * parametric dependencies within the PCM instance are solved using the |
460 | * dependency solver. The resulting PCM instance is then transformed into a |
461 | * Markov Chain. |
462 | * |
463 | * @param model |
464 | * the input PCM instance |
465 | * @param configuration |
466 | * configuration properties for the reliability solver workflow |
467 | * @param scenario |
468 | * the usage scenario to transform |
469 | * @return the transformation results |
470 | */ |
471 | private MarkovTransformationResult runScenarioTransform( |
472 | final PCMInstance model, |
473 | final PCMSolverWorkflowRunConfiguration configuration, |
474 | final UsageScenario scenario) { |
475 | |
476 | // Initialize failure type information: |
477 | List<MarkovFailureType> failureTypes = helper.getFailureTypes( |
478 | getEvaluationMode(configuration), model.getRepositories(), |
479 | model.getResourceEnvironment(), model.getSystem()); |
480 | |
481 | // Initialize state information: |
482 | MarkovTransformationSource markovSource = new MarkovTransformationSource( |
483 | model, true); |
484 | MarkovTransformationResult markovResult = new MarkovTransformationResult( |
485 | configuration, markovSource, scenario, failureTypes); |
486 | boolean approximate = false; |
487 | |
488 | // As a first step, solve parametric dependencies of the PCM instance: |
489 | try { |
490 | runDSolver(scenario, markovSource); |
491 | } catch (Exception e) { |
492 | |
493 | // The parametric dependencies could not be solved: |
494 | logger |
495 | .error("Solving of parametric dependencies caused exception: " |
496 | + e.getMessage() + " [" + e.getClass() + "]"); |
497 | e.printStackTrace(); |
498 | |
499 | return null; |
500 | } |
501 | |
502 | // Second, the PCM instance is transformed into a Markov Chain instance |
503 | // and solved for determining system reliability: |
504 | try { |
505 | approximate = runPcm2Markov(configuration, scenario, markovSource, |
506 | markovResult); |
507 | } catch (Exception e) { |
508 | logger.error("PCM 2 Markov transformation caused exception: " |
509 | + e.getMessage() + " [" + e.getClass() + "]"); |
510 | e.printStackTrace(); |
511 | } |
512 | |
513 | markovResult.setApproximate(approximate); |
514 | |
515 | // Return the transformation results: |
516 | return markovResult; |
517 | } |
518 | |
519 | /** |
520 | * Performs a PCM2Markov transformation. |
521 | * |
522 | * @param model |
523 | * the input PCM instance |
524 | * @param configuration |
525 | * configuration properties for the reliability solver workflow |
526 | * @param sensitivity |
527 | * sensitivity analysis configuration |
528 | * @return the transformation results |
529 | */ |
530 | public List<MarkovTransformationResult> runTransform( |
531 | final PCMInstance model, |
532 | final PCMSolverWorkflowRunConfiguration configuration, |
533 | final MarkovSensitivity sensitivity) { |
534 | if (sensitivity != null) { |
535 | return runTransformIteratively(model, configuration, sensitivity); |
536 | } else { |
537 | return runTransformSingle(model, configuration); |
538 | } |
539 | } |
540 | |
541 | /** |
542 | * Transforms a PCM instance into a Markov Chain instance. |
543 | * |
544 | * The transformation is repeated to enable sensitivity analysis. |
545 | * |
546 | * @param model |
547 | * the PCM instance |
548 | * @param configuration |
549 | * configuration properties for the reliability solver workflow |
550 | * @param sensitivity |
551 | * sensitivity analysis configuration |
552 | * @return the transformation results (only the results of the last step in |
553 | * the sensitivity analysis are returned) |
554 | */ |
555 | private List<MarkovTransformationResult> runTransformIteratively( |
556 | final PCMInstance model, |
557 | final PCMSolverWorkflowRunConfiguration configuration, |
558 | final MarkovSensitivity sensitivity) { |
559 | int sensitivityStepCount = 0; |
560 | List<MarkovTransformationResult> markovResults = null; |
561 | sensitivity.initialize(model); |
562 | PCMInstance step = sensitivity.getNextModel(); |
563 | while (step != null) { |
564 | sensitivityStepCount++; |
565 | logger.info("Starting sensitivity analysis step " |
566 | + sensitivityStepCount + "..."); |
567 | markovResults = runTransformSingle(step, configuration); |
568 | sensitivity.logResults(markovResults); |
569 | logger.info("Sensitivity analysis step " + sensitivityStepCount |
570 | + " completed"); |
571 | step = sensitivity.getNextModel(); |
572 | } |
573 | sensitivity.finalize(); |
574 | return markovResults; |
575 | } |
576 | |
577 | /** |
578 | * Transforms a PCM instance into a Markov Chain instance. |
579 | * |
580 | * The transformation is performed in two steps. In the first step, |
581 | * parametric dependencies within the PCM instance are solved using the |
582 | * dependency solver. The resulting PCM instance is then transformed into a |
583 | * Markov Chain. |
584 | * |
585 | * @param model |
586 | * the input PCM instance |
587 | * @param configuration |
588 | * configuration properties for the reliability solver workflow |
589 | * @return the transformation results |
590 | */ |
591 | private List<MarkovTransformationResult> runTransformSingle( |
592 | final PCMInstance model, |
593 | final PCMSolverWorkflowRunConfiguration configuration) { |
594 | |
595 | // Declare result list: |
596 | ArrayList<MarkovTransformationResult> resultList = new ArrayList<MarkovTransformationResult>(); |
597 | |
598 | // Transform all usage scenarios individually: |
599 | for (UsageScenario scenario : model.getUsageModel() |
600 | .getUsageScenario_UsageModel()) { |
601 | resultList |
602 | .add(runScenarioTransform(model, configuration, scenario)); |
603 | } |
604 | |
605 | // Return all transformation results: |
606 | return resultList; |
607 | } |
608 | } |