Skip to content
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

Numpy strategy fails with timedelta64 dtype #2351

Closed
langfield opened this issue Feb 12, 2020 · 5 comments · Fixed by #2354
Closed

Numpy strategy fails with timedelta64 dtype #2351

langfield opened this issue Feb 12, 2020 · 5 comments · Fixed by #2354
Labels
bug something is clearly wrong here legibility make errors helpful and Hypothesis grokable

Comments

@langfield
Copy link

I'm getting an error I can't make sense of when generating a numpy array with dtype=np.dtype("m8"), which corresponds to dtype=timedelta64, and shape=(1,1).
The trace is given below.

Any help would be greatly appreciated!

If it's useful to know, the context is that I'm trying to write a strategy which will generate an arbitrary numpy array of any valid type and shape, within reasonable memory constraints.

============================= test session starts ==============================
platform linux -- Python 3.7.6, pytest-5.3.2, py-1.8.1, pluggy-0.13.1
rootdir: /home/whitaker/pkgs/asta
plugins: hypothesis-4.57.1
collected 1 item

asta/tests/test_array.py F                                               [100%]

=================================== FAILURES ===================================
_____________________ test_array_passes_generic_isinstance _____________________

    @given(hnp.arrays(dtype=strats.dtypes(), shape=strats.shapes()))
>   def test_array_passes_generic_isinstance(arr: Array) -> None:

asta/tests/test_array.py:14: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/core.py:698: in _execute_once_for_engine
    escalate_hypothesis_internal_error()
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/core.py:669: in _execute_once_for_engine
    result = self.execute_once(data)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/core.py:624: in execute_once
    result = self.test_runner(data, run)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/executors.py:56: in default_new_style_executor
    return function(data)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/core.py:565: in run
    args, kwargs = data.draw(self.search_strategy)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:887: in draw
    return strategy.do_draw(self)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/strategies/_internal/collections.py:61: in do_draw
    return tuple(data.draw(e) for e in self.element_strategies)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/strategies/_internal/collections.py:61: in <genexpr>
    return tuple(data.draw(e) for e in self.element_strategies)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:882: in draw
    return strategy.do_draw(self)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/strategies/_internal/strategies.py:669: in do_draw
    result = self.pack(data.draw(self.mapped_strategy))
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:882: in draw
    return strategy.do_draw(self)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/strategies/_internal/lazy.py:156: in do_draw
    return data.draw(self.wrapped_strategy)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:882: in draw
    return strategy.do_draw(self)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/strategies/_internal/strategies.py:669: in do_draw
    result = self.pack(data.draw(self.mapped_strategy))
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:882: in draw
    return strategy.do_draw(self)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/strategies/_internal/collections.py:61: in do_draw
    return tuple(data.draw(e) for e in self.element_strategies)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/strategies/_internal/collections.py:61: in <genexpr>
    return tuple(data.draw(e) for e in self.element_strategies)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:882: in draw
    return strategy.do_draw(self)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/strategies/_internal/lazy.py:156: in do_draw
    return data.draw(self.wrapped_strategy)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:882: in draw
    return strategy.do_draw(self)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/strategies/_internal/flatmapped.py:46: in do_draw
    return data.draw(expanded_source)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:882: in draw
    return strategy.do_draw(self)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/strategies/_internal/lazy.py:156: in do_draw
    return data.draw(self.wrapped_strategy)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:882: in draw
    return strategy.do_draw(self)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/strategies/_internal/flatmapped.py:46: in do_draw
    return data.draw(expanded_source)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:882: in draw
    return strategy.do_draw(self)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/strategies/_internal/lazy.py:156: in do_draw
    return data.draw(self.wrapped_strategy)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/internal/conjecture/data.py:882: in draw
    return strategy.do_draw(self)
../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/extra/numpy.py:275: in do_draw
    self.set_element(data, one_element, 0, self.fill)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <hypothesis.extra.numpy.ArrayStrategy object at 0x7f370ea63bd0>
data = ConjectureData(VALID, 14 bytes, frozen)
result = array([0], dtype=timedelta64), idx = 0
strategy = from_dtype(dtype('<m8'))

    def set_element(self, data, result, idx, strategy=None):
        strategy = strategy or self.element_strategy
        val = data.draw(strategy)
>       result[idx] = val
E       TypeError: Cannot cast NumPy timedelta64 scalar from metadata [Y] to  according to the rule 'same_kind'

../../conda3/envs/bees/lib/python3.7/site-packages/hypothesis/extra/numpy.py:161: TypeError
----------------------------- Captured stdout call -----------------------------
Type id: m8
---------------------------------- Hypothesis ----------------------------------
You can add @seed(122549914271068826677002920208694012959) to this test or run pytest with --hypothesis-seed=122549914271068826677002920208694012959 to reproduce this failure.
============================== 1 failed in 0.35s ===============================
@Zac-HD
Copy link
Member

Zac-HD commented Feb 13, 2020

I think what must be happening is that the array and the scalar value have different units with no consistent conversion factor (e.g years to days, or days to months).

arrays(dtype="m8", shape=1).example() suffices to reproduce this for me 😅

@Zac-HD Zac-HD added bug something is clearly wrong here legibility make errors helpful and Hypothesis grokable labels Feb 13, 2020
@Zac-HD
Copy link
Member

Zac-HD commented Feb 13, 2020

IMO we should either change our code so that this is not an error, or so that the error message explains what's happening and gives enough information to reproduce the problem.

@langfield
Copy link
Author

Thanks for your input, I think for now I'll just exclude any timedelta scalar types from the strategy. I agree, it would be nice if the extras strategy worked for all dtypes in np.sctypeDict, or raised an error indicated the specified dtype was unsupported by hypothesis, or if there was an built-in option to generate all supported dtypes.

@Zac-HD
Copy link
Member

Zac-HD commented Feb 13, 2020

The scalar_dtypes() or array_dtypes() strategy will do that for you!


The problematic from_type() code is:

elif dtype.kind in ("m", "M"):
if "[" in dtype.str:
res = st.just(dtype.str.split("[")[-1][:-1])
else:
res = st.sampled_from(TIME_RESOLUTIONS)
result = st.builds(dtype.type, st.integers(-(2 ** 63), 2 ** 63 - 1), res)

and I think the second branch should actually be an error, and explain that dtypes without a time unit can't be resolved to a strategy.

We don't have to hurt usability much if we then get arrays() to convert generic datetime64 or timedelta64 dtypes into a sampled_from(...) over each possible resolution. Net result: working as intended via arrays(), forces explicit units to ensure validity via from_dtype().

@langfield
Copy link
Author

Ah fantastic, I should have read the entire docs page before deciding I needed to implement one myself. Thanks for the tip!

to convert generic datetime64 or timedelta64 dtypes into a sampled_from(...) over each possible resolution.

Yes if I understand you correctly that seems like a nice solution that covers every case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug something is clearly wrong here legibility make errors helpful and Hypothesis grokable
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants