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