LineSampler.java

  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. package org.apache.commons.rng.sampling.shape;

  18. import org.apache.commons.rng.UniformRandomProvider;
  19. import org.apache.commons.rng.sampling.SharedStateObjectSampler;

  20. /**
  21.  * Generate points uniformly distributed on a line.
  22.  *
  23.  * <p>Sampling uses:</p>
  24.  *
  25.  * <ul>
  26.  *   <li>{@link UniformRandomProvider#nextDouble()}
  27.  * </ul>
  28.  *
  29.  * @since 1.4
  30.  */
  31. public abstract class LineSampler implements SharedStateObjectSampler<double[]> {
  32.     /** The dimension for 1D sampling. */
  33.     private static final int ONE_D = 1;
  34.     /** The dimension for 2D sampling. */
  35.     private static final int TWO_D = 2;
  36.     /** The dimension for 3D sampling. */
  37.     private static final int THREE_D = 3;
  38.     /** The source of randomness. */
  39.     private final UniformRandomProvider rng;

  40.     // The following code defines a point on a line as:
  41.     // p = a + u * (b - a), u in [0, 1]
  42.     //
  43.     // This is rearranged to:
  44.     // p = a + ub - ua
  45.     //   = (1 - u)a + ub
  46.     //
  47.     // This is the same method used in the
  48.     // o.a.c.rng.sampling.distribution.ContinuousUniformSampler but extended to N-dimensions.

  49.     /**
  50.      * Sample uniformly from a line in 1D. This is an non-array based specialisation of
  51.      * {@link LineSamplerND} for performance.
  52.      */
  53.     private static final class LineSampler1D extends LineSampler {
  54.         /** The x component of vertex a. */
  55.         private final double ax;
  56.         /** The x component of vertex b. */
  57.         private final double bx;

  58.         /**
  59.          * @param rng Source of randomness.
  60.          * @param a The first vertex.
  61.          * @param b The second vertex.
  62.          */
  63.         LineSampler1D(UniformRandomProvider rng, double[] a, double[] b) {
  64.             super(rng);
  65.             ax = a[0];
  66.             bx = b[0];
  67.         }

  68.         /**
  69.          * @param rng Source of randomness.
  70.          * @param source Source to copy.
  71.          */
  72.         LineSampler1D(UniformRandomProvider rng, LineSampler1D source) {
  73.             super(rng);
  74.             ax = source.ax;
  75.             bx = source.bx;
  76.         }

  77.         @Override
  78.         public double[] createSample(double p1mu, double u) {
  79.             return new double[] {p1mu * ax + u * bx};
  80.         }

  81.         @Override
  82.         public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
  83.             return new LineSampler1D(rng, this);
  84.         }
  85.     }

  86.     /**
  87.      * Sample uniformly from a line in 2D. This is an non-array based specialisation of
  88.      * {@link LineSamplerND} for performance.
  89.      */
  90.     private static final class LineSampler2D extends LineSampler {
  91.         /** The x component of vertex a. */
  92.         private final double ax;
  93.         /** The y component of vertex a. */
  94.         private final double ay;
  95.         /** The x component of vertex b. */
  96.         private final double bx;
  97.         /** The y component of vertex b. */
  98.         private final double by;

  99.         /**
  100.          * @param rng Source of randomness.
  101.          * @param a The first vertex.
  102.          * @param b The second vertex.
  103.          */
  104.         LineSampler2D(UniformRandomProvider rng, double[] a, double[] b) {
  105.             super(rng);
  106.             ax = a[0];
  107.             ay = a[1];
  108.             bx = b[0];
  109.             by = b[1];
  110.         }

  111.         /**
  112.          * @param rng Source of randomness.
  113.          * @param source Source to copy.
  114.          */
  115.         LineSampler2D(UniformRandomProvider rng, LineSampler2D source) {
  116.             super(rng);
  117.             ax = source.ax;
  118.             ay = source.ay;
  119.             bx = source.bx;
  120.             by = source.by;
  121.         }

  122.         @Override
  123.         public double[] createSample(double p1mu, double u) {
  124.             return new double[] {p1mu * ax + u * bx,
  125.                                  p1mu * ay + u * by};
  126.         }

  127.         @Override
  128.         public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
  129.             return new LineSampler2D(rng, this);
  130.         }
  131.     }

  132.     /**
  133.      * Sample uniformly from a line in 3D. This is an non-array based specialisation of
  134.      * {@link LineSamplerND} for performance.
  135.      */
  136.     private static final class LineSampler3D extends LineSampler {
  137.         /** The x component of vertex a. */
  138.         private final double ax;
  139.         /** The y component of vertex a. */
  140.         private final double ay;
  141.         /** The z component of vertex a. */
  142.         private final double az;
  143.         /** The x component of vertex b. */
  144.         private final double bx;
  145.         /** The y component of vertex b. */
  146.         private final double by;
  147.         /** The z component of vertex b. */
  148.         private final double bz;

  149.         /**
  150.          * @param rng Source of randomness.
  151.          * @param a The first vertex.
  152.          * @param b The second vertex.
  153.          */
  154.         LineSampler3D(UniformRandomProvider rng, double[] a, double[] b) {
  155.             super(rng);
  156.             ax = a[0];
  157.             ay = a[1];
  158.             az = a[2];
  159.             bx = b[0];
  160.             by = b[1];
  161.             bz = b[2];
  162.         }

  163.         /**
  164.          * @param rng Source of randomness.
  165.          * @param source Source to copy.
  166.          */
  167.         LineSampler3D(UniformRandomProvider rng, LineSampler3D source) {
  168.             super(rng);
  169.             ax = source.ax;
  170.             ay = source.ay;
  171.             az = source.az;
  172.             bx = source.bx;
  173.             by = source.by;
  174.             bz = source.bz;
  175.         }

  176.         @Override
  177.         public double[] createSample(double p1mu, double u) {
  178.             return new double[] {p1mu * ax + u * bx,
  179.                                  p1mu * ay + u * by,
  180.                                  p1mu * az + u * bz};
  181.         }

  182.         @Override
  183.         public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
  184.             return new LineSampler3D(rng, this);
  185.         }
  186.     }

  187.     /**
  188.      * Sample uniformly from a line in ND.
  189.      */
  190.     private static final class LineSamplerND extends LineSampler {
  191.         /** The first vertex. */
  192.         private final double[] a;
  193.         /** The second vertex. */
  194.         private final double[] b;

  195.         /**
  196.          * @param rng Source of randomness.
  197.          * @param a The first vertex.
  198.          * @param b The second vertex.
  199.          */
  200.         LineSamplerND(UniformRandomProvider rng, double[] a, double[] b) {
  201.             super(rng);
  202.             // Defensive copy
  203.             this.a = a.clone();
  204.             this.b = b.clone();
  205.         }

  206.         /**
  207.          * @param rng Source of randomness.
  208.          * @param source Source to copy.
  209.          */
  210.         LineSamplerND(UniformRandomProvider rng, LineSamplerND source) {
  211.             super(rng);
  212.             // Shared state is immutable
  213.             a = source.a;
  214.             b = source.b;
  215.         }

  216.         @Override
  217.         public double[] createSample(double p1mu, double u) {
  218.             final double[] x = new double[a.length];
  219.             for (int i = 0; i < x.length; i++) {
  220.                 x[i] = p1mu * a[i] + u * b[i];
  221.             }
  222.             return x;
  223.         }

  224.         @Override
  225.         public LineSampler withUniformRandomProvider(UniformRandomProvider rng) {
  226.             return new LineSamplerND(rng, this);
  227.         }
  228.     }

  229.     /**
  230.      * @param rng Source of randomness.
  231.      */
  232.     LineSampler(UniformRandomProvider rng) {
  233.         this.rng = rng;
  234.     }

  235.     /**
  236.      * @return a random Cartesian coordinate on the line.
  237.      */
  238.     @Override
  239.     public double[] sample() {
  240.         final double u = rng.nextDouble();
  241.         return createSample(1.0 - u, u);
  242.     }

  243.     /**
  244.      * Creates the sample given the random variate {@code u} in the
  245.      * interval {@code [0, 1]}. The sum {@code 1 - u} is provided.
  246.      * The sample can be obtained from the line ab using:
  247.      * <pre>
  248.      * p = a(1 - u) + ub
  249.      * </pre>
  250.      *
  251.      * @param p1mu plus 1 minus u (1 - u)
  252.      * @param u the variate u
  253.      * @return the sample
  254.      */
  255.     protected abstract double[] createSample(double p1mu, double u);

  256.     /** {@inheritDoc} */
  257.     // Redeclare the signature to return a LineSampler not a SharedStateObjectSampler<double[]>
  258.     @Override
  259.     public abstract LineSampler withUniformRandomProvider(UniformRandomProvider rng);

  260.     /**
  261.      * Create a line sampler with vertices {@code a} and {@code b}.
  262.      * Sampled points are uniformly distributed on the line segment {@code ab}.
  263.      *
  264.      * <p>Sampling is supported in dimensions of 1 or above.
  265.      *
  266.      * @param rng Source of randomness.
  267.      * @param a The first vertex.
  268.      * @param b The second vertex.
  269.      * @return the sampler
  270.      * @throws IllegalArgumentException If the vertices do not have the same
  271.      * dimension; the dimension is less than 1; or vertices have non-finite coordinates.
  272.      */
  273.     public static LineSampler of(UniformRandomProvider rng,
  274.                                  double[] a,
  275.                                  double[] b) {
  276.         final int dimension = a.length;
  277.         if (dimension != b.length) {
  278.             throw new IllegalArgumentException(
  279.                 new StringBuilder("Mismatch of vertex dimensions: ").append(dimension).append(',')
  280.                                                                     .append(b.length).toString());
  281.         }
  282.         // Detect non-finite vertices
  283.         Coordinates.requireFinite(a, "Vertex a");
  284.         Coordinates.requireFinite(b, "Vertex b");
  285.         // Low dimension specialisations
  286.         if (dimension == TWO_D) {
  287.             return new LineSampler2D(rng, a, b);
  288.         } else if (dimension == THREE_D) {
  289.             return new LineSampler3D(rng, a, b);
  290.         } else if (dimension > THREE_D) {
  291.             return new LineSamplerND(rng, a, b);
  292.         } else if (dimension == ONE_D) {
  293.             // Unlikely case of 1D is placed last.
  294.             // Use o.a.c.rng.sampling.distribution.ContinuousUniformSampler for non-array samples.
  295.             return new LineSampler1D(rng, a, b);
  296.         }
  297.         // Less than 1D
  298.         throw new IllegalArgumentException("Unsupported dimension: " + dimension);
  299.     }
  300. }