diff --git a/.gitignore b/.gitignore index cbac6e0..cbf1292 100644 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,5 @@ _sandbox/ *_profile Untitled.ipynb +# IDEs +.idea/ diff --git a/README.rst b/README.rst index 9372725..205abcb 100644 --- a/README.rst +++ b/README.rst @@ -75,17 +75,19 @@ Basic Usage 8: ['Brand X', '98', 'Internal', 'Hourly', 60] 9: ['Brand Y', '2000', 'Modem', 'Contr.', 60] 10: ['Brand Y', 'NT', 'Modem', 'Salaried', 60] - 11: ['Brand Y', 'XP', 'Modem', 'Part-Time', 60] - 12: ['Brand Y', '2000', 'Modem', 'Hourly', 30] + 11: ['Brand X', 'XP', 'Internal', 'Part-Time', 6] + 12: ['Brand X', '2000', 'Internal', 'Hourly', 30] 13: ['Brand Y', '98', 'Modem', 'Contr.', 15] - 14: ['Brand Y', 'XP', 'Modem', 'Salaried', 15] - 15: ['Brand Y', 'NT', 'Modem', 'Part-Time', 15] - 16: ['Brand Y', 'XP', 'Modem', 'Part-Time', 30] - 17: ['Brand Y', '98', 'Modem', 'Part-Time', 6] + 14: ['Brand Y', 'XP', 'Modem', 'Salaried', 10] + 15: ['Brand X', 'NT', 'Internal', 'Part-Time', 15] + 16: ['Brand X', 'XP', 'Internal', 'Part-Time', 30] + 17: ['Brand Y', '98', 'Modem', 'Part-Time', 60] 18: ['Brand Y', '2000', 'Modem', 'Salaried', 6] - 19: ['Brand Y', '98', 'Modem', 'Salaried', 10] - 20: ['Brand Y', 'XP', 'Modem', 'Contr.', 6] - 21: ['Brand Y', 'NT', 'Modem', 'Hourly', 10] + 19: ['Brand X', 'NT', 'Internal', 'Contr.', 10] + 20: ['Brand X', '98', 'Internal', 'Hourly', 10] + 21: ['Brand Y', 'XP', 'Modem', 'Contr.', 6] + 22: ['Brand Y', 'XP', 'Modem', 'Salaried', 60] + 23: ['Brand X', 'NT', 'Internal', 'Salaried', 15] Filtering @@ -153,10 +155,19 @@ You can restrict pairs by setting a filtering function to ``filter_func`` at 8: ['Brand Y', 'NT', 'Internal', 'Part-Time', 30] 9: ['Brand X', '2000', 'Modem', 'Hourly', 10] 10: ['Brand Y', 'XP', 'Modem', 'Contr.', 30] - 11: ['Brand Y', '2000', 'Modem', 'Salaried', 15] - 12: ['Brand Y', 'NT', 'Modem', 'Salaried', 10] - 13: ['Brand Y', 'XP', 'Modem', 'Part-Time', 6] - 14: ['Brand Y', '2000', 'Modem', 'Contr.', 60] + 11: ['Brand X', '98', 'Internal', 'Contr.', 60] + 12: ['Brand X', '2000', 'Internal', 'Salaried', 15] + 13: ['Brand Y', 'NT', 'Modem', 'Salaried', 10] + 14: ['Brand Y', 'XP', 'Modem', 'Part-Time', 6] + 15: ['Brand X', '98', 'Internal', 'Hourly', 30] + 16: ['Brand X', '2000', 'Internal', 'Contr.', 60] + 17: ['Brand Y', 'NT', 'Modem', 'Hourly', 15] + 18: ['Brand X', '98', 'Internal', 'Part-Time', 10] + 19: ['Brand Y', 'XP', 'Modem', 'Salaried', 10] + 20: ['Brand X', '2000', 'Internal', 'Salaried', 6] + 21: ['Brand Y', 'NT', 'Modem', 'Part-Time', 60] + 22: ['Brand X', '98', 'Internal', 'Hourly', 60] + 23: ['Brand Y', 'XP', 'Modem', 'Salaried', 30] Data Source: OrderedDict @@ -191,10 +202,10 @@ Pairs will be returned as ``collections.namedtuple`` instances. 4: Pairs(brand='Brand X', os='2000', minute=60) 5: Pairs(brand='Brand Y', os='XP', minute=60) 6: Pairs(brand='Brand Y', os='98', minute=60) - 7: Pairs(brand='Brand X', os='NT', minute=60) - 8: Pairs(brand='Brand X', os='NT', minute=30) - 9: Pairs(brand='Brand X', os='98', minute=30) - 10: Pairs(brand='Brand X', os='XP', minute=15) + 7: Pairs(brand='Brand X', os='NT', minute=30) + 8: Pairs(brand='Brand X', os='NT', minute=60) + 9: Pairs(brand='Brand Y', os='98', minute=30) + 10: Pairs(brand='Brand Y', os='XP', minute=15) 11: Pairs(brand='Brand X', os='2000', minute=15) @@ -241,12 +252,12 @@ Parameterized testing: value matrix test_parameterize.py::TestParameterized::test[Brand Y-98-60] PASSED [ 43%] test_parameterize.py::TestParameterized::test[Brand X-NT-60] PASSED [ 50%] test_parameterize.py::TestParameterized::test[Brand X-NT-30] PASSED [ 56%] - test_parameterize.py::TestParameterized::test[Brand X-98-30] PASSED [ 62%] - test_parameterize.py::TestParameterized::test[Brand X-XP-60] PASSED [ 68%] - test_parameterize.py::TestParameterized::test[Brand X-2000-60] PASSED [ 75%] - test_parameterize.py::TestParameterized::test[Brand X-2000-10] PASSED [ 81%] - test_parameterize.py::TestParameterized::test[Brand X-XP-10] PASSED [ 87%] - test_parameterize.py::TestParameterized::test[Brand X-98-15] PASSED [ 93%] + test_parameterize.py::TestParameterized::test[Brand Y-98-15] PASSED [ 62%] + test_parameterize.py::TestParameterized::test[Brand Y-XP-60] PASSED [ 68%] + test_parameterize.py::TestParameterized::test[Brand X-2000-10] PASSED [ 75%] + test_parameterize.py::TestParameterized::test[Brand X-2000-60] PASSED [ 81%] + test_parameterize.py::TestParameterized::test[Brand Y-XP-10] PASSED [ 87%] + test_parameterize.py::TestParameterized::test[Brand Y-98-30] PASSED [ 93%] test_parameterize.py::TestParameterized::test[Brand X-NT-15] PASSED [100%] Parameterized testing: OrderedDict diff --git a/allpairspy/allpairs.py b/allpairspy/allpairs.py index 4d0422a..783a015 100644 --- a/allpairspy/allpairs.py +++ b/allpairspy/allpairs.py @@ -141,13 +141,14 @@ def __next__(self): direction = 0 i += direction - if len(self.__working_item_matrix) != len(chosen_item_list): - raise StopIteration() - - self.__pairs.add_sequence(chosen_item_list) + if i == len(self.__working_item_matrix): + self.__pairs.add_sequence(chosen_item_list) + if len(self.__pairs) == previous_unique_pairs_count: + # could not find new unique pairs - go back to scanning + direction = -1 + i += direction - if len(self.__pairs) == previous_unique_pairs_count: - # could not find new unique pairs - stop + if len(self.__working_item_matrix) != len(chosen_item_list): raise StopIteration() # replace returned array elements with real values and return it @@ -180,21 +181,16 @@ def __resort_working_array(self, chosen_item_list, num): for i in range(0, self.__n) ] - # weighting the node node that creates most of new pairs is the best - weights = [-len(new_combs[-1])] - - # less used outbound connections most likely to produce more new - # pairs while search continues + # weighting the nodes + weights = [] weights.extend( - [len(data_node.out)] + [-len(new_combs[-1])] # node that creates most new pairs is the best + + [len(data_node.out)] # less used outbound connections produce more new pairs + [len(x) for x in reversed(new_combs[:-1])] - + [-data_node.counter] # less used node is better + + [-len(data_node.in_)] # prefer node with most free inbound connections + + [data_node.counter] # less used node is better ) - # otherwise we will prefer node with most of free inbound - # connections; somehow it works out better ;) - weights.append(-len(data_node.in_)) - item.set_weights(weights) self.__working_item_matrix[num].sort(key=cmp_to_key(cmp_item)) diff --git a/tests/test_allpairs.py b/tests/test_allpairs.py index 2886f79..b95389a 100644 --- a/tests/test_allpairs.py +++ b/tests/test_allpairs.py @@ -46,17 +46,18 @@ def test_normal(self): ["Brand X", "98", "Internal", "Hourly", 60], ["Brand Y", "2000", "Modem", "Contr.", 60], ["Brand Y", "NT", "Modem", "Salaried", 60], - ["Brand Y", "XP", "Modem", "Part-Time", 60], - ["Brand Y", "2000", "Modem", "Hourly", 30], + ["Brand X", "XP", "Internal", "Part-Time", 60], + ["Brand X", "2000", "Internal", "Hourly", 30], ["Brand Y", "98", "Modem", "Contr.", 15], ["Brand Y", "XP", "Modem", "Salaried", 15], - ["Brand Y", "NT", "Modem", "Part-Time", 15], - ["Brand Y", "XP", "Modem", "Part-Time", 30], + ["Brand X", "NT", "Internal", "Part-Time", 15], + ["Brand X", "XP", "Internal", "Part-Time", 30], ["Brand Y", "98", "Modem", "Part-Time", 6], ["Brand Y", "2000", "Modem", "Salaried", 6], + ["Brand X", "NT", "Internal", "Contr.", 6], + ["Brand X", "NT", "Internal", "Hourly", 10], ["Brand Y", "98", "Modem", "Salaried", 10], - ["Brand Y", "XP", "Modem", "Contr.", 6], - ["Brand Y", "NT", "Modem", "Hourly", 10], + ["Brand Y", "XP", "Modem", "Hourly", 6], ] @@ -82,24 +83,115 @@ def test_normal(self): ["Brand Y", "NT", "Internal", "Salaried", 10], ["Brand X", "98", "Modem", "Contr.", 15], ["Brand X", "98", "Modem", "Hourly", 10], - ["Brand Y", "NT", "Modem", "Contr.", 30], - ["Brand X", "XP", "Internal", "Hourly", 30], - ["Brand X", "2000", "Modem", "Salaried", 30], + ["Brand Y", "NT", "Internal", "Part-Time", 30], + ["Brand Y", "2000", "Modem", "Contr.", 30], + ["Brand X", "XP", "Modem", "Salaried", 30], + ["Brand X", "XP", "Internal", "Hourly", 60], + ["Brand Y", "2000", "Modem", "Salaried", 60], + ["Brand Y", "NT", "Internal", "Contr.", 60], + ["Brand X", "98", "Internal", "Part-Time", 30], + ["Brand X", "98", "Internal", "Contr.", 60], + ["Brand Y", "NT", "Modem", "Part-Time", 15], + ["Brand Y", "2000", "Modem", "Hourly", 60], + ["Brand X", "XP", "Internal", "Salaried", 15], + ["Brand X", "2000", "Internal", "Part-Time", 60], + ["Brand Y", "2000", "Modem", "Contr.", 6], + ["Brand Y", "2000", "Modem", "Hourly", 30], + ["Brand Y", "2000", "Internal", "Salaried", 6], + ["Brand Y", "2000", "Internal", "Contr.", 15], + ["Brand Y", "2000", "Modem", "Salaried", 15], + ["Brand Y", "2000", "Modem", "Hourly", 10], + ["Brand Y", "2000", "Internal", "Part-Time", 6], + ["Brand Y", "2000", "Internal", "Part-Time", 10], + ["Brand Y", "2000", "Modem", "Hourly", 15], + ["Brand Y", "2000", "Modem", "Salaried", 30], + ["Brand Y", "2000", "Internal", "Contr.", 30], + ["Brand Y", "2000", "Internal", "Contr.", 10], + ["Brand Y", "2000", "Modem", "Salaried", 10], + ["Brand Y", "2000", "Modem", "Hourly", 6], + ["Brand Y", "2000", "Internal", "Part-Time", 15], + ["Brand Y", "2000", "Internal", "Part-Time", 60], + ["Brand Y", "2000", "Modem", "Salaried", 6], + ["Brand Y", "2000", "Internal", "Contr.", 60], ["Brand Y", "2000", "Internal", "Contr.", 6], + ["Brand Y", "2000", "Internal", "Salaried", 30], + ["Brand Y", "2000", "Internal", "Part-Time", 30], + ["Brand Y", "2000", "Internal", "Salaried", 60], + ["Brand Y", "2000", "Modem", "Contr.", 10], + ["Brand Y", "2000", "Modem", "Contr.", 60], + ["Brand Y", "2000", "Modem", "Part-Time", 30], + ["Brand Y", "2000", "Internal", "Hourly", 10], + ["Brand Y", "2000", "Modem", "Part-Time", 60], + ["Brand Y", "2000", "Internal", "Hourly", 6], + ["Brand Y", "2000", "Internal", "Hourly", 30], + ["Brand Y", "NT", "Internal", "Hourly", 15], + ["Brand Y", "NT", "Internal", "Salaried", 6], + ["Brand Y", "NT", "Internal", "Salaried", 15], + ["Brand Y", "NT", "Internal", "Hourly", 10], + ["Brand Y", "NT", "Internal", "Part-Time", 10], + ["Brand Y", "NT", "Internal", "Contr.", 15], + ["Brand Y", "NT", "Internal", "Contr.", 6], ["Brand Y", "NT", "Internal", "Part-Time", 60], - ["Brand Y", "XP", "Modem", "Salaried", 15], - ["Brand X", "98", "Modem", "Part-Time", 60], + ["Brand Y", "NT", "Internal", "Hourly", 30], + ["Brand Y", "NT", "Internal", "Salaried", 30], + ["Brand Y", "NT", "Internal", "Salaried", 60], + ["Brand Y", "NT", "Internal", "Hourly", 60], + ["Brand Y", "NT", "Internal", "Part-Time", 6], + ["Brand Y", "NT", "Internal", "Contr.", 10], + ["Brand Y", "NT", "Internal", "Contr.", 30], + ["Brand Y", "NT", "Modem", "Salaried", 10], + ["Brand Y", "NT", "Modem", "Contr.", 30], + ["Brand Y", "NT", "Modem", "Part-Time", 60], + ["Brand Y", "XP", "Modem", "Hourly", 15], + ["Brand X", "XP", "Modem", "Contr.", 60], + ["Brand X", "XP", "Modem", "Hourly", 6], + ["Brand X", "XP", "Modem", "Part-Time", 10], + ["Brand X", "XP", "Modem", "Salaried", 10], ["Brand X", "XP", "Modem", "Salaried", 60], - ["Brand X", "2000", "Internal", "Part-Time", 15], - ["Brand X", "2000", "Modem", "Contr.", 60], - ["Brand X", "98", "Modem", "Salaried", 10], - ["Brand X", "98", "Modem", "Part-Time", 30], - ["Brand X", "NT", "Modem", "Part-Time", 10], - ["Brand Y", "NT", "Modem", "Salaried", 60], - ["Brand Y", "NT", "Modem", "Hourly", 15], - ["Brand Y", "NT", "Modem", "Hourly", 30], - ["Brand Y", "NT", "Modem", "Hourly", 60], - ["Brand Y", "NT", "Modem", "Hourly", 10], + ["Brand X", "XP", "Modem", "Part-Time", 15], + ["Brand X", "XP", "Modem", "Hourly", 30], + ["Brand X", "XP", "Modem", "Hourly", 15], + ["Brand X", "XP", "Modem", "Part-Time", 30], + ["Brand X", "XP", "Modem", "Salaried", 6], + ["Brand X", "XP", "Modem", "Contr.", 6], + ["Brand X", "XP", "Modem", "Contr.", 30], + ["Brand X", "XP", "Modem", "Part-Time", 60], + ["Brand X", "XP", "Internal", "Part-Time", 6], + ["Brand X", "XP", "Internal", "Hourly", 10], + ["Brand X", "XP", "Internal", "Hourly", 30], + ["Brand X", "XP", "Internal", "Contr.", 15], + ["Brand X", "2000", "Modem", "Contr.", 15], + ["Brand X", "2000", "Modem", "Hourly", 10], + ["Brand X", "2000", "Internal", "Salaried", 6], + ["Brand X", "2000", "Internal", "Part-Time", 30], + ["Brand X", "98", "Modem", "Part-Time", 6], + ["Brand X", "98", "Modem", "Salaried", 60], + ["Brand X", "98", "Internal", "Hourly", 15], + ["Brand X", "98", "Internal", "Contr.", 10], + ["Brand X", "98", "Modem", "Contr.", 30], + ["Brand X", "98", "Modem", "Hourly", 6], + ["Brand X", "98", "Internal", "Part-Time", 15], + ["Brand X", "98", "Internal", "Part-Time", 60], + ["Brand X", "98", "Modem", "Hourly", 60], + ["Brand X", "98", "Modem", "Contr.", 6], + ["Brand Y", "XP", "Internal", "Contr.", 10], + ["Brand Y", "XP", "Internal", "Part-Time", 6], + ["Brand Y", "XP", "Modem", "Salaried", 60], + ["Brand Y", "XP", "Modem", "Hourly", 30], + ["Brand Y", "98", "Internal", "Hourly", 30], + ["Brand X", "98", "Internal", "Part-Time", 10], + ["Brand X", "98", "Modem", "Salaried", 15], + ["Brand X", "98", "Internal", "Salaried", 10], + ["Brand X", "98", "Internal", "Salaried", 30], + ["Brand X", "NT", "Internal", "Salaried", 15], + ["Brand Y", "98", "Modem", "Salaried", 15], + ["Brand Y", "98", "Internal", "Contr.", 10], + ["Brand Y", "98", "Internal", "Part-Time", 60], + ["Brand Y", "98", "Internal", "Contr.", 6], + ["Brand X", "NT", "Modem", "Hourly", 6], + ["Brand X", "NT", "Internal", "Contr.", 60], + ["Brand X", "NT", "Internal", "Part-Time", 10], + ["Brand X", "NT", "Internal", "Salaried", 30], ] @@ -129,18 +221,18 @@ def test_normal(self): ["Brand X", "NT", "Modem", "Salaried", 60], ["Brand Y", "XP", "Internal", "Hourly", 15], ["Brand Y", "NT", "Modem", "Hourly", 30], - ["Brand Y", "2000", "Modem", "Part-Time", 15], - ["Brand Y", "2000", "Modem", "Contr.", 10], - ["Brand Y", "XP", "Modem", "Salaried", 10], - ["Brand Y", "98", "Modem", "Part-Time", 6], - ["Brand Y", "NT", "Modem", "Contr.", 15], - ["Brand Y", "98", "Modem", "Contr.", 30], - ["Brand Y", "XP", "Modem", "Part-Time", 60], - ["Brand Y", "2000", "Modem", "Hourly", 60], - ["Brand Y", "NT", "Modem", "Salaried", 30], - ["Brand Y", "NT", "Modem", "Salaried", 15], - ["Brand Y", "NT", "Modem", "Hourly", 6], - ["Brand Y", "NT", "Modem", "Contr.", 60], + ["Brand X", "2000", "Modem", "Part-Time", 15], + ["Brand X", "2000", "Internal", "Contr.", 10], + ["Brand Y", "XP", "Internal", "Salaried", 10], + ["Brand Y", "98", "Internal", "Part-Time", 6], + ["Brand X", "NT", "Modem", "Contr.", 15], + ["Brand X", "98", "Modem", "Contr.", 30], + ["Brand Y", "XP", "Internal", "Part-Time", 60], + ["Brand Y", "2000", "Internal", "Hourly", 60], + ["Brand X", "NT", "Modem", "Salaried", 30], + ["Brand X", "NT", "Modem", "Salaried", 15], + ["Brand Y", "NT", "Internal", "Hourly", 6], + ["Brand Y", "2000", "Internal", "Contr.", 60] ] @@ -192,10 +284,19 @@ def is_valid_combination(row): ["Brand Y", "NT", "Internal", "Part-Time", 30], ["Brand X", "2000", "Modem", "Hourly", 10], ["Brand Y", "XP", "Modem", "Contr.", 30], - ["Brand Y", "2000", "Modem", "Salaried", 15], + ["Brand X", "98", "Internal", "Contr.", 30], + ["Brand X", "2000", "Internal", "Salaried", 15], ["Brand Y", "NT", "Modem", "Salaried", 10], ["Brand Y", "XP", "Modem", "Part-Time", 6], - ["Brand Y", "2000", "Modem", "Contr.", 60], + ["Brand X", "98", "Internal", "Hourly", 60], + ["Brand X", "2000", "Internal", "Contr.", 60], + ["Brand Y", "NT", "Modem", "Hourly", 15], + ["Brand X", "2000", "Internal", "Part-Time", 60], + ["Brand Y", "2000", "Modem", "Salaried", 6], + ["Brand X", "98", "Internal", "Salaried", 30], + ["Brand Y", "XP", "Modem", "Hourly", 30], + ["Brand X", "98", "Internal", "Part-Time", 10], + ["Brand Y", "XP", "Modem", "Part-Time", 10] ] def test_normal_example22(self): @@ -255,8 +356,17 @@ def is_valid_combination(values, names): ["Brand Y", "NT", "Internal", "Part-Time", 30], ["Brand X", "2000", "Modem", "Hourly", 10], ["Brand Y", "XP", "Modem", "Contr.", 30], - ["Brand Y", "2000", "Modem", "Salaried", 15], + ["Brand X", "98", "Internal", "Contr.", 30], + ["Brand X", "2000", "Internal", "Salaried", 15], ["Brand Y", "NT", "Modem", "Salaried", 10], ["Brand Y", "XP", "Modem", "Part-Time", 6], - ["Brand Y", "2000", "Modem", "Contr.", 60], + ["Brand X", "98", "Internal", "Hourly", 60], + ["Brand X", "2000", "Internal", "Contr.", 60], + ["Brand Y", "NT", "Modem", "Hourly", 15], + ["Brand X", "2000", "Internal", "Part-Time", 60], + ["Brand Y", "2000", "Modem", "Salaried", 6], + ["Brand X", "98", "Internal", "Salaried", 30], + ["Brand Y", "XP", "Modem", "Hourly", 30], + ["Brand X", "98", "Internal", "Part-Time", 10], + ["Brand Y", "XP", "Modem", "Part-Time", 10] ]