Hi.

Le mar. 30 avr. 2019 à 17:08, Alex Herbert <alex.d.herb...@gmail.com> a écrit :
>
> On 29/04/2019 22:14, Gilles Sadowski wrote:
> > Hello.
> >
> > Le lun. 29 avr. 2019 à 19:09, Alex Herbert <alex.d.herb...@gmail.com> a 
> > écrit :
> >> On 28/04/2019 19:11, Gilles Sadowski wrote:
> >>> Le dim. 28 avr. 2019 à 17:02, Alex Herbert <alex.d.herb...@gmail.com> a 
> >>> écrit :
> >>>>
> >>>>> On 28 Apr 2019, at 00:59, Bernd Eckenfels <e...@zusammenkunft.net> 
> >>>>> wrote:
> >>>>>
> >>>>> Hello,
> >>>>>
> >>>>> Just a question, I am unclear on the terminology, is „jump“ (did I miss 
> >>>>> the discussion leading toot?) something invented here? It sounds to me 
> >>>>> like this is a generator where the state can be cloned and it is 
> >>>>> „seekable“. It probably makes sense to have those two dimensions 
> >>>>> separated anyway.
> >>>> Hi Bernd, thanks for the input.
> >>>>
> >>>> This thread started with the definition:
> >>>> Jump:
> >>>>
> >>>> To create a new instance of the generator that is deterministically 
> >>>> based on the state of the current instance but advanced a set number of 
> >>>> iterations.
> >>>>
> >>>>
> >>>> However it is not required to create a new instance at the same time as 
> >>>> jumping. You are correct in that this is two functionalities:
> >>>>
> >>>> 1. Jump forward in the sequence
> >>>> 2. Copy
> >>>>
> >>>> However the two are coupled. Having jump on its own is useless (why move 
> >>>> forward in the sequence without using it?). So a copy needs to be 
> >>>> created somewhere before/after the jump.
> >>>>
> >>>> The idea of a jump is to create a series of the generator at different 
> >>>> points in the state. The generators can be used for parallel 
> >>>> computations and will be ensured to not overlap in their output sequence 
> >>>> for number of outputs skipped by the jump length.
> >>>>
> >>>> FYI. The generators that support this have jump sizes of 2^64, 96, 128, 
> >>>> 192, 256 and 512. So this is a lot of output sequence to jump.
> >>>>
> >>>> Copy on its own works but for what purpose? If you want a second 
> >>>> generator at the moment you just create a new one (with a different 
> >>>> seed). Duplicate copies of generators is prone to potential pitfalls 
> >>>> where simulations are not as random as you intend. For a special use 
> >>>> case where you wish to run multiple simulations with the same generator 
> >>>> you can use the Restorable interface to save the state of one and 
> >>>> re-create it in other instances.
> >>>>
> >>>> The current thread came to the choice of:
> >>>>
> >>>>>>> So the options are (in all cases returning the copy):
> >>>>>>>
> >>>>>>> 1. createAndJumpCopy
> >>>>>>> 2. copyAndJumpParent
> >>>>>>> 3. jumpParentAndCopy
> >>>>>>> 4. jump and copy separately
> >>>> Jump and copy separately was ruled out to discourage misuse of copy.
> >>>>
> >>>> The current suggestion is 1. Create a copy and jump that ahead. The 
> >>>> current instance is not affected.
> >>>>
> >>>> I now consider this to be weaker for a variety of use cases than 2. This 
> >>>> copies the current state for use and then jumps the parent ahead. So 
> >>>> this alters the state of the parent generator.
> >>>>
> >>>> Note that all other methods of a generator alter its state. So having 
> >>>> jump alter its state is reasonable.
> >>>>
> >>>> The most flexible API is to separate jump and copy into two methods. We 
> >>>> can still support helper functions that take in a Jumpable generator and 
> >>>> create a jump series of generators for parallel work. Separating jump 
> >>>> and copy allows the functionality to be used in a larger number of ways 
> >>>> than any other interface that attempts to combine jump and copy.
> >>>>
> >>>> I am fine with having separate jump and copy. If so the copy method, 
> >>>> being part of the Jumpable interface, will be functionally coupled with 
> >>>> the jump method and should be described in Javadoc with the intended 
> >>>> purpose to use it to copy the parent state either before or after a jump 
> >>>> into a child generator.
> >>>>
> >>>> As a precursor this API is very flexible:
> >>>>
> >>>> JumpableUniformRandomProvider extends UniformRandomProvider {
> >>>>       /** Jump and return same instance. */
> >>>>       JumpableUniformRandomProvider jump();
> >>>>       /** Copy the instance. */
> >>>>       JumpableUniformRandomProvider copy();
> >>>> }
> >>>>
> >>>> Returning the same instance in jump() allows method chaining such as 
> >>>> either:
> >>>>
> >>>> rng.jump().copy();
> >>>> rng.copy().jump();
> >>>>
> >>>> This potential pitfall is that a user may do this:
> >>>>
> >>>> UniformRandomProvider rng1 = rng.copy().jump();
> >>>> UniformRandomProvider rng2 = rng.copy().jump();
> >>>>
> >>>> Where rng1 and 2 will be the same, 1 jump ahead of the parent state. Or:
> >>>>
> >>>> UniformRandomProvider rng1 = rng.jump();
> >>>> UniformRandomProvider rng2 = rng.jump();
> >>>>
> >>>> Where rng, rng1 and rng2 are the same instance all 2 jumps ahead of the 
> >>>> start point.
> >>>>
> >>>> I think our purpose is to provide an API for the generators that can 
> >>>> jump and that is not too restrictive given the use cases we have so far 
> >>>> thought up. There may be other ideas of use cases that cannot be done 
> >>>> with the coupled functionality of:
> >>>>
> >>>> JumpableUniformRandomProvider extends UniformRandomProvider {
> >>>>       /** Copy the instance, then jump ahead. Return the copy of the 
> >>>> previous state. */
> >>>>       JumpableUniformRandomProvider jump();
> >>>> }
> >>>>
> >>>> JumpableUniformRandomProvider extends UniformRandomProvider {
> >>>>       /** Copy the instance, then jump the copy ahead. Return the copy. 
> >>>> The current instance is not affected. */
> >>>>       JumpableUniformRandomProvider jump();
> >>>> }
> >>>>
> >>>> So the split functions without allowing method chaining:
> >>>>
> >>>> JumpableUniformRandomProvider extends UniformRandomProvider {
> >>>>       /** Jump the current instance ahead. */
> >>>>       void jump();
> >>>>       /** Copy the instance. This is intended to be used either before 
> >>>> or after a call to jump()
> >>>>        * to create a series of generators. */
> >>>>       JumpableUniformRandomProvider copy();
> >>>> }
> >>> As you indicated above, there is no advantage in having separate
> >>> "jump()" and "copy()",
> >>> as counter-intuitive it may look at first sight.
> >> So is the favoured approach now to make the jump method return a copy of
> >> the current state and then increment the state of the current instance
> >> with a jump, as in:
> >>
> >> JumpableUniformRandomProvider extends UniformRandomProvider {
> >>       /** Copy the instance, then jump ahead. Return the copy of the 
> >> previous state. */
> >>       JumpableUniformRandomProvider jump();
> >> }
> >>
> >> This would appear to solve more use cases than returning a copy that is
> >> further ahead. For example the dynamic generation of a jump sequence of
> >> an unknown size:
> >>
> >> List<UniformRandomProvider> rngs = ...;
> >> JumpableUniformRandomProvider rng;
> >> for (Data data : Iterable<Data>) {
> >>       rngs.add(rng.jump());
> >> }
> >> // Do something with all the Data and rngs
> > I confirm agreement. ;-)
> OK. I'll modify the Jira tickets to match.
> >
> >> I note that if this is the functionality then the jump() method can be
> >> written to return a UniformRandomProvider:
> >>
> >> JumpableUniformRandomProvider extends UniformRandomProvider {
> >>       /** Copy the instance, then jump ahead. Return the copy of the 
> >> previous state. */
> >>       UniformRandomProvider jump();
> >> }
> >>
> >> It would not have to be enforced to remove jump functionality from the
> >> copy but should make it clear that the returned object is not to be
> >> jumped again.
> > Yes, but a cast could work around the artificial limitation of
> > the return type, while "restrict" prevents misuse.
>
> My point was which of the following do we want the interface to support:
>
> 1. Return JumpableUniformRandomProvider (i.e. a direct copy)
>
> 2. Return UniformRandomProvider (this could be a copy that could be cast
> to Jumpable)
>
> 3. Really return a UniformRandomProvider by enforcing the removal of the
> jump function
>
> For option 3 we will have to provide a helper method somewhere to do
> this. Implementations should then use this to wrap a return copy.
>
> However removal of the jump function by restriction to just
> UniformRandomProvider will also remove support for
> RestorableUniformRandomProvider, and any other interface the
> implementation is providing.
>
> I am thinking that option 2 is OK. It would not restrict the returned
> copy but subtly suggests that the copy is not to be used for jumping.

