Skip to content

config

ComboGenerator #

ComboGenerator class.

Source code in src/config/combo_generator.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
class ComboGenerator:
    """ComboGenerator class."""

    def __init__(self, config_raw: dict) -> None:
        self._combo_params: List[str] = []
        self._combo_values: List[Union[List[int], List[float]]] = []
        self._search_combo_and_format_tuple(config_raw["Graph structure"])
        self._search_combo_and_format_tuple(config_raw["Properties"])
        self._config = Config(config_raw)

    def get_num_combos(self) -> int:
        """Get number of combinations.

        Returns
        -------
        int
            Number of combinations.
        """
        if not self._combo_values:
            return 0
        if len(self._combo_values) == 1:
            return 1

        num_combos = len(self._combo_values[0])
        for v in self._combo_values[1:]:
            num_combos *= len(v)

        return num_combos

    def get_combo_iter(self) -> Generator[Tuple[str, dict, Config], None, None]:
        """Get iterator for combinations.

        Yields
        ------
        Generator[Tuple[str, dict, Config], None, None]
            (combo_dir_name, combo_log, combo_config)
            - combo_dir_name:
                Directory name where the generated DAG set is stored.
            - combo_log:
                Dictionary containing parameter names and chosen values
                for which 'Combination' is specified.
            - combo_config:
                Configuration in which the chosen value is stored
                for the parameter specified as 'Combination'.

        """
        for i, combo in enumerate(itertools.product(*self._combo_values)):
            combo_dir_name = self._create_combo_dir_name(combo, i) or "DAGs"  # type: ignore
            combo_log = {}
            combo_config = copy.deepcopy(self._config)
            for k, v in zip(self._combo_params, combo):
                combo_log[k] = v
                combo_config.update_param_value(k, {"Fixed": v})
            combo_config.optimize()
            combo_config.set_random_seed()

            yield (combo_dir_name, combo_log, combo_config)

    def _create_combo_dir_name(
        self,
        combo: Tuple[Union[int, float]],
        index: int,
    ) -> str:
        combo_dir_name: str = ""
        naming = self._config.naming_of_combination_directory
        if Util.ambiguous_equals(naming, "Full spell"):
            for param, value in zip(self._combo_params, combo):
                param_str_list = param.split(" ")
                param_str_list = [s.capitalize() for s in param_str_list]
                if combo_dir_name:
                    combo_dir_name += f'_{"".join(param_str_list)}_{value}'
                else:
                    combo_dir_name = f'{"".join(param_str_list)}_{value}'

        elif Util.ambiguous_equals(naming, "Abbreviation"):
            for param, value in zip(self._combo_params, combo):
                try:
                    param = TO_ABB[param.lower()]
                except KeyError:  # Additional parameter
                    param = param
                if combo_dir_name:
                    combo_dir_name += f"_{param}_{value}"
                else:
                    combo_dir_name = f"{param}_{value}"

        elif Util.ambiguous_equals(naming, "Index of combination"):
            combo_dir_name = f"Combination_{index+1}"

        else:
            raise NotImplementedError

        return combo_dir_name

    @staticmethod
    def _convert_tuple_to_list(tuple_str: str) -> Union[List[int], List[float]]:
        tuple_str = tuple_str.replace("(", "")
        tuple_str = tuple_str.replace(")", "")
        tuple_str = tuple_str.replace(" ", "")

        args: Dict[str, float] = {}
        for i, arg in enumerate(tuple_str.split(",")):
            if i == 0 or "start" in arg:
                args["start"] = float(arg.replace("start=", ""))
            elif i == 1 or "stop" in arg:
                args["stop"] = float(arg.replace("stop=", ""))
            elif i == 2 or "step" in arg:
                args["step"] = float(arg.replace("step=", ""))

        if args["start"].is_integer() and args["stop"].is_integer() and args["step"].is_integer():
            # int case
            converted = list(range(int(args["start"]), int(args["stop"]), int(args["step"])))
            if (args["stop"] - args["start"]) % args["step"] == 0:
                converted.append(int(args["stop"]))
        else:
            # float case
            def get_num_decimal_places(n: float) -> int:
                ctx = decimal.Context()
                d = ctx.create_decimal(repr(n))
                return len(format(d, "f").split(".")[1])

            m = max(
                get_num_decimal_places(args["start"]),
                get_num_decimal_places(args["stop"]),
                get_num_decimal_places(args["step"]),
            )
            converted = [round(n, m) for n in np.arange(**args)]  # type: ignore
            if ((args["stop"] * (10**m)) - (args["start"]) * (10**m)) % (
                args["step"] * (10**m)
            ) < 10**-10:
                converted.append(args["stop"])  # type: ignore

        return converted

    def _search_combo_and_format_tuple(self, param_dict: dict) -> None:
        for k, v in param_dict.items():
            if isinstance(v, dict):
                if "Combination" in v.keys():
                    self._combo_params.append(k)
                    if isinstance(v["Combination"], str):
                        v["Combination"] = self._convert_tuple_to_list(v["Combination"])  # format
                    self._combo_values.append(v["Combination"])
                elif "Random" in v.keys() and isinstance(v["Random"], str):
                    v["Random"] = self._convert_tuple_to_list(v["Random"])
                else:
                    self._search_combo_and_format_tuple(v)

