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 045 * <li>Return a sample from the selected sampler 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="http://en.wikipedia.org/wiki/Probability_distribution#Discrete_probability_distribution"> 074 * discrete probability distribution</a>. 075 */ 076 public interface DiscreteProbabilitySamplerFactory { 077 /** 078 * Creates the sampler. 079 * 080 * @param rng Source of randomness. 081 * @param probabilities Discrete probability distribution. 082 * @return the sampler 083 */ 084 DiscreteSampler create(UniformRandomProvider rng, 085 double[] probabilities); 086 } 087 088 /** 089 * The DiscreteProbabilitySampler class defines implementations that sample from a user-defined 090 * <a href="http://en.wikipedia.org/wiki/Probability_distribution#Discrete_probability_distribution"> 091 * discrete probability distribution</a>. 092 * 093 * <p>All implementations support the {@link SharedStateDiscreteSampler} interface. 094 */ 095 public enum DiscreteProbabilitySampler implements DiscreteProbabilitySamplerFactory { 096 /** Sample using a guide table (see {@link GuideTableDiscreteSampler}). */ 097 GUIDE_TABLE { 098 @Override 099 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}