LineSampler.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.rng.sampling.shape;
import org.apache.commons.rng.UniformRandomProvider;
import org.apache.commons.rng.sampling.SharedStateObjectSampler;
/**
* Generate points uniformly distributed on a line.
*
* <p>Sampling uses:</p>
*
* <ul>
* <li>{@link UniformRandomProvider#nextDouble()}
* </ul>
*
* @since 1.4
*/
public abstract class LineSampler implements SharedStateObjectSampler<double[]> {
/** The dimension for 1D sampling. */
private static final int ONE_D = 1;
/** The dimension for 2D sampling. */
private static final int TWO_D = 2;
/** The dimension for 3D sampling. */
private static final int THREE_D = 3;
/** The source of randomness. */
private final UniformRandomProvider rng;
// The following code defines a point on a line as:
// p = a + u * (b - a), u in [0, 1]
//
// This is rearranged to:
// p = a + ub - ua
// = (1 - u)a + ub
//
// This is the same method used in the
// o.a.c.rng.sampling.distribution.ContinuousUniformSampler but extended to N-dimensions.
/**
* Sample uniformly from a line in 1D. This is an non-array based specialisation of
* {@link LineSamplerND} for performance.
*/
private static final class LineSampler1D extends LineSampler {
/** The x component of vertex a. */
private final double ax;
/** The x component of vertex b. */
private final double bx;
/**
* @param rng Source of randomness.
* @param a The first vertex.
* @param b The second vertex.
*/
LineSampler1D(UniformRandomProvider rng, double[] a, double[] b) {
super(rng);
ax = a[0];
bx = b[0];
}
/**
* @param rng Source of randomness.
* @param source Source to copy.
*/
LineSampler1D(UniformRandomProvider rng, LineSampler1D source) {
super(rng);
ax = source.ax;
bx = source.bx;
}
@Override
public double[] createSample(double p1mu, double u) {
return new double[] {p1mu * ax + u * bx};
}
@Override
public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
return new LineSampler1D(rng, this);
}
}
/**
* Sample uniformly from a line in 2D. This is an non-array based specialisation of
* {@link LineSamplerND} for performance.
*/
private static final class LineSampler2D extends LineSampler {
/** The x component of vertex a. */
private final double ax;
/** The y component of vertex a. */
private final double ay;
/** The x component of vertex b. */
private final double bx;
/** The y component of vertex b. */
private final double by;
/**
* @param rng Source of randomness.
* @param a The first vertex.
* @param b The second vertex.
*/
LineSampler2D(UniformRandomProvider rng, double[] a, double[] b) {
super(rng);
ax = a[0];
ay = a[1];
bx = b[0];
by = b[1];
}
/**
* @param rng Source of randomness.
* @param source Source to copy.
*/
LineSampler2D(UniformRandomProvider rng, LineSampler2D source) {
super(rng);
ax = source.ax;
ay = source.ay;
bx = source.bx;
by = source.by;
}
@Override
public double[] createSample(double p1mu, double u) {
return new double[] {p1mu * ax + u * bx,
p1mu * ay + u * by};
}
@Override
public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
return new LineSampler2D(rng, this);
}
}
/**
* Sample uniformly from a line in 3D. This is an non-array based specialisation of
* {@link LineSamplerND} for performance.
*/
private static final class LineSampler3D extends LineSampler {
/** The x component of vertex a. */
private final double ax;
/** The y component of vertex a. */
private final double ay;
/** The z component of vertex a. */
private final double az;
/** The x component of vertex b. */
private final double bx;
/** The y component of vertex b. */
private final double by;
/** The z component of vertex b. */
private final double bz;
/**
* @param rng Source of randomness.
* @param a The first vertex.
* @param b The second vertex.
*/
LineSampler3D(UniformRandomProvider rng, double[] a, double[] b) {
super(rng);
ax = a[0];
ay = a[1];
az = a[2];
bx = b[0];
by = b[1];
bz = b[2];
}
/**
* @param rng Source of randomness.
* @param source Source to copy.
*/
LineSampler3D(UniformRandomProvider rng, LineSampler3D source) {
super(rng);
ax = source.ax;
ay = source.ay;
az = source.az;
bx = source.bx;
by = source.by;
bz = source.bz;
}
@Override
public double[] createSample(double p1mu, double u) {
return new double[] {p1mu * ax + u * bx,
p1mu * ay + u * by,
p1mu * az + u * bz};
}
@Override
public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
return new LineSampler3D(rng, this);
}
}
/**
* Sample uniformly from a line in ND.
*/
private static final class LineSamplerND extends LineSampler {
/** The first vertex. */
private final double[] a;
/** The second vertex. */
private final double[] b;
/**
* @param rng Source of randomness.
* @param a The first vertex.
* @param b The second vertex.
*/
LineSamplerND(UniformRandomProvider rng, double[] a, double[] b) {
super(rng);
// Defensive copy
this.a = a.clone();
this.b = b.clone();
}
/**
* @param rng Source of randomness.
* @param source Source to copy.
*/
LineSamplerND(UniformRandomProvider rng, LineSamplerND source) {
super(rng);
// Shared state is immutable
a = source.a;
b = source.b;
}
@Override
public double[] createSample(double p1mu, double u) {
final double[] x = new double[a.length];
for (int i = 0; i < x.length; i++) {
x[i] = p1mu * a[i] + u * b[i];
}
return x;
}
@Override
public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
return new LineSamplerND(rng, this);
}
}
/**
* @param rng Source of randomness.
*/
LineSampler(UniformRandomProvider rng) {
this.rng = rng;
}
/**
* @return a random Cartesian coordinate on the line.
*/
@Override
public double[] sample() {
final double u = rng.nextDouble();
return createSample(1.0 - u, u);
}
/**
* Creates the sample given the random variate {@code u} in the
* interval {@code [0, 1]}. The sum {@code 1 - u} is provided.
* The sample can be obtained from the line ab using:
* <pre>
* p = a(1 - u) + ub
* </pre>
*
* @param p1mu plus 1 minus u (1 - u)
* @param u the variate u
* @return the sample
*/
protected abstract double[] createSample(double p1mu, double u);
/** {@inheritDoc} */
// Redeclare the signature to return a LineSampler not a SharedStateObjectSampler<double[]>
@Override
public abstract LineSampler withUniformRandomProvider(UniformRandomProvider rng);
/**
* Create a line sampler with vertices {@code a} and {@code b}.
* Sampled points are uniformly distributed on the line segment {@code ab}.
*
* <p>Sampling is supported in dimensions of 1 or above.
*
* @param rng Source of randomness.
* @param a The first vertex.
* @param b The second vertex.
* @return the sampler
* @throws IllegalArgumentException If the vertices do not have the same
* dimension; the dimension is less than 1; or vertices have non-finite coordinates.
*/
public static LineSampler of(UniformRandomProvider rng,
double[] a,
double[] b) {
final int dimension = a.length;
if (dimension != b.length) {
throw new IllegalArgumentException(
new StringBuilder("Mismatch of vertex dimensions: ").append(dimension).append(',')
.append(b.length).toString());
}
// Detect non-finite vertices
Coordinates.requireFinite(a, "Vertex a");
Coordinates.requireFinite(b, "Vertex b");
// Low dimension specialisations
if (dimension == TWO_D) {
return new LineSampler2D(rng, a, b);
} else if (dimension == THREE_D) {
return new LineSampler3D(rng, a, b);
} else if (dimension > THREE_D) {
return new LineSamplerND(rng, a, b);
} else if (dimension == ONE_D) {
// Unlikely case of 1D is placed last.
// Use o.a.c.rng.sampling.distribution.ContinuousUniformSampler for non-array samples.
return new LineSampler1D(rng, a, b);
}
// Less than 1D
throw new IllegalArgumentException("Unsupported dimension: " + dimension);
}
}