get_combo_iter() #

Get iterator for combinations.

Yields:

Type Description
Generator[Tuple[str, dict, Config], None, None]

(combo_dir_name, combo_log, combo_config) - combo_dir_name: Directory name where the generated DAG set is stored. - combo_log: Dictionary containing parameter names and chosen values for which 'Combination' is specified. - combo_config: Configuration in which the chosen value is stored for the parameter specified as 'Combination'.

Source code in src/config/combo_generator.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def get_combo_iter(self) -> Generator[Tuple[str, dict, Config], None, None]:
    """Get iterator for combinations.

    Yields
    ------
    Generator[Tuple[str, dict, Config], None, None]
        (combo_dir_name, combo_log, combo_config)
        - combo_dir_name:
            Directory name where the generated DAG set is stored.
        - combo_log:
            Dictionary containing parameter names and chosen values
            for which 'Combination' is specified.
        - combo_config:
            Configuration in which the chosen value is stored
            for the parameter specified as 'Combination'.

    """
    for i, combo in enumerate(itertools.product(*self._combo_values)):
        combo_dir_name = self._create_combo_dir_name(combo, i) or "DAGs"  # type: ignore
        combo_log = {}
        combo_config = copy.deepcopy(self._config)
        for k, v in zip(self._combo_params, combo):
            combo_log[k] = v
            combo_config.update_param_value(k, {"Fixed": v})
        combo_config.optimize()
        combo_config.set_random_seed()

        yield (combo_dir_name, combo_log, combo_config)

get_num_combos() #

Get number of combinations.

Returns:

Type Description
int

Number of combinations.

Source code in src/config/combo_generator.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def get_num_combos(self) -> int:
    """Get number of combinations.

    Returns
    -------
    int
        Number of combinations.
    """
    if not self._combo_values:
        return 0
    if len(self._combo_values) == 1:
        return 1

    num_combos = len(self._combo_values[0])
    for v in self._combo_values[1:]:
        num_combos *= len(v)

    return num_combos

Config #

Config class

This class provides the value of the config entered by the property. The property names of this class correspond to all parameter names at any level.

Notes#

If the structure of the configuration file changes, this class must be reprogrammed.

