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

Test filled compare slow #108

Merged
merged 2 commits into from Feb 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/base.h
Expand Up @@ -99,6 +99,9 @@ class BaseContourGenerator
void closed_line_wrapper(
const Location& start_location, OuterOrHole outer_or_hole, ChunkLocal& local);

// If point/line/hole counts not consistent, throw runtime error.
void check_consistent_counts(const ChunkLocal& local) const;

// Write points and offsets/codes to output numpy arrays.
void export_filled(ChunkLocal& local, std::vector<py::list>& return_lists);

Expand Down
103 changes: 68 additions & 35 deletions src/base_impl.h
Expand Up @@ -235,6 +235,64 @@ void BaseContourGenerator<Derived>::closed_line(
local.hole_count++;
}

template <typename Derived>
void BaseContourGenerator<Derived>::check_consistent_counts(const ChunkLocal& local) const
{
if (local.total_point_count > 0) {
if (local.points.size != 2*local.total_point_count ||
local.points.current != local.points.start + 2*local.total_point_count) {
throw std::runtime_error(
"Inconsistent total_point_count for chunk " + std::to_string(local.chunk) +
". This may indicate a bug in ContourPy.");
}
}
else {
if (local.points.size != 0 ||
local.points.start != nullptr || local.points.current != nullptr) {
throw std::runtime_error(
"Inconsistent zero total_point_count for chunk " + std::to_string(local.chunk) +
". This may indicate a bug in ContourPy.");
}
}

if (local.line_count > 0) {
if (local.line_offsets.size != local.line_count + 1 ||
local.line_offsets.current == nullptr ||
local.line_offsets.current != local.line_offsets.start + local.line_count + 1) {
throw std::runtime_error(
"Inconsistent line_count for chunk " + std::to_string(local.chunk) +
". This may indicate a bug in ContourPy.");
}
}
else {
if (local.line_offsets.size != 0 ||
local.line_offsets.start != nullptr || local.line_offsets.current != nullptr) {
throw std::runtime_error(
"Inconsistent zero line_count for chunk " + std::to_string(local.chunk) +
". This may indicate a bug in ContourPy.");
}
}

if (_identify_holes && local.line_count > 0) {
if (local.outer_offsets.size != local.line_count - local.hole_count + 1 ||
local.outer_offsets.current == nullptr ||
local.outer_offsets.current != local.outer_offsets.start + local.line_count -
local.hole_count + 1) {
throw std::runtime_error(
"Inconsistent hole_count for chunk " + std::to_string(local.chunk) +
". This may indicate a bug in ContourPy.");
}
}
else {
if (local.outer_offsets.size != 0 ||
local.outer_offsets.start != nullptr || local.outer_offsets.current != nullptr) {
throw std::runtime_error(
"Inconsistent zero hole_count for chunk " + std::to_string(local.chunk) +
". This may indicate a bug in ContourPy.");
}
}
}

template <typename Derived>
void BaseContourGenerator<Derived>::closed_line_wrapper(
const Location& start_location, OuterOrHole outer_or_hole, ChunkLocal& local)
Expand Down Expand Up @@ -2159,46 +2217,21 @@ void BaseContourGenerator<Derived>::march_chunk(
}
} // pass

// Check both passes returned same number of points, lines, etc.
if (local.total_point_count == 0) {
assert(local.points.size == 0);
assert(local.points.start == nullptr && local.points.current == nullptr);
}
else {
assert(local.points.size == 2*local.total_point_count);
assert(local.points.current = local.points.start + 2*local.total_point_count);
}

// Set final line and outer offsets.
if (local.line_count > 0) {
assert(local.line_offsets.size == local.line_count+1);
assert(local.line_offsets.current != nullptr);
assert(local.line_offsets.current == local.line_offsets.start + local.line_count);

// Append final total_point_count to line_offsets.
*local.line_offsets.current++ = local.total_point_count;
}
else {
assert(local.line_offsets.size == 0);
assert(local.line_offsets.start == nullptr && local.line_offsets.current == nullptr);
}

if (_identify_holes && local.line_count > 0) {
assert(local.outer_offsets.size == local.line_count - local.hole_count + 1);
assert(local.outer_offsets.current != nullptr);
assert(local.outer_offsets.current ==
local.outer_offsets.start + local.line_count - local.hole_count);

// Append final total_point_count or line_count to outer_offsets.
if (_outer_offsets_into_points)
*local.outer_offsets.current++ = local.total_point_count;
else
*local.outer_offsets.current++ = local.line_count;
}
else {
assert(local.outer_offsets.size == 0);
assert(local.outer_offsets.start == nullptr && local.outer_offsets.current == nullptr);
if (_identify_holes) {
if (_outer_offsets_into_points)
*local.outer_offsets.current++ = local.total_point_count;
else
*local.outer_offsets.current++ = local.line_count;
}
}

// Throw exception if the two passes returned different number of points, lines, etc.
check_consistent_counts(local);

if (local.total_point_count == 0) {
if (_output_chunked) {
typename Derived::Lock lock(static_cast<Derived&>(*this));
Expand Down
21 changes: 21 additions & 0 deletions tests/conftest.py
@@ -0,0 +1,21 @@
import pytest


def pytest_addoption(parser):
parser.addoption(
"--runslow", action="store_true", default=False, help="run slow tests"
)


def pytest_configure(config):
config.addinivalue_line("markers", "slow: mark test as slow to run")


def pytest_collection_modifyitems(config, items):
if config.getoption("--runslow"):
# --runslow given in cli: do not skip slow tests
return
skip_slow = pytest.mark.skip(reason="need --runslow option to run")
for item in items:
if "slow" in item.keywords:
item.add_marker(skip_slow)
33 changes: 33 additions & 0 deletions tests/test_filled.py
@@ -1,5 +1,7 @@
from functools import reduce
import numpy as np
from numpy.testing import assert_array_equal
from operator import add
import pytest

from contourpy import contour_generator, FillType
Expand Down Expand Up @@ -354,3 +356,34 @@ def test_filled_random_big(name, fill_type, corner_mask):
for i in range(len(levels)-1):
filled = cont_gen.filled(levels[i], levels[i+1])
util_test.assert_filled(filled, fill_type)


@pytest.mark.slow
@pytest.mark.parametrize('seed', np.arange(1000))
def test_filled_compare_slow(seed):
x, y, z = random((1000, 1000), seed=seed)
levels = np.arange(0.0, 1.01, 0.1)

cont_gen_mpl2014 = contour_generator(x, y, z, name="mpl2014", corner_mask=True)
cont_gen_serial = contour_generator(
x, y, z, name="serial", corner_mask=True, fill_type=FillType.ChunkCombinedOffsetOffset)

assert cont_gen_mpl2014.fill_type == FillType.OuterCode
assert cont_gen_serial.fill_type == FillType.ChunkCombinedOffsetOffset

for i in range(len(levels)-1):
filled_mpl2014 = cont_gen_mpl2014.filled(levels[i], levels[i+1])
util_test.assert_filled(filled_mpl2014, cont_gen_mpl2014.fill_type)

filled_serial = cont_gen_serial.filled(levels[i], levels[i+1])
util_test.assert_filled(filled_serial, cont_gen_serial.fill_type)

# Check same results obtained for each in terms of number of points, etc.
code_list = filled_mpl2014[1]
n_points = reduce(add, map(len, code_list))
n_outers = len(code_list)
n_lines = reduce(add, map(lambda c: np.count_nonzero(c == 1), code_list))

assert n_points == len(filled_serial[0][0])
assert n_lines == len(filled_serial[1][0]) - 1
assert n_outers == len(filled_serial[2][0]) - 1