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 within a n-dimension box (hyperrectangle). 025 * 026 * <p>Sampling uses:</p> 027 * 028 * <ul> 029 * <li>{@link UniformRandomProvider#nextDouble()} 030 * </ul> 031 * 032 * @see <a href="https://en.wikipedia.org/wiki/Hyperrectangle">Hyperrectangle (Wikipedia)</a> 033 * @since 1.4 034 */ 035public abstract class BoxSampler implements SharedStateObjectSampler<double[]> { 036 /** The dimension for 2D sampling. */ 037 private static final int TWO_D = 2; 038 /** The dimension for 3D sampling. */ 039 private static final int THREE_D = 3; 040 /** The source of randomness. */ 041 private final UniformRandomProvider rng; 042 043 // The following code defines a point within the range ab: 044 // p = (1 - u)a + ub, u in [0, 1] 045 // 046 // This is the same method used in the 047 // o.a.c.rng.sampling.distribution.ContinuousUniformSampler but extended to N-dimensions. 048 049 /** 050 * Sample uniformly from a box in 2D. This is an non-array based specialisation of 051 * {@link BoxSamplerND} for performance. 052 */ 053 private static class BoxSampler2D extends BoxSampler { 054 /** The x component of bound a. */ 055 private final double ax; 056 /** The y component of bound a. */ 057 private final double ay; 058 /** The x component of bound b. */ 059 private final double bx; 060 /** The y component of bound b. */ 061 private final double by; 062 063 /** 064 * @param rng Source of randomness. 065 * @param a Bound a. 066 * @param b Bound b. 067 */ 068 BoxSampler2D(UniformRandomProvider rng, double[] a, double[] b) { 069 super(rng); 070 ax = a[0]; 071 ay = a[1]; 072 bx = b[0]; 073 by = b[1]; 074 } 075 076 /** 077 * @param rng Source of randomness. 078 * @param source Source to copy. 079 */ 080 BoxSampler2D(UniformRandomProvider rng, BoxSampler2D source) { 081 super(rng); 082 ax = source.ax; 083 ay = source.ay; 084 bx = source.bx; 085 by = source.by; 086 } 087 088 @Override 089 public double[] sample() { 090 return new double[] {createSample(ax, bx), 091 createSample(ay, by)}; 092 } 093 094 @Override 095 public BoxSampler withUniformRandomProvider(UniformRandomProvider rng) { 096 return new BoxSampler2D(rng, this); 097 } 098 } 099 100 /** 101 * Sample uniformly from a box in 3D. This is an non-array based specialisation of 102 * {@link BoxSamplerND} for performance. 103 */ 104 private static class BoxSampler3D extends BoxSampler { 105 /** The x component of bound a. */ 106 private final double ax; 107 /** The y component of bound a. */ 108 private final double ay; 109 /** The z component of bound a. */ 110 private final double az; 111 /** The x component of bound b. */ 112 private final double bx; 113 /** The y component of bound b. */ 114 private final double by; 115 /** The z component of bound b. */ 116 private final double bz; 117 118 /** 119 * @param rng Source of randomness. 120 * @param a Bound a. 121 * @param b Bound b. 122 */ 123 BoxSampler3D(UniformRandomProvider rng, double[] a, double[] b) { 124 super(rng); 125 ax = a[0]; 126 ay = a[1]; 127 az = a[2]; 128 bx = b[0]; 129 by = b[1]; 130 bz = b[2]; 131 } 132 133 /** 134 * @param rng Source of randomness. 135 * @param source Source to copy. 136 */ 137 BoxSampler3D(UniformRandomProvider rng, BoxSampler3D source) { 138 super(rng); 139 ax = source.ax; 140 ay = source.ay; 141 az = source.az; 142 bx = source.bx; 143 by = source.by; 144 bz = source.bz; 145 } 146 147 @Override 148 public double[] sample() { 149 return new double[] {createSample(ax, bx), 150 createSample(ay, by), 151 createSample(az, bz)}; 152 } 153 154 @Override 155 public BoxSampler withUniformRandomProvider(UniformRandomProvider rng) { 156 return new BoxSampler3D(rng, this); 157 } 158 } 159 160 /** 161 * Sample uniformly from a box in ND. 162 */ 163 private static class BoxSamplerND extends BoxSampler { 164 /** Bound a. */ 165 private final double[] a; 166 /** Bound b. */ 167 private final double[] b; 168 169 /** 170 * @param rng Source of randomness. 171 * @param a Bound a. 172 * @param b Bound b. 173 */ 174 BoxSamplerND(UniformRandomProvider rng, double[] a, double[] b) { 175 super(rng); 176 // Defensive copy 177 this.a = a.clone(); 178 this.b = b.clone(); 179 } 180 181 /** 182 * @param rng Source of randomness. 183 * @param source Source to copy. 184 */ 185 BoxSamplerND(UniformRandomProvider rng, BoxSamplerND source) { 186 super(rng); 187 // Shared state is immutable 188 a = source.a; 189 b = source.b; 190 } 191 192 @Override 193 public double[] sample() { 194 final double[] x = new double[a.length]; 195 for (int i = 0; i < x.length; i++) { 196 x[i] = createSample(a[i], b[i]); 197 } 198 return x; 199 } 200 201 @Override 202 public BoxSampler withUniformRandomProvider(UniformRandomProvider rng) { 203 return new BoxSamplerND(rng, this); 204 } 205 } 206 207 /** 208 * @param rng Source of randomness. 209 */ 210 BoxSampler(UniformRandomProvider rng) { 211 this.rng = rng; 212 } 213 214 /** 215 * @return a random Cartesian coordinate within the box. 216 */ 217 @Override 218 public abstract double[] sample(); 219 220 /** 221 * Creates the sample between bound a and b. 222 * 223 * @param a Bound a 224 * @param b Bound b 225 * @return the sample 226 */ 227 double createSample(double a, double b) { 228 final double u = rng.nextDouble(); 229 return (1.0 - u) * a + u * b; 230 } 231 232 /** {@inheritDoc} */ 233 // Redeclare the signature to return a BoxSampler not a SharedStateObjectSampler<double[]> 234 @Override 235 public abstract BoxSampler withUniformRandomProvider(UniformRandomProvider rng); 236 237 /** 238 * Create a box sampler with bounds {@code a} and {@code b}. 239 * Sampled points are uniformly distributed within the box defined by the bounds. 240 * 241 * <p>Sampling is supported in dimensions of 2 or above. Single dimension sampling 242 * can be performed using a {@link LineSampler}. 243 * 244 * <p>Note: There is no requirement that {@code a <= b}. The samples will be uniformly 245 * distributed in the range {@code a} to {@code b} for each dimension. 246 * 247 * @param rng Source of randomness. 248 * @param a Bound a. 249 * @param b Bound b. 250 * @return the sampler 251 * @throws IllegalArgumentException If the bounds do not have the same 252 * dimension; the dimension is less than 2; or bounds have non-finite coordinates. 253 */ 254 public static BoxSampler of(UniformRandomProvider rng, 255 double[] a, 256 double[] b) { 257 final int dimension = a.length; 258 if (dimension != b.length) { 259 throw new IllegalArgumentException( 260 new StringBuilder("Mismatch of box dimensions: ").append(dimension).append(',') 261 .append(b.length).toString()); 262 } 263 // Detect non-finite bounds 264 Coordinates.requireFinite(a, "Bound a"); 265 Coordinates.requireFinite(b, "Bound b"); 266 // Low dimension specialisations 267 if (dimension == TWO_D) { 268 return new BoxSampler2D(rng, a, b); 269 } else if (dimension == THREE_D) { 270 return new BoxSampler3D(rng, a, b); 271 } else if (dimension > THREE_D) { 272 return new BoxSamplerND(rng, a, b); 273 } 274 // Less than 2D 275 throw new IllegalArgumentException("Unsupported dimension: " + dimension); 276 } 277}