001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  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
018package org.apache.commons.rng.sampling.shape;
019
020import org.apache.commons.rng.UniformRandomProvider;
021import org.apache.commons.rng.sampling.SharedStateObjectSampler;
022
023/**
024 * Generate points uniformly distributed on a line.
025 *
026 * <p>Sampling uses:</p>
027 *
028 * <ul>
029 *   <li>{@link UniformRandomProvider#nextDouble()}
030 * </ul>
031 *
032 * @since 1.4
033 */
034public abstract class LineSampler implements SharedStateObjectSampler<double[]> {
035    /** The dimension for 1D sampling. */
036    private static final int ONE_D = 1;
037    /** The dimension for 2D sampling. */
038    private static final int TWO_D = 2;
039    /** The dimension for 3D sampling. */
040    private static final int THREE_D = 3;
041    /** The source of randomness. */
042    private final UniformRandomProvider rng;
043
044    // The following code defines a point on a line as:
045    // p = a + u * (b - a), u in [0, 1]
046    //
047    // This is rearranged to:
048    // p = a + ub - ua
049    //   = (1 - u)a + ub
050    //
051    // This is the same method used in the
052    // o.a.c.rng.sampling.distribution.ContinuousUniformSampler but extended to N-dimensions.
053
054    /**
055     * Sample uniformly from a line in 1D. This is an non-array based specialisation of
056     * {@link LineSamplerND} for performance.
057     */
058    private static final class LineSampler1D extends LineSampler {
059        /** The x component of vertex a. */
060        private final double ax;
061        /** The x component of vertex b. */
062        private final double bx;
063
064        /**
065         * @param rng Source of randomness.
066         * @param a The first vertex.
067         * @param b The second vertex.
068         */
069        LineSampler1D(UniformRandomProvider rng, double[] a, double[] b) {
070            super(rng);
071            ax = a[0];
072            bx = b[0];
073        }
074
075        /**
076         * @param rng Source of randomness.
077         * @param source Source to copy.
078         */
079        LineSampler1D(UniformRandomProvider rng, LineSampler1D source) {
080            super(rng);
081            ax = source.ax;
082            bx = source.bx;
083        }
084
085        @Override
086        public double[] createSample(double p1mu, double u) {
087            return new double[] {p1mu * ax + u * bx};
088        }
089
090        @Override
091        public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
092            return new LineSampler1D(rng, this);
093        }
094    }
095
096    /**
097     * Sample uniformly from a line in 2D. This is an non-array based specialisation of
098     * {@link LineSamplerND} for performance.
099     */
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}