comparison env/lib/python3.9/site-packages/galaxy/util/permutations.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 """ There is some shared logic between matching/multiplying inputs in workflows
2 and tools. This module is meant to capture some general permutation logic that
3 can be applicable for both cases but will only be used in the newer tools case
4 first.
5
6 Maybe this doesn't make sense and maybe much of this stuff could be replaced
7 with itertools product and permutations. These are open questions.
8 """
9
10 from galaxy.exceptions import MessageException
11 from galaxy.util.bunch import Bunch
12
13 input_classification = Bunch(
14 SINGLE="single",
15 MATCHED="matched",
16 MULTIPLIED="multiplied",
17 )
18
19
20 class InputMatchedException(MessageException):
21 """ Indicates problem matching inputs while building up inputs
22 permutations. """
23
24
25 def expand_multi_inputs(inputs, classifier, key_filter=None):
26 key_filter = key_filter or (lambda x: True)
27
28 single_inputs, matched_multi_inputs, multiplied_multi_inputs = __split_inputs(
29 inputs,
30 classifier,
31 key_filter
32 )
33
34 # Build up every combination of inputs to be run together.
35 input_combos = __extend_with_matched_combos(single_inputs, matched_multi_inputs)
36 input_combos = __extend_with_multiplied_combos(input_combos, multiplied_multi_inputs)
37
38 return input_combos
39
40
41 def __split_inputs(inputs, classifier, key_filter):
42 key_filter = key_filter or (lambda x: True)
43
44 single_inputs = {}
45 matched_multi_inputs = {}
46 multiplied_multi_inputs = {}
47
48 for input_key in filter(key_filter, inputs):
49 input_type, expanded_val = classifier(input_key)
50 if input_type == input_classification.SINGLE:
51 single_inputs[input_key] = expanded_val
52 elif input_type == input_classification.MATCHED:
53 matched_multi_inputs[input_key] = expanded_val
54 elif input_type == input_classification.MULTIPLIED:
55 multiplied_multi_inputs[input_key] = expanded_val
56
57 return (single_inputs, matched_multi_inputs, multiplied_multi_inputs)
58
59
60 def __extend_with_matched_combos(single_inputs, multi_inputs):
61 """
62
63 {a => 1, b => 2} and {c => {3, 4}, d => {5, 6}}
64
65 Becomes
66
67 [ {a => 1, b => 2, c => 3, d => 5}, {a => 1, b => 2, c => 4, d => 6}, ]
68
69 """
70
71 if len(multi_inputs) == 0:
72 return [single_inputs]
73
74 matched_multi_inputs = []
75
76 first_multi_input_key = next(iter(multi_inputs.keys()))
77 first_multi_value = multi_inputs.get(first_multi_input_key)
78
79 for value in first_multi_value:
80 new_inputs = __copy_and_extend_inputs(single_inputs, first_multi_input_key, value)
81 matched_multi_inputs.append(new_inputs)
82
83 for multi_input_key, multi_input_values in multi_inputs.items():
84 if multi_input_key == first_multi_input_key:
85 continue
86 if len(multi_input_values) != len(first_multi_value):
87 raise InputMatchedException("Received %d inputs for '%s' and %d inputs for '%s', these should be of equal length" %
88 (len(multi_input_values), multi_input_key, len(first_multi_value), first_multi_input_key))
89
90 for index, value in enumerate(multi_input_values):
91 matched_multi_inputs[index][multi_input_key] = value
92
93 return matched_multi_inputs
94
95
96 def __extend_with_multiplied_combos(input_combos, multi_inputs):
97 combos = input_combos
98
99 for multi_input_key, multi_input_value in multi_inputs.items():
100 iter_combos = []
101
102 for combo in combos:
103 for input_value in multi_input_value:
104 iter_combo = __copy_and_extend_inputs(combo, multi_input_key, input_value)
105 iter_combos.append(iter_combo)
106
107 combos = iter_combos
108
109 return combos
110
111
112 def __copy_and_extend_inputs(inputs, key, value):
113 new_inputs = dict(inputs)
114 new_inputs[key] = value
115 return new_inputs