New shape samplers have been added to the library to sampler coordinates
from different shapes (see RNG-132 [1]).
I have been working on an idea to combine shape samplers together so that a
more complex shape can be sampled, for example a surface or volume.
This requires that different samplers can be combined as a common sampler.
This is facilitated by adding new interfaces to the library which are the
generic typed version of the current DiscreteSampler (for int) and
ContinuousSampler (for double) and their SharedStateSampler extensions:
public interface ObjectSampler {
T sample();
}
public interface SharedStateObjectSampler extends
ObjectSampler,
SharedStateSampler> {
// Composite interface
}
All the samplers in the library that create object samples already use the
method name sample() and implement SharedStateSampler. The exception is the
UnitSphereSampler which has a sampling method nextVector(). So adding these
interfaces is a small change to facilitate a composite sampler.
The composite sampler should combine many samplers, each with its own
weight. The weights can be used to create a discrete probability
distribution. We have 3 samplers that can sample efficiently from this:
GuideTableDiscreteSampler
AliasMethodDiscreteSampler
MarsagliaTsangWangDiscreteSampler.Enumerated
So a composite sampler must accept a set of weighted samplers (of the same
type) and create a discrete sampler to select which one to sample. This is
facilitated using a builder API:
S is the type of sampler
public interface Builder {
int size();
Builder add(S sampler, double weight);
Builder setFactory(DiscreteProbabilitySamplerFactory factory);
// Only works if size > 0
S build(UniformRandomProvider rng);
}
The factory specifies a mechanism to create the users choice of discrete
sampler:
public interface DiscreteProbabilitySamplerFactory {
DiscreteSampler create(UniformRandomProvider rng,
double[] probabilities);
}
It is not required to be set as a default will exist. The choice for
the DiscreteProbabilityCollectionSampler was the GuideTableDiscreteSampler
due to its low construction overhead (see RNG-109 [2]).
A static class provides a mechanism to create composite samplers via
builders typed to the final sample type:
public final class CompositeSamplers {
public static Builder> newObjectSamplerBuilder();
public static Builder>
newSharedStateObjectSamplerBuilder();
public static Builder newDiscreteSamplerBuilder();
public static Builder
newSharedStateDiscreteSamplerBuilder();
public static Builder newContinuousSamplerBuilder();
public static Builder
newSharedStateContinuousSamplerBuilder();
}
An example of usage would be:
UniformRandomProvider rng = ...;
DiscreteSampler dayOfMonth = CompositeSamplers.newDiscreteSamplerBuilder()
.add(DiscreteUniformSampler.of(rng, 1, 31), 31) // Jan
.add(DiscreteUniformSampler.of(rng, 1, 28), 28) // Feb
.add(DiscreteUniformSampler.of(rng, 1, 31), 31) // Mar
.add(DiscreteUniformSampler.of(rng, 1, 30), 30) // Apr
.add(DiscreteUniformSampler.of(rng, 1, 31), 31) // May
.add(DiscreteUniformSampler.of(rng, 1, 30), 30) // Jun
.add(DiscreteUniformSampler.of(rng, 1, 31), 31) // Jul
.add(DiscreteUniformSampler.of(rng, 1, 31), 31) // Aug
.add(DiscreteUniformSampler.of(rng, 1, 30), 30) // Sep
.add(DiscreteUniformSampler.of(rng, 1, 31), 31) // Oct
.add(DiscreteUniformSampler.of(rng, 1, 30), 30) // Nov
.add(DiscreteUniformSampler.of(rng, 1, 31), 31) // Dec
.build(rng);
int day = dayOfMonth.sample();
// Diamond vertices
double[] a = {0, 0};
double[] b = {1, 1};
double[] c = {2, 0};
double[] d = {1, -1};
// Note: The sample type (double[]) must be specified if the builder is not
assigned
ObjectSampler diamond =
CompositeSamplers.newObjectSamplerBuilder()
.add(TriangleSampler.of(a, b, c, rng), 1) // Upper
.add(TriangleSampler.of(a, d, c, rng), 1) // Lower
.build(rng);
double[] coord = diamond.sample();
// Note: Type is inferred if the builder is assigned and then used:
Builder> builder =
CompositeSamplers.newObjectSamplerBuilder();
builder.add(TriangleSampler.of(a, b, c, rng), 1); // Upper
etc.
I have a working version of the above and can create a WIP pull request for
a detailed inspection.
I suggest starting with adding the two new interfaces (ObjectSampler and
SharedStateObjectSampler) and changing the codebase to implement it.
Then adding a composite sampler in a separate change that will require
further discussion.
Note: I had started with the idea of a static factory method:
public static SharedStateObjectSampler of(UniformRandomProvider rng,
List> samplers,
double[] weights) {
This is a similar idea to the factory constructor for
the DiscreteProbabilityCollectionSampler. However to add similar m