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