View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.rng.sampling.shape;
19  
20  import org.apache.commons.rng.UniformRandomProvider;
21  import org.apache.commons.rng.sampling.SharedStateObjectSampler;
22  
23  /**
24   * Generate points uniformly distributed on a line.
25   *
26   * <p>Sampling uses:</p>
27   *
28   * <ul>
29   *   <li>{@link UniformRandomProvider#nextDouble()}
30   * </ul>
31   *
32   * @since 1.4
33   */
34  public abstract class LineSampler implements SharedStateObjectSampler<double[]> {
35      /** The dimension for 1D sampling. */
36      private static final int ONE_D = 1;
37      /** The dimension for 2D sampling. */
38      private static final int TWO_D = 2;
39      /** The dimension for 3D sampling. */
40      private static final int THREE_D = 3;
41      /** The source of randomness. */
42      private final UniformRandomProvider rng;
43  
44      // The following code defines a point on a line as:
45      // p = a + u * (b - a), u in [0, 1]
46      //
47      // This is rearranged to:
48      // p = a + ub - ua
49      //   = (1 - u)a + ub
50      //
51      // This is the same method used in the
52      // o.a.c.rng.sampling.distribution.ContinuousUniformSampler but extended to N-dimensions.
53  
54      /**
55       * Sample uniformly from a line in 1D. This is an non-array based specialisation of
56       * {@link LineSamplerND} for performance.
57       */
58      private static class LineSampler1D extends LineSampler {
59          /** The x component of vertex a. */
60          private final double ax;
61          /** The x component of vertex b. */
62          private final double bx;
63  
64          /**
65           * @param rng Source of randomness.
66           * @param a The first vertex.
67           * @param b The second vertex.
68           */
69          LineSampler1D(UniformRandomProvider rng, double[] a, double[] b) {
70              super(rng);
71              ax = a[0];
72              bx = b[0];
73          }
74  
75          /**
76           * @param rng Source of randomness.
77           * @param source Source to copy.
78           */
79          LineSampler1D(UniformRandomProvider rng, LineSampler1D source) {
80              super(rng);
81              ax = source.ax;
82              bx = source.bx;
83          }
84  
85          @Override
86          public double[] createSample(double p1mu, double u) {
87              return new double[] {p1mu * ax + u * bx};
88          }
89  
90          @Override
91          public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
92              return new LineSampler1D(rng, this);
93          }
94      }
95  
96      /**
97       * Sample uniformly from a line in 2D. This is an non-array based specialisation of
98       * {@link LineSamplerND} for performance.
99       */
100     private static class LineSampler2D extends LineSampler {
101         /** The x component of vertex a. */
102         private final double ax;
103         /** The y component of vertex a. */
104         private final double ay;
105         /** The x component of vertex b. */
106         private final double bx;
107         /** The y component of vertex b. */
108         private final double by;
109 
110         /**
111          * @param rng Source of randomness.
112          * @param a The first vertex.
113          * @param b The second vertex.
114          */
115         LineSampler2D(UniformRandomProvider rng, double[] a, double[] b) {
116             super(rng);
117             ax = a[0];
118             ay = a[1];
119             bx = b[0];
120             by = b[1];
121         }
122 
123         /**
124          * @param rng Source of randomness.
125          * @param source Source to copy.
126          */
127         LineSampler2D(UniformRandomProvider rng, LineSampler2D source) {
128             super(rng);
129             ax = source.ax;
130             ay = source.ay;
131             bx = source.bx;
132             by = source.by;
133         }
134 
135         @Override
136         public double[] createSample(double p1mu, double u) {
137             return new double[] {p1mu * ax + u * bx,
138                                  p1mu * ay + u * by};
139         }
140 
141         @Override
142         public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
143             return new LineSampler2D(rng, this);
144         }
145     }
146 
147     /**
148      * Sample uniformly from a line in 3D. This is an non-array based specialisation of
149      * {@link LineSamplerND} for performance.
150      */
151     private static class LineSampler3D extends LineSampler {
152         /** The x component of vertex a. */
153         private final double ax;
154         /** The y component of vertex a. */
155         private final double ay;
156         /** The z component of vertex a. */
157         private final double az;
158         /** The x component of vertex b. */
159         private final double bx;
160         /** The y component of vertex b. */
161         private final double by;
162         /** The z component of vertex b. */
163         private final double bz;
164 
165         /**
166          * @param rng Source of randomness.
167          * @param a The first vertex.
168          * @param b The second vertex.
169          */
170         LineSampler3D(UniformRandomProvider rng, double[] a, double[] b) {
171             super(rng);
172             ax = a[0];
173             ay = a[1];
174             az = a[2];
175             bx = b[0];
176             by = b[1];
177             bz = b[2];
178         }
179 
180         /**
181          * @param rng Source of randomness.
182          * @param source Source to copy.
183          */
184         LineSampler3D(UniformRandomProvider rng, LineSampler3D source) {
185             super(rng);
186             ax = source.ax;
187             ay = source.ay;
188             az = source.az;
189             bx = source.bx;
190             by = source.by;
191             bz = source.bz;
192         }
193 
194         @Override
195         public double[] createSample(double p1mu, double u) {
196             return new double[] {p1mu * ax + u * bx,
197                                  p1mu * ay + u * by,
198                                  p1mu * az + u * bz};
199         }
200 
201         @Override
202         public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
203             return new LineSampler3D(rng, this);
204         }
205     }
206 
207     /**
208      * Sample uniformly from a line in ND.
209      */
210     private static class LineSamplerND extends LineSampler {
211         /** The first vertex. */
212         private final double[] a;
213         /** The second vertex. */
214         private final double[] b;
215 
216         /**
217          * @param rng Source of randomness.
218          * @param a The first vertex.
219          * @param b The second vertex.
220          */
221         LineSamplerND(UniformRandomProvider rng, double[] a, double[] b) {
222             super(rng);
223             // Defensive copy
224             this.a = a.clone();
225             this.b = b.clone();
226         }
227 
228         /**
229          * @param rng Source of randomness.
230          * @param source Source to copy.
231          */
232         LineSamplerND(UniformRandomProvider rng, LineSamplerND source) {
233             super(rng);
234             // Shared state is immutable
235             a = source.a;
236             b = source.b;
237         }
238 
239         @Override
240         public double[] createSample(double p1mu, double u) {
241             final double[] x = new double[a.length];
242             for (int i = 0; i < x.length; i++) {
243                 x[i] = p1mu * a[i] + u * b[i];
244             }
245             return x;
246         }
247 
248         @Override
249         public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
250             return new LineSamplerND(rng, this);
251         }
252     }
253 
254     /**
255      * @param rng Source of randomness.
256      */
257     LineSampler(UniformRandomProvider rng) {
258         this.rng = rng;
259     }
260 
261     /**
262      * @return a random Cartesian coordinate on the line.
263      */
264     @Override
265     public double[] sample() {
266         final double u = rng.nextDouble();
267         return createSample(1.0 - u, u);
268     }
269 
270     /**
271      * Creates the sample given the random variate {@code u} in the
272      * interval {@code [0, 1]}. The sum {@code 1 - u} is provided.
273      * The sample can be obtained from the line ab using:
274      * <pre>
275      * p = a(1 - u) + ub
276      * </pre>
277      *
278      * @param p1mu plus 1 minus u (1 - u)
279      * @param u the variate u
280      * @return the sample
281      */
282     protected abstract double[] createSample(double p1mu, double u);
283 
284     /** {@inheritDoc} */
285     // Redeclare the signature to return a LineSampler not a SharedStateObjectSampler<double[]>
286     @Override
287     public abstract LineSampler withUniformRandomProvider(UniformRandomProvider rng);
288 
289     /**
290      * Create a line sampler with vertices {@code a} and {@code b}.
291      * Sampled points are uniformly distributed on the line segment {@code ab}.
292      *
293      * <p>Sampling is supported in dimensions of 1 or above.
294      *
295      * @param rng Source of randomness.
296      * @param a The first vertex.
297      * @param b The second vertex.
298      * @return the sampler
299      * @throws IllegalArgumentException If the vertices do not have the same
300      * dimension; the dimension is less than 1; or vertices have non-finite coordinates.
301      */
302     public static LineSampler of(UniformRandomProvider rng,
303                                  double[] a,
304                                  double[] b) {
305         final int dimension = a.length;
306         if (dimension != b.length) {
307             throw new IllegalArgumentException(
308                 new StringBuilder("Mismatch of vertex dimensions: ").append(dimension).append(',')
309                                                                     .append(b.length).toString());
310         }
311         // Detect non-finite vertices
312         Coordinates.requireFinite(a, "Vertex a");
313         Coordinates.requireFinite(b, "Vertex b");
314         // Low dimension specialisations
315         if (dimension == TWO_D) {
316             return new LineSampler2D(rng, a, b);
317         } else if (dimension == THREE_D) {
318             return new LineSampler3D(rng, a, b);
319         } else if (dimension > THREE_D) {
320             return new LineSamplerND(rng, a, b);
321         } else if (dimension == ONE_D) {
322             // Unlikely case of 1D is placed last.
323             // Use o.a.c.rng.sampling.distribution.ContinuousUniformSampler for non-array samples.
324             return new LineSampler1D(rng, a, b);
325         }
326         // Less than 1D
327         throw new IllegalArgumentException("Unsupported dimension: " + dimension);
328     }
329 }