allpairspy icon indicating copy to clipboard operation
allpairspy copied to clipboard

Insufficient combination when filter function is specified?

Open kmaehashi opened this issue 6 years ago • 10 comments

Hello, thank you for providing a great library! I tried to use filter function to cut invalid combination, but it seems generated combinations are insufficient.

# Generate combinations of every supported Python + NumPy version combination.

from allpairspy import AllPairs

numpy = ['1.9', '1.10', '1.11', '1.12', '1.13', '1.14']
python = ['2.7', '3.4', '3.5', '3.6']

def _validate(numpy, python):
    valid = []
    if numpy == '1.9':
        valid = ['2.7', '3.4']
    elif numpy == '1.10':
        valid = ['2.7', '3.4']
    elif numpy == '1.11':
        valid = ['2.7', '3.4', '3.5']
    elif numpy == '1.12':
        valid = ['2.7', '3.4', '3.5', '3.6']
    elif numpy == '1.13':
        valid = ['2.7', '3.4', '3.5', '3.6']
    elif numpy == '1.14':
        valid = ['2.7', '3.4', '3.5', '3.6']
    return python in valid


def _filter_func(row):
    if len(row) == 2:
        stat = _validate(*row)
        #print('Valid? ', row, ' -> ', stat)
        return stat
    return True


if __name__ == '__main__':
    for i, pairs in enumerate(AllPairs([numpy, python], filter_func=_filter_func)):
        print(i, ':', pairs)

I got:

0 : ['1.9', '2.7']
1 : ['1.10', '2.7']
2 : ['1.11', '2.7']
3 : ['1.12', '2.7']
4 : ['1.13', '2.7']
5 : ['1.14', '2.7']
6 : ['1.14', '3.4']
7 : ['1.13', '3.4']
8 : ['1.12', '3.4']
9 : ['1.11', '3.4']
10 : ['1.10', '3.4']
11 : ['1.9', '3.4']

It seems Python 3.5/3.6 variations are totally ignored. Am I missing something? Any suggestions are welcome. Thanks in advance!

kmaehashi avatar Apr 09 '18 05:04 kmaehashi

Hello, @kmaehashi

AllPairs may not properly work when you try to generate combinations from two parameters. filter_func will properly work if you use three or more parameters as following (a little bit modified your sample code):

from allpairspy import AllPairs

numpy = ['1.9', '1.10', '1.11', '1.12', '1.13', '1.14']
python = ['2.7', '3.4', '3.5', '3.6']
os = ["Linux", "Windows", "macOS"]

def _validate(row):
    numpy, python = row[0], row[1]
    valid = []
    if numpy == '1.9':
        valid = ['2.7', '3.4']
    elif numpy == '1.10':
        valid = ['2.7', '3.4']
    elif numpy == '1.11':
        valid = ['2.7', '3.4', '3.5']
    elif numpy == '1.12':
        valid = ['2.7', '3.4', '3.5', '3.6']
    elif numpy == '1.13':
        valid = ['2.7', '3.4', '3.5', '3.6']
    elif numpy == '1.14':
        valid = ['2.7', '3.4', '3.5', '3.6']

    return python in valid


def _filter_func(row):
    if len(row) > 2:
        stat = _validate(row)
        #print('Valid? ', row, ' -> ', stat)
        return stat

    return True


if __name__ == '__main__':
    for i, pairs in enumerate(AllPairs([numpy, python, os], filter_func=_filter_func)):
        print(i, ':', pairs)
0 : ['1.9', '2.7', 'Linux']
1 : ['1.10', '3.4', 'Linux']
2 : ['1.11', '3.5', 'Linux']
3 : ['1.12', '3.6', 'Linux']
4 : ['1.13', '3.6', 'Windows']
5 : ['1.14', '3.5', 'Windows']
6 : ['1.14', '3.4', 'macOS']
7 : ['1.13', '2.7', 'macOS']
8 : ['1.12', '2.7', 'Windows']
9 : ['1.11', '3.4', 'Windows']
10 : ['1.10', '2.7', 'Windows']
11 : ['1.9', '3.4', 'Windows']
12 : ['1.9', '3.4', 'macOS']
13 : ['1.10', '3.4', 'macOS']
14 : ['1.11', '2.7', 'macOS']
15 : ['1.12', '3.5', 'macOS']
16 : ['1.13', '3.4', 'Linux']
17 : ['1.14', '3.6', 'macOS']
18 : ['1.14', '2.7', 'Linux']

product with filter functions might be more suitable for your sample data:

from allpairspy import AllPairs
from itertools import product

numpy = ['1.9', '1.10', '1.11', '1.12', '1.13', '1.14']
python = ['2.7', '3.4', '3.5', '3.6']


