Skip to content

Commit

Permalink
Non-redundant integer_range
Browse files Browse the repository at this point in the history
  • Loading branch information
Zac-HD committed Jul 2, 2019
1 parent 2fd2493 commit b52f59b
Show file tree
Hide file tree
Showing 8 changed files with 33 additions and 44 deletions.
6 changes: 6 additions & 0 deletions hypothesis-python/RELEASE.rst
@@ -0,0 +1,6 @@
RELEASE_TYPE: patch

This patch substantially improves our ability to avoid generating redundant
inputs when choosing between a non-power-of-two number of alternatives.
In certain circumstances, this was causing serious performance problems -
see :issue:`1864`, :issue:`1982`, and :issue:`2027`.
4 changes: 2 additions & 2 deletions hypothesis-python/src/hypothesis/extra/numpy.py
Expand Up @@ -709,8 +709,8 @@ def valid_tuple_axes(ndim, min_size=0, max_size=None):
check_valid_interval(max_size, ndim, "max_size", "ndim")

# shrink axis values from negative to positive
axes = st.integers(0, max(0, 2 * ndim - 1)).map(
lambda x: x if x < ndim else x - 2 * ndim
axes = st.tuples(st.booleans(), st.integers(0, ndim - 1)).map(
lambda args: args[1] - ndim if args[0] else args[1]
)
return st.lists(axes, min_size, max_size, unique_by=lambda x: x % ndim).map(tuple)

Expand Down
16 changes: 11 additions & 5 deletions hypothesis-python/src/hypothesis/internal/conjecture/utils.py
Expand Up @@ -90,7 +90,6 @@ def integer_range(data, lower, upper, center=None):
assert gap > 0

bits = bit_length(gap)
probe = gap + 1

if bits > 24 and data.draw_bits(3):
# For large ranges, we combine the uniform random distribution from draw_bits
Expand All @@ -100,10 +99,17 @@ def integer_range(data, lower, upper, center=None):
sizes = [8, 16, 32, 64, 128]
bits = min(bits, sizes[idx])

while probe > gap:
data.start_example(INTEGER_RANGE_DRAW_LABEL)
probe = data.draw_bits(bits)
data.stop_example(discard=probe > gap)
data.start_example(INTEGER_RANGE_DRAW_LABEL)
probe = data.draw_bits(bits)
data.stop_example()
# Rejection sampling interacts badly with our prefix tree (see e.g. #1864),
# so we want to adjust this value to fit in the range *without* distorting
# the distribution.
high = (1 << bits) - 1
if high > gap:
leave = gap - (high - gap)
if probe > leave:
probe = leave + (probe - leave) // 2

if above:
result = center + probe
Expand Down
23 changes: 1 addition & 22 deletions hypothesis-python/tests/cover/test_conjecture_engine.py
Expand Up @@ -43,7 +43,7 @@
)
from hypothesis.internal.conjecture.shrinker import Shrinker, block_program
from hypothesis.internal.conjecture.shrinking import Float
from hypothesis.internal.conjecture.utils import Sampler, calc_label_from_name
from hypothesis.internal.conjecture.utils import calc_label_from_name
from hypothesis.internal.entropy import deterministic_PRNG
from tests.common.strategies import SLOW, HardToShrink
from tests.common.utils import counts_calls, no_shrink
Expand Down Expand Up @@ -1111,27 +1111,6 @@ def accept(f):
return accept


def test_dependent_block_pairs_is_up_to_shrinking_integers():
# Unit test extracted from a failure in tests/nocover/test_integers.py
distribution = Sampler([4.0, 8.0, 1.0, 1.0, 0.5])

sizes = [8, 16, 32, 64, 128]

@shrinking_from(b"\x03\x01\x00\x00\x00\x00\x00\x01\x00\x02\x01")
def shrinker(data):
size = sizes[distribution.sample(data)]
result = data.draw_bits(size)
sign = (-1) ** (result & 1)
result = (result >> 1) * sign
cap = data.draw_bits(8)

if result >= 32768 and cap == 1:
data.mark_interesting()

shrinker.minimize_individual_blocks()
assert list(shrinker.shrink_target.buffer) == [1, 1, 0, 1, 0, 0, 1]


def test_finding_a_minimal_balanced_binary_tree():
# Tests iteration while the shape of the thing being iterated over can
# change. In particular the current example can go from trivial to non
Expand Down
1 change: 0 additions & 1 deletion hypothesis-python/tests/cover/test_simple_characters.py
Expand Up @@ -130,7 +130,6 @@ def test_whitelisted_characters_override():
assert_no_examples(st, lambda c: c not in good_characters + "0123456789")


@pytest.mark.skip # temporary skip due to 560 second (!) perf regression; see #1864
def test_blacklisted_characters():
bad_chars = u"te02тест49st"
st = characters(
Expand Down
11 changes: 11 additions & 0 deletions hypothesis-python/tests/nocover/test_sampled_from.py
Expand Up @@ -73,3 +73,14 @@ def test_unsat_sets_of_samples(x):
@given(st.sets(st.sampled_from(range(50)), min_size=50))
def test_efficient_sets_of_samples(x):
assert x == set(range(50))


def test_stops_quickly():
# https://github.com/HypothesisWorks/hypothesis/issues/2027
@given(st.sampled_from(range(3)))
def inner(x):
count[0] += 1

count = [0]
inner()
assert 2 < count[0] <= 4
2 changes: 1 addition & 1 deletion hypothesis-python/tests/pandas/test_data_frame.py
Expand Up @@ -211,7 +211,7 @@ def test_uniqueness_does_not_affect_other_rows_1():
pdst.column("A", dtype=int, unique=True),
pdst.column("B", dtype=int, unique=False),
],
rows=st.tuples(st.integers(0, 10), st.integers(0, 10)),
rows=st.tuples(st.integers(0, 3), st.integers(0, 3)),
index=pdst.range_indexes(2, 2),
)
find_any(data_frames, lambda x: x["B"][0] == x["B"][1])
Expand Down
14 changes: 1 addition & 13 deletions hypothesis-python/tests/quality/test_integers.py
Expand Up @@ -20,16 +20,7 @@
from random import Random

import hypothesis.strategies as st
from hypothesis import (
HealthCheck,
Phase,
Verbosity,
assume,
example,
given,
reject,
settings,
)
from hypothesis import HealthCheck, Phase, Verbosity, assume, given, reject, settings
from hypothesis.internal.compat import hbytes
from hypothesis.internal.conjecture.data import ConjectureData, Status, StopTest
from hypothesis.internal.conjecture.engine import ConjectureRunner
Expand All @@ -52,9 +43,6 @@ def problems(draw):
pass


@example((2, b"\x00\x00\n\x01"))
@example((1, b"\x00\x00\x06\x01"))
@example(problem=(32768, b"\x03\x01\x00\x00\x00\x00\x00\x01\x00\x02\x01"))
@settings(
suppress_health_check=HealthCheck.all(),
deadline=None,
Expand Down

0 comments on commit b52f59b

Please sign in to comment.