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<double[]> sampler =
61 * CompositeSamplers.<double[]>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 final 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 final 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 final 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 List<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 < 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 List<T> newSamplers = new ArrayList<>(samplers.size());
1070 for (final T s : samplers) {
1071 newSamplers.add(s.withUniformRandomProvider(rng));
1072 }
1073 return newSamplers;
1074 }
1075 }