View Javadoc
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  
18  package org.apache.commons.rng.sampling;
19  
20  import java.util.List;
21  import java.util.Objects;
22  import java.util.ArrayList;
23  
24  import org.apache.commons.rng.UniformRandomProvider;
25  import org.apache.commons.rng.sampling.distribution.AliasMethodDiscreteSampler;
26  import org.apache.commons.rng.sampling.distribution.ContinuousSampler;
27  import org.apache.commons.rng.sampling.distribution.DiscreteSampler;
28  import org.apache.commons.rng.sampling.distribution.DiscreteUniformSampler;
29  import org.apache.commons.rng.sampling.distribution.GuideTableDiscreteSampler;
30  import org.apache.commons.rng.sampling.distribution.LongSampler;
31  import org.apache.commons.rng.sampling.distribution.MarsagliaTsangWangDiscreteSampler;
32  import org.apache.commons.rng.sampling.distribution.SharedStateContinuousSampler;
33  import org.apache.commons.rng.sampling.distribution.SharedStateDiscreteSampler;
34  import org.apache.commons.rng.sampling.distribution.SharedStateLongSampler;
35  
36  /**
37   * Factory class to create a sampler that combines sampling from multiple samplers.
38   *
39   * <p>The composite sampler is constructed using a {@link Builder builder} for the type of samplers
40   * that will form the composite. Each sampler has a weight in the composition.
41   * Samples are returned using a 2 step algorithm:
42   *
43   * <ol>
44   *  <li>Select a sampler based on its weighting
45   *  <li>Return a sample from the selected sampler
46   * </ol>
47   *
48   * <p>The weights used for each sampler create a discrete probability distribution. This is
49   * sampled using a discrete probability distribution sampler. The builder provides methods
50   * to change the default implementation.
51   *
52   * <p>The following example will create a sampler to uniformly sample the border of a triangle
53   * using the line segment lengths as weights:
54   *
55   * <pre>
56   * UniformRandomProvider rng = RandomSource.KISS.create();
57   * double[] a = {1.23, 4.56};
58   * double[] b = {6.78, 9.01};
59   * double[] c = {3.45, 2.34};
60   * ObjectSampler&lt;double[]&gt; sampler =
61   *     CompositeSamplers.&lt;double[]&gt;newObjectSamplerBuilder()
62   *         .add(LineSampler.of(rng, a, b), Math.hypot(a[0] - b[0], a[1] - b[1]))
63   *         .add(LineSampler.of(rng, b, c), Math.hypot(b[0] - c[0], b[1] - c[1]))
64   *         .add(LineSampler.of(rng, c, a), Math.hypot(c[0] - a[0], c[1] - a[1]))
65   *         .build(rng);
66   * </pre>
67   *
68   * @since 1.4
69   */
70  public final class CompositeSamplers {
71      /**
72       * A factory for creating a sampler of a user-defined
73       * <a href="http://en.wikipedia.org/wiki/Probability_distribution#Discrete_probability_distribution">
74       * discrete probability distribution</a>.
75       */
76      public interface DiscreteProbabilitySamplerFactory {
77          /**
78           * Creates the sampler.
79           *
80           * @param rng Source of randomness.
81           * @param probabilities Discrete probability distribution.
82           * @return the sampler
83           */
84          DiscreteSampler create(UniformRandomProvider rng,
85                                 double[] probabilities);
86      }
87  
88      /**
89       * The DiscreteProbabilitySampler class defines implementations that sample from a user-defined
90       * <a href="http://en.wikipedia.org/wiki/Probability_distribution#Discrete_probability_distribution">
91       * discrete probability distribution</a>.
92       *
93       * <p>All implementations support the {@link SharedStateDiscreteSampler} interface.
94       */
95      public enum DiscreteProbabilitySampler implements DiscreteProbabilitySamplerFactory {
96          /** Sample using a guide table (see {@link GuideTableDiscreteSampler}). */
97          GUIDE_TABLE {
98              @Override
99              public SharedStateDiscreteSampler create(UniformRandomProvider rng, double[] probabilities) {
100                 return GuideTableDiscreteSampler.of(rng, probabilities);
101             }
102         },
103         /** Sample using the alias method (see {@link AliasMethodDiscreteSampler}). */
104         ALIAS_METHOD {
105             @Override
106             public SharedStateDiscreteSampler create(UniformRandomProvider rng, double[] probabilities) {
107                 return AliasMethodDiscreteSampler.of(rng, probabilities);
108             }
109         },
110         /**
111          * Sample using an optimised look-up table (see
112          * {@link org.apache.commons.rng.sampling.distribution.MarsagliaTsangWangDiscreteSampler.Enumerated
113          * MarsagliaTsangWangDiscreteSampler.Enumerated}).
114          */
115         LOOKUP_TABLE {
116             @Override
117             public SharedStateDiscreteSampler create(UniformRandomProvider rng, double[] probabilities) {
118                 return MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng, probabilities);
119             }
120         }
121     }
122 
123     /**
124      * A class to implement the SharedStateDiscreteSampler interface for a discrete probability
125      * sampler given a factory and the probability distribution. Each new instance will recreate
126      * the distribution sampler using the factory.
127      */
128     private static class SharedStateDiscreteProbabilitySampler implements SharedStateDiscreteSampler {
129         /** The sampler. */
130         private final DiscreteSampler sampler;
131         /** The factory to create a new discrete sampler. */
132         private final DiscreteProbabilitySamplerFactory factory;
133         /** The probabilities. */
134         private final double[] probabilities;
135 
136         /**
137          * @param sampler Sampler of the discrete distribution.
138          * @param factory Factory to create a new discrete sampler.
139          * @param probabilities Probabilities of the discrete distribution.
140          * @throws NullPointerException if the {@code sampler} is null
141          */
142         SharedStateDiscreteProbabilitySampler(DiscreteSampler sampler,
143                                               DiscreteProbabilitySamplerFactory factory,
144                                               double[] probabilities) {
145             this.sampler = Objects.requireNonNull(sampler, "discrete sampler");
146             // Assume the factory and probabilities are not null
147             this.factory = factory;
148             this.probabilities = probabilities;
149         }
150 
151         @Override
152         public int sample() {
153             // Delegate
154             return sampler.sample();
155         }
156 
157         @Override
158         public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
159             // The factory may destructively modify the probabilities
160             return new SharedStateDiscreteProbabilitySampler(factory.create(rng, probabilities.clone()),
161                                                              factory, probabilities);
162         }
163     }
164 
165     /**
166      * Builds a composite sampler.
167      *
168      * <p>A composite sampler is a combination of multiple samplers
169      * that all return the same sample type. Each sampler has a weighting in the composition.
170      * Samples are returned using a 2 step algorithm:
171      *
172      * <ol>
173      *  <li>Select a sampler based on its weighting
174      *  <li>Return a sample from the selected sampler
175      * </ol>
176      *
177      * <p>Step 1 requires a discrete sampler constructed from a discrete probability distribution.
178      * The probability for each sampler is the sampler weight divided by the sum of the weights:
179      * <pre>
180      * p(i) = w(i) / sum(w)
181      * </pre>
182      *
183      * <p>The builder provides a method to set the factory used to generate the discrete sampler.
184      *
185      * @param <S> Type of sampler
186      */
187     public interface Builder<S> {
188         /**
189          * Return the number of samplers in the composite. The size must be non-zero before
190          * the {@link #build(UniformRandomProvider) build} method can create a sampler.
191          *
192          * @return the size
193          */
194         int size();
195 
196         /**
197          * Adds the sampler to the composite. A sampler with a zero weight is ignored.
198          *
199          * @param sampler Sampler.
200          * @param weight Weight for the composition.
201          * @return a reference to this builder
202          * @throws IllegalArgumentException if {@code weight} is negative, infinite or {@code NaN}.
203          * @throws NullPointerException if {@code sampler} is null.
204          */
205         Builder<S> add(S sampler, double weight);
206 
207         /**
208          * Sets the factory to use to generate the composite's discrete sampler from the sampler
209          * weights.
210          *
211          * <p>Note: If the factory is not explicitly set then a default will be used.
212          *
213          * @param factory Factory.
214          * @return a reference to this builder
215          * @throws NullPointerException if {@code factory} is null.
216          */
217         Builder<S> setFactory(DiscreteProbabilitySamplerFactory factory);
218 
219         /**
220          * Builds the composite sampler. The {@code rng} is the source of randomness for selecting
221          * which sampler to use for each sample.
222          *
223          * <p>Note: When the sampler is created the builder is reset to an empty state.
224          * This prevents building multiple composite samplers with the same samplers and
225          * their identical underlying source of randomness.
226          *
227          * @param rng Generator of uniformly distributed random numbers.
228          * @return the sampler
229          * @throws IllegalStateException if no samplers have been added to create a composite.
230          * @see #size()
231          */
232         S build(UniformRandomProvider rng);
233     }
234 
235     /**
236      * Builds a composite sampler.
237      *
238      * <p>A single builder can be used to create composites of different implementing classes
239      * which support different sampler interfaces. The type of sampler is generic. The individual
240      * samplers and their weights can be collected by the builder. The build method creates
241      * the discrete probability distribution from the weights. The final composite is created
242      * using a factory to create the class.
243      *
244      * @param <S> Type of sampler
245      */
246     private static class SamplerBuilder<S> implements Builder<S> {
247         /** The specialisation of the sampler. */
248         private final Specialisation specialisation;
249         /** The weighted samplers. */
250         private final List<WeightedSampler<S>> weightedSamplers;
251         /** The factory to create the discrete probability sampler from the weights. */
252         private DiscreteProbabilitySamplerFactory factory;
253         /** The factory to create the composite sampler. */
254         private final SamplerFactory<S> compositeFactory;
255 
256         /**
257          * The specialisation of composite sampler to build.
258          * This is used to determine if specialised interfaces from the sampler
259          * type must be supported, e.g. {@link SharedStateSampler}.
260          */
261         enum Specialisation {
262             /** Instance of {@link SharedStateSampler}. */
263             SHARED_STATE_SAMPLER,
264             /** No specialisation. */
265             NONE
266         }
267 
268         /**
269          * A factory for creating composite samplers.
270          *
271          * <p>This interface is used to build concrete implementations
272          * of different sampler interfaces.
273          *
274          * @param <S> Type of sampler
275          */
276         interface SamplerFactory<S> {
277             /**
278              * Creates a new composite sampler.
279              *
280              * <p>If the composite specialisation is a
281              * {@link Specialisation#SHARED_STATE_SAMPLER shared state sampler}
282              * the discrete sampler passed to this method will be an instance of
283              * {@link SharedStateDiscreteSampler}.
284              *
285              * @param discreteSampler Discrete sampler.
286              * @param samplers Samplers.
287              * @return the sampler
288              */
289             S createSampler(DiscreteSampler discreteSampler,
290                             List<S> samplers);
291         }
292 
293         /**
294          * Contains a weighted sampler.
295          *
296          * @param <S> Sampler type
297          */
298         private static class WeightedSampler<S> {
299             /** The weight. */
300             private final double weight;
301             /** The sampler. */
302             private final S sampler;
303 
304             /**
305              * @param weight the weight
306              * @param sampler the sampler
307              * @throws IllegalArgumentException if {@code weight} is negative, infinite or {@code NaN}.
308              * @throws NullPointerException if {@code sampler} is null.
309              */
310             WeightedSampler(double weight, S sampler) {
311                 this.weight = requirePositiveFinite(weight, "weight");
312                 this.sampler = Objects.requireNonNull(sampler, "sampler");
313             }
314 
315             /**
316              * Gets the weight.
317              *
318              * @return the weight
319              */
320             double getWeight() {
321                 return weight;
322             }
323 
324             /**
325              * Gets the sampler.
326              *
327              * @return the sampler
328              */
329             S getSampler() {
330                 return sampler;
331             }
332 
333             /**
334              * Checks that the specified value is positive finite and throws a customized
335              * {@link IllegalArgumentException} if it is not.
336              *
337              * @param value the value
338              * @param message detail message to be used in the event that a {@code
339              *                IllegalArgumentException} is thrown
340              * @return {@code value} if positive finite
341              * @throws IllegalArgumentException if {@code weight} is negative, infinite or {@code NaN}.
342              */
343             private static double requirePositiveFinite(double value, String message) {
344                 // Must be positive finite
345                 if (!(value >= 0 && value < Double.POSITIVE_INFINITY)) {
346                     throw new IllegalArgumentException(message + " is not positive finite: " + value);
347                 }
348                 return value;
349             }
350         }
351 
352         /**
353          * @param specialisation Specialisation of the sampler.
354          * @param compositeFactory Factory to create the final composite sampler.
355          */
356         SamplerBuilder(Specialisation specialisation,
357                        SamplerFactory<S> compositeFactory) {
358             this.specialisation = specialisation;
359             this.compositeFactory = compositeFactory;
360             weightedSamplers = new ArrayList<>();
361             factory = DiscreteProbabilitySampler.GUIDE_TABLE;
362         }
363 
364         @Override
365         public int size() {
366             return weightedSamplers.size();
367         }
368 
369         @Override
370         public Builder<S> add(S sampler, double weight) {
371             // Ignore zero weights. The sampler and weight are validated by the WeightedSampler.
372             if (weight != 0) {
373                 weightedSamplers.add(new WeightedSampler<>(weight, sampler));
374             }
375             return this;
376         }
377 
378         /**
379          * {@inheritDoc}
380          *
381          * <p>If the weights are uniform the factory is ignored and composite's discrete sampler
382          * is a {@link DiscreteUniformSampler uniform distribution sampler}.
383          */
384         @Override
385         public Builder<S> setFactory(DiscreteProbabilitySamplerFactory samplerFactory) {
386             this.factory = Objects.requireNonNull(samplerFactory, "factory");
387             return this;
388         }
389 
390         /**
391          * {@inheritDoc}
392          *
393          * <p>If only one sampler has been added to the builder then the sampler is returned
394          * and the builder is reset.
395          *
396          * @throws IllegalStateException if no samplers have been added to create a composite.
397          */
398         @Override
399         public S build(UniformRandomProvider rng) {
400             final List<WeightedSampler<S>> list = this.weightedSamplers;
401             final int n = list.size();
402             if (n == 0) {
403                 throw new IllegalStateException("No samplers to build the composite");
404             }
405             if (n == 1) {
406                 // No composite
407                 final S sampler = list.get(0).sampler;
408                 reset();
409                 return sampler;
410             }
411 
412             // Extract the weights and samplers.
413             final double[] weights = new double[n];
414             final ArrayList<S> samplers = new ArrayList<>(n);
415             for (int i = 0; i < n; i++) {
416                 final WeightedSampler<S> weightedItem = list.get(i);
417                 weights[i] = weightedItem.getWeight();
418                 samplers.add(weightedItem.getSampler());
419             }
420 
421             reset();
422 
423             final DiscreteSampler discreteSampler = createDiscreteSampler(rng, weights);
424 
425             return compositeFactory.createSampler(discreteSampler, samplers);
426         }
427 
428         /**
429          * Reset the builder.
430          */
431         private void reset() {
432             weightedSamplers.clear();
433         }
434 
435         /**
436          * Creates the discrete sampler of the enumerated probability distribution.
437          *
438          * <p>If the specialisation is a {@link Specialisation#SHARED_STATE_SAMPLER shared state sampler}
439          * the discrete sampler will be an instance of {@link SharedStateDiscreteSampler}.
440          *
441          * @param rng Generator of uniformly distributed random numbers.
442          * @param weights Weight associated to each item.
443          * @return the sampler
444          */
445         private DiscreteSampler createDiscreteSampler(UniformRandomProvider rng,
446                                                       double[] weights) {
447             // Edge case. Detect uniform weights.
448             final int n = weights.length;
449             if (uniform(weights)) {
450                 // Uniformly sample from the size.
451                 // Note: Upper bound is inclusive.
452                 return DiscreteUniformSampler.of(rng, 0, n - 1);
453             }
454 
455             // If possible normalise with a simple sum.
456             final double sum = sum(weights);
457             if (sum < Double.POSITIVE_INFINITY) {
458                 // Do not use f = 1.0 / sum and multiplication by f.
459                 // Use of divide handles a sub-normal sum.
460                 for (int i = 0; i < n; i++) {
461                     weights[i] /= sum;
462                 }
463             } else {
464                 // The sum is not finite. We know the weights are all positive finite.
465                 // Compute the mean without overflow and divide by the mean and number of items.
466                 final double mean = mean(weights);
467                 for (int i = 0; i < n; i++) {
468                     // Two step division avoids using the denominator (mean * n)
469                     weights[i] = weights[i] / mean / n;
470                 }
471             }
472 
473             // Create the sampler from the factory.
474             // Check if a SharedStateSampler is required.
475             // If a default factory then the result is a SharedStateDiscreteSampler,
476             // otherwise the sampler must be checked.
477             if (specialisation == Specialisation.SHARED_STATE_SAMPLER &&
478                 !(factory instanceof DiscreteProbabilitySampler)) {
479                 // If the factory was user defined then clone the weights as they may be required
480                 // to create a SharedStateDiscreteProbabilitySampler.
481                 final DiscreteSampler sampler = factory.create(rng, weights.clone());
482                 return sampler instanceof SharedStateDiscreteSampler ?
483                      sampler :
484                      new SharedStateDiscreteProbabilitySampler(sampler, factory, weights);
485             }
486 
487             return factory.create(rng, weights);
488         }
489 
490         /**
491          * Check if all the values are the same.
492          *
493          * <p>Warning: This method assumes there are input values. If the length is zero an
494          * {@link ArrayIndexOutOfBoundsException} will be thrown.
495          *
496          * @param values the values
497          * @return true if all values are the same
498          */
499         private static boolean uniform(double[] values) {
500             final double value = values[0];
501             for (int i = 1; i < values.length; i++) {
502                 if (value != values[i]) {
503                     return false;
504                 }
505             }
506             return true;
507         }
508 
509         /**
510          * Compute the sum of the values.
511          *
512          * @param values the values
513          * @return the sum
514          */
515         private static double sum(double[] values) {
516             double sum = 0;
517             for (final double value : values) {
518                 sum += value;
519             }
520             return sum;
521         }
522 
523         /**
524          * Compute the mean of the values. Uses a rolling algorithm to avoid overflow of a simple sum.
525          * This method can be used to compute the mean of observed counts for normalisation to a
526          * probability:
527          *
528          * <pre>
529          * double[] values = ...;
530          * int n = values.length;
531          * double mean = mean(values);
532          * for (int i = 0; i &lt; n; i++) {
533          *     // Two step division avoids using the denominator (mean * n)
534          *     values[i] = values[i] / mean / n;
535          * }
536          * </pre>
537          *
538          * <p>Warning: This method assumes there are input values. If the length is zero an
539          * {@link ArrayIndexOutOfBoundsException} will be thrown.
540          *
541          * @param values the values
542          * @return the mean
543          */
544         private static double mean(double[] values) {
545             double mean = values[0];
546             int i = 1;
547             while (i < values.length) {
548                 // Deviation from the mean
549                 final double dev = values[i] - mean;
550                 i++;
551                 mean += dev / i;
552             }
553             return mean;
554         }
555     }
556 
557     /**
558      * A composite sampler.
559      *
560      * <p>The source sampler for each sampler is chosen based on a user-defined continuous
561      * probability distribution.
562      *
563      * @param <S> Type of sampler
564      */
565     private static class CompositeSampler<S> {
566         /** Continuous sampler to choose the individual sampler to sample. */
567         protected final DiscreteSampler discreteSampler;
568         /** Collection of samplers to be sampled from. */
569         protected final List<S> samplers;
570 
571         /**
572          * @param discreteSampler Continuous sampler to choose the individual sampler to sample.
573          * @param samplers Collection of samplers to be sampled from.
574          */
575         CompositeSampler(DiscreteSampler discreteSampler,
576                          List<S> samplers) {
577             this.discreteSampler = discreteSampler;
578             this.samplers = samplers;
579         }
580 
581         /**
582          * Gets the next sampler to use to create a sample.
583          *
584          * @return the sampler
585          */
586         S nextSampler() {
587             return samplers.get(discreteSampler.sample());
588         }
589     }
590 
591     /**
592      * A factory for creating a composite ObjectSampler.
593      *
594      * @param <T> Type of sample
595      */
596     private static class ObjectSamplerFactory<T> implements
597             SamplerBuilder.SamplerFactory<ObjectSampler<T>> {
598         /** The instance. */
599         @SuppressWarnings("rawtypes")
600         private static final ObjectSamplerFactory INSTANCE = new ObjectSamplerFactory();
601 
602         /**
603          * Get an instance.
604          *
605          * @param <T> Type of sample
606          * @return the factory
607          */
608         @SuppressWarnings("unchecked")
609         static <T> ObjectSamplerFactory<T> instance() {
610             return (ObjectSamplerFactory<T>) INSTANCE;
611         }
612 
613         @Override
614         public ObjectSampler<T> createSampler(DiscreteSampler discreteSampler,
615                                               List<ObjectSampler<T>> samplers) {
616             return new CompositeObjectSampler<>(discreteSampler, samplers);
617         }
618 
619         /**
620          * A composite object sampler.
621          *
622          * @param <T> Type of sample
623          */
624         private static class CompositeObjectSampler<T>
625                 extends CompositeSampler<ObjectSampler<T>>
626                 implements ObjectSampler<T> {
627             /**
628              * @param discreteSampler Discrete sampler to choose the individual sampler to sample.
629              * @param samplers Collection of samplers to be sampled from.
630              */
631             CompositeObjectSampler(DiscreteSampler discreteSampler,
632                                    List<ObjectSampler<T>> samplers) {
633                 super(discreteSampler, samplers);
634             }
635 
636             @Override
637             public T sample() {
638                 return nextSampler().sample();
639             }
640         }
641     }
642 
643     /**
644      * A factory for creating a composite SharedStateObjectSampler.
645      *
646      * @param <T> Type of sample
647      */
648     private static class SharedStateObjectSamplerFactory<T> implements
649             SamplerBuilder.SamplerFactory<SharedStateObjectSampler<T>> {
650         /** The instance. */
651         @SuppressWarnings("rawtypes")
652         private static final SharedStateObjectSamplerFactory INSTANCE = new SharedStateObjectSamplerFactory();
653 
654         /**
655          * Get an instance.
656          *
657          * @param <T> Type of sample
658          * @return the factory
659          */
660         @SuppressWarnings("unchecked")
661         static <T> SharedStateObjectSamplerFactory<T> instance() {
662             return (SharedStateObjectSamplerFactory<T>) INSTANCE;
663         }
664 
665         @Override
666         public SharedStateObjectSampler<T> createSampler(DiscreteSampler discreteSampler,
667                                                          List<SharedStateObjectSampler<T>> samplers) {
668             // The input discrete sampler is assumed to be a SharedStateDiscreteSampler
669             return new CompositeSharedStateObjectSampler<>(
670                 (SharedStateDiscreteSampler) discreteSampler, samplers);
671         }
672 
673         /**
674          * A composite object sampler with shared state support.
675          *
676          * <p>The source sampler for each sampler is chosen based on a user-defined
677          * discrete probability distribution.
678          *
679          * @param <T> Type of sample
680          */
681         private static class CompositeSharedStateObjectSampler<T>
682                 extends CompositeSampler<SharedStateObjectSampler<T>>
683                 implements SharedStateObjectSampler<T> {
684             /**
685              * @param discreteSampler Discrete sampler to choose the individual sampler to sample.
686              * @param samplers Collection of samplers to be sampled from.
687              */
688             CompositeSharedStateObjectSampler(SharedStateDiscreteSampler discreteSampler,
689                                               List<SharedStateObjectSampler<T>> samplers) {
690                 super(discreteSampler, samplers);
691             }
692 
693             @Override
694             public T sample() {
695                 return nextSampler().sample();
696             }
697 
698             @Override
699             public CompositeSharedStateObjectSampler<T> withUniformRandomProvider(UniformRandomProvider rng) {
700                 // Duplicate each sampler with the same source of randomness
701                 return new CompositeSharedStateObjectSampler<>(
702                     ((SharedStateDiscreteSampler) this.discreteSampler).withUniformRandomProvider(rng),
703                     copy(samplers, rng));
704             }
705         }
706     }
707 
708     /**
709      * A factory for creating a composite DiscreteSampler.
710      */
711     private static class DiscreteSamplerFactory implements
712             SamplerBuilder.SamplerFactory<DiscreteSampler> {
713         /** The instance. */
714         static final DiscreteSamplerFactory INSTANCE = new DiscreteSamplerFactory();
715 
716         @Override
717         public DiscreteSampler createSampler(DiscreteSampler discreteSampler,
718                                              List<DiscreteSampler> samplers) {
719             return new CompositeDiscreteSampler(discreteSampler, samplers);
720         }
721 
722         /**
723          * A composite discrete sampler.
724          */
725         private static class CompositeDiscreteSampler
726                 extends CompositeSampler<DiscreteSampler>
727                 implements DiscreteSampler {
728             /**
729              * @param discreteSampler Discrete sampler to choose the individual sampler to sample.
730              * @param samplers Collection of samplers to be sampled from.
731              */
732             CompositeDiscreteSampler(DiscreteSampler discreteSampler,
733                                      List<DiscreteSampler> samplers) {
734                 super(discreteSampler, samplers);
735             }
736 
737             @Override
738             public int sample() {
739                 return nextSampler().sample();
740             }
741         }
742     }
743 
744     /**
745      * A factory for creating a composite SharedStateDiscreteSampler.
746      */
747     private static class SharedStateDiscreteSamplerFactory implements
748             SamplerBuilder.SamplerFactory<SharedStateDiscreteSampler> {
749         /** The instance. */
750         static final SharedStateDiscreteSamplerFactory INSTANCE = new SharedStateDiscreteSamplerFactory();
751 
752         @Override
753         public SharedStateDiscreteSampler createSampler(DiscreteSampler discreteSampler,
754                                                         List<SharedStateDiscreteSampler> samplers) {
755             // The input discrete sampler is assumed to be a SharedStateDiscreteSampler
756             return new CompositeSharedStateDiscreteSampler(
757                 (SharedStateDiscreteSampler) discreteSampler, samplers);
758         }
759 
760         /**
761          * A composite discrete sampler with shared state support.
762          */
763         private static class CompositeSharedStateDiscreteSampler
764                 extends CompositeSampler<SharedStateDiscreteSampler>
765                 implements SharedStateDiscreteSampler {
766             /**
767              * @param discreteSampler Discrete sampler to choose the individual sampler to sample.
768              * @param samplers Collection of samplers to be sampled from.
769              */
770             CompositeSharedStateDiscreteSampler(SharedStateDiscreteSampler discreteSampler,
771                                                 List<SharedStateDiscreteSampler> samplers) {
772                 super(discreteSampler, samplers);
773             }
774 
775             @Override
776             public int sample() {
777                 return nextSampler().sample();
778             }
779 
780             @Override
781             public CompositeSharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
782                 // Duplicate each sampler with the same source of randomness
783                 return new CompositeSharedStateDiscreteSampler(
784                     ((SharedStateDiscreteSampler) this.discreteSampler).withUniformRandomProvider(rng),
785                     copy(samplers, rng));
786             }
787         }
788     }
789 
790     /**
791      * A factory for creating a composite ContinuousSampler.
792      */
793     private static class ContinuousSamplerFactory implements
794             SamplerBuilder.SamplerFactory<ContinuousSampler> {
795         /** The instance. */
796         static final ContinuousSamplerFactory INSTANCE = new ContinuousSamplerFactory();
797 
798         @Override
799         public ContinuousSampler createSampler(DiscreteSampler discreteSampler,
800                                                List<ContinuousSampler> samplers) {
801             return new CompositeContinuousSampler(discreteSampler, samplers);
802         }
803 
804         /**
805          * A composite continuous sampler.
806          */
807         private static class CompositeContinuousSampler
808                 extends CompositeSampler<ContinuousSampler>
809                 implements ContinuousSampler {
810             /**
811              * @param discreteSampler Continuous sampler to choose the individual sampler to sample.
812              * @param samplers Collection of samplers to be sampled from.
813              */
814             CompositeContinuousSampler(DiscreteSampler discreteSampler,
815                                        List<ContinuousSampler> samplers) {
816                 super(discreteSampler, samplers);
817             }
818 
819             @Override
820             public double sample() {
821                 return nextSampler().sample();
822             }
823         }
824     }
825 
826     /**
827      * A factory for creating a composite SharedStateContinuousSampler.
828      */
829     private static class SharedStateContinuousSamplerFactory implements
830             SamplerBuilder.SamplerFactory<SharedStateContinuousSampler> {
831         /** The instance. */
832         static final SharedStateContinuousSamplerFactory INSTANCE = new SharedStateContinuousSamplerFactory();
833 
834         @Override
835         public SharedStateContinuousSampler createSampler(DiscreteSampler discreteSampler,
836                                                           List<SharedStateContinuousSampler> samplers) {
837             // The sampler is assumed to be a SharedStateContinuousSampler
838             return new CompositeSharedStateContinuousSampler(
839                 (SharedStateDiscreteSampler) discreteSampler, samplers);
840         }
841 
842         /**
843          * A composite continuous sampler with shared state support.
844          */
845         private static class CompositeSharedStateContinuousSampler
846                 extends CompositeSampler<SharedStateContinuousSampler>
847                 implements SharedStateContinuousSampler {
848             /**
849              * @param discreteSampler Continuous sampler to choose the individual sampler to sample.
850              * @param samplers Collection of samplers to be sampled from.
851              */
852             CompositeSharedStateContinuousSampler(SharedStateDiscreteSampler discreteSampler,
853                                                   List<SharedStateContinuousSampler> samplers) {
854                 super(discreteSampler, samplers);
855             }
856 
857             @Override
858             public double sample() {
859                 return nextSampler().sample();
860             }
861 
862             @Override
863             public CompositeSharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) {
864                 // Duplicate each sampler with the same source of randomness
865                 return new CompositeSharedStateContinuousSampler(
866                     ((SharedStateDiscreteSampler) this.discreteSampler).withUniformRandomProvider(rng),
867                     copy(samplers, rng));
868             }
869         }
870     }
871 
872     /**
873      * A factory for creating a composite LongSampler.
874      */
875     private static class LongSamplerFactory implements
876             SamplerBuilder.SamplerFactory<LongSampler> {
877         /** The instance. */
878         static final LongSamplerFactory INSTANCE = new LongSamplerFactory();
879 
880         @Override
881         public LongSampler createSampler(DiscreteSampler discreteSampler,
882                                          List<LongSampler> samplers) {
883             return new CompositeLongSampler(discreteSampler, samplers);
884         }
885 
886         /**
887          * A composite long sampler.
888          */
889         private static class CompositeLongSampler
890                 extends CompositeSampler<LongSampler>
891                 implements LongSampler {
892             /**
893              * @param discreteSampler Long sampler to choose the individual sampler to sample.
894              * @param samplers Collection of samplers to be sampled from.
895              */
896             CompositeLongSampler(DiscreteSampler discreteSampler,
897                                  List<LongSampler> samplers) {
898                 super(discreteSampler, samplers);
899             }
900 
901             @Override
902             public long sample() {
903                 return nextSampler().sample();
904             }
905         }
906     }
907 
908     /**
909      * A factory for creating a composite SharedStateLongSampler.
910      */
911     private static class SharedStateLongSamplerFactory implements
912             SamplerBuilder.SamplerFactory<SharedStateLongSampler> {
913         /** The instance. */
914         static final SharedStateLongSamplerFactory INSTANCE = new SharedStateLongSamplerFactory();
915 
916         @Override
917         public SharedStateLongSampler createSampler(DiscreteSampler discreteSampler,
918                                                     List<SharedStateLongSampler> samplers) {
919             // The input discrete sampler is assumed to be a SharedStateLongSampler
920             return new CompositeSharedStateLongSampler(
921                 (SharedStateDiscreteSampler) discreteSampler, samplers);
922         }
923 
924         /**
925          * A composite long sampler with shared state support.
926          */
927         private static class CompositeSharedStateLongSampler
928                 extends CompositeSampler<SharedStateLongSampler>
929                 implements SharedStateLongSampler {
930             /**
931              * @param discreteSampler Long sampler to choose the individual sampler to sample.
932              * @param samplers Collection of samplers to be sampled from.
933              */
934             CompositeSharedStateLongSampler(SharedStateDiscreteSampler discreteSampler,
935                                             List<SharedStateLongSampler> samplers) {
936                 super(discreteSampler, samplers);
937             }
938 
939             @Override
940             public long sample() {
941                 return nextSampler().sample();
942             }
943 
944             @Override
945             public CompositeSharedStateLongSampler withUniformRandomProvider(UniformRandomProvider rng) {
946                 // Duplicate each sampler with the same source of randomness
947                 return new CompositeSharedStateLongSampler(
948                     ((SharedStateDiscreteSampler) this.discreteSampler).withUniformRandomProvider(rng),
949                     copy(samplers, rng));
950             }
951         }
952     }
953 
954     /** No public instances. */
955     private CompositeSamplers() {}
956 
957     /**
958      * Create a new builder for a composite {@link ObjectSampler}.
959      *
960      * <p>Note: If the compiler cannot infer the type parameter of the sampler it can be specified
961      * within the diamond operator {@code <T>} preceding the call to
962      * {@code newObjectSamplerBuilder()}, for example:
963      *
964      * <pre>{@code
965      * CompositeSamplers.<double[]>newObjectSamplerBuilder()
966      * }</pre>
967      *
968      * @param <T> Type of the sample.
969      * @return the builder
970      */
971     public static <T> Builder<ObjectSampler<T>> newObjectSamplerBuilder() {
972         final SamplerBuilder.SamplerFactory<ObjectSampler<T>> factory = ObjectSamplerFactory.instance();
973         return new SamplerBuilder<>(
974             SamplerBuilder.Specialisation.NONE, factory);
975     }
976 
977     /**
978      * Create a new builder for a composite {@link SharedStateObjectSampler}.
979      *
980      * <p>Note: If the compiler cannot infer the type parameter of the sampler it can be specified
981      * within the diamond operator {@code <T>} preceding the call to
982      * {@code newSharedStateObjectSamplerBuilder()}, for example:
983      *
984      * <pre>{@code
985      * CompositeSamplers.<double[]>newSharedStateObjectSamplerBuilder()
986      * }</pre>
987      *
988      * @param <T> Type of the sample.
989      * @return the builder
990      */
991     public static <T> Builder<SharedStateObjectSampler<T>> newSharedStateObjectSamplerBuilder() {
992         final SamplerBuilder.SamplerFactory<SharedStateObjectSampler<T>> factory =
993             SharedStateObjectSamplerFactory.instance();
994         return new SamplerBuilder<>(
995             SamplerBuilder.Specialisation.SHARED_STATE_SAMPLER, factory);
996     }
997 
998     /**
999      * Create a new builder for a composite {@link DiscreteSampler}.
1000      *
1001      * @return the builder
1002      */
1003     public static Builder<DiscreteSampler> newDiscreteSamplerBuilder() {
1004         return new SamplerBuilder<>(
1005             SamplerBuilder.Specialisation.NONE, DiscreteSamplerFactory.INSTANCE);
1006     }
1007 
1008     /**
1009      * Create a new builder for a composite {@link SharedStateDiscreteSampler}.
1010      *
1011      * @return the builder
1012      */
1013     public static Builder<SharedStateDiscreteSampler> newSharedStateDiscreteSamplerBuilder() {
1014         return new SamplerBuilder<>(
1015             SamplerBuilder.Specialisation.SHARED_STATE_SAMPLER, SharedStateDiscreteSamplerFactory.INSTANCE);
1016     }
1017 
1018     /**
1019      * Create a new builder for a composite {@link ContinuousSampler}.
1020      *
1021      * @return the builder
1022      */
1023     public static Builder<ContinuousSampler> newContinuousSamplerBuilder() {
1024         return new SamplerBuilder<>(
1025             SamplerBuilder.Specialisation.NONE, ContinuousSamplerFactory.INSTANCE);
1026     }
1027 
1028     /**
1029      * Create a new builder for a composite {@link SharedStateContinuousSampler}.
1030      *
1031      * @return the builder
1032      */
1033     public static Builder<SharedStateContinuousSampler> newSharedStateContinuousSamplerBuilder() {
1034         return new SamplerBuilder<>(
1035             SamplerBuilder.Specialisation.SHARED_STATE_SAMPLER, SharedStateContinuousSamplerFactory.INSTANCE);
1036     }
1037 
1038     /**
1039      * Create a new builder for a composite {@link LongSampler}.
1040      *
1041      * @return the builder
1042      */
1043     public static Builder<LongSampler> newLongSamplerBuilder() {
1044         return new SamplerBuilder<>(
1045             SamplerBuilder.Specialisation.NONE, LongSamplerFactory.INSTANCE);
1046     }
1047 
1048     /**
1049      * Create a new builder for a composite {@link SharedStateLongSampler}.
1050      *
1051      * @return the builder
1052      */
1053     public static Builder<SharedStateLongSampler> newSharedStateLongSamplerBuilder() {
1054         return new SamplerBuilder<>(
1055             SamplerBuilder.Specialisation.SHARED_STATE_SAMPLER, SharedStateLongSamplerFactory.INSTANCE);
1056     }
1057 
1058     /**
1059      * Create a copy instance of each sampler in the list of samplers using the given
1060      * uniform random provider as the source of randomness.
1061      *
1062      * @param <T> the type of sampler
1063      * @param samplers Source to copy.
1064      * @param rng Generator of uniformly distributed random numbers.
1065      * @return the copy
1066      */
1067     private static <T extends SharedStateSampler<T>> List<T> copy(List<T> samplers,
1068                                                                   UniformRandomProvider rng) {
1069         final ArrayList<T> newSamplers = new ArrayList<>(samplers.size());
1070         for (final T s : samplers) {
1071             newSamplers.add(s.withUniformRandomProvider(rng));
1072         }
1073         return newSamplers;
1074     }
1075 }