def _validate(numpy, python):
    valid = []
    if numpy == '1.9':
        valid = ['2.7', '3.4']
    elif numpy == '1.10':
        valid = ['2.7', '3.4']
    elif numpy == '1.11':
        valid = ['2.7', '3.4', '3.5']
    elif numpy == '1.12':
        valid = ['2.7', '3.4', '3.5', '3.6']
    elif numpy == '1.13':
        valid = ['2.7', '3.4', '3.5', '3.6']
    elif numpy == '1.14':
        valid = ['2.7', '3.4', '3.5', '3.6']

    return python in valid


def _filter_func(row):
    return _validate(*row)


if __name__ == '__main__':
    for i, pairs in enumerate(filter(_filter_func, product(numpy, python))):
        print(i, ':', pairs)
0 : ('1.9', '2.7')
1 : ('1.9', '3.4')
2 : ('1.10', '2.7')
3 : ('1.10', '3.4')
4 : ('1.11', '2.7')
5 : ('1.11', '3.4')
6 : ('1.11', '3.5')
7 : ('1.12', '2.7')
8 : ('1.12', '3.4')
9 : ('1.12', '3.5')
10 : ('1.12', '3.6')
11 : ('1.13', '2.7')
12 : ('1.13', '3.4')
13 : ('1.13', '3.5')
14 : ('1.13', '3.6')
15 : ('1.14', '2.7')
16 : ('1.14', '3.4')
17 : ('1.14', '3.5')
18 : ('1.14', '3.6')

thombashi avatar Apr 09 '18 13:04 thombashi

@thombashi Thank you for your help. According to the output from your code, when focusing on Python = 3.5 combination,

2 : ['1.11', '3.5', 'Linux']
5 : ['1.14', '3.5', 'Windows']
15 : ['1.12', '3.5', 'macOS']

NumPy = 1.13 & Python = 3.5 combination, which is not expected to be filtered, seems missing. Is this an expected behavior?

kmaehashi avatar Apr 09 '18 13:04 kmaehashi

@kmaehashi Thank you for your comment. That is unexpected behavior. I will check into it.

thombashi avatar Apr 10 '18 14:04 thombashi

@thombashi Thanks for the quick response! Actually I was trying to generate combinations of 8 axes with filtering (https://github.com/chainer/chainer-test/pull/403) and found some pairs missing from generated patterns. (The first example (NumPy/Python) I wrote was the minimum code to reproduce the issue, not the actual usecase.)

kmaehashi avatar Apr 11 '18 02:04 kmaehashi

@kmaehashi Thank you for additional information. This issue might take some time (I had little time to solve the issue for now).

thombashi avatar Apr 12 '18 13:04 thombashi

Hello, any progress on this issue?

kmaehashi avatar Dec 19 '18 06:12 kmaehashi

Sorry to keep you waiting. Still work in progress.

thombashi avatar Dec 29 '18 12:12 thombashi

@thombashi Also, the example generation provided in the README lacks 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

Are there any plans to fix it?

pavelicii avatar Dec 06 '21 12:12 pavelicii

Fixed in this PR.

For @kmaehashi's original input the result is:

{numpy=1.9, python=2.7}
{numpy=1.1, python=2.7}
{numpy=1.11, python=2.7}
{numpy=1.12, python=2.7}
{numpy=1.13, python=2.7}
{numpy=1.14, python=2.7}
{numpy=1.14, python=3.4}
{numpy=1.13, python=3.4}
{numpy=1.12, python=3.4}
{numpy=1.11, python=3.4}
{numpy=1.1, python=3.4}
{numpy=1.9, python=3.4}
{numpy=1.11, python=3.5}
{numpy=1.12, python=3.5}
{numpy=1.13, python=3.5}
{numpy=1.14, python=3.5}
{numpy=1.14, python=3.6}
{numpy=1.13, python=3.6}
{numpy=1.12, python=3.6}

pavelicii avatar Dec 10 '21 20:12 pavelicii

Code:

parameters = OrderedDict(
    {"x1": [True, False],
     "x2": [True, False]})


def constraints(row):
    n = len(row)
    if n > 1:
        if row[0] is False and row[1] is False:
            return False
    return True


print("PAIRWISE:")
res = AllPairs(parameters, filter_func=constraints, n=2)

Desired Output:

0 Pairs(x1=True, x2=True)
1 Pairs(x1=False, x2=True)
2 Pairs(x1=True, x2=False)

Actual Output:

0 Pairs(x1=True, x2=True)
1 Pairs(x1=False, x2=True)

ashfaq92 avatar Dec 01 '22 14:12 ashfaq92