| 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 | |