In Python I am using the itertools.product() function to generate input parameters for a simulation.
I have a test function that requires 4 input parameters a1, a2, b1 and b2. I use the following code generate the parameters. Example:
params = itertools.product(range(10,41,2), range(10,41,2), range(0, 2), range(5, 31, 5))
… which gives me 3072 combinations. Unfortunately some combinations logically make no sense. E. g. if a2 is larger than a1 the test results are useless, also when b1 equals 0 the value of b2 is completely irrelevant – so it wouldn’t make sense to test such combinations.
Is there a possibility to restrict or filter the cartesian product beside doing it manually and nesting for-loops? Because my real use case has way more than 4 parameters, that’s why I like the convenience of the cartesian product function from itertools.
Any ideas or alternatives? Any help appreciated, thanks.
Practical Data Science using Python As we know if two lists are like (a, b) and (c, d) then the Cartesian product will be {(a, c), (a, d), (b, c), (b, d)}. To do this we shall use itertools library and use the product() function present in this library. The returned value of this function is an iterator.
The cartesian product (or cross product) of A and B, denoted by A x B, is the set A x B = {(a,b) | a ∈ A and b ∈ B}. The elements (a,b) are ordered pairs. For example if A = {1,2} and B = {4,5,6} then the cartesian products of A and B is AxB = {(1,4),(1,5),(1,6),(2,4),(2,5),(2,6)}.
product() can used in two different ways: itertools. product(*iterables, repeat=1): It returns the cartesian product of the provided iterable with itself for the number of times specified by the optional keyword “repeat”.
Itertools is a module in Python, it is used to iterate over data structures that can be stepped over using a for-loop. Such data structures are also known as iterables. This module works as a fast, memory-efficient tool that is used either by themselves or in combination to form iterator algebra.
In Python 3 you can filter out undesirable combinations using itertools.filterfalse:
# predicate is true when need to skip the combination
predicate = (lambda (a1, a2, b1, b2): a1 <= a2 and (b1 != 0 or b2 == 5), params)
filtered_params = itertools.filterfalse(predicate, params)
You can use list comprehension or itertools.ifilter
:
filtered_params = itertools.ifilter
(lambda (a1, a2, b1, b2): a1 <= a2 and (b1 != 0 or b2 == 5), params)
Note that both of these versions loop and filter out under the hood. If you want to avoid that, you'll need to construct an improved algorithm that creates the tuples without the undesirables.
If you have many parameters, a constraint-based approach using a module like python-constraint may be easier to work with - let it do the hard work of figuring out which combinations are valid.
This would look something like
from constraint import Problem
prob = Problem()
prob.addVariables(["a1", "a2"], range(10,41,2))
prob.addVariable("b1", [0, 2])
prob.addVariable("b2", range(5, 31, 5))
prob.addConstraint(lambda a1, a2: a2 <= a1, ["a1", "a2"])
prob.addConstraint(lambda b1, b2: b1 != 0 or b2 == 5, ["b1", "b2"])
for params in prob.getSolutionIter():
run_sim(**params)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With