Skip to content

Commit

Permalink
Merge pull request #68 from jwg4/trimino_error
Browse files Browse the repository at this point in the history
Trimino error
  • Loading branch information
jwg4 authored Oct 20, 2023
2 parents cb4a587 + ff341af commit aff2e74
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 16 deletions.
13 changes: 9 additions & 4 deletions exact_cover/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
from .error import NoSolution
from .io import DTYPE_FOR_ARRAY

import numpy as np


def get_exact_cover(matrix):
if matrix.dtype == DTYPE_FOR_ARRAY:
result = raw_get_exact_cover(matrix)
else:
result = raw_get_exact_cover(matrix.astype(DTYPE_FOR_ARRAY))
transformed = np.require(
matrix, dtype=DTYPE_FOR_ARRAY, requirements=["C_CONTIGUOUS"]
)
assert (
transformed.flags.c_contiguous
), "We depend on the input array being C contiguous for raw goodness."
result = raw_get_exact_cover(transformed)
if result.size == 0:
raise NoSolution("No solutions found by the C code.")
return result
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "exact_cover"
version = "1.2.2"
version = "1.3.0a0"
description = "Solve exact cover problems"
readme = "README.md"
authors = ["Moy Easwaran"]
Expand All @@ -25,6 +25,8 @@ numpy = [
]
setuptools = ">=51.1.2"

# When we build wheels, we always do so with an explicit pinned numpy version
#
[tool.poetry.group.wheel_builder]
optional = true

Expand Down
Binary file added tests/files/tiny_cover_problem.npy
Binary file not shown.
25 changes: 25 additions & 0 deletions tests/test_exact_cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,31 @@ def test_exact_cover():
np.testing.assert_array_equal(actual, expected)


def test_exact_cover_c_order_array():
data = np.array(
[[1, 0, 0], [0, 1, 0], [0, 1, 1], [0, 0, 1]], dtype=DTYPE_FOR_ARRAY, order="C"
)
expected = np.array([0, 1, 3])
actual = get_exact_cover(data)
np.testing.assert_array_equal(actual, expected)


def test_exact_cover_fortran_order_array():
data = np.array(
[[1, 0, 0], [0, 1, 0], [0, 1, 1], [0, 0, 1]], dtype=DTYPE_FOR_ARRAY, order="F"
)
expected = np.array([0, 1, 3])
actual = get_exact_cover(data)
np.testing.assert_array_equal(actual, expected)


def test_exact_cover_read_from_file():
data = np.load("tests/files/tiny_cover_problem.npy")
expected = np.array([0, 1, 3])
actual = get_exact_cover(data)
np.testing.assert_array_equal(actual, expected)


@given(sampled_from([np.int32, np.int8, np.bool_, None, DTYPE_FOR_ARRAY]))
def test_exact_cover_different_dtypes(dtype):
"""
Expand Down
22 changes: 19 additions & 3 deletions tests/test_exact_cover_problems.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from hypothesis import given, example
from hypothesis.strategies import integers, lists, booleans
from hypothesis.strategies import composite, one_of, permutations
from hypothesis.strategies import just
from hypothesis.strategies import just, sampled_from

from exact_cover import get_exact_cover
from exact_cover.error import NoSolution
Expand All @@ -13,6 +13,19 @@
from tests.config import GLOBAL_CONFIG


@composite
def numpy_array_params(draw):
# Mix up the way we construct a numpy array a bit, to ensure stability.
order = draw(sampled_from([None, "C", "F"]))
dtype = draw(sampled_from([None, DTYPE_FOR_ARRAY, np.bool_, np.int8, np.int32]))
params = {}
if order is not None:
params["order"] = order
if dtype is not None:
params["dtype"] = dtype
return params


@composite
def exact_cover_problem(draw):
width = draw(integers(min_value=1, max_value=15))
Expand All @@ -21,7 +34,9 @@ def exact_cover_problem(draw):
lists(booleans(), min_size=width, max_size=width), min_size=1, max_size=30
)
)
return np.array(data, dtype=DTYPE_FOR_ARRAY)
params = draw(numpy_array_params())

return np.array(data, **params)


@given(exact_cover_problem())
Expand Down Expand Up @@ -56,7 +71,8 @@ def array_with_exact_cover(draw):
cover_data = [[a == i for a in cover] for i in range(0, cover_size)]
data = cover_data + dummy_data
shuffled_data = draw(permutations(data))
return np.array(shuffled_data, dtype=DTYPE_FOR_ARRAY)
params = draw(numpy_array_params())
return np.array(shuffled_data, **params)


@composite
Expand Down
15 changes: 11 additions & 4 deletions tests/test_trimino_based.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from exact_cover import get_exact_cover
from exact_cover.error import NoSolution
from exact_cover.io import DTYPE_FOR_ARRAY

# from exact_cover.helpers import is_solution

DTYPE = dict(dtype=DTYPE_FOR_ARRAY)
Expand Down Expand Up @@ -31,6 +32,7 @@
# |xx| | |
# +--+--+--+


# the exact cover matrix built manually
def input1():
to_cover = [
Expand All @@ -51,27 +53,32 @@ def input1():
]
return np.array(to_cover, **DTYPE)


def input2():
return np.load("tests/files/small_trimino_problem.npy")


def test_inputs_are_equal():
m1 = input1()
m2 = input2()

# really equal, including their dtype
assert (np.all(np.equal(m1, m2))
and (m1.dtype is m2.dtype)
and (m1.shape == m2.shape))
assert (
np.all(np.equal(m1, m2)) and (m1.dtype is m2.dtype) and (m1.shape == m2.shape)
)


def run_on_input(array, expected):
try:
solution = get_exact_cover(array)
assert np.all(solution == expected)
except NoSolution as exc:
except NoSolution:
pytest.fail(f"no solution found for {array}")


def test_input1():
run_on_input(input1(), [5, 13])


def test_input2():
run_on_input(input2(), [5, 13])
11 changes: 7 additions & 4 deletions tools/run_tests.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import doctest
import sys

import pytest

from tests.config import GLOBAL_CONFIG # noqa: F401


def test():
pytest.main()
ret = pytest.main()
sys.exit(ret)


def quicktest():
global GLOBAL_CONFIG
GLOBAL_CONFIG["SKIP_SLOW"] = True
pytest.main(["--fail-slow", "2s"])
ret = pytest.main(["--fail-slow", "2s"])
sys.exit(ret)


def run_doctest():
doctest.testfile("../README.md")
doctest.testfile("../examples.md")
doctest.testfile("../README.md", raise_on_error=True)
doctest.testfile("../examples.md", raise_on_error=True)

0 comments on commit aff2e74

Please sign in to comment.