from operator import itemgetter
from itertools import (repeat,
combinations, product, zip_longest, chain, dropwhile)permutations
set_from_index = lambda item:
def set(map(itemgetterpairwise(1)*variables, item)**kwargs):
dual_intersection = lambda item, index: next(dropwhile(lambda x: x[1]num_vars != index, item))
def pw_vals_onlylen(buildervariables):
"""
Convert a set generated by#Organize the builder algorithm
to a tuple of testing values"""
variable states into pairs for comparisons
resultindexed_vars = tuple(map(itemgetter(0), sorted(builder, key=itemgetter(1))))
return result
def pairwise(*variables):
"""
Generate a complete set of pairwise combinations.
In other words, a minimal spanning set where all
parameter options are paired up against another option
at least once."""
#Convert variables to tuples with 2nd element denoting original position
#For this algorithm, variable sequences must be sorted by length
r_vars = sorted((list(zip[(varindex, repeat(i))v) for v in vals] for iindex, varvals in enumerate(variables)),
key=len,
reverse=True)
num_vars = len(r_vars)
#Used to fill in missing elements in the final step of the algorithm
new_ordervar_combos = dict(zipcombinations(range(num_vars), (i[0][1] for i in r_vars))2)
#Build the sequence of pairs
pw_tablevar_products = zip_longest(*(product(r_vars[a]indexed_vars[i], r_vars[b]indexed_vars[j]) for i, j in var_combos)
vars = chain.from_iterable(zip_longest(*var_products))
for a, b#Initialize inthe combinations(range(num_vars),builder 2)))list
pw_chainbuilders = chain.from_iterable(pw_table)
[]
seen_pairs = set()
builder_sets#Main =algorithm []
loop
for pair in pw_chainvars:
if not pair or pair in seen_pairs:
continue
p_set = set(pair)
p_index_setd_pair = set_from_indexdict(pair)
#Linear search ofthrough allbuilders ofto thefind "underan construction"empty setsslot
#to#First seepass: iflook therefor issets freethat slothave intersections
for index, builder in enumerate(builder_sets)builders:
intersection = builder.keys() & d_pair.keys()
index_inters = p_index_set & set_from_indexif len(builderintersection) == 1:
#See if the set intersectionv is= non-emptyintersection.pop()
if (len(index_inters)builder[v] == 1)d_pair[v]:
[t_index] = list builder.update(index_intersd_pair)
if (dual_intersection(p_set, t_index) == dual_intersection(builder,#Update t_index)):
seen pairs
if lenseen_pairs.update(combinations(builder) == .items(num_vars -), 12):)
break
#every variable is represented in builder setelse:
#Second Pass
del builder_sets[index]
for builder in builders:
combinedintersection = p_set | builder.keys() & d_pair.keys()
if yieldnot pw_vals_onlylen(combinedintersection):
breakbuilder.update(d_pair)
else:
#Update seen pairs
seen_pairs.update(combinations(builder.items(), 2))
#builder set is still missing variables
break
builder &= p_setelse:
#No empty slots/pairs identified. Make combineda =new builder
builders.append(d_pair)
break
else:seen_pairs.add(pair)
#Fill in remaining values
#No matchescomplete_set found,= makeset(range(num_vars))
a new builder
defaults = {var[0][0]: var[0][1] for var in indexed_vars}
builder_sets.append(p_set)
for builder in builders:
combined = p_set
if len(builder) == num_vars:
sorted_vars = sorted(combined, key=lambda x: new_order[x[1]])
yield tuple(val for index, val in seen_pairs.updatesorted(combinationsbuilder.items(sorted_vars, 2)))
#fill in remaining builders with default valueselse:
defaults = {i: variables[i][0] for i in range(num_vars)}
for key in def_keyscomplete_set =- defaultsbuilder.keys():
for builder in builder_sets:
for missingbuilder[key] in= def_keysdefaults[key]
- set_from_index(builder):
yield tuple(val for index, val in sorted(builder.add(items(defaults[missing], missing)))
yield pw_vals_only(builder)
from operator import itemgetter
from itertools import (repeat,
combinations, product, zip_longest, chain, dropwhile)
set_from_index = lambda item: set(map(itemgetter(1), item))
dual_intersection = lambda item, index: next(dropwhile(lambda x: x[1] != index, item))
def pw_vals_only(builder):
"""
Convert a set generated by the builder algorithm
to a tuple of testing values"""
result = tuple(map(itemgetter(0), sorted(builder, key=itemgetter(1))))
return result
def pairwise(*variables):
"""
Generate a complete set of pairwise combinations.
In other words, a minimal spanning set where all
parameter options are paired up against another option
at least once."""
#Convert variables to tuples with 2nd element denoting original position
#For this algorithm, variable sequences must be sorted by length
r_vars = sorted((list(zip(var, repeat(i))) for i, var in enumerate(variables)),
key=len,
reverse=True)
num_vars = len(r_vars)
#Used to fill in missing elements in the final step of the algorithm
new_order = dict(zip(range(num_vars), (i[0][1] for i in r_vars)))
#Build the sequence of pairs
pw_table = zip_longest(*(product(r_vars[a], r_vars[b]) for a, b in combinations(range(num_vars), 2)))
pw_chain = chain.from_iterable(pw_table)
seen_pairs = set()
builder_sets = []
for pair in pw_chain:
if not pair or pair in seen_pairs:
continue
p_set = set(pair)
p_index_set = set_from_index(pair)
#Linear search of all of the "under construction" sets
#to see if there is free slot
for index, builder in enumerate(builder_sets):
index_inters = p_index_set & set_from_index(builder)
#See if the set intersection is non-empty
if (len(index_inters) == 1):
[t_index] = list(index_inters)
if (dual_intersection(p_set, t_index) == dual_intersection(builder, t_index)):
if len(builder) == (num_vars - 1):
#every variable is represented in builder set
del builder_sets[index]
combined = p_set | builder yield pw_vals_only(combined)
break
else:
#builder set is still missing variables
builder &= p_set
combined = builder
break
else:
#No matches found, make a new builder
builder_sets.append(p_set)
combined = p_set
sorted_vars = sorted(combined, key=lambda x: new_order[x[1]])
seen_pairs.update(combinations(sorted_vars, 2))
#fill in remaining builders with default values
defaults = {i: variables[i][0] for i in range(num_vars)}
def_keys = defaults.keys()
for builder in builder_sets:
for missing in def_keys - set_from_index(builder):
builder.add((defaults[missing], missing))
yield pw_vals_only(builder)
from operator import itemgetter
from itertools import combinations, product, zip_longest, chain, permutations
def pairwise(*variables, **kwargs):
num_vars = len(variables)
#Organize the variable states into pairs for comparisons
indexed_vars = sorted(
([(index, v) for v in vals] for index, vals in enumerate(variables)),
key=len,
reverse=True)
var_combos = combinations(range(num_vars), 2)
var_products = (product(indexed_vars[i], indexed_vars[j]) for i, j in var_combos)
vars = chain.from_iterable(zip_longest(*var_products))
#Initialize the builder list
builders = []
seen_pairs = set()
#Main algorithm loop
for pair in vars:
if not pair or pair in seen_pairs:
continue
d_pair = dict(pair)
#Linear search through builders to find an empty slot
#First pass: look for sets that have intersections
for builder in builders:
intersection = builder.keys() & d_pair.keys()
if len(intersection) == 1:
v = intersection.pop()
if builder[v] == d_pair[v]:
builder.update(d_pair)
#Update seen pairs
seen_pairs.update(combinations(builder.items(), 2))
break
else:
#Second Pass
for builder in builders:
intersection = builder.keys() & d_pair.keys()
if not len(intersection):
builder.update(d_pair)
#Update seen pairs
seen_pairs.update(combinations(builder.items(), 2))
break
else:
#No empty slots/pairs identified. Make a new builder
builders.append(d_pair)
seen_pairs.add(pair)
#Fill in remaining values
complete_set = set(range(num_vars))
defaults = {var[0][0]: var[0][1] for var in indexed_vars}
for builder in builders:
if len(builder) == num_vars:
yield tuple(val for index, val in sorted(builder.items()))
else:
for key in complete_set - builder.keys():
builder[key] = defaults[key]
yield tuple(val for index, val in sorted(builder.items()))
Pairwise Combinations Generator
Below is an algorithm to generate all pairwise combinations for a set of random variables. See here for an explanation of what constitutes a minimal all-pairs set. The algorithm below works but I feel that it is inefficient. Any suggestions would be welcome.
from operator import itemgetter
from itertools import (repeat,
combinations,
product,
zip_longest,
chain,
dropwhile)
set_from_index = lambda item: set(map(itemgetter(1), item))
dual_intersection = lambda item, index: next(dropwhile(lambda x: x[1] != index, item))
def pw_vals_only(builder):
"""
Convert a set generated by the builder algorithm
to a tuple of testing values"""
result = tuple(map(itemgetter(0), sorted(builder, key=itemgetter(1))))
return result
def pairwise(*variables):
"""
Generate a complete set of pairwise combinations.
In other words, a minimal spanning set where all
parameter options are paired up against another option
at least once."""
#Convert variables to tuples with 2nd element denoting original position
#For this algorithm, variable sequences must be sorted by length
r_vars = sorted((list(zip(var, repeat(i)))
for i, var in enumerate(variables)),
key=len,
reverse=True)
num_vars = len(r_vars)
#Used to fill in missing elements in the final step of the algorithm
new_order = dict(zip(range(num_vars), (i[0][1] for i in r_vars)))
#Build the sequence of pairs
pw_table = zip_longest(*(product(r_vars[a], r_vars[b])
for a, b in combinations(range(num_vars), 2)))
pw_chain = chain.from_iterable(pw_table)
seen_pairs = set()
builder_sets = []
for pair in pw_chain:
if not pair or pair in seen_pairs:
continue
p_set = set(pair)
p_index_set = set_from_index(pair)
#Linear search of all of the "under construction" sets
#to see if there is free slot
for index, builder in enumerate(builder_sets):
index_inters = p_index_set & set_from_index(builder)
#See if the set intersection is non-empty
if (len(index_inters) == 1):
[t_index] = list(index_inters)
if (dual_intersection(p_set, t_index) == dual_intersection(builder, t_index)):
if len(builder) == (num_vars - 1):
#every variable is represented in builder set
del builder_sets[index]
combined = p_set | builder
yield pw_vals_only(combined)
break
else:
#builder set is still missing variables
builder &= p_set
combined = builder
break
else:
#No matches found, make a new builder
builder_sets.append(p_set)
combined = p_set
sorted_vars = sorted(combined, key=lambda x: new_order[x[1]])
seen_pairs.update(combinations(sorted_vars, 2))
#fill in remaining builders with default values
defaults = {i: variables[i][0] for i in range(num_vars)}
def_keys = defaults.keys()
for builder in builder_sets:
for missing in def_keys - set_from_index(builder):
builder.add((defaults[missing], missing))
yield pw_vals_only(builder)
Usage Example:
u_vars = [
[1, 2, 3, 4],
["A", "B", "C"],
["a", "b"],
]
result = list(pairwise(*u_vars))
print(result) #prints list of length 12
lang-py