1 | /** |
2 | * |
3 | */ |
4 | package de.uka.ipd.sdq.probfunction.math.impl; |
5 | |
6 | import java.util.ArrayList; |
7 | import java.util.Collections; |
8 | import java.util.Iterator; |
9 | import java.util.List; |
10 | |
11 | import de.uka.ipd.sdq.probfunction.math.IProbabilityMassFunction; |
12 | import de.uka.ipd.sdq.probfunction.math.IRandomGenerator; |
13 | import de.uka.ipd.sdq.probfunction.math.ISample; |
14 | import de.uka.ipd.sdq.probfunction.math.IUnit; |
15 | import de.uka.ipd.sdq.probfunction.math.exception.DifferentDomainsException; |
16 | import de.uka.ipd.sdq.probfunction.math.exception.DomainNotNumbersException; |
17 | import de.uka.ipd.sdq.probfunction.math.exception.InvalidSampleValueException; |
18 | import de.uka.ipd.sdq.probfunction.math.exception.ProbabilitySumNotOneException; |
19 | import de.uka.ipd.sdq.probfunction.math.exception.UnitNameNotSetException; |
20 | import de.uka.ipd.sdq.probfunction.math.exception.UnitNotSetException; |
21 | import de.uka.ipd.sdq.probfunction.math.exception.UnorderedDomainException; |
22 | import de.uka.ipd.sdq.probfunction.math.util.MathTools; |
23 | |
24 | /** |
25 | * @author Ihssane |
26 | * |
27 | */ |
28 | public class ProbabilityMassFunctionImpl extends ProbabilityFunctionImpl |
29 | implements |
30 | IProbabilityMassFunction { |
31 | |
32 | private List<ISample> samples; |
33 | |
34 | private enum Operation { |
35 | ADD, SUB, MULT, DIV, SHIFT, STRETCH |
36 | } |
37 | |
38 | protected ProbabilityMassFunctionImpl(List<ISample> samples, IUnit unit, |
39 | boolean hasOrderedDomain, boolean isInFrequencyDomain, |
40 | IRandomGenerator generator) { |
41 | super(unit, hasOrderedDomain, isInFrequencyDomain); |
42 | Collections.sort(samples, MathTools.getSampleComparator()); |
43 | this.samples = samples; |
44 | |
45 | randomGenerator = generator; |
46 | } |
47 | |
48 | private IProbabilityMassFunction performOperation(Operation op, |
49 | IProbabilityMassFunction pmf1, IProbabilityMassFunction pmf2) { |
50 | List<ISample> resultList = new ArrayList<ISample>(); |
51 | |
52 | Iterator<ISample> iterator = pmf2.getSamples().iterator(); |
53 | for (ISample s1 : pmf1.getSamples()) { |
54 | ISample s2 = iterator.next(); |
55 | double result; |
56 | switch (op) { |
57 | case ADD : |
58 | result = s1.getProbability() + s2.getProbability(); |
59 | break; |
60 | case SUB : |
61 | result = s1.getProbability() - s2.getProbability(); |
62 | break; |
63 | case MULT : |
64 | result = s1.getProbability() * s2.getProbability(); |
65 | break; |
66 | case DIV : |
67 | result = s1.getProbability() / s2.getProbability(); |
68 | break; |
69 | default : |
70 | result = 0.0; |
71 | break; |
72 | } |
73 | resultList.add(pfFactory.createSample(s1.getValue(), result)); |
74 | } |
75 | return pfFactory.createProbabilityMassFunction(resultList, this |
76 | .getUnit(), hasOrderedDomain()); |
77 | } |
78 | |
79 | public IProbabilityMassFunction add(IProbabilityMassFunction pmf) |
80 | throws DifferentDomainsException { |
81 | if (!this.haveSameDomain(pmf)) |
82 | throw new DifferentDomainsException(); |
83 | |
84 | return performOperation(Operation.ADD, this, pmf); |
85 | } |
86 | |
87 | public IProbabilityMassFunction mult(IProbabilityMassFunction pmf) |
88 | throws DifferentDomainsException { |
89 | if (!this.haveSameDomain(pmf)) |
90 | throw new DifferentDomainsException(); |
91 | |
92 | return performOperation(Operation.MULT, this, pmf); |
93 | } |
94 | |
95 | public IProbabilityMassFunction scale(double scalar) { |
96 | |
97 | List<ISample> newList = new ArrayList<ISample>(); |
98 | for (ISample sample : samples) |
99 | newList.add(pfFactory.createSample(sample.getValue(), sample |
100 | .getProbability() |
101 | * scalar)); |
102 | |
103 | return pfFactory.createProbabilityMassFunction(newList, this.getUnit(), |
104 | this.hasOrderedDomain()); |
105 | } |
106 | |
107 | public IProbabilityMassFunction div(IProbabilityMassFunction pmf) |
108 | throws DifferentDomainsException { |
109 | if (!this.haveSameDomain(pmf)) |
110 | throw new DifferentDomainsException(); |
111 | |
112 | return performOperation(Operation.DIV, this, pmf); |
113 | } |
114 | |
115 | public IProbabilityMassFunction sub(IProbabilityMassFunction pmf) |
116 | throws DifferentDomainsException { |
117 | if (!this.haveSameDomain(pmf)) |
118 | throw new DifferentDomainsException(); |
119 | |
120 | return performOperation(Operation.SUB, this, pmf); |
121 | } |
122 | |
123 | /** |
124 | * Adjust this PMF and the passed PMF so that both have the same domain (probabilities |
125 | * for the same values). |
126 | * |
127 | * Assumes that the samples are sorted. |
128 | * |
129 | * FIXME: The implementation is yet incomplete and needs to be revised. |
130 | * |
131 | * @param otherPMF |
132 | * @return the adjusted other PMF otherPMF |
133 | */ |
134 | /* private IProbabilityMassFunction makeSameDomain (IProbabilityMassFunction otherPMF){ |
135 | |
136 | List<ISample> valuesThis = this.samples; |
137 | List<ISample> valuesOther = otherPMF.getSamples(); |
138 | |
139 | if (valuesThis.size() == 0 && valuesOther.size() == 0) |
140 | return otherPMF; |
141 | |
142 | if (valuesThis.size() == 0 && valuesOther.size() ==0){ |
143 | throw new RuntimeException("Cannot adjust the domains of an empty PMF."); |
144 | } |
145 | |
146 | // test for integer and double? |
147 | // can also be Strings.. need to be the same type. |
148 | // If different, we need to be able to cast one number into another. |
149 | boolean isIntegerThis = true; |
150 | boolean isIntegerOther = true; |
151 | |
152 | //store values to add, the add after iterating, then sort. |
153 | List<ISample> valuesToAddToThis = new ArrayList<ISample>(valuesOther.size()); |
154 | List<ISample> valuesToAddToOther = new ArrayList<ISample>(valuesThis.size()); |
155 | |
156 | Comparator<ISample> comparator = new ValueBasedComparator(); |
157 | |
158 | //the following assumes a sorted list, I am not sure whether that is already guaranteed |
159 | Collections.sort(valuesThis, comparator); |
160 | Collections.sort(valuesOther, comparator); |
161 | |
162 | valuesToAddToThis = getAllMissingSamples(valuesThis, valuesOther); |
163 | valuesToAddToOther = getAllMissingSamples(valuesOther, valuesThis); |
164 | |
165 | |
166 | valuesThis.addAll(valuesToAddToThis); |
167 | valuesOther.addAll(valuesToAddToOther); |
168 | Collections.sort(valuesThis, comparator); |
169 | Collections.sort(valuesOther, comparator); |
170 | |
171 | //TODO: align sample types if possible. |
172 | |
173 | otherPMF.setSamples(valuesOther); |
174 | return otherPMF; |
175 | |
176 | }*/ |
177 | |
178 | |
179 | /** |
180 | * Add all values from valuesOther to valuesThis that have not been there before. |
181 | * Afterwards, valuesThis should contain all values of valuesOther (and possibly more) |
182 | * @param valuesThis |
183 | * @param valuesOther |
184 | * @return |
185 | */ |
186 | /*private List<ISample> getAllMissingSamples(List<ISample> valuesThis, |
187 | List<ISample> valuesOther) { |
188 | |
189 | List<ISample> result = new ArrayList<ISample>(); |
190 | |
191 | Comparator<ISample> comparator = new ValueBasedComparator(); |
192 | int indexOther = 0; |
193 | ISample sampleOther = valuesOther.get(0); |
194 | |
195 | for (int indexThis = 0; indexThis < valuesThis.size(); indexThis++) { |
196 | |
197 | ISample sampleThis = valuesThis.get(indexThis); |
198 | |
199 | int compareResult = comparator.compare(sampleThis, sampleOther); |
200 | if (compareResult < 0){ |
201 | //sampleThis is smaller -> continue |
202 | } else if (compareResult > 0){ |
203 | //sampleThis is larger -> add the other sample to results |
204 | result.add(new SampleImpl(sampleOther.getValue(), 0)); |
205 | sampleOther = valuesOther.get(indexOther); |
206 | indexOther++; |
207 | //sampleThis needs to be compared again in the next iteration of the for loop. |
208 | indexThis--; |
209 | } else { |
210 | //Both have a sample for the same values, nothing to be done |
211 | sampleOther = valuesOther.get(indexOther); |
212 | indexOther++; |
213 | } |
214 | |
215 | if (indexOther < valuesOther.size()){ |
216 | sampleOther = valuesOther.get(indexOther); |
217 | } else { |
218 | break; |
219 | } |
220 | |
221 | } |
222 | |
223 | //maybe there are still values not addressed in list valuesOther |
224 | for (int i = indexOther; i < valuesOther.size(); i++) { |
225 | result.add(new SampleImpl(valuesOther.get(i).getValue(),0)); |
226 | } |
227 | |
228 | return result; |
229 | }*/ |
230 | |
231 | public boolean haveSameDomain(IProbabilityMassFunction pmf) { |
232 | List<ISample> values1 = this.samples; |
233 | List<ISample> values2 = pmf.getSamples(); |
234 | |
235 | if (values1.size() != values2.size()) |
236 | return false; |
237 | if (values1.size() == 0 && values2.size() == 0) |
238 | return true; |
239 | if (!(values1.get(0).getValue().getClass().isInstance(values2.get(0) |
240 | .getValue()))) |
241 | // if (new ValueBasedComparator().compare(values1.get(0),values2.get(0))!=0) |
242 | return false; |
243 | |
244 | boolean result = true; |
245 | Iterator<ISample> iterator = values2.iterator(); |
246 | for (ISample o : values1) { |
247 | //use comparator here to really compare the value, not just the object. |
248 | if (!o.getValue().equals((iterator.next().getValue()))) |
249 | result = false; |
250 | } |
251 | |
252 | return result; |
253 | } |
254 | |
255 | public IProbabilityMassFunction getFourierTramsform() { |
256 | // TODO Auto-generated method stub |
257 | return null; |
258 | } |
259 | |
260 | public IProbabilityMassFunction getInverseFourierTransform() { |
261 | // TODO Auto-generated method stub |
262 | return null; |
263 | } |
264 | |
265 | /** |
266 | * @return the samples |
267 | */ |
268 | public List<ISample> getSamples() { |
269 | return new ArrayList<ISample>(samples); |
270 | } |
271 | |
272 | /** |
273 | * @param samples |
274 | * the samples to set |
275 | * @throws ProbabilitySumNotOneException |
276 | */ |
277 | public void setSamples(List<ISample> samples) { |
278 | // if (!MathTools.equalsDouble(MathTools.sumOfSamples(samples), 1.0)) |
279 | // throw new ProbabilitySumNotOneException(); |
280 | if (samples.size() > 1 |
281 | && samples.get(0).getValue() instanceof Comparable) |
282 | Collections.sort(samples, MathTools.getSampleComparator()); |
283 | this.samples = samples; |
284 | } |
285 | |
286 | public double getArithmeticMeanValue() throws DomainNotNumbersException { |
287 | double mean = 0; |
288 | for (ISample sample : this.samples) { |
289 | Object value = sample.getValue(); |
290 | if (value instanceof Double) { |
291 | Double d = (Double) value; |
292 | mean += d * sample.getProbability(); |
293 | } else if (value instanceof Integer) { |
294 | Integer i = (Integer) value; |
295 | mean += i * sample.getProbability(); |
296 | } else if (value instanceof Long) { |
297 | Long i = (Long) value; |
298 | mean += i * sample.getProbability(); |
299 | } else if (value instanceof Float) { |
300 | Float i = (Float) value; |
301 | mean += i * sample.getProbability(); |
302 | } else { |
303 | throw new DomainNotNumbersException(); |
304 | } |
305 | } |
306 | return mean; |
307 | } |
308 | |
309 | public Object getMedian() throws UnorderedDomainException { |
310 | return getPercentile(50); |
311 | } |
312 | |
313 | public Object getPercentile(int p) throws IndexOutOfBoundsException, |
314 | UnorderedDomainException { |
315 | if (!hasOrderedDomain()) |
316 | throw new UnorderedDomainException(); |
317 | if (p < 0 || p > 100) |
318 | throw new IndexOutOfBoundsException(); |
319 | |
320 | int rank = (int) Math.floor((p * (samples.size() + 1.0)) / 100.0); |
321 | return samples.get(rank).getValue(); |
322 | } |
323 | |
324 | public Object drawSample() { |
325 | Object result = 0.0; |
326 | List<Double> intervals = MathTools |
327 | .computeCumulativeProbabilities(getProbabilities()); |
328 | |
329 | double random = randomGenerator.random(); |
330 | for (int j = 0; j < intervals.size(); j++) |
331 | if (random < intervals.get(j)) { |
332 | result = samples.get(j).getValue(); |
333 | break; |
334 | } |
335 | return result; |
336 | } |
337 | |
338 | private List<Double> getProbabilities() { |
339 | List<Double> prob = new ArrayList<Double>(); |
340 | for (ISample s : samples) |
341 | prob.add(s.getProbability()); |
342 | return prob; |
343 | } |
344 | |
345 | /** |
346 | * @see java.lang.Object#equals(java.lang.Object) |
347 | */ |
348 | @Override |
349 | public boolean equals(Object obj) { |
350 | if (((IProbabilityMassFunction) obj).getSamples().size() != this.samples |
351 | .size()) |
352 | return false; |
353 | |
354 | boolean result = true; |
355 | Iterator<ISample> iterator = ((IProbabilityMassFunction) obj) |
356 | .getSamples().iterator(); |
357 | for (ISample o : this.getSamples()) { |
358 | if (!o.equals(iterator.next())) |
359 | result = false; |
360 | } |
361 | |
362 | return result; |
363 | } |
364 | |
365 | public double getProbabilitySum() { |
366 | double sum = 0; |
367 | for (ISample sample : samples) { |
368 | sum += sample.getProbability(); |
369 | } |
370 | return sum; |
371 | } |
372 | |
373 | public void checkConstrains() throws ProbabilitySumNotOneException, |
374 | InvalidSampleValueException, UnitNotSetException, |
375 | UnitNameNotSetException { |
376 | if (!MathTools.equalsDouble(getProbabilitySum(), 1.0)) |
377 | throw new ProbabilitySumNotOneException(); |
378 | |
379 | //TODO: Fix this when units work again |
380 | //if (getUnit() == null) |
381 | // throw new UnitNotSetException(); |
382 | //if (getUnit().getUnitName() == null) |
383 | // throw new UnitNameNotSetException(); |
384 | |
385 | for (ISample sample : this.samples) { |
386 | Object value = sample.getValue(); |
387 | if (value == null) |
388 | throw new InvalidSampleValueException(); |
389 | if (sample.getProbability() < 0.0 || sample.getProbability() > 1.0) |
390 | throw new InvalidSampleValueException(); |
391 | } |
392 | } |
393 | |
394 | public IProbabilityMassFunction getCumulativeFunction() { |
395 | List<Double> newProb = MathTools |
396 | .computeCumulativeProbabilities(getProbabilities()); |
397 | List<ISample> newSamples = new ArrayList<ISample>(); |
398 | int index = 0; |
399 | for (Double d : newProb) { |
400 | newSamples.add(pfFactory.createSample( |
401 | samples.get(index).getValue(), d)); |
402 | index++; |
403 | } |
404 | |
405 | IProbabilityMassFunction pmf = null; |
406 | pmf = pfFactory.createProbabilityMassFunction(newSamples, this |
407 | .getUnit(), this.hasOrderedDomain()); |
408 | |
409 | return pmf; |
410 | } |
411 | |
412 | public IProbabilityMassFunction shiftDomain(double scalar) |
413 | throws DomainNotNumbersException { |
414 | return transformDomainValues(scalar, Operation.SHIFT); |
415 | } |
416 | |
417 | public IProbabilityMassFunction stretchDomain(double scalar) |
418 | throws DomainNotNumbersException { |
419 | return transformDomainValues(scalar, Operation.STRETCH); |
420 | } |
421 | |
422 | /** |
423 | * Shifts or stretches the domain of the PMF according to the scalar factor |
424 | * given as a parameter. Only works if the sample values are numbers. |
425 | * |
426 | * Tries to preserve the value types. For example, if the factor scalar is |
427 | * an Integer and all sample values are Integer, then the resulting PMF will |
428 | * also contain Integer-samples. |
429 | * |
430 | * Scalar must not be equal to 0 if Operation is stretch. |
431 | * |
432 | * @author Koziolek (kill me) |
433 | * @param scalar |
434 | * the factor to shift or stretch the domain |
435 | * @return |
436 | */ |
437 | private IProbabilityMassFunction transformDomainValues(double scalar, |
438 | Operation op) throws DomainNotNumbersException { |
439 | List<ISample> resultList = new ArrayList<ISample>(); |
440 | |
441 | // determine whether scalar is an int |
442 | // (i.e., zero behind the point; example: 2.0) |
443 | Double factorDouble = scalar; |
444 | Integer factorInteger = factorDouble.intValue(); |
445 | boolean factorIsInteger = (factorDouble == factorInteger.doubleValue()); |
446 | |
447 | for (ISample sample : samples) { |
448 | Object value = sample.getValue(); |
449 | Number resultValue = null; |
450 | // Ok, there are several cases, as we would like to |
451 | // preserve the type of the value. Please beautify |
452 | // this if possible. :) |
453 | switch (op) { |
454 | case SHIFT : |
455 | if (value instanceof Integer && factorIsInteger) { |
456 | resultValue = new Integer((Integer) value |
457 | + factorInteger); |
458 | } else if (value instanceof Long && factorIsInteger) { |
459 | resultValue = new Long((Long) value + factorInteger); |
460 | } else if (value instanceof Number) { |
461 | Number valueNumber = (Number) value; |
462 | resultValue = new Double(valueNumber.doubleValue() |
463 | + factorDouble); |
464 | } else |
465 | throw new DomainNotNumbersException(); |
466 | break; |
467 | case STRETCH : |
468 | if (value instanceof Integer && factorIsInteger) { |
469 | resultValue = new Integer((Integer) value |
470 | * factorInteger); |
471 | } else if (value instanceof Long && factorIsInteger) { |
472 | resultValue = new Long((Long) value * factorInteger); |
473 | } else if (value instanceof Number) { |
474 | Number valueNumber = (Number) value; |
475 | resultValue = new Double(valueNumber.doubleValue() |
476 | * factorDouble); |
477 | } else |
478 | throw new DomainNotNumbersException(); |
479 | break; |
480 | default : |
481 | resultValue = 0.0; |
482 | } |
483 | |
484 | resultList.add(pfFactory.createSample(resultValue, sample |
485 | .getProbability())); |
486 | } |
487 | |
488 | return pfFactory.createProbabilityMassFunction(resultList, this |
489 | .getUnit(), hasOrderedDomain()); |
490 | } |
491 | } |
492 | |
493 | //class ValueBasedComparator implements Comparator<ISample>{ |
494 | // |
495 | // |
496 | // @Override |
497 | // public int compare(ISample arg0, ISample arg1) { |
498 | // Object value0 = arg0.getValue(); |
499 | // Object value1 = arg1.getValue(); |
500 | // |
501 | // if (value0.getClass().isInstance(value1)){ |
502 | // return ((Comparable) value0).compareTo(value1); |
503 | // } else if (value1.getClass().isInstance(value0)){ |
504 | // int result = ((Comparable) value1).compareTo(value0); |
505 | // return result * -1; |
506 | // } else if (value1 instanceof Number && value0 instanceof Number){ |
507 | // Number number0 = (Number)value0; |
508 | // Number number1 = (Number)value1; |
509 | // |
510 | // return new Double(number0.doubleValue()).compareTo(new Double(number1.doubleValue())); |
511 | // } else { |
512 | // throw new DifferentDomainsException(); |
513 | // } |
514 | // } |
515 | // |
516 | //} |
517 | |