Source code in src/config/config.py
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
class Config:
    """Config class

    This class provides the value of the config entered by the property.
    The property names of this class correspond to all parameter names at any level.

    Notes
    -----
    If the structure of the configuration file changes, this class must be reprogrammed.

    """

    def __init__(self, config_raw: dict) -> None:
        self.seed = config_raw["Seed"]
        self.number_of_dags = config_raw["Number of DAGs"]
        self.graph_structure = config_raw["Graph structure"]
        self.properties = config_raw["Properties"]
        self.output_formats = config_raw["Output formats"]

    def update_param_value(self, param_name: str, value: Any) -> None:
        """Update parameter value.

        Parameters
        ----------
        param_name : str
            Parameter name.
        value : Any
            Value to be set.

        """
        property_name = Util.convert_to_property(param_name)
        if hasattr(self, property_name):
            object.__setattr__(self, property_name, value)
        else:  # Additional properties
            if self.node_properties and param_name in self.node_properties.keys():
                self.node_properties[param_name] = value
            elif self.edge_properties and param_name in self.edge_properties.keys():
                self.edge_properties[param_name] = value

    def set_random_seed(self) -> None:
        random.seed(self.seed)

    def optimize(self) -> None:
        """Remove 'Random' and 'Fixed'"""
        self._remove_random_fixed(self.graph_structure)
        self._remove_random_fixed(self.properties)

    @staticmethod
    def _remove_random_fixed(param_dict: dict) -> None:
        for k, v in param_dict.items():
            if isinstance(v, dict):
                if set(v.keys()) <= {"Random", "Fixed"}:
                    # Remove
                    value = list(v.values())
                    assert len(value) == 1
                    param_dict[k] = value[0]
                else:
                    Config._remove_random_fixed(v)

    # ----- Graph Structure -----
    @property
    def generation_method(self):
        return self.graph_structure.get("Generation method")

    @property
    def number_of_nodes(self):
        return self.graph_structure.get("Number of nodes")

    @number_of_nodes.setter
    def number_of_nodes(self, value):
        self.graph_structure["Number of nodes"] = value

    @property
    def ensure_weakly_connected(self):
        return self.graph_structure.get("Ensure weakly connected")

    @property
    def out_degree(self):
        return self.graph_structure.get("Out-degree")

    @out_degree.setter
    def out_degree(self, value):
        self.graph_structure["Out-degree"] = value

    @property
    def in_degree(self):
        return self.graph_structure.get("In-degree")

    @in_degree.setter
    def in_degree(self, value):
        self.graph_structure["In-degree"] = value

    @property
    def probability_of_edge(self):
        return self.graph_structure.get("Probability of edge")

    @probability_of_edge.setter
    def probability_of_edge(self, value):
        self.graph_structure["Probability of edge"] = value

    @property
    def number_of_chains(self):
        return self.graph_structure.get("Number of chains")

    @number_of_chains.setter
    def number_of_chains(self, value):
        self.graph_structure["Number of chains"] = value

    @property
    def main_sequence_length(self):
        return self.graph_structure.get("Main sequence length")

    @main_sequence_length.setter
    def main_sequence_length(self, value):
        self.graph_structure["Main sequence length"] = value

    @property
    def number_of_sub_sequences(self):
        return self.graph_structure.get("Number of sub sequences")

    @number_of_sub_sequences.setter
    def number_of_sub_sequences(self, value):
        self.graph_structure["Number of sub sequences"] = value

    @property
    def vertically_link_chains(self):
        return self.graph_structure.get("Vertically link chains")

    @property
    def main_sequence_tail(self):
        if self.vertically_link_chains:
            return self.graph_structure["Vertically link chains"].get("Main sequence tail")
        else:
            return None

    @main_sequence_tail.setter
    def main_sequence_tail(self, value):
        self.graph_structure["Vertically link chains"]["Main sequence tail"] = value

    @property
    def sub_sequence_tail(self):
        if self.vertically_link_chains:
            return self.graph_structure["Vertically link chains"].get("Sub sequence tail")
        else:
            return None

    @sub_sequence_tail.setter
    def sub_sequence_tail(self, value):
        self.graph_structure["Vertically link chains"]["Sub sequence tail"] = value

    @property
    def number_of_entry_nodes(self):
        if Util.ambiguous_equals(self.generation_method, "chain-based"):
            if self.vertically_link_chains:
                return self.graph_structure["Vertically link chains"].get("Number of entry nodes")
            else:
                return None
        else:
            return self.graph_structure.get("Number of entry nodes")

    @number_of_entry_nodes.setter
    def number_of_entry_nodes(self, value):
        if Util.ambiguous_equals(self.generation_method, "chain-based"):
            self.graph_structure["Vertically link chains"]["Number of entry nodes"] = value
        else:
            self.graph_structure["Number of entry nodes"] = value

    @property
    def merge_chains(self):
        return self.graph_structure.get("Merge chains")

    @property
    def middle_of_chain(self):
        if self.merge_chains:
            return self.graph_structure["Merge chains"].get("Middle of chain")
        else:
            return None

    @middle_of_chain.setter
    def middle_of_chain(self, value):
        self.graph_structure["Merge chains"]["Middle of chain"] = value

    @property
    def exit_node(self):
        if self.merge_chains:
            return self.graph_structure["Merge chains"].get("Exit node")
        else:
            return None

    @exit_node.setter
    def exit_node(self, value):
        self.graph_structure["Merge chains"]["Exit node"] = value

    @property
    def number_of_exit_nodes(self):
        if Util.ambiguous_equals(self.generation_method, "chain-based"):
            if self.merge_chains:
                return self.graph_structure["Merge chains"].get("Number of exit nodes")
            else:
                return None
        else:
            return self.graph_structure.get("Number of exit nodes")

    @number_of_exit_nodes.setter
    def number_of_exit_nodes(self, value):
        if Util.ambiguous_equals(self.generation_method, "chain-based"):
            self.graph_structure["Merge chains"]["Number of exit nodes"] = value
        else:
            self.graph_structure["Number of exit nodes"] = value

    # ----- Properties -----
    @property
    def execution_time(self):
        return self.properties.get("Execution time")

    @execution_time.setter
    def execution_time(self, value):
        self.properties["Execution time"] = value

    @property
    def communication_time(self):
        return self.properties.get("Communication time")

    @communication_time.setter
    def communication_time(self, value):
        self.properties["Communication time"] = value

    @property
    def ccr(self):
        return self.properties.get("CCR")

    @ccr.setter
    def ccr(self, value):
        self.properties["CCR"] = value

    @property
    def end_to_end_deadline(self):
        return self.properties.get("End-to-end deadline")

    @property
    def ratio_of_deadline_to_critical_path(self):
        if self.end_to_end_deadline:
            return self.properties["End-to-end deadline"].get("Ratio of deadline to critical path")
        else:
            return None

    @ratio_of_deadline_to_critical_path.setter
    def ratio_of_deadline_to_critical_path(self, value):
        self.properties["End-to-end deadline"]["Ratio of deadline to critical path"] = value

    @property
    def multi_rate(self):
        return self.properties.get("Multi-rate")

    @property
    def periodic_type(self):
        if self.multi_rate:
            return self.properties["Multi-rate"].get("Periodic type")
        else:
            return None

    @periodic_type.setter
    def periodic_type(self, value):
        self.properties["Multi-rate"]["Periodic type"] = value

    @property
    def period(self):
        if self.multi_rate:
            return self.properties["Multi-rate"].get("Period")
        else:
            return None

    @period.setter
    def period(self, value):
        self.properties["Multi-rate"]["Period"] = value

    @property
    def entry_node_period(self):
        if self.multi_rate:
            return self.properties["Multi-rate"].get("Entry node period")
        else:
            return None

    @entry_node_period.setter
    def entry_node_period(self, value):
        self.properties["Multi-rate"]["Entry node period"] = value

    @property
    def exit_node_period(self):
        if self.multi_rate:
            return self.properties["Multi-rate"].get("Exit node period")
        else:
            return None

    @exit_node_period.setter
    def exit_node_period(self, value):
        self.properties["Multi-rate"]["Exit node period"] = value

    @property
    def offset(self):
        if self.multi_rate:
            return self.properties["Multi-rate"].get("Offset")
        else:
            return None

    @offset.setter
    def offset(self, value):
        self.properties["Multi-rate"]["Offset"] = value

    @property
    def total_utilization(self):
        if self.multi_rate:
            return self.properties["Multi-rate"].get("Total utilization")
        else:
            return None

    @total_utilization.setter
    def total_utilization(self, value):
        self.properties["Multi-rate"]["Total utilization"] = value

    @property
    def maximum_utilization(self):
        if self.multi_rate:
            return self.properties["Multi-rate"].get("Maximum utilization")
        else:
            return None

    @maximum_utilization.setter
    def maximum_utilization(self, value):
        self.properties["Multi-rate"]["Maximum utilization"] = value

    @property
    def additional_properties(self) -> Optional[dict]:
        return self.properties.get("Additional properties")

    @property
    def node_properties(self) -> Optional[dict]:
        additional_properties = self.additional_properties
        if additional_properties:
            return additional_properties.get("Node properties")
        else:
            return None

    @property
    def edge_properties(self) -> Optional[dict]:
        additional_properties = self.additional_properties
        if additional_properties:
            return additional_properties.get("Edge properties")
        else:
            return None

    # ----- Output formats -----
    @property
    def naming_of_combination_directory(self):
        return self.output_formats.get("Naming of combination directory")

    @property
    def yaml(self):
        return self.output_formats["DAG"].get("YAML")

    @property
    def json(self):
        return self.output_formats["DAG"].get("JSON")

    @property
    def xml(self):
        return self.output_formats["DAG"].get("XML")

    @property
    def dot(self):
        return self.output_formats["DAG"].get("DOT")

    @property
    def figure(self):
        return self.output_formats.get("Figure")

    @property
    def draw_legend(self):
        if self.figure:
            return self.output_formats["Figure"].get("Draw legend")
        else:
            return None

    @property
    def png(self):
        if self.figure:
            return self.output_formats["Figure"].get("PNG")
        else:
            return None

    @property
    def svg(self):
        if self.figure:
            return self.output_formats["Figure"].get("SVG")
        else:
            return None

    @property
    def eps(self):
        if self.figure:
            return self.output_formats["Figure"].get("EPS")
        else:
            return None

    @property
    def pdf(self):
        if self.figure:
            return self.output_formats["Figure"].get("PDF")
        else:
            return None

