From 21b3c639075fc1d3d799261483f9be0d1ad570c0 Mon Sep 17 00:00:00 2001 From: vgolubev <nakmak1998@gmail.com> Date: Sun, 31 May 2020 19:08:18 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D0=B7=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8E=20=D0=B3=D1=80=D0=B0=D1=84=D0=BE=D0=B2=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8=20=D0=B2=20=D0=B2=D0=B8?= =?UTF-8?q?=D0=B4=D0=B5=20=D1=81=D0=BB=D0=BE=D0=B2=D0=B0=D1=80=D1=8F=20?= =?UTF-8?q?=D0=B2=20GraphFactory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Данный функционал позволяет отправлять на клиент структурное описание графовой модели для последующей визуализации и интерактивной работы. Граф представляется в виде словаря со следующей структурой: graph: { 'init_state_name': { 'subgraph': None/{subgraph}, 'connect_to': [{'next': next_state_name, 'morph_f': morphism, 'pred_f': predicate}, ...] // здесь отобразаются связи вершины с другими вершинами }, 'state_name2': {...}, ... 'term_state_name': {...} } Данный словарь заполняется во время парсинга adot-файла. Не зависит от режима парсинга: c генарацией cpp или без. --- comsdk/graph.py | 164 +++++++++++++++++++++++++---------------------- comsdk/parser.py | 35 ++++++++-- 2 files changed, 119 insertions(+), 80 deletions(-) diff --git a/comsdk/graph.py b/comsdk/graph.py index 99e9af7..79a4caf 100644 --- a/comsdk/graph.py +++ b/comsdk/graph.py @@ -1,13 +1,14 @@ import collections +import importlib as imp import os from enum import Enum, auto from functools import partial -import importlib as imp - import pycomsdk.comsdk.comaux as aux -ImplicitParallelizationInfo = collections.namedtuple('ImplicitParallelizationInfo', ['array_keys_mapping', 'branches_number', 'branch_i']) +ImplicitParallelizationInfo = collections.namedtuple('ImplicitParallelizationInfo', + ['array_keys_mapping', 'branches_number', 'branch_i']) + class Func(): __slots__ = ( @@ -16,42 +17,45 @@ class Func(): 'comment', 'name' ) - def __init__(self, module="", name="", dummy=False,func=None, comment=''): + + def __init__(self, module="", name="", dummy=False, func=None, comment=''): self.module = module self.name = name - self.comment=comment.replace("\0", " ") if comment is not None else "" - if module =="" or name =="" or module is None or name is None: + self.comment = comment.replace("\0", " ") if comment is not None else "" + if module == "" or name == "" or module is None or name is None: dummy = True if func is not None: self.func = func elif dummy: self.func = lambda data: data - else: - print("LOADING function {} from {} module".format(name, module) ) + else: + print("LOADING function {} from {} module".format(name, module)) try: self.func = getattr(imp.import_module(module), name) except Exception: raise Exception("Could not load function {} from {} module".format(name, module)) def __str__(self): - if self.module =="" or self.name =="": + if self.module == "" or self.name == "": return '' return "{}_{}".format(self.module, self.name) + class Selector(Func): def __init__(self, ntransf, module="", name="", dummy=False): - if module=="" and name =="": + if module == "" and name == "": dummy = True self.dummy = dummy super().__init__(module, name, func=(lambda x: [True for i in range(ntransf)]) if dummy else None) + def __str__(self): - if self.module =="" or self.name =="": + if self.module == "" or self.name == "": return '' return "{}_{}".format(self.module, self.name) class Transfer: - def __init__(self, edge, output_state, order=0, comment = None): + def __init__(self, edge, output_state, order=0, comment=None): self.edge = edge self.output_state = output_state self.order = order @@ -60,10 +64,12 @@ class Transfer: self.edge.morph(data, dynamic_keys_mapping) return self.output_state + class IdleRunType(Enum): INIT = auto() CLEANUP = auto() + class PluralState: def __init__(self, states): self.states = states @@ -73,10 +79,12 @@ class PluralState: for init_state, term_state in zip(self.states, term_states): init_state.transfers.append(Transfer(edge, term_state)) + class Graph: ''' Class describing a graph-based computational method. Graph execution must start from this object. ''' + def __init__(self, init_state, term_state=None, ): @@ -100,15 +108,15 @@ class Graph: cur_state = self.init_state implicit_parallelization_info = None while cur_state is not None: - # print('1) In main loop', implicit_parallelization_info) -# morph = _run_state(cur_state, data, implicit_parallelization_info) + # print('1) In main loop', implicit_parallelization_info) + # morph = _run_state(cur_state, data, implicit_parallelization_info) transfer_f, implicit_parallelization_info = _run_state(cur_state, data, implicit_parallelization_info) -# print('2) In main loop', implicit_parallelization_info) + # print('2) In main loop', implicit_parallelization_info) if '__EXCEPTION__' in data: return False -# cur_state, implicit_parallelization_info = morph(data) + # cur_state, implicit_parallelization_info = morph(data) cur_state = transfer_f(data) -# print(morph) + # print(morph) if '__EXCEPTION__' in data: return False return True @@ -124,12 +132,10 @@ class Graph: data['__WORKING_DIR__'] = data['__CURRENT_WORKING_DIR__'] - - class State: __slots__ = [ 'name', - 'input_edges_number', #output_edges_number == len(transfers) + 'input_edges_number', # output_edges_number == len(transfers) 'looped_edges_number', 'activated_input_edges_number', 'transfers', @@ -141,11 +147,13 @@ class State: '_proxy_state', 'possible_branches', 'comment' - ] - def __init__(self, name, + ] + + def __init__(self, name, parallelization_policy=None, selector=None, - array_keys_mapping=None, # if array_keys_mapping is not None, we have implicit parallelization in this state + array_keys_mapping=None, + # if array_keys_mapping is not None, we have implicit parallelization in this state ): self.name = name self.parallelization_policy = SerialParallelizationPolicy() if parallelization_policy is None else parallelization_policy @@ -155,19 +163,17 @@ class State: self.looped_edges_number = 0 self.activated_input_edges_number = 0 self.transfers = [] - self.possible_branches=[] - self.is_term_state=False + self.possible_branches = [] + self.is_term_state = False self._branching_states_history = None - self._proxy_state=None + self._proxy_state = None self.comment = None def idle_run(self, idle_run_type, branching_states_history): def __sort_by_order(tr): return tr.edge.order - self.transfers.sort(key = __sort_by_order) - # print(self.name) - # for t in self.transfers: - # print("\t", t.edge.order, t.edge.pred_name, t.edge.morph_name) + + self.transfers.sort(key=__sort_by_order) if self._proxy_state is not None: return self._proxy_state.idle_run(idle_run_type, branching_states_history) if idle_run_type == IdleRunType.INIT: @@ -175,7 +181,7 @@ class State: if self.input_edges_number != 1: if self._is_looped_branch(branching_states_history): self.looped_edges_number += 1 - return # no need to go further if we already were there + return # no need to go further if we already were there if self._branching_states_history is None: self._branching_states_history = branching_states_history elif idle_run_type == IdleRunType.CLEANUP: @@ -186,9 +192,7 @@ class State: if self._branching_states_history is None: self._branching_states_history = branching_states_history else: - self.activated_input_edges_number += 1 # BUG: here we need to choose somehow whether we proceed or not - # if len(self.transfers) == 0: - # print('Terminate state found') + self.activated_input_edges_number += 1 # BUG: here we need to choose somehow whether we proceed or not if len(self.transfers) == 1: self.transfers[0].output_state.idle_run(idle_run_type, branching_states_history) else: @@ -201,26 +205,23 @@ class State: self.comment = comment self.transfers.append(Transfer(edge, term_state)) self.selector = Selector(len(self.transfers)) -# edge.set_output_state(term_state) -# self.output_edges.append(edge) def replace_with_graph(self, graph): self._proxy_state = graph.init_state graph.term_state.transfers = self.transfers graph.term_state.selector = self.selector - def run(self, data, implicit_parallelization_info=None): - print('STATE {}\n\tjust entered, implicit_parallelization_info: {}'.format(self.name, implicit_parallelization_info)) - # print('\t{}'.format(data)) + print('STATE {}\n\tjust entered, implicit_parallelization_info: {}'.format(self.name, + implicit_parallelization_info)) if self._proxy_state is not None: return self._proxy_state.run(data, implicit_parallelization_info) self._activate_input_edge(implicit_parallelization_info) - #self.activated_input_edges_number += 1 - print('\trequired input: {}, active: {}, looped: {}'.format(self.input_edges_number, self.activated_input_edges_number, self.looped_edges_number)) -# print('qwer') + print('\trequired input: {}, active: {}, looped: {}'.format(self.input_edges_number, + self.activated_input_edges_number, + self.looped_edges_number)) if not self._ready_to_transfer(implicit_parallelization_info): - return None, None # it means that this state waits for some incoming edges (it is a point of collision of several edges) + return None, None # it means that this state waits for some incoming edges (it is a point of collision of several edges) self._reset_activity(implicit_parallelization_info) if self.is_term_state: implicit_parallelization_info = None @@ -231,16 +232,20 @@ class State: if not selected_edges: raise GraphUnexpectedTermination( "STATE {}: error in selector: {} ".format(self.name, selected_edges)) - selected_transfers = [self.transfers[i] for i, _ in enumerate(selected_edges) if selected_edges[i]==True] + selected_transfers = [self.transfers[i] for i, _ in enumerate(selected_edges) if selected_edges[i] == True] for transf in selected_transfers: if not transf.edge.predicate(data, dynamic_keys_mapping): - raise Exception("\tERROR: predicate {} returns {} running from state {}\n data{}".format(transf.edge.pred_f.name,transf.edge.predicate(data, dynamic_keys_mapping), self.name, data)) + raise Exception( + "\tERROR: predicate {} returns {} running from state {}\n data{}".format(transf.edge.pred_f.name, + transf.edge.predicate(data, + dynamic_keys_mapping), + self.name, data)) return self.parallelization_policy.make_transfer_func(selected_transfers, - array_keys_mapping=self.array_keys_mapping, - implicit_parallelization_info=implicit_parallelization_info, state=self), \ + array_keys_mapping=self.array_keys_mapping, + implicit_parallelization_info=implicit_parallelization_info, + state=self), \ implicit_parallelization_info - def _activate_input_edge(self, implicit_parallelization_info=None): if implicit_parallelization_info is None or self.is_term_state: self.activated_input_edges_number += 1 @@ -255,16 +260,17 @@ class State: if self.is_term_state: required_activated_input_edges_number = implicit_parallelization_info.branches_number return self.activated_input_edges_number == required_activated_input_edges_number - return self.activated_input_edges_number[implicit_parallelization_info.branch_i] == required_activated_input_edges_number + return self.activated_input_edges_number[ + implicit_parallelization_info.branch_i] == required_activated_input_edges_number else: return self.activated_input_edges_number == required_activated_input_edges_number -# if implicit_parallelization_info is None or self.is_term_state: -# if self.is_term_state: -# required_activated_input_edges_number = implicit_parallelization_info.branches_number -# return self.activated_input_edges_number == required_activated_input_edges_number -# else: -# return self.activated_input_edges_number[implicit_parallelization_info.branch_i] == required_activated_input_edges_number + # if implicit_parallelization_info is None or self.is_term_state: + # if self.is_term_state: + # required_activated_input_edges_number = implicit_parallelization_info.branches_number + # return self.activated_input_edges_number == required_activated_input_edges_number + # else: + # return self.activated_input_edges_number[implicit_parallelization_info.branch_i] == required_activated_input_edges_number def _reset_activity(self, implicit_parallelization_info=None): self._branching_states_history = None @@ -274,7 +280,7 @@ class State: else: self.activated_input_edges_number[implicit_parallelization_info.branch_i] -= 1 else: -# self.activated_input_edges_number = 0 + # self.activated_input_edges_number = 0 if implicit_parallelization_info is None or self.is_term_state: self.activated_input_edges_number = 0 else: @@ -290,23 +296,25 @@ class State: def transfer_to_termination(data): return None + class SerialParallelizationPolicy: -# def __init__(self, data): -# self.data = data + # def __init__(self, data): + # self.data = data def __init__(self): pass def make_transfer_func(self, morphisms, array_keys_mapping=None, implicit_parallelization_info=None, state=None): def _morph(data): - # print("MORPHING FROM {}".format(state.name)) if array_keys_mapping is None: dynamic_keys_mapping = build_dynamic_keys_mapping(implicit_parallelization_info) - next_morphs = [partial(morphism.transfer, dynamic_keys_mapping=dynamic_keys_mapping) for morphism in morphisms] + next_morphs = [partial(morphism.transfer, dynamic_keys_mapping=dynamic_keys_mapping) for morphism in + morphisms] next_impl_para_infos = [implicit_parallelization_info for _ in morphisms] - # print('\t\t {}'.format(implicit_parallelization_infos)) else: if len(morphisms) != 1: - raise BadGraphStructure('Impossible to create implicit paralleilzation in the state with {} output edges'.format(len(morphisms))) + raise BadGraphStructure( + 'Impossible to create implicit paralleilzation in the state with {} output edges'.format( + len(morphisms))) dynamic_keys_mapping = build_dynamic_keys_mapping(implicit_parallelization_info) proxy_data = aux.ProxyDict(data, keys_mappings=array_keys_mapping) anykey = next(iter(array_keys_mapping.keys())) @@ -314,49 +322,53 @@ class SerialParallelizationPolicy: next_morphs = [] next_impl_para_infos = [] for branch_i in range(implicit_branches_number): - implicit_parallelization_info_ = ImplicitParallelizationInfo(array_keys_mapping, implicit_branches_number, branch_i) + implicit_parallelization_info_ = ImplicitParallelizationInfo(array_keys_mapping, + implicit_branches_number, branch_i) dynamic_keys_mapping = build_dynamic_keys_mapping(implicit_parallelization_info_) -# print(dynamic_keys_mapping) next_morphs.append(partial(morphisms[0].morph, dynamic_keys_mapping=dynamic_keys_mapping)) next_impl_para_infos.append(implicit_parallelization_info_) cur_morphs = [] cur_impl_para_infos = [] - #while len(next_morphs) != 1 or _is_implicitly_parallelized(next_impl_para_infos): - while len(next_morphs) != 1 or _requires_joint_of_implicit_parallelization(array_keys_mapping, next_impl_para_infos): + # while len(next_morphs) != 1 or _is_implicitly_parallelized(next_impl_para_infos): + while len(next_morphs) != 1 or _requires_joint_of_implicit_parallelization(array_keys_mapping, + next_impl_para_infos): if next_impl_para_infos == []: raise Exception("Morphs count on state {} is {}".format(state.name, str(len(next_morphs)))) -# print(array_keys_mapping, next_impl_para_infos) + # print(array_keys_mapping, next_impl_para_infos) cur_morphs[:] = next_morphs[:] cur_impl_para_infos[:] = next_impl_para_infos[:] del next_morphs[:] del next_impl_para_infos[:] for morph, impl_para_info in zip(cur_morphs, cur_impl_para_infos): next_state = morph(data) -# print('\t next_state: {}, with impl para info: {}'.format(next_state.name, impl_para_info)) + # print('\t next_state: {}, with impl para info: {}'.format(next_state.name, impl_para_info)) if next_state is None: return None next_morph, next_impl_para_info = _run_state(next_state, data, impl_para_info) -# print('\t next_morph: {}'.format(next_morph)) + # print('\t next_morph: {}'.format(next_morph)) if '__EXCEPTION__' in data: return None if next_morph is not None: next_morphs.append(next_morph) next_impl_para_infos.append(next_impl_para_info) -# print(array_keys_mapping, next_impl_para_infos) - #print(len(next_morphs)) -# print('\t last morph: {}'.format(next_morphs[0])) + # print(array_keys_mapping, next_impl_para_infos) + # print(len(next_morphs)) + # print('\t last morph: {}'.format(next_morphs[0])) next_state = next_morphs[0](data) -# print(next_state.name, next_impl_para_infos[0]) + # print(next_state.name, next_impl_para_infos[0]) return next_state + return _morph class BadGraphStructure(Exception): pass + class GraphUnexpectedTermination(Exception): pass + def _requires_joint_of_implicit_parallelization(array_keys_mapping, impl_para_infos): if array_keys_mapping is None: return False @@ -365,10 +377,12 @@ def _requires_joint_of_implicit_parallelization(array_keys_mapping, impl_para_in return True return False + def _get_trues(boolean_list): return [i for i, val in enumerate(boolean_list) if val == True] -#def _run_state(state, data, implicit_parallelization_info=None): + +# def _run_state(state, data, implicit_parallelization_info=None): # try: # next_morphism = state.run(data, implicit_parallelization_info) # except GraphUnexpectedTermination as e: @@ -391,4 +405,4 @@ def build_dynamic_keys_mapping(implicit_parallelization_info=None): dynamic_keys_mapping = {} for key, keys_path in implicit_parallelization_info.array_keys_mapping.items(): dynamic_keys_mapping[key] = aux.ArrayItemGetter(keys_path, implicit_parallelization_info.branch_i) - return dynamic_keys_mapping \ No newline at end of file + return dynamic_keys_mapping diff --git a/comsdk/parser.py b/comsdk/parser.py index a47cc11..bde9f7c 100644 --- a/comsdk/parser.py +++ b/comsdk/parser.py @@ -6,7 +6,7 @@ from pycomsdk.comsdk.edge import Edge from pycomsdk.comsdk.graph import Graph, Func, State, Selector -class Params(): +class Params: __slots__ = ( 'module', 'entry_func', @@ -33,28 +33,42 @@ class Params(): # entities = {} -class GraphFactory(): +class GraphFactory: __slots__ = ( 'name', 'states', 'graph', 'issub', 'tocpp', - 'entities' + 'entities', + 'graph_structure' ) def __init__(self, tocpp=False): + """ + Функция инициализации. Существует два режима конфигурации: + 1) Без генерации cpp-кода + 2) Генерация cpp-кода + + :param tocpp: переключатель режима работы фабрики + """ self.states = {} self.entities = {} self.tocpp = tocpp self.name = None self.issub = False + self.graph_structure = {} # dict-representation of graph def add_state(self, statename): if statename not in self.states: self.states[statename] = State(statename) if statename in self.entities: self.states[statename].comment = self.entities[statename].comment + self.graph_structure.update({statename: { + 'subgraph': None, + 'connect_to': [], + 'graph_name': self.name + }}) def _create_morphism(self, morphname=None): comment = "" @@ -91,6 +105,11 @@ class GraphFactory(): def add_connection(self, st1, st2, morphism=None, ordr=0): pred, entr, comm = self._create_morphism(morphism) self.states[st1].connect_to(self.states[st2], edge=Edge(pred, entr, order=ordr, comment=comm)) + self.graph_structure[st1]['connect_to'].append({ + 'next': st2, + 'pred_f': str(pred), + 'morph_f': str(entr), + }) print("{} -> {}".format(st1, st2)) def build(self, nsub): @@ -123,15 +142,22 @@ class GraphFactory(): else: self.states[s].selector = Selector(len(self.states[s].transfers)) if s in self.entities and self.entities[s].subgraph is not None: + # print(s + " is subgraph") print("Replacing state {} with subgraph {}".format(s, self.entities[s].subgraph)) parsr = Parser(subgraph=True, tocpp=self.tocpp) subgr = parsr.parse_file(self.entities[s].subgraph) self.states[s].replace_with_graph(subgr) + self.graph_structure[s]['subgraph'] = { + 'graph': parsr.fact.graph_structure, + 'init_state': subgr.init_state.name, + 'term_state': subgr.term_state.name, + 'subgraph_name': parsr.fact.name + } self.graph = Graph(self.graph.init_state, self.graph.term_state) return self.graph -class Parser(): +class Parser: __slots__ = ( 'fact', 'issub' @@ -462,7 +488,6 @@ class _Bush(): res = "" for tr in self.branches[i]: edge = tr.edge - print(tr.output_state.name) if edge.comment != "": res += "\t//{}\n".format(edge.comment) if edge.pred_f.name != "": -- 2.17.1