<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Jun 11, 2018 at 10:26 AM,  <span dir="ltr"><<a href="mailto:josef.pktd@gmail.com" target="_blank">josef.pktd@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote"><div><div class="h5">On Mon, Jun 11, 2018 at 2:43 AM, Ralf Gommers <span dir="ltr"><<a href="mailto:ralf.gommers@gmail.com" target="_blank">ralf.gommers@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote"><span>On Sun, Jun 10, 2018 at 10:36 PM, Robert Kern <span dir="ltr"><<a href="mailto:robert.kern@gmail.com" target="_blank">robert.kern@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><span class="m_1029893216381633408m_-1499330186916221782gmail-">On Sun, Jun 10, 2018 at 8:04 PM Ralf Gommers <<a href="mailto:ralf.gommers@gmail.com" target="_blank">ralf.gommers@gmail.com</a>> wrote:<br>><br>> On Sun, Jun 10, 2018 at 6:08 PM, Robert Kern <<a href="mailto:robert.kern@gmail.com" target="_blank">robert.kern@gmail.com</a>> wrote:<br>>><br>>> On Sun, Jun 10, 2018 at 5:27 PM Ralf Gommers <<a href="mailto:ralf.gommers@gmail.com" target="_blank">ralf.gommers@gmail.com</a>> wrote:<br>>> ><br>>> > On Mon, Jun 4, 2018 at 3:18 PM, Robert Kern <<a href="mailto:robert.kern@gmail.com" target="_blank">robert.kern@gmail.com</a>> wrote:<br>>> >><br>>> >> On Sun, Jun 3, 2018 at 8:22 PM Ralf Gommers <<a href="mailto:ralf.gommers@gmail.com" target="_blank">ralf.gommers@gmail.com</a>> wrote:<br>>> >>><br>>> >>> It may be worth having a look at test suites for scipy, statsmodels, scikit-learn, etc. and estimate how much work this NEP causes those projects. If the devs of those packages are forced to do large scale migrations from RandomState to StableState, then why not instead keep RandomState and just add a new API next to it?<br>>> >><br>>> >> The problem is that we can't really have an ecosystem with two different general purpose systems.<br>>> ><br>>> > Can't = prefer not to.<br>>><br>>> I meant what I wrote. :-)<br>>><br>>> > But yes, that's true. That's not what I was saying though. We want one generic one, and one meant for unit testing only. You can achieve that in two ways:<br>>> > 1. Change the current np.random API to new generic, and add a new RandomStable for unit tests.<br>>> > 2. Add a new generic API, and document the current np.random API as being meant for unit tests only, for other usage <new API> should be preferred.<br>>> ><br>>> > (2) has a couple of pros:<br>>> > - you're not forcing almost every library and end user out there to migrate their unit tests.<br>>><br>>> But it has the cons that I talked about. RandomState *is* a fully functional general purpose PRNG system. After all, that's its current use. Documenting it as intended to be something else will not change that fact. Documentation alone provides no real impetus to move to the new system outside of the unit tests. And the community does need to move together to the new system in their library code, or else we won't be able to combine libraries together; these PRNG objects need to thread all the way through between code from different authors if we are to write programs with a controlled seed. The failure mode when people don't pay attention to the documentation is that I can no longer write programs that compose these libraries together. That's why I wrote "can't". It's not a mere preference for not having two systems to maintain. It has binary Go/No Go implications for building reproducible programs.<br>><br>> I strongly suspect you are right, but only because you're asserting "can't" so heavily. I have trouble formulating what would go wrong in case there's two PRNGs used in a single program. It's not described in the NEP, nor in the numpy.random docs (those don't even have any recommendations for best practices listed as far as I can tell - that needs fixing). All you explain in the NEP is that reproducible research isn't helped by the current stream-compat guarantee. So a bit of (probably incorrect) devil's advocate reasoning:<br>> - If there's no stream-compat guarantee, all a user can rely on is the properties of drawing from a seeded PRNG.<br>> - Any use of a PRNG in library code can also only rely on properties<br>> - So now whether in a user's program libraries draw from one or two seeded PRNGs doesn't matter for reproducibility, because those properties don't change.<br><br></span><div>Correctly making a stochastic program reproducible while retaining good statistical properties is difficult. People don't do it well in the best of circumstances. The best way that we've found to manage that difficulty is to instantiate a single stream and use it all throughout your code. Every new stream requires the management of more seeds (unless if we use the fancy new algorithms that have settable stream IDs, but by stipulation, we don't have these in this case). And now I have to thread both of these objects through my code, and pass the right object to each third-party library. These third-party libraries don't know anything about this weird 2-stream workaround that you are doing, so we now have libraries that can't build on each other unless if they are using the same compatible API, even if I can make workarounds to build a program that combines two libraries side-to-side.</div><div><br></div><div>So yeah, people "can" do this. "It's just a matter of code" as my boss likes to say. But it's making an already-difficult task more difficult.</div></div></blockquote><div><br></div></span><div>Okay, that makes more sense to me now. It would be really useful to document such best practices and rationales. <br></div><div><br></div><div>Note that scipy.stats distributions allow passing in either a RandomState instance or an integer as seed (which will be used for seeding a new instance, not for np.random.seed) [1]. That seems like a fine design pattern as well, and passing on a seed that way is fairly easy and as good for reproducibility as passing in a single PRNG.<br></div><div><br></div><div>[1] <a href="https://github.com/scipy/scipy/blob/master/scipy/stats/_distn_infrastructure.py#L612" target="_blank">https://github.com/scipy/scipy<wbr>/blob/master/scipy/stats/_<wbr>distn_infrastructure.py#L612</a><br></div><span><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div><br></div><div><span class="m_1029893216381633408m_-1499330186916221782gmail-">> Also, if there is to be a multi-year transitioning to the new API, would there be two PRNG systems anyway during those years?<br><br></span>Sure, but with a deadline and not-just-documentation to motivate transitioning.</div><div><br></div><div>But if we follow my alternative proposal, there'll be no need for deprecation! You've convinced me to not deprecate RandomState.</div></div></blockquote><div><br></div></span><div>That's not how I had read it, but great to hear that!</div><span><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div> I just want to change some of its internal implementation details, add a less-stable set of distributions on the side, and a framework of core uniform PRNGs that can be shared by both.<span class="m_1029893216381633408m_-1499330186916221782gmail-"><br><br>>> > - more design freedom for the new generic API. The current one is clearly sub-optimal; in a new one you wouldn't have to expose all the global state/functions that np.random exposes now. You could even restrict it to a single class and put that in the main numpy namespace.<br>>><br>>> I'm not sure why you are talking about the global state and np.random.* convenience functions. What we do with those functions is out of scope for this NEP and would be talked about it another NEP fully introducing the new system.<br>><br>> To quote you from one of the first emails in this thread: "<br>> I deliberately left it out of this one as it may, depending on our choices, impinge upon the design of the new PRNG subsystem, which I declared out of scope for this NEP. I have ideas (besides the glib "Let them eat AttributeErrors!"), and now that I think more about it, that does seem like it might be in scope just like the discussion of freezing RandomState and StableRandom are. But I think I'd like to hold that thought a little bit and get a little more screaming^Wfeedback on the core proposal first. I'll return to this in a few days if not sooner.<br>> "<br>><br>> So consider this some screaming^Wfeedback:)<br><br></span></div><div>Ahem. Yes, I just remembered I said that. :-) But still, there will be lots of options about what to do with np.random.*, whatever proposal we go with. It doesn't really impose constraints on the core proposals.</div><div><br></div><div><span class="m_1029893216381633408m_-1499330186916221782gmail-">>> >> To properly use pseudorandom numbers, I need to instantiate a PRNG and thread it through all of the code in my program: both the parts that I write and the third party libraries that I don't write.<br>>> >><br>>> >> Generating test data for unit tests is separable, though. That's why I propose having a StableRandom built on the new architecture. Its purpose would be well-documented, and in my proposal is limited in features such that it will be less likely to be abused outside of that purpose. If you make it fully-featured, it is more likely to be abused by building library code around it. But even if it is so abused, because it is built on the new architecture, at least I can thread the same core PRNG state through the StableRandom distributions from the abusing library and use the better distributions class elsewhere (randomgen names it "Generator"). Just keeping RandomState around can't work like that because it doesn't have a replaceable core PRNG.<br>>> >><br>>> >> But that does suggest another alternative that we should explore:<br>>> >><br>>> >> The new architecture separates the core uniform PRNG from the wide variety of non-uniform probability distributions. That is, the core PRNG state is encapsulated in a discrete object that can be shared between instances of different distribution-providing classes. numpy.random should provide two such distribution-providing classes. The main one (let us call it ``Generator``, as it is called in the prototype) will follow the new policy: distribution methods can break the stream in feature releases. There will also be a secondary distributions class (let us call it ``LegacyGenerator``) which contains distribution methods exactly as they exist in the current ``RandomState`` implementation. When one combines ``LegacyGenerator`` with the MT19937 core PRNG, it should reproduce the exact same stream as ``RandomState`` for all distribution methods. The ``LegacyGenerator`` methods will be forever frozen. ``numpy.random.RandomState()`` will instantiate a ``LegacyGenerator`` with the MT19937 core PRNG, and whatever tricks needed to make ``isinstance(prng, RandomState)`` and unpickling work should be done. This way of creating the ``LegacyGenerator`` by way of ``RandomState`` will be deprecated, becoming progressively noisier over a number of release cycles, in favor of explicitly instantiating ``LegacyGenerator``.<br>>> >><br>>> >> ``LegacyGenerator`` CAN be used during this deprecation period in library and application code until libraries and applications can migrate to the new ``Generator``. Libraries and applications SHOULD migrate but MUST NOT be forced to. ``LegacyGenerator`` CAN be used to generate test data for unit tests where cross-release stability of the streams is important. Test writers SHOULD consider ways to mitigate their reliance on such stability and SHOULD limit their usage to distribution methods that have fewer cross-platform stability risks.<br>>><br>>> I would appreciate your consideration of this proposal. Does it address your concerns? It addresses my concerns with keeping around a fully-functional RandomState implementation.<br>><br>> My concerns are:<br>> 1. The amount of work caused by making libraries and end users migrate.<br>> 2. That this is a backwards compatibility break, which will cause problems for users who relied on the old guarantees (the arguments in the NEP that the old guarantees weren't 100% watertight don't mean that backcompat doesn't matter at all).<br>><br>> As far as I can tell, this new proposal doesn't deal with those concerns directly. What it does seem to do is making transitioning a bit easier for users that were already using RandomState instances.<br><br></span>Let me drop the deprecation of the name RandomState. RandomState(int_seed) will forever and always create a backwards- and stream-compatible object. No one will have to migrate.</div><div><br></div><div>How does that strike you?<br></div></div></blockquote><div><br></div></span><div>Sounds good.</div></div></div></div></blockquote><div><br></div><div><br></div></div></div><div>I'm trying to catch up here but I'm not sure what the latest version of the proposal is.</div><div><br></div><div>IMO we need a stable stream of random numbers for the various distribution forever. Talking about deprecation misses the point that we don't want to have to migrate our unit tests to a "non-stable" stream of random numbers.</div><div><br></div><div>In terms of user API we need some instance of a random state or random generator that can be used with scikit-learn's check_random_state (which was copied to scipy and will be copied to statsmodels when we get around to it.)</div><div><br></div><div>IMO naming or pure API changes are fine, with deprecation of the old "style", as long as the changes can be done mechanically, e.g. adding "legacy" somewhere in the names or options.</div><div>E.g. for scikit-learn and scipy.stats it might be just a small change in the check_random_state function, but maybe more changes in the unit tests that actually use and create a Random stream.</div><div><br></div><div>Implementation</div><div>I don't know or didn't pay enough attention to the details.</div><div><br></div><div>The proposal sounds now like separating the distribution rvs generation from the underlying random stream. I had thought that was already in the proposal.</div><div><br></div><div>If I were writing this for statsmodels, then I would hand a `method` keyword around that defaults to `method=None` which uses the latest and greatest available method independent of backwards compatibility, and method='stable' or method='legacy' as alternative. And maybe some distribution specific methods.</div><div>This is separate from the option which underlying PRNG method to use.</div><div><br></div><div>IIUC, the choices in the proposal are now 3 combinations</div><div><br></div><div>- legacy: 

<span style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">MT19937 core PRNG + distribution_method='stable'</span></div><div><span style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">- mixed: 

<span style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">MT19937 core PRNG + distribution_method=None</span></span></div><div><span style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><span style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">- new: 

<span style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">??? core PRNG <span style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">+ distribution_method=None</span>

</span></span></span></div><div><br></div><div>where the second might be just a special case of the third option, so it reduces to binary choice.</div><div><br></div><div>aside to </div><span class=""><div>

<span style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:small;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">> The best way that we've found to manage that difficulty is to instantiate a single stream and use it all throughout your code.</span>

<br></div><div><br></div></span><div>First, with check_random_state option it's up to a user</div><div><br></div><div>As a user I have cases where I the use cases are independent and it doesn't matter if it uses the same seed.</div><div>As a user I have cases where I would prefer if two methods use the same random stream. (e.g. bootstrap confidence intervals computed with two different methods where I wouldn't want the difference to come from different random streams when I compare them.)</div><div>Also as a user, in some cases I used two different RandomState instances to get random numbers for different parts of the simulation. (Example: I generate y and x for a regression simulation separately, so that when I increase the number of observations, the initial sample stays the same, i.e. is recreated each time.)<br></div><div><br></div><div>Some of this might make it into library code when statsmodels gets more simulation and bootstrap methods.</div></div></div></div></blockquote><div><br></div><div><br></div><div>> 

<span style="color:rgb(80,0,80);font-family:arial,sans-serif;font-size:12.8px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">Test writers SHOULD consider ways to mitigate their reliance on such stability and SHOULD limit their usage to distribution methods that have fewer cross-platform stability risks.</span>

</div><div><span style="color:rgb(80,0,80);font-family:arial,sans-serif;font-size:12.8px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><br></span></div><div><span style="color:rgb(80,0,80);font-family:arial,sans-serif;font-size:12.8px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">Is there somewhere a list on what might be unstable across platforms?</span></div><div><span style="color:rgb(80,0,80);font-family:arial,sans-serif;font-size:12.8px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><br></span></div><div><span style="color:rgb(80,0,80);font-family:arial,sans-serif;font-size:12.8px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">In statsmodels we struggle quite a bit with cross-platform problems in the unit tests. But most of them are because many test tolerances are pretty tight, and then e.g. linalg noise might fluctuate too much across machines and LAPACK versions. Other cases are because behavior in not nice cases differs across machines and versions, those cases are sometimes added intentionally and sometimes by accident.</span></div><div><span style="color:rgb(80,0,80);font-family:arial,sans-serif;font-size:12.8px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><br></span></div><div><span style="color:rgb(80,0,80);font-family:arial,sans-serif;font-size:12.8px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">But I don't think we had a problem because of random number generation. E.g. for integers we are mostly limited to small numbers, either like in Poisson because exp might overflow, or because test cases are usually small for speed reasons.</span></div><div><span style="color:rgb(80,0,80);font-family:arial,sans-serif;font-size:12.8px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><br></span></div><div><span style="color:rgb(80,0,80);font-family:arial,sans-serif;font-size:12.8px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">Josef</span></div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><br></div><div>Josef</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><br></div><div>Cheers,<br></div><div>Ralf</div><div><br></div></div><br></div></div><span class="">
<br>______________________________<wbr>_________________<br>
NumPy-Discussion mailing list<br>
<a href="mailto:NumPy-Discussion@python.org" target="_blank">NumPy-Discussion@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/numpy-discussion" rel="noreferrer" target="_blank">https://mail.python.org/mailma<wbr>n/listinfo/numpy-discussion</a><br>
<br></span></blockquote></div><br></div></div>
</blockquote></div><br></div></div>