optimize() #

Remove 'Random' and 'Fixed'

Source code in src/config/config.py
49
50
51
52
def optimize(self) -> None:
    """Remove 'Random' and 'Fixed'"""
    self._remove_random_fixed(self.graph_structure)
    self._remove_random_fixed(self.properties)

update_param_value(param_name, value) #

Update parameter value.

Parameters:

Name Type Description Default
param_name str

Parameter name.

required
value Any

Value to be set.

required
Source code in src/config/config.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def update_param_value(self, param_name: str, value: Any) -> None:
    """Update parameter value.

    Parameters
    ----------
    param_name : str
        Parameter name.
    value : Any
        Value to be set.

    """
    property_name = Util.convert_to_property(param_name)
    if hasattr(self, property_name):
        object.__setattr__(self, property_name, value)
    else:  # Additional properties
        if self.node_properties and param_name in self.node_properties.keys():
            self.node_properties[param_name] = value
        elif self.edge_properties and param_name in self.edge_properties.keys():
            self.edge_properties[param_name] = value

ConfigValidator #

Config validator class.

Source code in src/config/config_validator.py
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
class ConfigValidator:
    """Config validator class."""

    base_schema = Schema(
        {
            Regex("Seed", flags=re.I): int,
            Regex("Number of DAGs", flags=re.I): int,
            Regex("Graph structure", flags=re.I): {
                Regex("Generation method", flags=re.I): Or(
                    Regex("Fan-in/Fan-out", flags=re.I),
                    Regex(r"G\(n,[ ]*p\)", flags=re.I),
                    Regex("Chain-based", flags=re.I),
                ),
                Optional(Regex("Number of nodes", flags=re.I)): Or(
                    {Regex("Fixed", flags=re.I): int},
                    {Regex("Random", flags=re.I): Or([int], str)},
                    {Regex("Combination", flags=re.I): Or([int], str)},
                ),
            },
            Regex("Properties", flags=re.I): {
                Optional(Regex("Execution time", flags=re.I)): Or(
                    {Regex("Fixed", flags=re.I): int},
                    {Regex("Random", flags=re.I): Or([int], str)},
                    {Regex("Combination", flags=re.I): Or([int], str)},
                ),
                Optional(Regex("Communication time", flags=re.I)): Or(
                    {Regex("Fixed", flags=re.I): int},
                    {Regex("Random", flags=re.I): Or([int], str)},
                    {Regex("Combination", flags=re.I): Or([int], str)},
                ),
                Optional(Regex("CCR", flags=re.I)): Or(
                    {Regex("Fixed", flags=re.I): float},
                    {Regex("Random", flags=re.I): Or([float], str)},
                    {Regex("Combination", flags=re.I): Or([float], str)},
                ),
                Optional(Regex("End-to-end deadline", flags=re.I)): {
                    Regex("Ratio of deadline to critical path", flags=re.I): Or(
                        {Regex("Fixed", flags=re.I): float},
                        {Regex("Random", flags=re.I): Or([float], str)},
                        {Regex("Combination", flags=re.I): Or([float], str)},
                    )
                },
                Optional(Regex("Multi-rate", flags=re.I)): {
                    Regex("Periodic type", flags=re.I): Or(
                        Regex("All", flags=re.I),
                        Regex("IO", flags=re.I),
                        Regex("Entry", flags=re.I),
                        Regex("Chain", flags=re.I),
                    ),
                    Regex("Period", flags=re.I): Or(
                        {Regex("Fixed", flags=re.I): int},
                        {Regex("Random", flags=re.I): Or([int], str)},
                        {Regex("Combination", flags=re.I): Or([int], str)},
                    ),
                    Optional(Regex("Entry node period", flags=re.I)): Or(
                        {Regex("Fixed", flags=re.I): int},
                        {Regex("Random", flags=re.I): Or([int], str)},
                        {Regex("Combination", flags=re.I): Or([int], str)},
                    ),
                    Optional(Regex("Exit node period", flags=re.I)): Or(
                        {Regex("Fixed", flags=re.I): int},
                        {Regex("Random", flags=re.I): Or([int], str)},
                        {Regex("Combination", flags=re.I): Or([int], str)},
                    ),
                    Optional(Regex("Offset", flags=re.I)): Or(
                        {Regex("Fixed", flags=re.I): int},
                        {Regex("Random", flags=re.I): Or([int], str)},
                        {Regex("Combination", flags=re.I): Or([int], str)},
                    ),
                    Optional(Regex("Total utilization", flags=re.I)): Or(
                        {Regex("Fixed", flags=re.I): float},
                        {Regex("Random", flags=re.I): Or([float], str)},
                        {Regex("Combination", flags=re.I): Or([float], str)},
                    ),
                    Optional(Regex("Maximum utilization", flags=re.I)): Or(
                        {Regex("Fixed", flags=re.I): float},
                        {Regex("Random", flags=re.I): Or([float], str)},
                        {Regex("Combination", flags=re.I): Or([float], str)},
                    ),
                },
                Optional(Regex("Additional properties", flags=re.I)): {
                    Optional(Regex("Node properties", flags=re.I)): {
                        str: Or(
                            {Regex("Fixed", flags=re.I): Or(float, int)},
                            {Regex("Random", flags=re.I): Or([float], [int], str)},
                            {Regex("Combination", flags=re.I): Or([float], [int], str)},
                        )
                    },
                    Optional(Regex("Edge properties", flags=re.I)): {
                        str: Or(
                            {Regex("Fixed", flags=re.I): Or(float, int)},
                            {Regex("Random", flags=re.I): Or([float], [int], str)},
                            {Regex("Combination", flags=re.I): Or([float], [int], str)},
                        )
                    },
                },
            },
            Regex("Output formats", flags=re.I): {
                Regex("Naming of combination directory", flags=re.I): Or(
                    Regex("Abbreviation", flags=re.I),
                    Regex("Full spell", flags=re.I),
                    Regex("Index of combination", flags=re.I),
                ),
                Regex("DAG", flags=re.I): {
                    Optional(Regex("YAML", flags=re.I)): bool,
                    Optional(Regex("JSON", flags=re.I)): bool,
                    Optional(Regex("XML", flags=re.I)): bool,
                    Optional(Regex("DOT", flags=re.I)): bool,
                },
                Optional(Regex("Figure", flags=re.I)): {
                    Optional(Regex("Draw legend", flags=re.I)): bool,
                    Optional(Regex("PNG", flags=re.I)): bool,
                    Optional(Regex("SVG", flags=re.I)): bool,
                    Optional(Regex("EPS", flags=re.I)): bool,
                    Optional(Regex("PDF", flags=re.I)): bool,
                },
            },
        },
        ignore_extra_keys=True,
    )

    fifo_gnp_common_schema = Schema(
        {
            Regex("Graph structure", flags=re.I): {
                Regex("Number of entry nodes", flags=re.I): Or(
                    {Regex("Fixed", flags=re.I): int},
                    {Regex("Random", flags=re.I): Or([int], str)},
                    {Regex("Combination", flags=re.I): Or([int], str)},
                ),
                Regex("Number of exit nodes", flags=re.I): Or(
                    {Regex("Fixed", flags=re.I): int},
                    {Regex("Random", flags=re.I): Or([int], str)},
                    {Regex("Combination", flags=re.I): Or([int], str)},
                ),
                Optional(Regex("Ensure weakly connected", flags=re.I)): bool,
            }
        },
        ignore_extra_keys=True,
    )

    fan_in_fan_out_schema = Schema(
        {
            Regex("Graph structure", flags=re.I): {
                Regex("In-degree", flags=re.I): Or(
                    {Regex("Fixed", flags=re.I): int},
                    {Regex("Random", flags=re.I): Or([int], str)},
                    {Regex("Combination", flags=re.I): Or([int], str)},
                ),
                Regex("Out-degree", flags=re.I): Or(
                    {Regex("Fixed", flags=re.I): int},
                    {Regex("Random", flags=re.I): Or([int], str)},
                    {Regex("Combination", flags=re.I): Or([int], str)},
                ),
            }
        },
        ignore_extra_keys=True,
    )

    g_n_p_schema = Schema(
        {
            Regex("Graph structure", flags=re.I): {
                Regex("Probability of edge", re.I): Or(
                    {Regex("Fixed", flags=re.I): float},
                    {Regex("Random", flags=re.I): Or([float], str)},
                    {Regex("Combination", flags=re.I): Or([float], str)},
                )
            }
        },
        ignore_extra_keys=True,
    )

    chain_based_schema = Schema(
        {
            Regex("Graph structure", flags=re.I): {
                Regex("Number of chains", re.I): Or(
                    {Regex("Fixed", flags=re.I): int},
                    {Regex("Random", flags=re.I): Or([int], str)},
                    {Regex("Combination", flags=re.I): Or([int], str)},
                ),
                Regex("Main sequence length", re.I): Or(
                    {Regex("Fixed", flags=re.I): int},
                    {Regex("Random", flags=re.I): Or([int], str)},
                    {Regex("Combination", flags=re.I): Or([int], str)},
                ),
                Regex("Number of sub sequences", re.I): Or(
                    {Regex("Fixed", flags=re.I): int},
                    {Regex("Random", flags=re.I): Or([int], str)},
                    {Regex("Combination", flags=re.I): Or([int], str)},
                ),
                Optional(Regex("Vertically link chains", re.I)): {
                    Regex("Number of entry nodes", re.I): Or(
                        {Regex("Fixed", flags=re.I): int},
                        {Regex("Random", flags=re.I): Or([int], str)},
                        {Regex("Combination", flags=re.I): Or([int], str)},
                    ),
                    Regex("Main sequence tail", re.I): bool,
                    Regex("Sub sequence tail", re.I): bool,
                },
                Optional(Regex("Merge chains", re.I)): {
                    Regex("Number of exit nodes", re.I): Or(
                        {Regex("Fixed", flags=re.I): int},
                        {Regex("Random", flags=re.I): Or([int], str)},
                        {Regex("Combination", flags=re.I): Or([int], str)},
                    ),
                    Regex("Middle of chain", re.I): bool,
                    Regex("Exit node", re.I): bool,
                },
            }
        },
        ignore_extra_keys=True,
    )

    def __init__(self, config_raw: dict) -> None:
        self._config_raw = config_raw

    def validate(self) -> None:
        """Validate config.

        Check the entered configurations according to the schema.
        For detail, see https://www.andrewvillazon.com/validate-yaml-python-schema/.

        """
        self.base_schema.validate(self._config_raw)
        gm = self._config_raw["Graph structure"]["Generation method"]
        if Util.ambiguous_equals(gm, "fan-in/fan-out"):
            self.fifo_gnp_common_schema.validate(self._config_raw)
            self.fan_in_fan_out_schema.validate(self._config_raw)
        elif Util.ambiguous_equals(gm, "g(n, p)"):
            self.fifo_gnp_common_schema.validate(self._config_raw)
            self.g_n_p_schema.validate(self._config_raw)
        elif Util.ambiguous_equals(gm, "chain-based"):
            self.chain_based_schema.validate(self._config_raw)

validate() #

Validate config.

Check the entered configurations according to the schema. For detail, see https://www.andrewvillazon.com/validate-yaml-python-schema/.

Source code in src/config/config_validator.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def validate(self) -> None:
    """Validate config.

    Check the entered configurations according to the schema.
    For detail, see https://www.andrewvillazon.com/validate-yaml-python-schema/.

    """
    self.base_schema.validate(self._config_raw)
    gm = self._config_raw["Graph structure"]["Generation method"]
    if Util.ambiguous_equals(gm, "fan-in/fan-out"):
        self.fifo_gnp_common_schema.validate(self._config_raw)
        self.fan_in_fan_out_schema.validate(self._config_raw)
    elif Util.ambiguous_equals(gm, "g(n, p)"):
        self.fifo_gnp_common_schema.validate(self._config_raw)
        self.g_n_p_schema.validate(self._config_raw)
    elif Util.ambiguous_equals(gm, "chain-based"):
        self.chain_based_schema.validate(self._config_raw)