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