Skip to content

Commit

Permalink
more tests
Browse files Browse the repository at this point in the history
renamed data.py into problems.py
add tests/bruteforce.py to compute brute force solutions
3 more problems with 2, 9 and 16 solutions
  • Loading branch information
parmentelat committed Nov 15, 2023
1 parent 26d8435 commit 4faecc5
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 74 deletions.
23 changes: 23 additions & 0 deletions tests/bruteforce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
bruteforce algorithm to compute the expected solutions
and help write tests
"""

from itertools import chain, combinations

import numpy as np

# from the itertools module documentation
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))


def bruteforce(data):
"""
Brute-force generator of all exact cover solutions
"""
for subset in powerset(range(data.shape[0])):
if np.all(data[list(subset)].sum(axis=0) == 1):
yield subset
71 changes: 0 additions & 71 deletions tests/data.py

This file was deleted.

126 changes: 126 additions & 0 deletions tests/problems.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import numpy as np

from exact_cover.io import DTYPE_FOR_ARRAY

# one specific problem that I had trouble with
# originally based on solving the trivial problem
# of arranging 2 identical triminos on a 3x3 board

# +--+
# | |
# +--+--+
# | | |
# +--+--+

# +--+--+--+
# |xx| |xx|
# +--+--+--+
# | | | |
# +--+--+--+
# |xx| | |
# +--+--+--+

# this problem has 2 solutions
# (5, 13) and (6, 12)
def small_trimino_problem():
to_cover = [
[1, 0, 0, 1, 1, 0, 1, 0],
[1, 0, 0, 0, 1, 1, 0, 1],
[1, 0, 0, 0, 1, 1, 1, 0],
[1, 0, 1, 0, 1, 1, 0, 0],
[1, 0, 0, 0, 1, 0, 1, 1],
[1, 0, 1, 1, 1, 0, 0, 0], # <- 5
[1, 0, 0, 0, 0, 1, 1, 1], # <- 6
[0, 1, 0, 1, 1, 0, 1, 0],
[0, 1, 0, 0, 1, 1, 0, 1],
[0, 1, 0, 0, 1, 1, 1, 0],
[0, 1, 1, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 1, 0, 1, 1],
[0, 1, 1, 1, 1, 0, 0, 0], # <- 12
[0, 1, 0, 0, 0, 1, 1, 1], # <- 13
]
return dict(
data=np.array(to_cover, dtype=DTYPE_FOR_ARRAY),
solution1=[5, 13],
solution_count=2,
)

def small_trimino_problem_from_file():
return dict(
data=np.load("tests/files/small_trimino_problem.npy"),
solution1=[5, 13],
solution_count=2,
)

# https://en.wikipedia.org/wiki/Exact_cover#Detailed_example
def detailed_wikipedia_problem():
sets = [
{1, 4, 7},
{1, 4}, # <- 1
{4, 5, 7},
{3, 5, 6}, # <- 3
{2, 3, 6, 7},
{2, 7}, # <- 5
]
return dict(
data=np.array(
[[1 if i in s else 0 for i in range(1, 8)] for s in sets],
dtype=DTYPE_FOR_ARRAY),
solution1=[1, 3, 5],
solution_count=1,
)

def bruteforce_problem1():
to_cover = [
[1, 0, 0, 1, 0, 0, 1, 0], # <- sol1
[0, 1, 0, 0, 1, 0, 0, 1], # <- sol1
[0, 0, 1, 0, 0, 1, 0, 0], # <- sol1
[0, 0, 0, 1, 0, 0, 0, 0], # <- sol2
[1, 0, 1, 0, 1, 0, 0, 1], # <- sol2
[0, 1, 0, 0, 0, 1, 1, 0], # <- sol2

]
return dict(
data=np.array(to_cover, dtype=DTYPE_FOR_ARRAY),
solution1=[0, 1, 2],
solution_count=2,
)

def bruteforce_problem2():
to_cover = [
[1, 0, 0, 1, 0, 0, 1, 0], # <- sol1
[0, 1, 0, 0, 1, 0, 0, 1], # <- sol1
[0, 0, 1, 0, 0, 1, 0, 0], # <- sol1
[0, 0, 0, 1, 0, 0, 0, 0], # <- sol2
[1, 0, 1, 0, 1, 0, 0, 1], # <- sol2
[0, 1, 0, 0, 0, 1, 1, 0], # <- sol2
[1, 0, 0, 1, 0, 0, 1, 0], # <- sol1
[0, 1, 0, 0, 1, 0, 0, 1], # <- sol1
[0, 0, 1, 0, 0, 1, 0, 0], # <- sol1
]
return dict(
data=np.array(to_cover, dtype=DTYPE_FOR_ARRAY),
solution1=[0, 1, 2],
solution_count=9,
)

def bruteforce_problem3():
to_cover = [
[1, 0, 0, 1, 0, 0, 1, 0], # <- sol1
[0, 1, 0, 0, 1, 0, 0, 1], # <- sol1
[0, 0, 1, 0, 0, 1, 0, 0], # <- sol1
[0, 0, 0, 1, 0, 0, 0, 0], # <- sol2
[1, 0, 1, 0, 1, 0, 0, 1], # <- sol2
[0, 1, 0, 0, 0, 1, 1, 0], # <- sol2
[1, 0, 0, 1, 0, 0, 1, 0], # <- sol1
[0, 1, 0, 0, 1, 0, 0, 1], # <- sol1
[0, 0, 1, 0, 0, 1, 0, 0], # <- sol1
[0, 0, 0, 1, 0, 0, 0, 0], # <- sol2
[1, 0, 1, 0, 1, 0, 0, 1], # <- sol2
[0, 1, 0, 0, 0, 1, 1, 0], # <- sol2
]
return dict(
data=np.array(to_cover, dtype=DTYPE_FOR_ARRAY),
solution1=[0, 1, 2],
solution_count=16,
)
23 changes: 21 additions & 2 deletions tests/test_solution_count.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
from exact_cover_impl import get_solution_count
from exact_cover.io import DTYPE_FOR_ARRAY

from .data import small_trimino_problem
from .data import detailed_wikipedia_problem
from .problems import small_trimino_problem
from .problems import detailed_wikipedia_problem
from .problems import bruteforce_problem1

def test_solution_count():
data = np.array([[1, 0, 0], [0, 1, 0], [0, 1, 1], [0, 0, 1]], dtype=DTYPE_FOR_ARRAY)
Expand Down Expand Up @@ -34,3 +35,21 @@ def test_detailed_wikipedia_problem():
data = problem['data']
result = get_solution_count(data)
assert result == problem['solution_count']

def test_bruteforce_problem1():
problem = bruteforce_problem1()
data = problem['data']
result = get_solution_count(data)
assert result == problem['solution_count']

def test_bruteforce_problem2():
problem = bruteforce_problem2()
data = problem['data']
result = get_solution_count(data)
assert result == problem['solution_count']

def test_bruteforce_problem3():
problem = bruteforce_problem3()
data = problem['data']
result = get_solution_count(data)
assert result == problem['solution_count']
2 changes: 1 addition & 1 deletion tests/test_trimino_based.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from exact_cover import get_exact_cover
from exact_cover.error import NoSolution

from .data import (
from .problems import (
# the exact cover matrix built manually
small_trimino_problem,
# the same store in a file
Expand Down

0 comments on commit 4faecc5

Please sign in to comment.