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

Fix filtering and uneven parameter distribution #10

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,5 @@ _sandbox/
*_profile
Untitled.ipynb

# IDEs
.idea/
57 changes: 34 additions & 23 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-filtered generations now also have different outputs due to fix of uneven distribution of parameters in the resulting set.

In this example, 'Modem' were used more than 'Internal'. Now they are distributed evenly.



Filtering
Expand Down Expand Up @@ -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]
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking into account filtering function, this output were lacking the following pairs:

98 - Hourly
98 - Contr.
98 - 10
98 - 30
98 - 60
NT - 15
2000 - 6
XP - 10
Salaried - 30
Hourly - 30
Hourly - 60
Part-Time - 60

Now they are all present in the resulting set.



Data Source: OrderedDict
Expand Down Expand Up @@ -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)
Copy link
Author

@pavelicii pavelicii Dec 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resulting set changed due to uneven parameter distribution fix.



Expand Down Expand Up @@ -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
Expand Down
30 changes: 13 additions & 17 deletions allpairspy/allpairs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Author

@pavelicii pavelicii Dec 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes insufficient combinations when filtering is applied.

If chosen_item_list doesn't present new pairs, we should get back to scanning until we tried all items. So we will either get new pairs, or brute force all items and raise StopIteration() when we got back to i == 0.


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
Expand Down Expand Up @@ -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
Copy link
Author

@pavelicii pavelicii Dec 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes uneven distribution of parameters.

If you want less used nodes to be first after resorting, then you shouldn't negate node's usage number. I believe it was just a typo here.

--

You can also consider swapping this weight (data_node.counter) with the last one (-len(data_node.in_)). Sometimes I was getting better results after doing this. Especially for such inputs. Let me know what you think, I can update it in the next commit.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you mentioned -len(data_node.in_) would be get better results.
Could you update to using -len(data_node.in_)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but I'll be able to do it in a week from now.

)

# 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))
Expand Down
184 changes: 147 additions & 37 deletions tests/test_allpairs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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],
]


Expand All @@ -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],
]
Copy link
Author

@pavelicii pavelicii Dec 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Major increase in number of combinations when n > 2.

Currently, this test have the following input and expected result:

parameters = [
	["Brand X", "Brand Y"],
	["98", "NT", "2000", "XP"],
	["Internal", "Modem"],
	["Salaried", "Hourly", "Part-Time", "Contr."],
	[6, 10, 15, 30, 60],
]

assert list(AllPairs(parameters, n=3)) == [
	["Brand X", "98", "Internal", "Salaried", 6],
	["Brand Y", "NT", "Modem", "Hourly", 6],
	["Brand Y", "2000", "Modem", "Part-Time", 10],
	["Brand X", "XP", "Internal", "Contr.", 10],
	["Brand X", "XP", "Modem", "Part-Time", 6],
	["Brand Y", "2000", "Internal", "Hourly", 15],
	["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", "2000", "Internal", "Contr.", 6],
	["Brand Y", "NT", "Internal", "Part-Time", 60],
	["Brand Y", "XP", "Modem", "Salaried", 15],
	["Brand X", "98", "Modem", "Part-Time", 60],
	["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],
]

Correct me if I'm wrong, but if I'm getting it right, when we say n = 3, we want all triple-wise combinations to be tested. So in the result we want to see, for example, following Brand X - NT - ... combinations:

Brand X - NT - Internal
Brand X - NT - Modem [+]
Brand X - NT - Salaried 
Brand X - NT - Hourly
Brand X - NT - Part-Time [+]
Brand X - NT - Contr.
Brand X - NT - 6
Brand X - NT - 10 [+]
Brand X - NT - 15
Brand X - NT - 30
Brand X - NT - 60

But currently there are only 3 of them (marked with [+]).

Filtering fix also fixed this issue,



Expand Down Expand Up @@ -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]
]


Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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]
]
Loading