OK. [Assuming that "jumping" main (only?) case is for a user to speed
up computations, so that it is his responsiblity to not misuse (e.g. "jump"
again) the returned instance.]

> We can enforce actual restriction in the helper method to be added to
> RandomSource to create a jump series of RNGs from a Jumpable one. But
> even then it should be optional (which you previously suggested) since
> downstream consumers of the RNG may want Restore (or Jump) functionality.

No, when getting a "restricted" instance, downstream consumers should
have no way to override it, especially in cases where they could mess
other parts of the code.

> Consider that this could be solved by a smart restriction method that
> takes an EnumSet of the interfaces that should be exposed. We then
> support combinations of the various interfaces. So far we have:
>
> UniformRandomProvider (always)
>
> RestorableUniformRandomProvider (2 methods)
>
> JumpableUniformRandomProvider (1 method)
>
> LongJumpableUniformRandomProvider (1 method)
>
> The total number of combinations of the 3 optional interfaces is 2^3 ==
> 8 (the 3rd row from Pascal's triangle). Although since you can't be
> LongJumpable and not Jumpable this drops to 6. Anyway it will get out of
> hand if more interfaces are added. The point being choosing what to
> restrict to has many options.
>
> My suggestion would be to not enforce restriction in the interface for
> jumps but make the return value the next level up the hierarchy:
>
> longJump returns JumpableUniformRandomProvider
>
> jump returns UniformRandomProvider

Yes, that seems interesting from a usage POV.

> Then in the helper method to create a jump series the returned array can
> be the same type (UniformRandomProvider or
> JumpableUniformRandomProvider) and the method has an option to enforce
> this interface by wrapping.

But IIUC, the now preferred implementation make the convenience
method not necessary (?).

Best,
Gilles

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org
For additional commands, e-mail: dev-h...@commons.apache.org

Reply via email to