View Javadoc
1   /*
2    * (c) Copyright 2006-2020 by rapiddweller GmbH & Volker Bergmann. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, is permitted under the terms of the
6    * GNU General Public License.
7    *
8    * For redistributing this software or a derivative work under a license other
9    * than the GPL-compatible Free Software License as defined by the Free
10   * Software Foundation or approved by OSI, you must first obtain a commercial
11   * license to this software product from rapiddweller GmbH & Volker Bergmann.
12   *
13   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14   * WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
15   * REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
17   * HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24   * POSSIBILITY OF SUCH DAMAGE.
25   */
26  
27  package com.rapiddweller.benerator.distribution;
28  
29  import com.rapiddweller.benerator.Generator;
30  import com.rapiddweller.benerator.NonNullGenerator;
31  import com.rapiddweller.benerator.primitive.number.AbstractNonNullNumberGenerator;
32  import com.rapiddweller.benerator.sample.ConstantGenerator;
33  import com.rapiddweller.benerator.sample.SampleGenerator;
34  import com.rapiddweller.benerator.util.GeneratorUtil;
35  import com.rapiddweller.common.Converter;
36  import com.rapiddweller.common.converter.ConverterManager;
37  
38  import java.util.List;
39  import java.util.Random;
40  
41  /**
42   * {@link Distribution} implementation which uses the inverse of a probability function integral
43   * for efficiently generating numbers with a given probability distribution.
44   * See <a href="http://www.stat.wisc.edu/~larget/math496/random2.html">Random
45   * Number Generation from Non-uniform Distributions</a>.<br/><br/>
46   * Created: 12.03.2010 13:31:16
47   *
48   * @author Volker Bergmann
49   * @since 0.6.0
50   */
51  public abstract class CumulativeDistributionFunction implements Distribution {
52  
53    /**
54     * Cumulative probability double.
55     *
56     * @param value the value
57     * @return the double
58     */
59    public abstract double cumulativeProbability(double value);
60  
61    /**
62     * Inverse double.
63     *
64     * @param probability the probability
65     * @return the double
66     */
67    public abstract double inverse(double probability);
68  
69    @Override
70    public <T> Generator<T> applyTo(Generator<T> source, boolean unique) {
71      if (unique) {
72        throw new IllegalArgumentException(this + " cannot generate unique values");
73      }
74      List<T> allProducts = GeneratorUtil.allProducts(source);
75      if (allProducts.size() == 1) {
76        return new ConstantGenerator<>(allProducts.get(0));
77      }
78      return new SampleGenerator<>(source.getGeneratedType(), this, unique, allProducts);
79    }
80  
81    @Override
82    public <T extends Number> NonNullGenerator<T> createNumberGenerator(
83        Class<T> numberType, T min, T max, T granularity, boolean unique) {
84      if (unique) {
85        throw new IllegalArgumentException(this + " cannot generate unique values");
86      }
87      return new IPINumberGenerator<>(this, numberType, min, max, granularity);
88    }
89  
90    @Override
91    public String toString() {
92      return getClass().getSimpleName();
93    }
94  
95    /**
96     * Generates numbers according to an {@link CumulativeDistributionFunction}.<br/><br/>
97     * Created: 12.03.2010 14:37:33
98     *
99     * @param <E> the type parameter
100    * @author Volker Bergmann
101    * @since 0.6.0
102    */
103   public static class IPINumberGenerator<E extends Number> extends AbstractNonNullNumberGenerator<E> {
104 
105     private final CumulativeDistributionFunction fcn;
106     private final Random random = new Random();
107     private final Converter<Double, E> converter;
108     private final double minProb;
109     private final double probScale;
110     private final double minD;
111     private double maxD;
112     private final double granularityD;
113 
114     /**
115      * Instantiates a new Ipi number generator.
116      *
117      * @param fcn         the fcn
118      * @param targetType  the target type
119      * @param min         the min
120      * @param max         the max
121      * @param granularity the granularity
122      */
123     public IPINumberGenerator(CumulativeDistributionFunction fcn, Class<E> targetType, E min, E max, E granularity) {
124       super(targetType, min, max, granularity);
125       this.fcn = fcn;
126       this.minD = (min != null ? min.doubleValue() : (max != null ? maxD - 9 : 0));
127       this.maxD = (max != null ? max.doubleValue() : (min != null ? minD + 9 : 0));
128       this.granularityD = granularity.doubleValue();
129       this.minProb = fcn.cumulativeProbability(minD);
130       this.probScale = fcn.cumulativeProbability(maxD + granularityD) - this.minProb;
131       this.converter = ConverterManager.getInstance().createConverter(Double.class, targetType);
132     }
133 
134     @Override
135     public E generate() {
136       double tmp;
137       double prob = minProb + random.nextDouble() * probScale;
138       tmp = fcn.inverse(prob);
139       tmp = Math.floor((tmp - minD) / granularityD) * granularityD + minD;
140       return converter.convert(tmp);
141     }
142 
143   }
144 
145 }