LinearDistribution.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-1.6.0).
003  * Copyright (c) 2007-2014 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
019  */
020 package org.jenetics.stat;
021 
022 import static java.lang.String.format;
023 import static java.util.Objects.requireNonNull;
024 import static org.jenetics.internal.util.object.eq;
025 
026 import java.io.Serializable;
027 import java.util.Locale;
028 
029 import org.jscience.mathematics.number.Float64;
030 
031 import org.jenetics.internal.util.HashBuilder;
032 
033 import org.jenetics.util.Function;
034 import org.jenetics.util.Range;
035 
036 
037 /**
038  <p>This distribution has the following cdf.</p>
039  <p><img src="doc-files/LinearDistribution.png" /></p>
040  <p>
041  * The only restriction is that the integral of the cdf must be one.
042  </p>
043  <p>
044  <img src="doc-files/linear-precondition.gif"
045  *      alt="\int_{x_1}^{x_2}\left(
046  *             \\underset{k} {\\underbrace {\frac{y_2-y_1}{x_2-x_1}}} \cdot x +
047  *             \\underset{d}{\\underbrace {y_1-\frac{y_2-y_1}{x_2-x_1}\cdot x_1}}
048  *           \right)\mathrm{d}x = 1"
049  *  />
050  *  </p>
051  *
052  *  Solving this integral leads to
053  *  <p>
054  *  <img src="doc-files/linear-precondition-y2.gif"
055  *       alt="y_2 = -\frac{(x_2-x_1)\cdot y_1 - 2}{x_2-x_1}"
056  *  />
057  *  </p>
058  *
059  *  for fixed values for <i>x<sub>1</sub></i><i>x<sub>2</sub></i> and
060  *  <i>y<sub>1</sub></i>.
061  *  <p>
062  *  If the value of <i>y<sub>2</sub></i> < 0, the value of <i>x<sub>2</sub></i>
063  *  is decreased so that the resulting triangle (<i>x<sub>1</sub></i>,0),
064  *  (<i>x<sub>1</sub></i>,<i>y<sub>1</sub></i>), (<i>x<sub>2</sub></i>,0) has
065  *  an area of <i>one</i>.
066  *  </p>
067  *
068  @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
069  @since 1.0
070  @version 1.0 &mdash; <em>$Date: 2014-03-01 $</em>
071  */
072 public class LinearDistribution<
073     extends Number & Comparable<? super N>
074 >
075     implements Distribution<N>
076 {
077 
078     /**
079      <p>
080      <img
081      *     src="doc-files/linear-pdf.gif"
082      *     alt="f(x) = \left(
083      *                      \frac{y_2-y_1}{x_2-x_1} \cdot x +
084      *                      y_1-\frac{y_2-y_1}{x_2-x_1}\cdot x_1
085      *                 \right)"
086      * />
087      </p>
088      *
089      @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
090      @since 1.0
091      @version 1.0 &mdash; <em>$Date: 2014-03-01 $</em>
092      */
093     static final class PDF<N extends Number & Comparable<? super N>>
094         implements
095             Function<N, Float64>,
096             Serializable
097     {
098         private static final long serialVersionUID = 1L;
099 
100         private final double _min;
101         private final double _max;
102         private final double _k;
103         private final double _d;
104 
105         public PDF(
106             final double x1, final double y1,
107             final double x2, final double y2
108         ) {
109             _min = x1;
110             _max = x2;
111             _k = (y2 - y1)/(x2 - x1);
112             _d = y1 - _k*x1;
113         }
114 
115         @Override
116         public Float64 apply(final N value) {
117             final double x = value.doubleValue();
118 
119             Float64 result = Float64.ZERO;
120             if (x >= _min && x <= _max) {
121                 result = Float64.valueOf(_k*x + _d);
122             }
123 
124             return result;
125         }
126 
127         @Override
128         public String toString() {
129             return format(Locale.ENGLISH, "p(x) = %f·x + %f", _k, _d);
130         }
131 
132     }
133 
134     /**
135      <p>
136      <img
137      *     src="doc-files/linear-cdf.gif"
138      *     alt="f(x)=-\frac{(x^2-2x_2x)y_1 - (x^2 - 2x_1x)y_2}
139      *      {2(x_2 - x_1)}"
140      * />
141      </p>
142      *
143      @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
144      @since 1.0
145      @version 1.0 &mdash; <em>$Date: 2014-03-01 $</em>
146      */
147     static final class CDF<N extends Number & Comparable<? super N>>
148         implements
149             Function<N, Float64>,
150             Serializable
151     {
152         private static final long serialVersionUID = 1L;
153 
154         private final double _x1;
155         private final double _x2;
156 
157         private final double _k;
158         private final double _d;
159 
160         public CDF(
161             final double x1, final double y1,
162             final double x2, final double y2
163         ) {
164             _x1 = x1;
165             _x2 = x2;
166             _k = (y2 - y1)/(x2 - x1);
167             _d = y1 - _k*x1;
168         }
169 
170         @Override
171         public Float64 apply(final N value) {
172             final double x = value.doubleValue();
173 
174             Float64 result = null;
175             if (x < _x1) {
176                 result = Float64.ZERO;
177             else if (x > _x2) {
178                 result = Float64.ONE;
179             else {
180 //                result = Float64.valueOf(
181 //                        -((x*x - 2*x*_x2)*_y1 - (x*x - 2*x*_x1)*_y2)/
182 //                        (2*(_x2 - _x1))
183 //                    );
184                 result = Float64.valueOf_k*x*x/2.0 + _d*x);
185             }
186 
187             return result;
188         }
189 
190         @Override
191         public String toString() {
192             return format(Locale.ENGLISH, "P(x) = %f·x² - %f·x", _k/2.0, _d);
193         }
194 
195     }
196 
197 
198     private final Range<N> _domain;
199     private final Function<N, Float64> _cdf;
200     private final Function<N, Float64> _pdf;
201 
202     private final double _x1;
203     private final double _x2;
204     private final double _y1;
205     private final double _y2;
206 
207     public LinearDistribution(final Range<N> domain, final double y1) {
208         _domain = requireNonNull(domain);
209 
210         _y1 = Math.max(y1, 0.0);
211         _x1 = domain.getMin().doubleValue();
212         _y2 = Math.max(y2(_x1, domain.getMax().doubleValue(), y1)0.0);
213         if (_y2 == 0) {
214             _x2 = 2.0/_y1 + _x1;
215         else {
216             _x2 = domain.getMax().doubleValue();
217         }
218 
219         _cdf = new CDF<>(_x1, _y1, _x2, _y2);
220         _pdf = new PDF<>(_x1, _y1, _x2, _y2);
221     }
222 
223     private static double y2(final double x1, final double x2, final double y1) {
224         return -((x2 - x1)*y1 - 2)/(x2 - x1);
225     }
226 
227     @Override
228     public Range<N> getDomain() {
229         return _domain;
230     }
231 
232     /**
233      * Return a new CDF object.
234      *
235      <p>
236      <img
237      *     src="doc-files/linear-cdf.gif"
238      *     alt="f(x)=-\frac{(x^2-2x_2x)y_1 - (x^2 - 2x_1x)y_2}
239      *      {2(x_2 - x_1)}"
240      * />
241      </p>
242      *
243      */
244     @Override
245     public Function<N, Float64> getCDF() {
246         return _cdf;
247     }
248 
249     /**
250      * Return a new PDF object.
251      *
252      <p>
253      <img
254      *     src="doc-files/linear-pdf.gif"
255      *     alt="f(x) = \left(
256      *                      \frac{y_2-y_1}{x_2-x_1} \cdot x +
257      *                      y_1-\frac{y_2-y_1}{x_2-x_1}\cdot x_1
258      *                 \right)"
259      * />
260      </p>
261      *
262      */
263     @Override
264     public Function<N, Float64> getPDF() {
265         return _pdf;
266     }
267 
268     @Override
269     public int hashCode() {
270         return HashBuilder.of(getClass()).
271                 and(_domain).
272                 and(_x1).and(_x2).
273                 and(_y1).and(_y2).value();
274     }
275 
276     @Override
277     public boolean equals(final Object obj) {
278         if (obj == this) {
279             return true;
280         }
281         if (obj == null || getClass() != obj.getClass()) {
282             return false;
283         }
284 
285         final LinearDistribution<?> dist = (LinearDistribution<?>)obj;
286         return eq(_domain, dist._domain&&
287                 eq(_x1, dist._x1&& eq(_x2, dist._x2&&
288                 eq(_y1, dist._y1&& eq(_y2, dist._y2);
289     }
290 
291     @Override
292     public String toString() {
293         return format(
294             "LinearDistribution[(%f, %f), (%f, %f)]",
295             _x1, _y1, _x2, _y2
296         ;
297     }
298 
299 }