Commit 21b3c639 authored by Vlad Golubev's avatar Vlad Golubev

Добавил презентацию графовой модели в виде словаря в GraphFactory

Данный функционал позволяет отправлять на клиент структурное описание графовой модели для последующей визуализации и интерактивной работы. Граф представляется в виде словаря со следующей структурой: 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 или без.
parent b86dcefc
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) )
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,
):
......@@ -101,14 +109,14 @@ class Graph:
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)
# 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',
......@@ -142,10 +148,12 @@ class State:
'possible_branches',
'comment'
]
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:
......@@ -187,8 +193,6 @@ class State:
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')
if len(self.transfers) == 1:
self.transfers[0].output_state.idle_run(idle_run_type, branching_states_history)
else:
......@@ -201,24 +205,21 @@ 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)
self._reset_activity(implicit_parallelization_info)
......@@ -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), \
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:
......
......@@ -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 != "":
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment