-
Notifications
You must be signed in to change notification settings - Fork 588
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Strategies for complex numbers #1076
Comments
Nope, not missing anything, it's probably just got neglected because none of us were actively using it! We'd be delighted to accept a PR. We might have to bikeshed a little bit about the API bit though. e.g. I don't think it makes sense to have a |
Bikeshedding's good, and I was thinking the same thing (in terms of min/max magnitude) since the ring axioms for polynomials are mostly inherited from the coefficients, and for floating point numbers they only apply (even approximately) away from over/underflow. I'll raise the PR in a couple of days. |
I think we have some actual design work to do, not bikeshedding! My main question is what we're doing beyond
I mean, that's strictly speaking an improvement from the zero-argument form we have now - but we could trivially suggest that in the docs for when independent real and imaginary parts are desired. So:
My proposal: implement |
I think it might make sense to generate complex numbers by drawing a magnitude and an angle rather than drawing the two coordinates independently and filtering. This makes narrow magnitudes work essentially automatically. Your proposal is essentially what I was thinking as the natural interface, yes. I don't think having the version that is trivially replaceable with builds makes much sense. |
I've been thinking about magnitude constraints. It seems natural mathematically to draw magnitude and argument from user-specified float ranges then use Euler to convert those values to real-imaginary, but
|
How about this. Draw the real & imaginary parts from Thoughts? |
Sounds at least reasonable to me - I think at this point it would be good to discuss a prototype, so we can look at the shrinking properties in more detail. Are you interested in opening a pull with this more complex (😜) proposal? I'd be happy to help out but don't have the time to write it myself at the moment. |
Sure, I'll have a crack at it. I suggest that in the first instance only the |
I've run into some issues on starting this, I did try IRC but got no responses, so perhaps I can ask in this, more asynchronous channel.
|
Sorry, IRC that time of day is a bit of a mixed bag! I'm likely to be on it on UK work hours and sometimes at other times. Others are present off and on.
#1100 is still a work in progress, but has some pointers. Short version is that if you've got the dependencies installed and src on the path then you can use pytest to run tests.
Some tests that don't have assertions are probably just asserting that they can find the thing at all, as the functions will raise an error if they don't (which should be But...
Uh, you know, I have absolutely no idea what that test is meant to be doing. It looks wrong on several fronts! Certainly if you added an assert to it like I'm pretty sure was intended it should fail. One thing to note is that floating point numbers don't minimize towards 0. Roughly speaking they minimize towards "fractionally simpler" - first by their integer part closer to zero, then by the lowering the number of places after the . in their binary expansion. So that should be |
In reverse order
and stock Ubuntu python
and
Hmm, possibly a missing package? but not clear from the backtrace which. I have Trying the stock pytest:
no
possibly too old? Seems not since
|
Ah, you're missing |
Thanks @Zac-HD ! Installing via
Will now set to coding -- thanks again 😄 |
More design bike-shedding requested. So, suggested above was that the real and imaginary parts be specified along the lines of
where
But the alternative (an "atomic" Still, the latter seems less bad to me than the former. Any thoughts (especially alternatives) on this? |
Here's my suggestion: def complex_numbers(min_magnitude=0, max_magnitude=None,
allow_infinity=None, allow_nan=None):
"""Returns a strategy that generates complex numbers.
This strategy views a complex number as a vector in the complex plane
with some magnitude (length) and argument (angle). If you would prefer
to define complex numbers in terms of real and imaginary parts, you can
do so with::
my_complex_numbers = builds(complex, floats(), floats())
``min_magnitude`` and ``max_magnitude`` must be positive real numbers,
or None for the upper limit.
TODO: describe inf and nan arguments here once behaviour is set
Examples from this strategy shrink by shrinking their magnitude as for
`~hypothesis.strategies.floats`, and simplifying the ratio between the
real and imaginary parts.
"""
# Check that both magnitude bounds are positive
# Check that both bounds are instances of numbers.Real (see issue #814)
# Check that low bound is lower than high bound
# Check that not max_magnitude is not None and allow_infinity and isfinite(max_magnitude)
# If allow_nan is None: allow_nan = max_magnitude is None
# Draw magnitude from floats(min_magnitude, max_magnitude,
# allow_infinity=allow_infinity, allow_nan=False)
# Draw argument. I have a cute scheme for this, but in practise you're
# probably better off drawing from floats(0, 1).map(lambda f: f * math.pi)
# (note: multiply by pi instead of drawing from range for better shrinking)
# Construct and return complex number. Yay trig!
pass Basically, we just document how to implement a strategy in terms of real and imag parts, because it's trivial. We provide the above, defined in terms of magnitude, and just leave more specific requirements to the user - after all, until now there have been no options whatsoever, and you're the first person ever to raise an issue 😄 (oh, and if we use this the whole ComplexStrategy class can be deleted!) |
Ah, so avoid the bulk of keywords by not supporting them, I like it 😄. The mag/arg intermediate representation I want to avoid, since it cannot represent a wide range of values (for large magnitudes, the granularity of floating point theta is amplified). The minimal-bounding/maximal-enclosed squares projected to the annulus in the native representation approach outlined above is easier without the min/max real/imag constraints obviously. |
A quickie, almost certainly me being dozy. With the
I get |
Hard to say without seeing the specific code, but is it possible it's an installed version confusion? e.g. are you accidentally using an installed version of Hypothesis (where |
Oh of course it is, I had manually added the hypothesis src dir to my PYTHONPATH on last session, and meant to add it to my .bashrc for persistence, but forgot; apologies for the noise. |
I'd suggest |
Slowly working through this, and thought that I sort-of understood the ideas. But I've hit a behaviour that I can't seem to grok. In my implementation, I project "out of magnitude" values onto the annulus radially, so the draws from the real and imaginary parts will always result in a complex value satisfying the magnitude constraints. For this test of
but for the constrained version (commented), for which
(those each being projected to (0.493, 0) for the output). It seems that the projection is causing the minimisation to "not try that hard" to simplify the result, but I don't really understand why ... any pointers? |
Simple once you know: Hypothesis minimises at the level of an underlying bytestream, not the output values. I think we therefore need a different technique to improve shrinking: using
Also: I'd suggest rebasing onto the current master, and opening a pull request - the review tools are pretty handy. On a related note, when testing that some combination of arguments does or does not raise InvalidArgument, you can just add them to the list in |
OK, I think I get why the "not try that hard" behaviour happens: since the simplicity of the input drives the shrinking and the input is (0, 0) which is as simple as you get, there's no point in shrinking further. I can see that we will need a proper complex shrinking strategy, but I'm not sure I understand the suggested approach, the second point is a typo surely (invalid arguments) ? In any case, I've moved the |
Closed by #1154. |
I've just started using Hypothesis to check that some polynomial classes from my MVPoly package satisfy the ring axioms. All very smooth, great library. But I'm a bit puzzled that the complex-numbers strategy is so weak (in terms of customisation) when compared to the float strategy. Perhaps I'm missing something.
Of course, one can define one's own strategy, as done here. I wonder if there would be any interest in a tiny PR which ports this into Hypothesis proper?
The text was updated successfully, but these errors were encountered: