class MockSimpleGraphDB\ ( Generic[MockVertexData], PartiallyStatefulDirectedGraphInterface[SimpleGraphKey, MockVertexData] ): ''' Type for mocking a simple database. ''' data: DiGraph def __init__(self) -> None: self.data = DiGraph() def write_stateful_vertex_(self, label: SimpleGraphKey, data: MockVertexData, *args: Any, **kwargs: Any) -> None: self.data.add_node(node_for_adding=label, memento=data) # type: ignore unkonwn def write_stateless_directed_edge_(self, source: SimpleGraphKey, destination: SimpleGraphKey, *args: Any, **kwargs: Any) -> None: self.data.add_edge( u_of_edge=source, v_of_edge=destination) # type: ignore unkonwn member
def remove_low_frequency_label( community_label_list_for_nodes: multivalued_dict) -> DiGraph: from sklearn.ensemble import IsolationForest digraph_of_node_labels_and_frequencies = DiGraph() for graph_of_node, label_list_of_nodes in community_label_list_for_nodes.items( ): digraph_of_node_labels_and_frequencies.add_node(graph_of_node) label_set = set(label_list_of_nodes) dict_of_frequency_of_label = dict( sorted([(label_list_of_nodes.count(label_item), label_item) for label_item in label_set], key=lambda frequency_and_label: frequency_and_label[0], reverse=True)) dict_of_sn_and_frequency = dict([ (sequence_number, frequency_of_label) for sequence_number, frequency_of_label in enumerate( dict_of_frequency_of_label.keys(), 1) ]) list_of_mapping_points = [] for sequence_number, frequency_of_label in dict_of_sn_and_frequency.items( ): list_of_mapping_points.extend([[sequence_number]] * frequency_of_label) clf = IsolationForest(n_estimators=120, contamination='auto') clf.fit(list_of_mapping_points) for sequence_number, frequency_of_label in dict_of_sn_and_frequency.items( ): if clf.predict([[sequence_number]])[0] == 1: label_item = dict_of_frequency_of_label.__getitem__( frequency_of_label) digraph_of_node_labels_and_frequencies.add_edge( graph_of_node, label_item, weight=frequency_of_label) return digraph_of_node_labels_and_frequencies
class buchi_graph(object): """ construct buchi automaton graph Parameter: formula: LTL formula specifying task """ def __init__(self, formula): self.formula = formula def formulaParser(self): """replace letter with symbol """ indicator = 'FG' if [True for i in indicator if i in self.formula]: self.formula.replace('F', '<>').replace('G', '[]') def execLtl2ba(self): """ given formula, exectute the ltl2ba Parameter: buchi_str: output string of program ltl2ba (utf-8 format) """ dirname = os.path.dirname(__file__) self.buchi_str = subprocess.check_output(dirname + "/./ltl2ba -f \"" + self.formula + "\"", shell=True).decode("utf-8") def buchiGraph(self): """parse the output of ltl2ba Parameter: buchi_graph: Graph of buchi automaton """ # find all states state_re = re.compile(r'\n(\w+):\n\t') state_group = re.findall(state_re, self.buchi_str) # find initial and accepting states init = [s for s in state_group if 'init' in s] accep = [s for s in state_group if 'accept' in s] """ Format: buchi_graph.node = NodeView(('T0_init', 'T1_S1', 'accept_S1')) buchi_graph.edges = OutEdgeView([('T0_init', 'T0_init'), ('T0_init', 'T1_S1'),....]) buchi_graph.succ = AdjacencyView({'T0_init': {'T0_init': {'label': '1'}, 'T1_S1': {'label': 'r3'}}}) """ self.buchi_graph = DiGraph(type='buchi', init=init, accept=accep) for state in state_group: # for each state, find transition relation # add node self.buchi_graph.add_node(state) state_if_fi = re.findall(state + r':\n\tif(.*?)fi', self.buchi_str, re.DOTALL) if state_if_fi: relation_group = re.findall(r':: \((.*?)\) -> goto (\w+)\n\t', state_if_fi[0]) for (label, state_dest) in relation_group: # add edge self.buchi_graph.add_edge(state, state_dest, label=label) return self.buchi_graph
def build_crm(roadmap_dir='./build_roadmap/roadmap_2D.p', wsn_rate_dir='./wsn_routing/rate_map.p', ws_img_dir='./build_roadmap/ws_img.p'): ''' build combined roadmap, crm via load roadmap and merge in wsn_rate info ''' roadmap_edges = pickle.load(open(roadmap_dir, 'rb')) wsn_rate, wifis_loc, sink_loc = pickle.load(open(wsn_rate_dir, 'rb')) img = pickle.load(open(ws_img_dir, 'rb')) crm = DiGraph(name='combined_model', ws_img=img, wifis=wifis_loc, sink=sink_loc) for e in roadmap_edges: (f_node, t_node) = e near_f_node = min(wsn_rate.keys(), key=lambda p: dist_2D(p, f_node)) f_node_rate = wsn_rate[near_f_node] crm.add_node(f_node, rate=f_node_rate) near_t_node = min(wsn_rate.keys(), key=lambda p: dist_2D(p, t_node)) t_node_rate = wsn_rate[near_t_node] crm.add_node(t_node, rate=t_node_rate) # interpolation dist_e = dist_2D(f_node, t_node) f_t_rate = intp_rate(f_node_rate, t_node_rate) crm.add_edge(f_node, t_node, rate=f_t_rate, dist=dist_e) t_f_rate = intp_rate(t_node_rate, f_node_rate) crm.add_edge(t_node, f_node, rate=t_f_rate, dist=dist_e) print '---crm constructed from %s and %s, %d nodes, %d edges---' % ( roadmap_dir, wsn_rate_dir, len(crm.nodes()), len(crm.edges())) return crm
def _digraph(self, index): g = DiGraph() g.add_node(index[self]) for _, (fun, _) in self.calls: g.add_node(index[fun]) g.add_edge(index[fun], index[self]) g = networkx.compose(g, fun._digraph(index)) return g
def load_graph(file_or_path:FileOrPath, ext:str=None) -> DiGraph: graph = DiGraph() for adj in load_jsonl(text_file_for(file_or_path)): src = adj[0] graph.add_node(src) for dst in adj[1:]: graph.add_edge(src, dst) return graph
def hoftask(init, buchi_graph, regions): h_task = DiGraph(type='subtask') try: label = to_dnf( buchi_graph.edges[(buchi_graph.graph['init'][0], buchi_graph.graph['init'][0])]['label'], True) except KeyError: # no self loop label = to_dnf('0') init_node = State((init, buchi_graph.graph['init'][0]), label) h_task.add_node(init_node) open_set = list() open_set.append(init_node) explore_set = list() while open_set: curr = open_set.pop(0) # assume co-safe # if curr.q in buchi_graph.graph['accept']: # continue # print(curr) for q_b in buchi_graph.succ[curr.q]: try: label = to_dnf(buchi_graph.edges[(q_b, q_b)]['label'], True) except KeyError: # no self loop label = to_dnf('0') if q_b != curr.q: # and q_b not in buchi_graph.graph['accept']: # region corresponding to the label/word edge_label = (buchi_graph.edges[(curr.q, q_b)]['label']) if edge_label == '(1)': x_set = [curr.x] else: x_set = target(to_dnf(edge_label), regions) if x_set: for x in x_set: cand = State((x, q_b), label) # print(cand) if cand not in open_set and cand not in explore_set: open_set.append(cand) # check the match with each subtask in h_task if not match(h_task, curr, cand): # print(cand) h_task.add_node(cand) h_task.add_edge(curr, cand) # continue # roots for accepting states # if q_b in buchi_graph.graph['accept']: # h_task.add_edge(curr, cand) explore_set.append(curr) # for x in h_task.nodes: # print(x) return h_task
class SimpleGraphDB\ ( Generic[SimpleVertexLabel, VertexData], PartiallyStatefulDirectedGraphInterface[SimpleVertexLabel, VertexData], StatefulVertexGraphLoaderInterface[SimpleVertexLabel, VertexData] ): ''' Class that can write stateful vertices and stateless directed edges into a graph-like structure. ''' __graph: DiGraph ''' Property and dunder methods ''' def __init__(self, *args: Any, **kwargs: Any) -> None: ''' Sets up a `networkx.DiGraph` structure for writing stateful vertices and stateless direWcted edges. ''' self.__graph = DiGraph() return None @property def _graph(self) -> DiGraph: return self.__graph ''' ABC extensions. ''' def write_stateful_vertex_(self, label: SimpleVertexLabel, data: VertexData, *args: Any, **kwargs: Any) -> None: ''' Writes a vertex with this `label` associated with this `data` into a `networkx.DiGraph` object. ''' self.__graph.add_node(node_for_adding = label, data = data) return None def write_stateless_directed_edge_(self, source: SimpleVertexLabel, destination: SimpleVertexLabel, *args: Any, **kwargs: Any) -> None: ''' Writes an unlabelled edge between a vertex with this `source` label and one with this `destination` label into a `networkx.DiGraph` object. ''' self.__graph.add_edge(u_of_edge = source, v_of_edge = destination) return None def load_stateful_vertex(self, label: SimpleVertexLabel, *args: Any, **kwargs: Any) -> VertexData: ''' Loads the `VertexData` associated with this `label` from a `networkx.DiGraph` object. ''' return self.__graph.nodes[label].get('data')
def buchi_from_ltl(formula,Type): promela_string = run_ltl2ba(formula) symbols = find_symbols(formula) edges = parse_ltl(promela_string) (states, initials, accepts) = find_states(edges) buchi = DiGraph(type=Type, initial=initials, accept=accepts, symbols=symbols) for state in states: buchi.add_node(state) for (ef,et) in edges.keys(): guard_formula = edges[(ef,et)] guard_expr = parse_guard(guard_formula) buchi.add_edge(ef, et, guard=guard_expr, guard_formula=guard_formula) return buchi
class BoardGraph(object): def walk(self, board, size_limit=maxint): pending_nodes = [] self.graph = DiGraph() self.start = board.display(cropped=True) self.graph.add_node(self.start) pending_nodes.append(self.start) self.min_domino_count = len(board.dominoes) while pending_nodes: if len(self.graph) >= size_limit: raise GraphLimitExceeded(size_limit) state = pending_nodes.pop() board = Board.create(state, border=1) dominoes = set(board.dominoes) self.min_domino_count = min(self.min_domino_count, len(dominoes)) for domino in dominoes: dx, dy = domino.direction self.try_move(state, domino, dx, dy, pending_nodes) self.try_move(state, domino, -dx, -dy, pending_nodes) self.last = state return set(self.graph.nodes()) def try_move(self, old_state, domino, dx, dy, pending_states): try: new_state = self.move(domino, dx, dy) move = domino.describe_move(dx, dy) if not self.graph.has_node(new_state): # new node self.graph.add_node(new_state) pending_states.append(new_state) self.graph.add_edge(old_state, new_state, move=move) except BoardError: pass def move(self, domino, dx, dy): """ Move a domino and calculate the new board state. Afterward, put the board back in its original state. @return: the new board state @raise BoardError: if the move is illegal """ domino.move(dx, dy) try: board = domino.head.board if not board.isConnected(): raise BoardError('Board is not connected.') if board.hasLoner(): raise BoardError('Board has a lonely domino.') return board.display(cropped=True) finally: domino.move(-dx, -dy)
def load_from_graphml(path): """ :type path: str :rtype: networkx.classes.graph.Graph """ #: :type : networkx.classes.graph.Graph g1 = nx.read_graphml(path) #: :type graph: networkx.classes.digraph.DiGraph g2 = DiGraph() typetest = re.compile(r"([a-zA-z]+)(\d*)") max_qualifiers = dict(crossing=0, poi=0, plcs=0) node_mapping = dict() for node, data in g1.nodes_iter(data=True): m = typetest.match(data["label"]) if m is None: raise(SALMAException("Wrong label format for node {}!".format(node))) loctype = m.group(1) if loctype in ["c"]: loctype = "crossing" elif loctype in ["p"]: loctype = "poi" elif loctype in ["pl"]: loctype = "plcs" if loctype not in ["poi", "plcs", "crossing"]: raise(SALMAException("Wrong loctype for node {}: {}".format(node, loctype))) qualifier = m.group(2) if len(qualifier) == 0: qualifier = max_qualifiers[loctype] + 1 nid = data["label"] + str(qualifier) else: nid = data["label"] max_qualifiers[loctype] = max(max_qualifiers[loctype], qualifier) pos = (round(float(data["x"])), round(float(data["y"]))) g2.add_node(nid, pos=pos, scaled_pos=pos, loctype=loctype) node_mapping[node] = nid for u, v in g1.edges_iter(): n1 = node_mapping[u] n2 = node_mapping[v] g2.add_edge(n1, n2) g2.add_edge(n2, n1) MapGenerator.__add_roadlengths(g2) return g2
def construct_arg_crm(self): arg_crm = DiGraph() for wp_f in self.crm.nodes_iter(): for d_f in iter(self.data_set): s_f = (wp_f, d_f) arg_crm.add_node(s_f) for wp_t in self.crm.neighbors(wp_f): d_evolve = d_f - self.crm.edge[wp_f][wp_t][ 'rate'] * self.sample_time - 2 * self.quant_size d_t = [d for d in self.data_set if d >= d_evolve][0] s_t = (wp_t, d_t) arg_crm.add_edge(s_f, s_t, weight=1.0) print '---static argumented crm constructed: %d nodes, %d edges----' % ( len(arg_crm.nodes()), len(arg_crm.edges())) self.arg_crm = arg_crm
def setUp(self): graph = DiGraph() graph.add_cycle([1, 2, 3, 4]) graph.add_cycle([5, 6, 7, 8]) graph.add_node(0) graph.add_edge(9, 10) self.graph_1 = graph graph = DiGraph() graph.add_cycle([1, 2, 3, 4]) graph.add_cycle([5, 6, 7, 8]) graph.add_node(0) graph.add_edge(9, 10) graph.add_edge(3, 9) graph.add_edge(10, 7) self.graph_2 = graph
def DuoBA_from_ltls(hard_spec, soft_spec): hard_buchi = buchi_from_ltl(hard_spec, 'hard_buchi') soft_buchi = buchi_from_ltl(soft_spec, 'soft_buchi') hard_symbols = hard_buchi.graph['symbols'] soft_symbols = soft_buchi.graph['symbols'] symbols = set(hard_symbols).union(set(soft_symbols)) DuoBA = DiGraph(type='safe_buchi', hard=hard_buchi, soft=soft_buchi, symols=symbols) initial = set() accept = set() for (h_node, s_node, l) in cartesian_product(hard_buchi.nodes(), soft_buchi.nodes(), [1, 2]): DuoNode = (h_node, s_node, l) DuoBA.add_node(DuoNode, hard=h_node, soft=s_node, level=l) if (h_node in hard_buchi.graph['initial'] and s_node in soft_buchi.graph['initial'] and l == 1): initial.add(DuoNode) if (h_node in hard_buchi.graph['accept'] and l == 1): accept.add(DuoNode) DuoBA.graph['accept'] = accept DuoBA.graph['initial'] = initial for f_duonode in DuoBA.nodes_iter(): for t_duonode in DuoBA.nodes_iter(): f_h_node, f_s_node, f_level = check_duo_attr(DuoBA, f_duonode) t_h_node, t_s_node, t_level = check_duo_attr(DuoBA, t_duonode) if (t_h_node not in DuoBA.graph['hard'].neighbors(f_h_node) or t_s_node not in DuoBA.graph['soft'].neighbors(f_s_node)): continue # relaxed because no common input alphabets are enabled hardguard = DuoBA.graph['hard'].edge[f_h_node][t_h_node]['guard'] softguard = DuoBA.graph['soft'].edge[f_s_node][t_s_node]['guard'] if ((f_h_node not in DuoBA.graph['hard'].graph['accept'] and f_level == 1 and t_level == 1) or (f_h_node in DuoBA.graph['hard'].graph['accept'] and f_level == 1 and t_level == 2) or (f_s_node not in DuoBA.graph['soft'].graph['accept'] and f_level == 2 and t_level == 2) or (f_s_node in DuoBA.graph['soft'].graph['accept'] and f_level == 2 and t_level == 1)): DuoBA.add_edge(f_duonode, t_duonode, hardguard=hardguard, softguard=softguard) return DuoBA
def buchi_from_ltl(formula, Type1): promela_string = "never { /* G(goods->Xdepot)&&G(depot->Xgoods)&&(G(!b)&&G(!door||open)) */ accept_init : /* init */ if :: (!goods && !depot && !b && !door) || (!goods && !depot && !b && open) -> goto accept_init :: (!goods && !b && !door) || (!goods && !b && open) -> goto accept_S2 :: (!depot && !b && !door) || (!depot && !b && open) -> goto accept_S3 :: (!b && !door) || (!b && open) -> goto accept_S4 fi;accept_S2 : /* 1 */ if :: (goods && !depot && !b && !door) || (goods && !depot && !b && open) -> goto accept_S3 :: (goods && !b && !door) || (goods && !b && open) -> goto accept_S4 fi;accept_S3 : /* 2 */ if :: (!goods && depot && !b && !door) || (!goods && depot && !b && open) -> goto accept_S2 :: (depot && !b && !door) || (depot && !b && open) -> goto accept_S4 fi;accept_S4 : /* 3 */ if :: (goods && depot && !b && !door) || (goods && depot && !b && open) -> goto accept_S4 fi;}" #promela_string = run_ltl2ba(formula) symbols = find_symbols(formula) edges = parse_ltl(promela_string) (states, initials, accepts) = find_states(edges) buchi = DiGraph(type=Type, initial=initials, accept=accepts, symbols=symbols) for state in states: buchi.add_node(state) for (ef, et) in edges.keys(): guard_formula = edges[(ef, et)] guard_expr = parse_guard(guard_formula) buchi.add_edge(ef, et, guard=guard_expr, guard_formula=guard_formula) return buchi
def _build_ast(cls, rpn_expression): """build an AST from an Excel formula :param rpn_expression: a string formula or the result of parse_to_rpn() :return: AST which can be used to generate code """ # use a directed graph to store the syntax tree tree = DiGraph() # production stack stack = [] for node in rpn_expression: # The graph does not maintain the order of adding nodes/edges, so # add an attribute 'pos' so we can always sort to the correct order node.ast = tree if isinstance(node, OperatorNode): if node.token.type == node.token.OP_IN: try: arg2 = stack.pop() arg1 = stack.pop() except IndexError: raise FormulaParserError( "'{}' operator missing operand".format( node.token.value)) tree.add_node(arg1, pos=0) tree.add_node(arg2, pos=1) tree.add_edge(arg1, node) tree.add_edge(arg2, node) else: try: arg1 = stack.pop() except IndexError: raise FormulaParserError( "'{}' operator missing operand".format( node.token.value)) tree.add_node(arg1, pos=1) tree.add_edge(arg1, node) elif isinstance(node, FunctionNode): if node.num_args: args = stack[-node.num_args:] del stack[-node.num_args:] for i, a in enumerate(args): tree.add_node(a, pos=i) tree.add_edge(a, node) else: tree.add_node(node, pos=0) stack.append(node) assert 1 == len(stack) return stack[0]
def hoftask_no_simplified(init, buchi_graph, regions): try: label = to_dnf( buchi_graph.edges[(buchi_graph.graph['init'][0], buchi_graph.graph['init'][0])]['label'], True) except KeyError: # no self loop label = to_dnf('0') init_node = State((init, buchi_graph.graph['init'][0]), label) h_task = DiGraph(type='subtask', init=init_node) h_task.add_node(init_node) open_set = list() open_set.append(init_node) explore_set = list() while open_set: curr = open_set.pop(0) for q_b in buchi_graph.succ[curr.q]: try: label = to_dnf(buchi_graph.edges[(q_b, q_b)]['label'], True) except KeyError: # no self loop label = to_dnf('0') if q_b != curr.q: # region corresponding to the label/word edge_label = (buchi_graph.edges[(curr.q, q_b)]['label']) if edge_label == '(1)': x_set = [curr.x] elif '~' in edge_label and to_dnf(edge_label).nargs == { 1 }: # ~l3_1 x_set = [curr.x] # !!!!!!! else: x_set = target(to_dnf(edge_label), regions) if x_set: for x in x_set: cand = State((x, q_b), label) # whether cand is already in the h_task # cand = find_node(cand, h_task) h_task.add_edge(curr, cand) if cand not in open_set and cand not in explore_set: open_set.append(cand) explore_set.append(curr) return h_task
def add_node(self, n, attr_dict=None, dHdS=(0, 0), **attr): """ Add state node n to reaction graph. """ assert n not in self.adj # edge-attrs dict, {src: {tgt1: {edge1_attrs}, ...}} # print("ReactionGraph.add_node(%s, %s, %s, %s)" % (n, attr_dict, dHdS, attr)) if attr_dict is None: attr_dict = attr elif attr: attr_dict.update(attr) # First dispatch attr_dict['dHdS'] = dHdS attr_dict['encounters'] = 1 for dispatcher in self.dispatchers: dispatcher.add_node(n, attr_dict) attr_dict['dHdS_count'] = {dHdS: 1} # MultiGraph, with edges keyed by (reacted_spec_pair, reaction_attr): # reaction_graph.adj[source][target][(reacted_spec_pair, reaction_attr)] = eattr #self.reaction_graph.add_node(target_state, node_attrs) DiGraph.add_node(self, n, attr_dict)
def build_txs_graph(datadir: str) -> DiGraph: """ read all block and transaction files in the given datadir and construct a full transaction graph. Each node represents a transaction. the node's id is the txid and it has the following attributes: - "tx" - the full tx json, as returned by bitcoind - "fee" - the tx fee - "height" - the block height in which the tx was included Each edge has the following attributes: - "value" the value in BTC of the output represented by this edge """ blocks = load_blocks(datadir) txs = load_txs(datadir) txid_to_fee = {txid: find_tx_fee(txid, txs) for txid in txs.keys()} txid_to_height = { txid: block["height"] for block in blocks.values() for txid in block["tx"] } graph = DiGraph() # add all transactions for txid in txs.keys(): graph.add_node(txid, tx=txs[txid], fee=txid_to_fee[txid], height=txid_to_height[txid]) # add edges between transactions for dest_txid, dest_tx in txs.items(): for entry in dest_tx["vin"]: if "coinbase" in entry: continue # coinbase transaction. no src src_txid = entry["txid"] index = entry["vout"] value = txs[src_txid]["vout"][index]["value"] graph.add_edge(src_txid, dest_txid, value=value) return graph
def find_SCCs(mdp, Sneg): #----simply find strongly connected components---- print 'Remaining states size', len(Sneg) SCC = set() simple_digraph = DiGraph() A = dict() for s in mdp.nodes(): A[s] = mdp.node[s]['act'].copy() for s_f in Sneg: if s_f not in simple_digraph: simple_digraph.add_node(s_f) for s_t in mdp.successors_iter(s_f): if s_t in Sneg: simple_digraph.add_edge(s_f,s_t) print "SubGraph of one Sf: %s states and %s edges" %(str(len(simple_digraph.nodes())), str(len(simple_digraph.edges()))) sccs = strongly_connected_component_subgraphs(simple_digraph) for scc in sccs: SCC.add(frozenset(scc.nodes())) return SCC, A
def build_graph(num_grid, t): env = DiGraph(name='Environment') open_set = set() explore_set = set() r = round(1 / num_grid, 10) open_set.add((round(r / 2, 10), round(r / 2, 10))) while open_set: point = open_set.pop() env.add_node(point) fea_point = direction(point, r, t) for p in fea_point: env.add_edge(point, p) env.add_edge(p, point) if point not in explore_set: open_set.add(p) explore_set.add(point) return env
def create_graph_with_new_data(): ''' A DiGraph object holds directed edges - Parallel edges still aren't allowed - For a Digraph, two edges are parallel if they connect the same ordered pair of vertices - Thus, two edges that connect the same vertices but go in different directions are not parallel ''' # Create with edge list g = DiGraph([(1, 5), (5, 1)]) #g = Graph([(1, 5), (5, 1)]) g.add_node(6) print(g.nodes()) # [1, 5, 6] # These are two distinct edges print(g.edges()) # [(1, 5), (5, 1)] print(g.neighbors(1)) # [5] print(g.neighbors(5)) # [1] g.edge[1][5]['foo'] = 'bar' print(g.edge[1][5]) # {'foo': 'bar'} print(g.edge[5][1]) # {}
def build_ast(expression, debug = False): """build an AST from an Excel formula expression in reverse polish notation""" #use a directed graph to store the tree G = DiGraph() stack = [] for n in expression: # Since the graph does not maintain the order of adding nodes/edges # add an extra attribute 'pos' so we can always sort to the correct order if isinstance(n,OperatorNode): if n.ttype == "operator-infix": arg2 = stack.pop() arg1 = stack.pop() # Hack to write the name of sheet in 2argument address if(n.tvalue == ':'): if '!' in arg1.tvalue and arg2.ttype == 'operand' and '!' not in arg2.tvalue: arg2.tvalue = arg1.tvalue.split('!')[0] + '!' + arg2.tvalue G.add_node(arg1,{'pos':1}) G.add_node(arg2,{'pos':2}) G.add_edge(arg1, n) G.add_edge(arg2, n) else: arg1 = stack.pop() G.add_node(arg1,{'pos':1}) G.add_edge(arg1, n) elif isinstance(n,FunctionNode): args = [] for _ in range(n.num_args): try: args.append(stack.pop()) except: raise Exception() #try: # args = [stack.pop() for _ in range(n.num_args)] #except: # print 'STACK', stack, type(n) # raise Exception('prut') args.reverse() for i,a in enumerate(args): G.add_node(a,{'pos':i}) G.add_edge(a,n) else: G.add_node(n,{'pos':0}) stack.append(n) return G,stack.pop()
def build_ast(expression, debug=False): """build an AST from an Excel formula expression in reverse polish notation""" #use a directed graph to store the tree G = DiGraph() stack = [] for n in expression: # Since the graph does not maintain the order of adding nodes/edges # add an extra attribute 'pos' so we can always sort to the correct order if isinstance(n, OperatorNode): if n.ttype == "operator-infix": arg2 = stack.pop() arg1 = stack.pop() # Hack to write the name of sheet in 2argument address if (n.tvalue == ':'): if '!' in arg1.tvalue and arg2.ttype == 'operand' and '!' not in arg2.tvalue: arg2.tvalue = arg1.tvalue.split( '!')[0] + '!' + arg2.tvalue G.add_node(arg1, {'pos': 1}) G.add_node(arg2, {'pos': 2}) G.add_edge(arg1, n) G.add_edge(arg2, n) else: arg1 = stack.pop() G.add_node(arg1, {'pos': 1}) G.add_edge(arg1, n) elif isinstance(n, FunctionNode): args = [] for _ in range(n.num_args): try: args.append(stack.pop()) except: raise Exception() #try: # args = [stack.pop() for _ in range(n.num_args)] #except: # print 'STACK', stack, type(n) # raise Exception('prut') args.reverse() for i, a in enumerate(args): G.add_node(a, {'pos': i}) G.add_edge(a, n) else: G.add_node(n, {'pos': 0}) stack.append(n) return G, stack.pop()
def DuoBA_from_ltls(hard_spec, soft_spec): hard_buchi = buchi_from_ltl(hard_spec, 'hard_buchi') soft_buchi = buchi_from_ltl(soft_spec, 'soft_buchi') hard_symbols = hard_buchi.graph['symbols'] soft_symbols = soft_buchi.graph['symbols'] symbols = set(hard_symbols).union(set(soft_symbols)) DuoBA = DiGraph(type='safe_buchi', hard=hard_buchi, soft=soft_buchi, symols=symbols) initial = set() accept = set() for (h_node, s_node, l) in cartesian_product(hard_buchi.nodes(), soft_buchi.nodes(), [1, 2]): DuoNode = (h_node, s_node, l) DuoBA.add_node(DuoNode,hard=h_node, soft=s_node, level=l) if (h_node in hard_buchi.graph['initial'] and s_node in soft_buchi.graph['initial'] and l == 1): initial.add(DuoNode) if (h_node in hard_buchi.graph['accept'] and l == 1): accept.add(DuoNode) DuoBA.graph['accept'] = accept DuoBA.graph['initial'] = initial for f_duonode in DuoBA.nodes(): for t_duonode in DuoBA.nodes(): f_h_node, f_s_node, f_level = check_duo_attr(DuoBA, f_duonode) t_h_node, t_s_node, t_level = check_duo_attr(DuoBA, t_duonode) if (t_h_node not in DuoBA.graph['hard'].neighbors(f_h_node) or t_s_node not in DuoBA.graph['soft'].neighbors(f_s_node)): continue # relaxed because no common input alphabets are enabled hardguard = DuoBA.graph['hard'].edges[f_h_node,t_h_node]['guard'] softguard = DuoBA.graph['soft'].edges[f_s_node,t_s_node]['guard'] if ((f_h_node not in DuoBA.graph['hard'].graph['accept'] and f_level == 1 and t_level == 1) or (f_h_node in DuoBA.graph['hard'].graph['accept'] and f_level == 1 and t_level == 2) or (f_s_node not in DuoBA.graph['soft'].graph['accept'] and f_level == 2 and t_level == 2) or (f_s_node in DuoBA.graph['soft'].graph['accept'] and f_level == 2 and t_level == 1)): DuoBA.add_edge(f_duonode, t_duonode, hardguard=hardguard, softguard=softguard) return DuoBA
def buchi_from_ltl(formula, Type): promela_string = run_ltl2ba(formula) symbols = find_symbols(formula) # print "Output from symbols" # print symbols edges = parse_ltl(promela_string) (states, initials, accepts) = find_states(edges) # print "Output from find_states" # print states # print initials # print accepts buchi = DiGraph(type=Type, initial=initials, accept=accepts, symbols=symbols) for state in states: buchi.add_node(state) for (ef, et) in edges.keys(): guard_formula = edges[(ef, et)] guard_expr = parse_guard(guard_formula) buchi.add_edge(ef, et, guard=guard_expr, guard_formula=guard_formula) # write_dot(buchi, "./result.dot") return buchi
def build_ast(expression): """build an AST from an Excel formula expression in reverse polish notation""" #use a directed graph to store the tree G = DiGraph() stack = [] for n in expression: # Since the graph does not maintain the order of adding nodes/edges # add an extra attribute 'pos' so we can always sort to the correct order if isinstance(n,OperatorNode): if n.ttype == "operator-infix": arg2 = stack.pop() arg1 = stack.pop() G.add_node(arg1,{'pos':1}) G.add_node(arg2,{'pos':2}) G.add_edge(arg1, n) G.add_edge(arg2, n) else: arg1 = stack.pop() G.add_node(arg1,{'pos':1}) G.add_edge(arg1, n) elif isinstance(n,FunctionNode): args = [stack.pop() for _ in range(n.num_args)] args.reverse() for i,a in enumerate(args): G.add_node(a,{'pos':i}) G.add_edge(a,n) #for i in range(n.num_args): # G.add_edge(stack.pop(),n) else: G.add_node(n,{'pos':0}) stack.append(n) return G,stack.pop()
def build_ast(expression): """build an AST from an Excel formula expression in reverse polish notation""" #use a directed graph to store the tree G = DiGraph() stack = [] for n in expression: # Since the graph does not maintain the order of adding nodes/edges # add an extra attribute 'pos' so we can always sort to the correct order if isinstance(n, OperatorNode): if n.ttype == "operator-infix": arg2 = stack.pop() arg1 = stack.pop() G.add_node(arg1, {'pos': 1}) G.add_node(arg2, {'pos': 2}) G.add_edge(arg1, n) G.add_edge(arg2, n) else: arg1 = stack.pop() G.add_node(arg1, {'pos': 1}) G.add_edge(arg1, n) elif isinstance(n, FunctionNode): args = [stack.pop() for _ in range(n.num_args)] args.reverse() for i, a in enumerate(args): G.add_node(a, {'pos': i}) G.add_edge(a, n) #for i in range(n.num_args): # G.add_edge(stack.pop(),n) else: G.add_node(n, {'pos': 0}) stack.append(n) return G, stack.pop()
def lst_dag(G, r, U, node_reward_key='r', edge_cost_key='c', edge_weight_decimal_point=None, fixed_point_func=round, debug=False): """ Param: ------------- binary_dag: a DAG in networkx format. Each node can have at most 2 child r: root node in dag U: the maximum threshold of edge weight sum Return: maximum-sum subtree rooted at r whose sum of edge weights <= A ------------ """ # round edge weight to fixed decimal point if necessary if edge_weight_decimal_point is not None: G = G.copy() G, U = round_edge_weights_by_multiplying( G, U, edge_weight_decimal_point, edge_cost_key=edge_cost_key, fixed_point_func=fixed_point_func ) if debug: print('U => {}'.format(U)) ns = G.nodes() if debug: print("total #nodes {}".format(len(ns))) A, D, BP = {}, {}, {} for n in ns: A[n] = {} # maximum sum of node u at a cost i A[n][0] = G.node[n][node_reward_key] D[n] = {} # set of nodes included corresponding to A[u][i] D[n][0] = {n} BP[n] = defaultdict(list) # backpointer corresponding to A[u][i] for n_i, n in enumerate( topological_sort(G, reverse=True)): # leaves come first if debug: print("#nodes processed {}".format(n_i)) children = G.neighbors(n) reward = G.node[n][node_reward_key] if len(children) == 1: child = children[0] w = G[n][child][edge_cost_key] for i in xrange(U, w - 1, -1): if (i-w) in A[child]: A[n][i] = A[child][i-w] + reward D[n][i] = D[child][i-w] | {n} BP[n][i] = [(child, i-w)] elif len(children) > 1: lchild, rchild = children lw = G[n][lchild][edge_cost_key] rw = G[n][rchild][edge_cost_key] for i in A[lchild]: c = lw + i if debug: print('n={}, D={}, cost_child_tuples={}'.format( n, D, [(i, lchild)]) ) print('c={}'.format(c)) if c <= U: if A[n].get(c) is None or A[lchild][i] + reward > A[n][c]: A[n][c] = A[lchild][i] + reward D[n][c] = D[lchild][i] | {n} BP[n][c] = [(lchild, i)] for i in A[rchild]: c = rw + i if c <= U: if A[n].get(c) is None or A[rchild][i] + reward > A[n][c]: A[n][c] = A[rchild][i] + reward D[n][c] = D[rchild][i] | {n} BP[n][c] = [(rchild, i)] for i in A[lchild]: for j in A[rchild]: c = lw + rw + i + j if c <= U: if (A[n].get(c) is None or A[lchild][i] + A[rchild][j] + reward > A[n][c]) and \ len(D[lchild][i] & D[rchild][j]) == 0: A[n][c] = A[lchild][i] + A[rchild][j] + reward D[n][c] = D[lchild][i] | D[rchild][j] | {n} BP[n][c] = [(lchild, i), (rchild, j)] # if n == r: # no need to continue once we processed root # break if debug: print('A[r]', A[r]) best_cost = max(xrange(U + 1), key=lambda i: A[r][i] if i in A[r] else float('-inf')) if debug: print("best_cost", best_cost) tree = DiGraph() tree.add_node(r) stack = [] for n, cost in BP[r][best_cost]: stack.append((r, n, cost)) while len(stack) > 0: # if debug: # print('stack size: {}'.format(len(stack))) # print('stack: {}'.format(stack)) parent, child, cost = stack.pop(0) tree.add_edge(parent, child) # copy the attributes tree[parent][child] = G[parent][child] tree.node[parent] = G.node[parent] tree.node[child] = G.node[child] for grandchild, cost2 in BP[child][cost]: # if debug: # print(grandchild, cost2) stack.append((child, grandchild, cost2)) return tree
# continue print(f'Processing log file {filepath}...') log = xes_import_factory.apply(filepath) print(f'Running Heuristic miner on {label}...') net, _i, _f = run_heuristic(log, label, 'dest_class_func', parameters=heuristic_b_params) print(f'Creating graph for petri net...') graph = DiGraph() all_nodes = set() for place in net.places: graph.add_node(str(place), label=str(place)) all_nodes.add(str(place)) for transition in net.transitions: graph.add_node(str(transition), label=str(transition)) all_nodes.add(str(transition)) for arc in net.arcs: graph.add_edge(str(arc.source), str(arc.target)) sp_nodes = set([label for label in nodes_in_shortest_path(graph) if heuristic_filter(label)]) filtered_nodes = set([label for label in all_nodes if heuristic_filter(label)]) # print("Filtered Nodes") # print(filtered_nodes)
class tree(object): """ construction of prefix and suffix tree """ def __init__(self, ts, buchi_graph, init, step_size, base=1e3): """ :param ts: transition system :param buchi_graph: Buchi graph :param init: product initial state """ self.robot = 1 self.goals = [] self.ts = ts self.buchi_graph = buchi_graph self.init = init self.step_size = step_size self.dim = len(self.ts['workspace']) uni_v = np.power(np.pi, self.robot * self.dim / 2) / math.gamma(self.robot * self.dim / 2 + 1) self.gamma = np.ceil( 4 * np.power(1 / uni_v, 1. / (self.dim * self.robot))) # unit workspace self.tree = DiGraph(type='PBA', init=init) label = self.label(init[0]) if label != '': label = label + '_' + str(1) # accepting state before current node acc = set() if 'accept' in init[1]: acc.add(init) self.tree.add_node(init, cost=0, label=label, acc=acc) self.search_goal(init, label, acc) # already used skilles self.used = set() self.base = base def sample(self): """ sample point from the workspace :return: sampled point, tuple """ x_rand = [] for i in range(self.dim): x_rand.append(uniform(0, self.ts['workspace'][i])) return tuple(x_rand) def nearest(self, x_rand): """ find the nearest class of vertices in the tree :param: x_rand randomly sampled point form: single point () :return: nearest class of vertices form: single point () """ min_dis = math.inf q_nearest = [] for vertex in self.tree.nodes: x_vertex = vertex[0] dis = np.linalg.norm(np.subtract(x_rand, x_vertex)) if dis < min_dis: q_nearest = list() q_nearest.append(vertex) min_dis = dis elif dis == min_dis: q_nearest.append(vertex) return q_nearest def steer(self, x_rand, x_nearest): """ steer :param: x_rand randomly sampled point form: single point () :param: x_nearest nearest point in the tree form: single point () :return: new point single point () """ if np.linalg.norm(np.subtract(x_rand, x_nearest)) <= self.step_size: return x_rand else: return tuple( np.asarray(x_nearest) + self.step_size * (np.subtract(x_rand, x_nearest)) / np.linalg.norm(np.subtract(x_rand, x_nearest))) def acpt_check(self, q_min, q_new): """ check the accepting state in the patg leading to q_new :param q_min: :param q_new: :return: """ changed = False acc = set(self.tree.nodes[q_min]['acc']) # copy if 'accept' in q_new[1]: acc.add(q_new) # print(acc) changed = True return acc, changed def search_goal(self, q_new, label_new, acc): """ whether q_new can connect to point before acc :param q_new: :param label_new: :param acc: :return: """ for ac in acc: # connect to path leading to accepting state, including accepting state path = self.findpath([ac])[0][1] for point in path: if list(self.obs_check([q_new], point[0], self.tree.nodes[point]['label']).values())[0] \ and self.checkTranB(q_new[1], label_new, point[1]): self.goals.append( (q_new, point, ac)) # endpoint, middle point, accepting point def extend(self, q_new, near_v, label, obs_check, succ_list): """ :param: q_new: new state form: tuple (mulp, buchi) :param: near_v: near state form: tuple (mulp, buchi) :param: obs_check: check obstacle free form: dict { (mulp, mulp): True } :param: succ: list of successor of the root :return: extending the tree """ added = 0 cost = np.inf q_min = () for near_vertex in near_v: if near_vertex in succ_list: # do not extend if there is a corresponding root continue if q_new != near_vertex and obs_check[(q_new[0], near_vertex[0])] \ and self.checkTranB(near_vertex[1], self.tree.nodes[near_vertex]['label'], q_new[1]): c = self.tree.nodes[near_vertex]['cost'] + \ np.linalg.norm(np.subtract(q_new[0], near_vertex[0])) if c < cost: added = 1 q_min = near_vertex cost = c if added == 1: self.tree.add_node(q_new, cost=cost, label=label) self.tree.nodes[q_new]['acc'] = set( self.acpt_check(q_min, q_new)[0]) self.tree.add_edge(q_min, q_new) # self.search_goal(q_new, label, self.tree.nodes[q_new]['acc']) return added def rewire(self, q_new, near_v, obs_check): """ :param: q_new: new state form: tuple (mul, buchi) :param: near_v: near state form: tuple (mul, buchi) :param: obs_check: check obstacle free form: dict { (mulp, mulp): True } :return: rewiring the tree """ for near_vertex in near_v: if obs_check[(q_new[0], near_vertex[0])] \ and self.checkTranB(q_new[1], self.tree.nodes[q_new]['label'], near_vertex[1]): c = self.tree.nodes[q_new]['cost'] \ + np.linalg.norm(np.subtract(q_new[0], near_vertex[0])) delta_c = self.tree.nodes[near_vertex]['cost'] - c # update the cost of node in the subtree rooted at near_vertex if delta_c > 0: # self.tree.nodes[near_vertex]['cost'] = c self.tree.remove_edge( list(self.tree.pred[near_vertex].keys())[0], near_vertex) self.tree.add_edge(q_new, near_vertex) edges = dfs_labeled_edges(self.tree, source=near_vertex) acc, changed = self.acpt_check(q_new, near_vertex) self.tree.nodes[near_vertex]['acc'] = set(acc) for u, v, d in edges: if d == 'forward': self.tree.nodes[v][ 'cost'] = self.tree.nodes[v]['cost'] - delta_c if changed: self.tree.nodes[v]['acc'] = set( self.acpt_check(u, v)[0]) # copy # better to research the goal but abandon the implementation def near(self, x_new): """ find the states in the near ball :param x_new: new point form: single point :return: p_near: near state, form: tuple (mulp, buchi) """ p_near = [] r = min( self.gamma * np.power( np.log(self.tree.number_of_nodes() + 1) / self.tree.number_of_nodes(), 1. / (self.dim * self.robot)), self.step_size) # r = self.step_size for vertex in self.tree.nodes: if np.linalg.norm(np.subtract(x_new, vertex[0])) <= r: p_near.append(vertex) return p_near def obs_check(self, q_near, x_new, label): """ check whether obstacle free along the line from x_near to x_new :param q_near: states in the near ball, tuple (mulp, buchi) :param x_new: new state form: multiple point :param label: label of x_new :param stage: regular stage or final stage, deciding whether it's goal state :return: dict (x_near, x_new): true (obs_free) """ obs_check_dict = {} checked = set() for x in q_near: if x[0] in checked: continue checked.add(x[0]) obs_check_dict[(x_new, x[0])] = True # the line connecting two points crosses an obstacle for (obs, boundary) in iter(self.ts['obs'].items()): if LineString([Point(x[0]), Point(x_new)]).intersects(boundary): obs_check_dict[(x_new, x[0])] = False break for (region, boundary) in iter(self.ts['region'].items()): if LineString([Point(x[0]), Point(x_new)]).intersects(boundary) \ and region + '_' + str(1) != label \ and region + '_' + str(1) != self.tree.nodes[x]['label']: # if stage == 'reg' or (stage == 'final' and region in self.no): obs_check_dict[(x_new, x[0])] = False break return obs_check_dict def label(self, x): """ generating the label of position state :param x: position :return: label """ point = Point(x) # whether x lies within obstacle for (obs, boundary) in iter(self.ts['obs'].items()): if point.within(boundary): return obs # whether x lies within regions for (region, boundary) in iter(self.ts['region'].items()): if point.within(boundary): return region # x lies within unlabeled region return '' def checkTranB(self, b_state, x_label, q_b_new): """ decide valid transition, whether b_state --L(x)---> q_b_new Algorithm2 in Chapter 2 Motion and Task Planning :param b_state: buchi state :param x_label: label of x :param q_b_new buchi state :return True satisfied """ b_state_succ = self.buchi_graph.succ[b_state] # q_b_new is not the successor of b_state if q_b_new not in b_state_succ: return False truth = self.buchi_graph.edges[(b_state, q_b_new)]['truth'] if self.t_satisfy_b_truth(x_label, truth): return True return False def t_satisfy_b_truth(self, x_label, truth): """ check whether transition enabled under current label :param x_label: current label :param truth: truth value making transition enabled :return: true or false """ if truth == '1': return True true_label = [ truelabel for truelabel in truth.keys() if truth[truelabel] ] for label in true_label: if label not in x_label: return False false_label = [ falselabel for falselabel in truth.keys() if not truth[falselabel] ] for label in false_label: if label in x_label: return False return True def findpath(self, goals): """ find the path backwards :param goals: goal state :return: dict path : cost """ paths = OrderedDict() for i in range(len(goals)): goal = goals[i] path = [goal] s = goal while s != self.init: s = list(self.tree.pred[s].keys())[0] if s == path[0]: print("loop") path.insert(0, s) paths[i] = [self.tree.nodes[goal]['cost'], path] return paths
class BoardGraph(object): def __init__(self, board_class=Board): self.graph = self.start = self.last = self.closest = None self.min_domino_count = None self.board_class = board_class def walk(self, board, size_limit=maxsize): pending_nodes = [] self.graph = DiGraph() self.start = board.display(cropped=True) self.graph.add_node(self.start) pending_nodes.append(self.start) self.last = self.start while pending_nodes: if len(self.graph) >= size_limit: raise GraphLimitExceeded(size_limit) state = pending_nodes.pop() board = self.board_class.create(state, border=1) for move, new_state in self.generate_moves(board): if not self.graph.has_node(new_state): # new node self.graph.add_node(new_state) pending_nodes.append(new_state) self.graph.add_edge(state, new_state, move=move) return set(self.graph.nodes()) def generate_moves(self, board): """ Generate all moves from the board's current state. :param Board board: the current state :return: a generator of (state, move_description) tuples """ dominoes = set(board.dominoes) domino_count = len(dominoes) if self.min_domino_count is None or domino_count < self.min_domino_count: self.min_domino_count = domino_count self.last = board.display(cropped=True) for domino in dominoes: dx, dy = domino.direction yield from self.try_move(domino, dx, dy) yield from self.try_move(domino, -dx, -dy) def try_move(self, domino, dx, dy): try: new_state = self.move(domino, dx, dy) move = domino.describe_move(dx, dy) yield move, new_state except BadPositionError: pass def move(self, domino, dx, dy): """ Move a domino and calculate the new board state. Afterward, put the board back in its original state. @return: the new board state @raise BadPositionError: if the move is illegal """ domino.move(dx, dy) try: board = domino.head.board if not board.isConnected(): raise BadPositionError('Board is not connected.') if board.hasLoner(): raise BadPositionError('Board has a lonely domino.') return board.display(cropped=True) finally: domino.move(-dx, -dy) def get_solution(self, return_partial=False): """ Find a solution from the graph of moves. @param return_partial: If True, a partial solution will be returned if no solution exists. @return: a list of strings describing each move. Each string is two digits describing the domino that moved plus a letter to show the direction. """ solution = [] goal = self.closest if return_partial else self.last or '' solution_nodes = shortest_path(self.graph, self.start, goal) for i in range(len(solution_nodes) - 1): source, target = solution_nodes[i:i + 2] solution.append(self.graph[source][target]['move']) return solution def get_choice_counts(self): solution_nodes = shortest_path(self.graph, self.start, self.last) return [len(self.graph[node]) for node in solution_nodes[:-1]] def get_average_choices(self): choices = self.get_choice_counts() return sum(choices) / float(len(choices)) if choices else maxsize def get_max_choices(self): choices = self.get_choice_counts() return max(choices) if choices else maxsize
def find_MECs(mdp, Sneg): #----implementation of Alg.47 P866 of Baier08---- print 'Remaining states size', len(Sneg) U = mdp.graph['U'] A = dict() for s in Sneg: A[s] = mdp.node[s]['act'].copy() if not A[s]: print "Isolated state" MEC = set() MECnew = set() MECnew.add(frozenset(Sneg)) #---- k = 0 while MEC != MECnew: print "<============iteration %s============>" %k k +=1 MEC = MECnew MECnew = set() print "MEC size: %s" %len(MEC) print "MECnew size: %s" %len(MECnew) for T in MEC: R = set() T_temp = set(T) simple_digraph = DiGraph() for s_f in T_temp: if s_f not in simple_digraph: simple_digraph.add_node(s_f) for s_t in mdp.successors_iter(s_f): if s_t in T_temp: simple_digraph.add_edge(s_f,s_t) print "SubGraph of one MEC: %s states and %s edges" %(str(len(simple_digraph.nodes())), str(len(simple_digraph.edges()))) Sccs = strongly_connected_component_subgraphs(simple_digraph) i = 0 for Scc in Sccs: i += 1 if (len(Scc.edges())>=1): for s in Scc.nodes(): U_to_remove = set() for u in A[s]: for t in mdp.successors_iter(s): if ((u in mdp.edge[s][t]['prop'].keys()) and (t not in Scc.nodes())): U_to_remove.add(u) A[s].difference_update(U_to_remove) if not A[s]: R.add(s) while R: s = R.pop() T_temp.remove(s) for f in mdp.predecessors(s): if f in T_temp: A[f].difference_update(set(mdp.edge[f][s]['prop'].keys())) if not A[f]: R.add(f) New_Sccs = strongly_connected_component_subgraphs(simple_digraph) j = 0 for Scc in New_Sccs: j += 1 if (len(Scc.edges()) >= 1): common = set(Scc.nodes()).intersection(T_temp) if common: MECnew.add(frozenset(common)) #--------------- print 'Final MEC and MECnew size:', len(MEC) return MEC, A
class StadynaMcgAnalysis: def __init__(self): self.androGuardObjects = [] self.nodes = {} self.nodes_id = {} self.entry_nodes = [] self.G = DiGraph() # self.internal_methods = [] #self.GI = DiGraph() def analyseFile(self, vmx, apk): vm = vmx.get_vm() self.androGuardObjects.append((apk, vm, vmx)) # self.internal_methods.extend(vm.get_methods()) #creating real internal nodes internal_called_methods = vmx.get_tainted_packages().stadyna_get_internal_called_methods() for method in internal_called_methods: class_name, method_name, descriptor = method nodeType = None if method_name == "<clinit>": nodeType = NODE_STATIC_INIT elif method_name == "<init>": nodeType = NODE_CONSTRUCTOR else: nodeType = NODE_METHOD n = self._get_node(nodeType, (class_name, method_name, descriptor)) n.set_attribute(ATTR_CLASS_NAME, class_name) n.set_attribute(ATTR_METHOD_NAME, method_name) n.set_attribute(ATTR_DESCRIPTOR, descriptor) self.G.add_node(n.id) #creating real edges (nodes are already there) #currently we are working only with internal packages. for j in vmx.get_tainted_packages().get_internal_packages(): src_class_name, src_method_name, src_descriptor = j.get_src(vm.get_class_manager()) dst_class_name, dst_method_name, dst_descriptor = j.get_dst(vm.get_class_manager()) n1 = self._get_existed_node((src_class_name, src_method_name, src_descriptor)) # n1.set_attribute(ATTR_CLASS_NAME, src_class_name) # n1.set_attribute(ATTR_METHOD_NAME, src_method_name) # n1.set_attribute(ATTR_DESCRIPTOR, src_descriptor) n2 = self._get_existed_node((dst_class_name, dst_method_name, dst_descriptor)) # n2.set_attribute(ATTR_CLASS_NAME, dst_class_name) # n2.set_attribute(ATTR_METHOD_NAME, dst_method_name) # n2.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) self.G.add_edge(n1.id, n2.id) #adding fake class nodes for method in internal_called_methods: src_class_name, src_method_name, src_descriptor = method if src_method_name == "<init>" or src_method_name == "<clinit>": n1 = self._get_existed_node((src_class_name, src_method_name, src_descriptor)) n2 = self._get_node(NODE_FAKE_CLASS, src_class_name, None, False) n2.set_attribute(ATTR_CLASS_NAME, src_class_name) if src_method_name == "<clinit>": self.G.add_edge(n1.id, n2.id) elif src_method_name == "<init>": self.G.add_edge(n2.id, n1.id) #real (external) reflection invoke nodes reflection_invoke_paths = analysis.seccon_get_invoke_method_paths(vmx) for j in reflection_invoke_paths: src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager() ) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager() ) n1 = self._get_existed_node((src_class_name, src_method_name, src_descriptor)) if n1 == None: logger.warning("Cannot find the node [%s], where reflection invoke is called!" % (src_class_name, src_method_name, src_descriptor)) continue key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_REFL_INVOKE) n2 = self._get_node(NODE_REFL_INVOKE, key, LABEL_REFL_INVOKE, True) n2.set_attribute(ATTR_CLASS_NAME, src_class_name) n2.set_attribute(ATTR_METHOD_NAME, src_method_name) n2.set_attribute(ATTR_DESCRIPTOR, src_descriptor) self.G.add_edge( n1.id, n2.id ) #real (external) reflection new instance nodes reflection_newInstance_paths = analysis.seccon_get_newInstance_method_paths(vmx) for j in reflection_newInstance_paths: src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager() ) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager() ) n1 = self._get_existed_node((src_class_name, src_method_name, src_descriptor)) if n1 == None: logger.warning("Cannot find the node [%s], where reflection new instance is called!" % (src_class_name, src_method_name, src_descriptor)) continue key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_REFL_NEWINSTANCE) n2 = self._get_node(NODE_REFL_NEWINSTANCE, key, LABEL_REFL_NEWINSTANCE, True) n2.set_attribute(ATTR_CLASS_NAME, src_class_name) n2.set_attribute(ATTR_METHOD_NAME, src_method_name) n2.set_attribute(ATTR_DESCRIPTOR, src_descriptor) self.G.add_edge( n1.id, n2.id ) #adding fake entry points if apk != None: for i in apk.get_activities() : j = bytecode.FormatClassToJava(i) n1 = self._get_existed_node((j, "onCreate", "(Landroid/os/Bundle;)V")) if n1 != None: key = "%s %s %s %s" % (j, "onCreate", "(Landroid/os/Bundle;)V", POSTFIX_ACTIVITY) n2 = self._get_node(NODE_FAKE_ACTIVITY, key, LABEL_ACTIVITY, False) self.G.add_edge( n2.id, n1.id ) self.entry_nodes.append( n1.id ) for i in apk.get_services() : j = bytecode.FormatClassToJava(i) n1 = self._get_existed_node( (j, "onCreate", "()V") ) if n1 != None : key = "%s %s %s %s" % (j, "onCreate", "()V", POSTFIX_SERVICE) n2 = self._get_node(NODE_FAKE_SERVICE, key, LABEL_SERVICE, False) self.G.add_edge( n2.id, n1.id ) self.entry_nodes.append( n1.id ) for i in apk.get_receivers() : j = bytecode.FormatClassToJava(i) n1 = self._get_existed_node( (j, "onReceive", "(Landroid/content/Context;Landroid/content/Intent;)V") ) if n1 != None : key = "%s %s %s %s" % (j, "onReceive", "(Landroid/content/Context;Landroid/content/Intent;)V", POSTFIX_RECEIVER) n2 = self._get_node(NODE_FAKE_SERVICE, key, LABEL_RECEIVER, False) self.G.add_edge( n2.id, n1.id ) self.entry_nodes.append( n1.id ) #fake permissions list_permissions = vmx.stadyna_get_permissions([]) for x in list_permissions: for j in list_permissions[x]: if isinstance(j, PathVar): continue src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager() ) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager() ) n1 = self._get_existed_node((src_class_name, src_method_name, src_descriptor)) if n1 == None: logger.warning("Cannot find node [%s %s %s] for permission [%s]!" % (src_class_name, src_method_name, src_descriptor, x)) continue #SOURCE, DEST, POSTFIX, PERMISSION_NAME key = "%s %s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_PERM, x) n2 = self._get_node(NODE_FAKE_PERMISSION, key, x, False) n2.set_attribute(ATTR_CLASS_NAME, dst_class_name) n2.set_attribute(ATTR_METHOD_NAME, dst_method_name) n2.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) n2.set_attribute(ATTR_PERM_NAME, x) n2.set_attribute(ATTR_PERM_LEVEL, MANIFEST_PERMISSIONS[ x ][0]) self.G.add_edge(n1.id, n2.id) #fake DexClassLoader nodes dyn_code_loading = analysis.seccon_get_dyncode_loading_paths(vmx) for j in dyn_code_loading: src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager() ) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager() ) n1 = self._get_existed_node((src_class_name, src_method_name, src_descriptor)) if n1 == None: logger.warning("Cannot find dexload node [%s]!" % (src_class_name, src_method_name, src_descriptor)) continue key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_DEXLOAD) n2 = self._get_node(NODE_FAKE_DEXLOAD, key, LABEL_DEXLOAD, False) n2.set_attribute(ATTR_CLASS_NAME, src_class_name) n2.set_attribute(ATTR_METHOD_NAME, src_method_name) n2.set_attribute(ATTR_DESCRIPTOR, src_descriptor) self.G.add_edge( n1.id, n2.id ) # Specific Java/Android library for c in vm.get_classes(): #if c.get_superclassname() == "Landroid/app/Service;" : # n1 = self._get_node( c.get_name(), "<init>", "()V" ) # n2 = self._get_node( c.get_name(), "onCreate", "()V" ) # self.G.add_edge( n1.id, n2.id ) if c.get_superclassname() == "Ljava/lang/Thread;" or c.get_superclassname() == "Ljava/util/TimerTask;" : for i in vm.get_method("run") : if i.get_class_name() == c.get_name() : n1 = self._get_node(NODE_METHOD, (i.get_class_name(), i.get_name(), i.get_descriptor())) n2 = self._get_node(NODE_METHOD, (i.get_class_name(), "start", i.get_descriptor())) # link from start to run self.G.add_edge( n2.id, n1.id ) #n2.add_edge( n1, {} ) # link from init to start for init in vm.get_method("<init>") : if init.get_class_name() == c.get_name(): #TODO: Leaving _get_existed_node to check if all the nodes are included #It is possible that internal_packages does not contain this node. Leaving _get_existed_node to check this n3 = self._get_node(NODE_CONSTRUCTOR, (init.get_class_name(), "<init>", init.get_descriptor())) self.G.add_edge( n3.id, n2.id ) #n3.add_edge( n2, {} ) def addInvokePath(self, src, through, dst): src_class_name, src_method_name, src_descriptor = src dst_class_name, dst_method_name, dst_descriptor = dst through_class_name, through_method_name, through_descriptor = through key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, through_class_name, through_method_name, through_descriptor, POSTFIX_REFL_INVOKE) n1 = self._get_existed_node(key) if n1 == None: logger.warning("Something wrong has happened! Could not find invoke Node in Graph with key [%s]" % str(key)) return n2 = self._get_node(NODE_METHOD, (dst_class_name, dst_method_name, dst_descriptor)) n2.set_attribute(ATTR_CLASS_NAME, dst_class_name) n2.set_attribute(ATTR_METHOD_NAME, dst_method_name) n2.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) self.G.add_edge(n1.id, n2.id) #check if called method calls protected feature data = "%s-%s-%s" % (dst_class_name, dst_method_name, dst_descriptor) if data in DVM_PERMISSIONS_BY_API_CALLS: logger.info("BINGOOOOOOO! The protected method is called through reflection!") perm = DVM_PERMISSIONS_BY_API_CALLS[ data ] key1 = "%s %s %s %s %s %s %s %s" % (through_class_name, through_method_name, through_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_PERM, perm) n3 = self._get_node(NODE_FAKE_PERMISSION, key1, perm, False) n3.set_attribute(ATTR_CLASS_NAME, dst_class_name) n3.set_attribute(ATTR_METHOD_NAME, dst_method_name) n3.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) n3.set_attribute(ATTR_PERM_NAME, perm) n3.set_attribute(ATTR_PERM_LEVEL, MANIFEST_PERMISSIONS[ perm ][0]) self.G.add_edge(n2.id, n3.id) def addNewInstancePath(self, src, through, dst): src_class_name, src_method_name, src_descriptor = src dst_class_name, dst_method_name, dst_descriptor = dst through_class_name, through_method_name, through_descriptor = through key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, through_class_name, through_method_name, through_descriptor, POSTFIX_REFL_NEWINSTANCE) n1 = self._get_existed_node(key) if n1 == None: logger.error("Something wrong has happened! Could not find Node in Graph with key [%s]" % str(key)) return n2 = self._get_node(NODE_CONSTRUCTOR, (dst_class_name, dst_method_name, dst_descriptor)) n2.set_attribute(ATTR_CLASS_NAME, dst_class_name) n2.set_attribute(ATTR_METHOD_NAME, dst_method_name) n2.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) self.G.add_edge(n1.id, n2.id) #we also need to add link to the class node #TODO: Think in the future what to do with this n_class = self._get_node(NODE_FAKE_CLASS, dst_class_name, None, False) n_class.set_attribute(ATTR_CLASS_NAME, dst_class_name) self.G.add_edge(n_class.id, n2.id) #checking if we need to add additional permission nodes data = "%s-%s-%s" % (dst_class_name, dst_method_name, dst_descriptor) if data in DVM_PERMISSIONS_BY_API_CALLS: logger.info("BINGOOOOOOO! The protected method is called through reflection!") perm = DVM_PERMISSIONS_BY_API_CALLS[ data ] key1 = "%s %s %s %s %s %s %s %s" % (through_class_name, through_method_name, through_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_PERM, perm) n3 = self._get_node(NODE_FAKE_PERMISSION, key1, perm, False) n3.set_attribute(ATTR_CLASS_NAME, dst_class_name) n3.set_attribute(ATTR_METHOD_NAME, dst_method_name) n3.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) n3.set_attribute(ATTR_PERM_NAME, perm) n3.set_attribute(ATTR_PERM_LEVEL, MANIFEST_PERMISSIONS[ perm ][0]) self.G.add_edge(n2.id, n3.id) def addDexloadPath(self, src, through, filename): src_class_name, src_method_name, src_descriptor = src through_class_name, through_method_name, through_descriptor = through key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, through_class_name, through_method_name, through_descriptor, POSTFIX_DEXLOAD) n1 = self._get_existed_node(key) if n1 == None: logger.error("Something wrong has happened! Could not find Node in Graph with key [%s]" % str(key)) return n2 = self._get_node(NODE_FAKE_DEXLOAD_FILE, filename, filename, False) n2.set_attribute(ATTR_DEXLOAD_FILENAME, filename) self.G.add_edge(n1.id, n2.id) def _get_node(self, nType, key, label=None, real=True): node_key = None if isinstance(key, basestring): node_key = key elif isinstance(key, tuple): node_key = "%s %s %s" % key else: logger.error("Unknown instance type of key!!!") if node_key not in self.nodes.keys(): new_node = NodeS(len(self.nodes), nType, node_key, label, real) self.nodes[node_key] = new_node self.nodes_id[new_node.id] = new_node return self.nodes[node_key] def _get_existed_node(self, key): node_key = None if isinstance(key, basestring): node_key = key elif isinstance(key, tuple): node_key = "%s %s %s" % key else: logger.error("Unknown instance type of key!!!") try: return self.nodes[node_key] except KeyError: logger.error("Could not find existed node [%s]!" % node_key) return None def export_to_gexf(self) : buff = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" buff += "<gexf xmlns=\"http://www.gephi.org/gexf\" xmlns:viz=\"http://www.gephi.org/gexf/viz\">\n" buff += "<graph type=\"static\">\n" buff += "<attributes class=\"node\" type=\"static\">\n" buff += "<attribute title=\"%s\" id=\"%d\" type=\"string\"/>\n" % (ATTR_TYPE, ID_ATTRIBUTES[ ATTR_TYPE ]) buff += "<attribute title=\"%s\" id=\"%d\" type=\"string\"/>\n" % (ATTR_CLASS_NAME, ID_ATTRIBUTES[ ATTR_CLASS_NAME ]) buff += "<attribute title=\"%s\" id=\"%d\" type=\"string\"/>\n" % (ATTR_METHOD_NAME, ID_ATTRIBUTES[ ATTR_METHOD_NAME ]) buff += "<attribute title=\"%s\" id=\"%d\" type=\"string\"/>\n" % (ATTR_DESCRIPTOR, ID_ATTRIBUTES[ ATTR_DESCRIPTOR ]) buff += "<attribute title=\"%s\" id=\"%d\" type=\"string\"/>\n" % (ATTR_REAL, ID_ATTRIBUTES[ ATTR_REAL ]) buff += "<attribute title=\"%s\" id=\"%d\" type=\"string\"/>\n" % (ATTR_PERM_NAME, ID_ATTRIBUTES[ ATTR_PERM_NAME ]) buff += "<attribute title=\"%s\" id=\"%d\" type=\"string\"/>\n" % (ATTR_PERM_LEVEL, ID_ATTRIBUTES[ ATTR_PERM_LEVEL ]) buff += "<attribute title=\"%s\" id=\"%d\" type=\"string\"/>\n" % (ATTR_DEXLOAD_FILENAME, ID_ATTRIBUTES[ ATTR_DEXLOAD_FILENAME ]) buff += "</attributes>\n" # buff += "<attributes class=\"node\" type=\"static\">\n" # buff += "<attribute id=\"%d\" title=\"type\" type=\"string\" default=\"normal\"/>\n" % ID_ATTRIBUTES[ "type"] # buff += "<attribute id=\"%d\" title=\"class_name\" type=\"string\"/>\n" % ID_ATTRIBUTES[ "class_name"] # buff += "<attribute id=\"%d\" title=\"method_name\" type=\"string\"/>\n" % ID_ATTRIBUTES[ "method_name"] # buff += "<attribute id=\"%d\" title=\"descriptor\" type=\"string\"/>\n" % ID_ATTRIBUTES[ "descriptor"] # # # buff += "<attribute id=\"%d\" title=\"permissions\" type=\"integer\" default=\"0\"/>\n" % ID_ATTRIBUTES[ "permissions"] # buff += "<attribute id=\"%d\" title=\"permissions_level\" type=\"string\" default=\"normal\"/>\n" % ID_ATTRIBUTES[ "permissions_level"] # # buff += "<attribute id=\"%d\" title=\"dynamic_code\" type=\"boolean\" default=\"false\"/>\n" % ID_ATTRIBUTES[ "dynamic_code"] # # buff += "</attributes>\n" buff += "<nodes>\n" for node in self.G.nodes() : buff += "<node id=\"%d\" label=\"%s\">\n" % (node, escape(self.nodes_id[ node ].label)) buff += self.nodes_id[ node ].get_attributes_gexf() buff += "</node>\n" buff += "</nodes>\n" buff += "<edges>\n" nb = 0 for edge in self.G.edges() : buff += "<edge id=\"%d\" source=\"%d\" target=\"%d\"/>\n" % (nb, edge[0], edge[1]) nb += 1 buff += "</edges>\n" buff += "</graph>\n" buff += "</gexf>\n" return buff def get_current_real_node_count(self): count = 0 for node in self.nodes_id.keys(): if self.nodes_id[node].get_attribute(ATTR_REAL) == "True": count += 1 return count def get_current_node_count(self): return len(self.G.nodes()) def get_current_edge_count(self): return len(self.G.edges()) def get_current_permission_level_node_count(self, permission_level): count = 0 for node in self.nodes_id.keys(): if self.nodes_id[node].get_attribute(ATTR_PERM_LEVEL) == permission_level: count += 1 return count def get_current_protected_node_count(self): count = 0 for node in self.nodes_id.keys(): if self.nodes_id[node].get_attribute(ATTR_PERM_LEVEL) != None: count += 1 return count
class tree(object): """ construction of prefix and suffix tree """ def __init__(self, n_robot, acpt, ts, buchi_graph, init, seg, step_size, no): """ :param acpt: accepting state :param ts: transition system :param buchi_graph: Buchi graph :param init: product initial state """ self.robot = n_robot self.acpt = acpt self.goals = [] self.ts = ts self.buchi_graph = buchi_graph self.init = init self.seg = seg self.step_size = step_size self.dim = len(self.ts['workspace']) uni_ball = [ 1, 2, 3.142, 4.189, 4.935, 5.264, 5.168, 4.725, 4.059, 3.299, 2.550 ] # uni_v = uni_ball[self.robot*self.dim] uni_v = np.power(np.pi, self.robot * self.dim / 2) / math.gamma(self.robot * self.dim / 2 + 1) self.gamma = np.ceil( 4 * np.power(1 / uni_v, 1. / (self.dim * self.robot))) # unit workspace self.tree = DiGraph(type='PBA', init=init) self.group = dict() label = [] for i in range(self.robot): l = self.label(init[0][i]) # exists one sampled point lies within obstacles if l != '': l = l + '_' + str(i + 1) label.append(l) self.tree.add_node(init, cost=0, label=label) self.add_group(init) # probability self.p = 0.9 # threshold for collision avoidance self.threshold = 0.02 # polygon obstacle polys = [[ vg.Point(0.4, 1.0), vg.Point(0.4, 0.7), vg.Point(0.6, 0.7), vg.Point(0.6, 1.0) ], [ vg.Point(0.3, 0.2), vg.Point(0.3, 0.0), vg.Point(0.7, 0.0), vg.Point(0.7, 0.2) ]] self.g = vg.VisGraph() self.g.build(polys, status=False) # region that has ! preceding it self.no = no def add_group(self, q_state): """ group nodes with same buchi state :param q_state: new state added to the tree """ try: self.group[q_state[1]].append(q_state) except KeyError: self.group[q_state[1]] = [q_state] def min2final(self, min_qb_dict, b_final, cand): """ collects the buchi state in the tree with minimum distance to the final state :param min_qb_dict: dict :param b_final: feasible final state :return: list of buchi states in the tree with minimum distance to the final state """ l_min = np.inf b_min = [] for b_state in cand: if min_qb_dict[(b_state, b_final)] < l_min: l_min = min_qb_dict[(b_state, b_final)] b_min = [b_state] elif min_qb_dict[(b_state, b_final)] == l_min: b_min.append(b_state) return b_min def all2one(self, b_min): """ partition nodes into 2 groups :param b_min: buchi states with minimum distance to the finals state :return: 2 groups """ q_min2final = [] q_minNot2final = [] for b_state in self.group.keys(): if b_state in b_min: q_min2final = q_min2final + self.group[b_state] else: q_minNot2final = q_minNot2final + self.group[b_state] return q_min2final, q_minNot2final def get_truncated_normal(self, mean=0, sd=1, low=0, upp=10): return truncnorm((low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd) def trunc(self, value): if value < 0: return 0 elif value > 1: return 1 else: return value def collision_avoidance(self, x, index): """ check whether any robots are collision-free from index-th robot :param x: all robots :param index: index-th robot :return: true collision free """ for i in range(len(x)): if i != index and np.linalg.norm(np.subtract( x[i], x[index])) <= self.threshold: return False return True def target(self, init, target, regions): """ find the closest vertex in the short path from init to target :param init: inital point :param target: target labeled region :param regions: regions :return: closest vertex """ tg = regions[((target, 'b'))][:2] shortest = self.g.shortest_path(vg.Point(init[0], init[1]), vg.Point(tg[0], tg[1])) return (shortest[1].x, shortest[1].y) def gaussian_guided(self, x, target): """ calculate new point following gaussian dist guided by the target :param x: mean point :param target: target point :return: new point """ # print(min(self.gamma * np.power(np.log(self.tree.number_of_nodes()+1)/self.tree.number_of_nodes(),1./(self.dim*self.robot)), self.step_size)/3) # d = self.get_truncated_normal(0, min(self.gamma * np.power(np.log(self.tree.number_of_nodes()+1)/self.tree.number_of_nodes(),1./(self.dim*self.robot)), self.step_size)/3, 0, np.inf) # d = self.get_truncated_normal(0, self.step_size/3, 0, np.inf) d = self.get_truncated_normal(0, 1 / 3, 0, np.inf) # d = self.get_truncated_normal(0, 1, 0, np.inf) d = d.rvs() # # print('d=',d) # if np.random.uniform(0,1,1) <= self.p: # angle = np.random.uniform(-np.pi/2, np.pi/2, 1) + np.arctan2(center[1]-x[1], center[0]-x[0]) # else: # angle = np.random.uniform(np.pi/2, 3*np.pi/2, 1) + np.arctan2(center[1]-x[1], center[0]-x[0]) angle = np.random.normal(0, np.pi / 12 / 3 / 3, 1) + np.arctan2( target[1] - x[1], target[0] - x[0]) # angle = np.arctan2(target[1] - x[1], target[0] - x[0]) x_rand = np.add(x, np.append(d * np.cos(angle), d * np.sin(angle))) x_rand = [self.trunc(x) for x in x_rand] return tuple(x_rand) def gaussian_unguided(self, x): """ :param x: :return: """ d = self.get_truncated_normal( 0, min( self.gamma * np.power( np.log(self.tree.number_of_nodes() + 1) / self.tree.number_of_nodes(), 1. / (self.dim * self.robot)), self.step_size) / 3, 0) d = d.rvs() angle = np.random.uniform(-np.pi, np.pi, 1) x_rand = np.add(x, np.append(d * np.cos(angle), d * np.sin(angle))) return tuple([self.trunc(x) for x in x_rand]) def buchi_guided_sample_by_label(self, x_rand, b_label, x_label, regions): if b_label.strip().strip('(').strip(')') == '1': return [] # not or be in some place else: # label of current position blabel = b_label.split('||')[0] # blabel = random.choice(b_label.split('||')) atomic_label = blabel.split('&&') for a in atomic_label: # a = a.strip().strip('(').strip(')') a = a.strip().strip('(').strip(')').split('or')[0].strip() # if in wrong position, sample randomly if '!' in a: if a[1:] in x_label: xi_rand = [] for i in range(self.dim): xi_rand.append(uniform(0, self.ts['workspace'][i])) ind = int(a[1:].split('_')[1]) - 1 x_rand[ind] = tuple(xi_rand) else: # move towards target position if not a in x_label: ind = a.split('_') weight = 0.8 if np.random.uniform(0, 1, 1) <= weight: tg = self.target(x_rand[int(ind[1]) - 1], ind[0], regions) x_rand[int(ind[1]) - 1] = self.gaussian_guided( x_rand[int(ind[1]) - 1], tg) else: xi_rand = [] for i in range(self.dim): xi_rand.append( uniform(0, self.ts['workspace'][i])) x_rand[int(ind[1]) - 1] = tuple(xi_rand) return x_rand def buchi_guided_sample_by_truthvalue(self, truth, x_rand, q_rand, x_label, regions): """ sample guided by truth value :param truth: the value making transition occur :param x_rand: random selected node :param x_label: label of x_rand :param regions: regions :return: new sampled point """ if truth == '1': return [], [] # not or be in some place else: for key in truth: # if in wrong position, sample randomly # if not truth[key] and key in x_label: # xi_rand = [] # for i in range(self.dim): # xi_rand.append(uniform(0, self.ts['workspace'][i])) # ind = key.split('_') # x_rand[int(ind[1]) - 1] = tuple(xi_rand) # elif truth[key]: # # move towards target position # if not key in x_label: # ind = key.split('_') # weight = 1 # if np.random.uniform(0, 1, 1) <= weight: # tg = self.target(x_rand[int(ind[1]) - 1], ind[0], regions) # # tg = self.target(orig_x_rand, ind[0], regions) # x_rand[int(ind[1]) - 1] = self.gaussian_guided(x_rand[int(ind[1]) - 1], tg) # else: # xi_rand = [] # for i in range(self.dim): # xi_rand.append(uniform(0, self.ts['workspace'][i])) # x_rand[int(ind[1]) - 1] = tuple(xi_rand) ind = key.split('_') orig_x_rand = x_rand[int(ind[1]) - 1] # save while 1: x_rand[int(ind[1]) - 1] = orig_x_rand # recover # if in wrong position, sample randomly if not truth[key] and key in x_label: xi_rand = [] for i in range(self.dim): xi_rand.append(uniform(0, self.ts['workspace'][i])) # ind = key.split('_') x_rand[int(ind[1]) - 1] = tuple(xi_rand) elif truth[key]: # move towards target position if not key in x_label: # ind = key.split('_') weight = 0.8 if np.random.uniform(0, 1, 1) <= weight: # tg = self.target(x_rand[int(ind[1]) - 1], ind[0], regions) tg = self.target(orig_x_rand, ind[0], regions) x_rand[int(ind[1]) - 1] = self.gaussian_guided( orig_x_rand, tg) else: xi_rand = [] for i in range(self.dim): xi_rand.append( uniform(0, self.ts['workspace'][i])) x_rand[int(ind[1]) - 1] = tuple(xi_rand) else: break if self.collision_avoidance(x_rand, int(ind[1]) - 1): break # x_rand x_nearest return self.mulp2sglp(x_rand), q_rand # return x_rand def sample(self, buchi_graph, min_qb_dict, regions): """ sample point from the workspace :return: sampled point, tuple """ if self.seg == 'pre': b_final = buchi_graph.graph['accept'][np.random.randint( 0, len(buchi_graph.graph['accept']) )] # feasible final buchi state else: b_final = buchi_graph.graph['accept'] # collects the buchi state in the tree with minimum distance to the final state b_min = self.min2final(min_qb_dict, b_final, self.group.keys()) # partition of nodes q_min2final, q_minNot2final = self.all2one(b_min) # sample random nodes p_rand = np.random.uniform(0, 1, 1) if (p_rand <= self.p and len(q_min2final) > 0) or not q_minNot2final: q_rand = q_min2final[np.random.randint(0, len(q_min2final))] elif p_rand > self.p or not q_min2final: q_rand = q_minNot2final[np.random.randint(0, len(q_minNot2final))] # find feasible succssor of buchi state in q_rand Rb_q_rand = [] x_label = [] for i in range(self.robot): l = self.label(q_rand[0][i]) if l != '': l = l + '_' + str(i + 1) x_label.append(l) for b_state in buchi_graph.succ[q_rand[1]]: # if self.t_satisfy_b(x_label, buchi_graph.edges[(q_rand[1], b_state)]['label']): if self.t_satisfy_b_truth( x_label, buchi_graph.edges[(q_rand[1], b_state)]['truth']): Rb_q_rand.append(b_state) # if empty if not Rb_q_rand: return Rb_q_rand, Rb_q_rand # collects the buchi state in the reachable set of qb_rand with minimum distance to the final state b_min = self.min2final(min_qb_dict, b_final, Rb_q_rand) # collects the buchi state in the reachable set of b_min with distance to the final state equal to that of b_min - 1 decr_dict = dict() for b_state in b_min: decr = [] for succ in buchi_graph.succ[b_state]: if min_qb_dict[(b_state, b_final)] - 1 == min_qb_dict[( succ, b_final)] or succ in buchi_graph.graph['accept']: decr.append(succ) decr_dict[b_state] = decr M_cand = [ b_state for b_state in decr_dict.keys() if decr_dict[b_state] ] # if empty if not M_cand: return M_cand, M_cand # sample b_min and b_decr b_min = M_cand[np.random.randint(0, len(M_cand))] b_decr = decr_dict[b_min][np.random.randint(0, len(decr_dict[b_min]))] # b_label = buchi_graph.edges[(b_min, b_decr)]['label'] # x_rand = list(q_rand[0]) # # return self.buchi_guided_sample_by_label(x_rand, b_label, x_label, regions) truth = buchi_graph.edges[(b_min, b_decr)]['truth'] x_rand = list(q_rand[0]) return self.buchi_guided_sample_by_truthvalue(truth, x_rand, q_rand, x_label, regions) # x_rand x_nearest # return self.mulp2sglp(x_rand), self.mulp2sglp(q_rand[0]) # return x_rand # def nearest(self, x_rand): # """ # find the nearest vertex in the tree # :param: x_rand randomly sampled point form: single point () # :return: nearest vertex form: single point () # """ # min_dis = math.inf # x_nearest = x_rand # for vertex in self.tree.nodes: # x_vertex = self.mulp2sglp(vertex[0]) # dis = np.linalg.norm(np.subtract(x_rand, x_vertex)) # if dis < min_dis: # x_nearest = x_vertex # min_dis = dis # return x_nearest def nearest(self, x_rand): """ find the nearest class of vertices in the tree :param: x_rand randomly sampled point form: single point () :return: nearest class of vertices form: single point () """ min_dis = math.inf q_nearest = [] for vertex in self.tree.nodes: x_vertex = self.mulp2sglp(vertex[0]) dis = np.linalg.norm(np.subtract(x_rand, x_vertex)) if dis < min_dis: q_nearest = list() q_nearest.append(vertex) min_dis = dis elif dis == min_dis: q_nearest.append(vertex) return q_nearest def steer(self, x_rand, x_nearest): """ steer :param: x_rand randomly sampled point form: single point () :param: x_nearest nearest point in the tree form: single point () :return: new point single point () """ #return np.asarray([0.8,0.4]) if np.linalg.norm(np.subtract(x_rand, x_nearest)) <= self.step_size: return x_rand else: return tuple( np.asarray(x_nearest) + self.step_size * (np.subtract(x_rand, x_nearest)) / np.linalg.norm(np.subtract(x_rand, x_nearest))) def extend(self, q_new, near_v, label, obs_check): """ :param: q_new: new state form: tuple (mulp, buchi) :param: near_v: near state form: tuple (mulp, buchi) :param: obs_check: check obstacle free form: dict { (mulp, mulp): True } :return: extending the tree """ added = 0 cost = np.inf q_min = () for near_vertex in near_v: if q_new != near_vertex and obs_check[( q_new[0], near_vertex[0])] and self.checkTranB( near_vertex[1], self.tree.nodes[near_vertex]['label'], q_new[1]): c = self.tree.nodes[near_vertex]['cost'] + np.linalg.norm( np.subtract(self.mulp2sglp(q_new[0]), self.mulp2sglp( near_vertex[0]))) # don't consider control if c < cost: added = 1 q_min = near_vertex cost = c if added == 1: self.tree.add_node(q_new, cost=cost, label=label) self.tree.add_edge(q_min, q_new) self.add_group(q_new) if self.seg == 'pre' and q_new[1] in self.acpt: q_n = list(list(self.tree.pred[q_new].keys())[0]) cost = self.tree.nodes[tuple(q_n)]['cost'] label = self.tree.nodes[tuple(q_n)]['label'] q_n[1] = q_new[1] q_n = tuple(q_n) self.tree.add_node(q_n, cost=cost, label=label) self.tree.add_edge(q_min, q_n) self.add_group(q_n) self.goals.append(q_n) if self.seg == 'suf' and self.checkTranB(q_new[1], label, self.init[1]): print('final') if self.seg == 'suf' and self.obs_check( [self.init], q_new[0], label, 'final')[(q_new[0], self.init[0])] and self.checkTranB( q_new[1], label, self.init[1]): # if self.seg == 'suf' and self.init in near_v and obs_check[(q_new[0], self.init[0])] and self.checkTranB(q_new[1], label, self.init[1]): self.goals.append(q_new) return added def rewire(self, q_new, near_v, obs_check): """ :param: q_new: new state form: tuple (mul, buchi) :param: near_v: near state form: tuple (mul, buchi) :param: obs_check: check obstacle free form: dict { (mulp, mulp): True } :return: rewiring the tree """ for near_vertex in near_v: if obs_check[(q_new[0], near_vertex[0])] and self.checkTranB( q_new[1], self.tree.nodes[q_new]['label'], near_vertex[1]): c = self.tree.nodes[q_new]['cost'] + np.linalg.norm( np.subtract(self.mulp2sglp( q_new[0]), self.mulp2sglp( near_vertex[0]))) # without considering control delta_c = self.tree.nodes[near_vertex]['cost'] - c # update the cost of node in the subtree rooted at near_vertex if delta_c > 0: # self.tree.nodes[near_vertex]['cost'] = c if not list(self.tree.pred[near_vertex].keys()): print('empty') self.tree.remove_edge( list(self.tree.pred[near_vertex].keys())[0], near_vertex) self.tree.add_edge(q_new, near_vertex) edges = dfs_labeled_edges(self.tree, source=near_vertex) for _, v, d in edges: if d == 'forward': self.tree.nodes[v][ 'cost'] = self.tree.nodes[v]['cost'] - delta_c def near(self, x_new): """ find the states in the near ball :param x_new: new point form: single point :return: p_near: near state, form: tuple (mulp, buchi) """ p_near = [] r = min( self.gamma * np.power( np.log(self.tree.number_of_nodes() + 1) / self.tree.number_of_nodes(), 1. / (self.dim * self.robot)), self.step_size) for vertex in self.tree.nodes: if np.linalg.norm(np.subtract(x_new, self.mulp2sglp( vertex[0]))) <= r: p_near.append(vertex) return p_near def obs_check(self, q_near, x_new, label, stage): """ check whether obstacle free along the line from x_near to x_new :param q_near: states in the near ball, tuple (mulp, buchi) :param x_new: new state form: multiple point :param label: label of x_new :param stage: regular stage or final stage, deciding whether it's goal state :return: dict (x_near, x_new): true (obs_free) """ # x_new = ((0.8944144022556246, 0.33267910821176216),) # label = ['l3_1'] # q_near = [(((0.8, 0.1),), 'T0_init'), (((0.9115062737314963, 0.10325925485437781),), 'T0_init')] obs_check_dict = {} for x in q_near: obs_check_dict[(x_new, x[0])] = True flag = True # indicate whether break and jump to outer loop for r in range(self.robot): for i in range(1, 11): mid = tuple( np.asarray(x[0][r]) + i / 10. * np.subtract(x_new[r], x[0][r])) mid_label = self.label(mid) if mid_label != '': mid_label = mid_label + '_' + str(r + 1) if stage == 'reg' and ( 'o' in mid_label or (mid_label != self.tree.nodes[x]['label'][r] and mid_label != label[r])): # obstacle pass through one region more than once obs_check_dict[(x_new, x[0])] = False flag = False break # elif stage == 'final' and ('o' in mid_label or (mid_label != self.tree.nodes[x]['label'][r] and mid_label != label[r] and mid_label != '')): # obstacle cannot pass through one region more than once expcet unlabeled region elif stage == 'final' and ( 'o' in mid_label or (mid_label != self.tree.nodes[x]['label'][r] and mid_label != label[r] and mid_label in self.no)): obs_check_dict[(x_new, x[0])] = False flag = False break if not flag: break return obs_check_dict def label(self, x): """ generating the label of position state :param x: position :return: label """ # whether x lies within obstacle for (obs, boundary) in iter(self.ts['obs'].items()): if obs[1] == 'b' and np.linalg.norm(np.subtract( x, boundary[0:-1])) <= boundary[-1]: return obs[0] elif obs[1] == 'p': dictator = True for i in range(len(boundary)): if np.dot(x, boundary[i][0:-1]) + boundary[i][-1] > 0: dictator = False break if dictator == True: return obs[0] # whether x lies within regions for (regions, boundary) in iter(self.ts['region'].items()): if regions[1] == 'b' and np.linalg.norm( x - np.asarray(boundary[0:-1])) <= boundary[-1]: return regions[0] elif regions[1] == 'p': dictator = True for i in range(len(boundary)): if np.dot(x, np.asarray( boundary[i][0:-1])) + boundary[i][-1] > 0: dictator = False break if dictator == True: return regions[0] return '' def checkTranB(self, b_state, x_label, q_b_new): """ decide valid transition, whether b_state --L(x)---> q_b_new Algorithm2 in Chapter 2 Motion and Task Planning :param b_state: buchi state :param x_label: label of x :param q_b_new buchi state :return True satisfied """ b_state_succ = self.buchi_graph.succ[b_state] # q_b_new is not the successor of b_state if q_b_new not in b_state_succ: return False # b_label = self.buchi_graph.edges[(b_state, q_b_new)]['label'] # if self.t_satisfy_b(x_label, b_label): # return True truth = self.buchi_graph.edges[(b_state, q_b_new)]['truth'] if self.t_satisfy_b_truth(x_label, truth): return True def t_satisfy_b(self, x_label, b_label): """ decide whether label of self.ts_graph can satisfy label of self.buchi_graph :param x_label: label of x :param b_label: label of buchi state :return t_s_b: true if satisfied """ t_s_b = True # split label with || b_label = b_label.split('||') for label in b_label: t_s_b = True # spit label with && atomic_label = label.split('&&') for a in atomic_label: a = a.strip() a = a.strip('(') a = a.strip(')') if a == '1': continue # whether ! in an atomic proposition if '!' in a: if a[1:] in x_label: t_s_b = False break else: if not a in x_label: t_s_b = False break # either one of || holds if t_s_b: return t_s_b return t_s_b def t_satisfy_b_truth(self, x_label, truth): """ check whether transition enabled under current label :param x_label: current label :param truth: truth value making transition enabled :return: true or false """ if truth == '1': return True true_label = [ truelabel for truelabel in truth.keys() if truth[truelabel] ] for label in true_label: if label not in x_label: return False false_label = [ falselabel for falselabel in truth.keys() if not truth[falselabel] ] for label in false_label: if label in x_label: return False return True def findpath(self, goals): """ find the path backwards :param goal: goal state :return: dict path : cost """ paths = OrderedDict() for i in range(len(goals)): goal = goals[i] path = [goal] s = goal while s != self.init: s = list(self.tree.pred[s].keys())[0] if s == path[0]: print("loop") path.insert(0, s) if self.seg == 'pre': paths[i] = [self.tree.nodes[goal]['cost'], path] elif self.seg == 'suf': # path.append(self.init) paths[i] = [ self.tree.nodes[goal]['cost'] + np.linalg.norm(np.subtract(goal[0], self.init[0])), path ] return paths def mulp2sglp(self, point): """ convert multiple form point ((),(),(),...) to single form point () :param point: multiple points ((),(),(),...) :return: signle point () """ sp = [] for p in point: sp = sp + list(p) return tuple(sp) def sglp2mulp(self, point): """ convert single form point () to multiple form point ((), (), (), ...) :param point: single form point () :return: multiple form point ((), (), (), ...) """ mp = [] for i in range(self.robot): mp.append(point[i * self.dim:(i + 1) * self.dim]) return tuple(mp)
class tree(object): """ construction of prefix and suffix tree """ def __init__(self, n_robot, acpt, ts, buchi_graph, init, seg, step_size, no): """ :param acpt: accepting state :param ts: transition system :param buchi_graph: Buchi graph :param init: product initial state """ self.robot = n_robot self.acpt = acpt self.goals = [] self.ts = ts self.buchi_graph = buchi_graph self.init = init self.seg = seg self.step_size = step_size self.dim = len(self.ts['workspace']) uni_ball = [ 1, 2, 3.142, 4.189, 4.935, 5.264, 5.168, 4.725, 4.059, 3.299, 2.550 ] self.gamma = np.ceil( 4 * np.power(1 / uni_ball[self.robot * self.dim], 1. / (self.dim * self.robot))) # unit workspace self.tree = DiGraph(type='PBA', init=init) label = [] for i in range(self.robot): l = self.label(init[0][i]) # exists one sampled point lies within obstacles if l != '': l = l + '_' + str(i + 1) label.append(l) self.tree.add_node(init, cost=0, label=label) self.no = no def sample(self): """ sample point from the workspace :return: sampled point, tuple """ x_rand = [] for i in range(self.dim): x_rand.append(uniform(0, self.ts['workspace'][i])) return tuple(x_rand) def nearest(self, x_rand): """ find the nearest vertex in the tree :param: x_rand randomly sampled point form: single point () :return: nearest vertex form: single point () """ # min_dis = math.inf # x_nearest = x_rand # for vertex in self.tree.nodes: # x_vertex = self.mulp2sglp(vertex[0]) # dis = np.linalg.norm(np.subtract(x_rand, x_vertex)) # if dis < min_dis: # x_nearest = x_vertex # min_dis = dis # return x_nearest min_dis = math.inf q_nearest = [] for vertex in self.tree.nodes: x_vertex = self.mulp2sglp(vertex[0]) dis = np.linalg.norm(np.subtract(x_rand, x_vertex)) if dis < min_dis: q_nearest = list() q_nearest.append(vertex) min_dis = dis elif dis == min_dis: q_nearest.append(vertex) return q_nearest def steer(self, x_rand, x_nearest): """ steer :param: x_rand randomly sampled point form: single point () :param: x_nearest nearest point in the tree form: single point () :return: new point single point () """ #return np.asarray([0.8,0.4]) if np.linalg.norm(np.subtract(x_rand, x_nearest)) <= self.step_size: return x_rand else: return tuple( np.asarray(x_nearest) + self.step_size * (np.subtract(x_rand, x_nearest)) / np.linalg.norm(np.subtract(x_rand, x_nearest))) def extend(self, q_new, q_nearest, label, obs_check): for v_nearest in q_nearest: # print(v_nearest) if obs_check[(q_new[0], v_nearest[0])] and self.checkTranB( v_nearest[1], self.tree.nodes[v_nearest]['label'], q_new[1]): if 'T1_S4' in v_nearest: print(self.tree.nodes[v_nearest]['label']) if 'l3_1' in self.tree.nodes[v_nearest]['label']: print(2) cost = self.tree.nodes[v_nearest]['cost'] + np.linalg.norm( np.subtract(self.mulp2sglp(q_new[0]), self.mulp2sglp(v_nearest[0]))) self.tree.add_node(q_new, cost=cost, label=label) self.tree.add_edge(v_nearest, q_new) if self.seg == 'pre' and q_new[1] in self.acpt: q_n = list(list(self.tree.pred[q_new].keys())[0]) cost = self.tree.nodes[tuple(q_n)]['cost'] label = self.tree.nodes[tuple(q_n)]['label'] q_n[1] = q_new[1] q_n = tuple(q_n) self.tree.add_node(q_n, cost=cost, label=label) self.tree.add_edge(v_nearest, q_n) self.goals.append(q_n) # self.goals.append(q_new) # if self.seg == 'suf' and self.init in near_v and obs_check[(q_new[0], self.init[0])] and self.checkTranB(q_new[1], label, self.init[1]): if self.seg == 'suf' and self.obs_check( [self.init], q_new[0], label, 'final')[(q_new[0], self.init[0])] and self.checkTranB( q_new[1], label, self.init[1]): self.goals.append(q_new) break def obs_check(self, q_near, x_new, label, stage): """ check whether obstacle free along the line from x_near to x_new :param q_near: states in the near ball, tuple (mulp, buchi) :param x_new: new state form: multiple point :return: dict (x_near, x_new): true (obs_free) """ obs_check_dict = {} for x in q_near: obs_check_dict[(x_new, x[0])] = True flag = True # indicate whether break and jump to outer loop for r in range(self.robot): for i in range(1, 11): mid = tuple( np.asarray(x[0][r]) + i / 10. * np.subtract(x_new[r], x[0][r])) mid_label = self.label(mid) if mid_label != '': mid_label = mid_label + '_' + str(r + 1) if stage == 'reg' and ( 'o' in mid_label or (mid_label != self.tree.nodes[x]['label'][r] and mid_label != label[r])): # obstacle pass through one region more than once obs_check_dict[(x_new, x[0])] = False flag = False break elif stage == 'final' and ( 'o' in mid_label or (mid_label != self.tree.nodes[x]['label'][r] and mid_label != label[r] and mid_label in self.no)): obs_check_dict[(x_new, x[0])] = False flag = False break if not flag: break return obs_check_dict def label(self, x): """ generating the label of position state :param x: position :return: label """ # whether x lies within obstacle for (obs, boundary) in iter(self.ts['obs'].items()): if obs[1] == 'b' and np.linalg.norm(np.subtract( x, boundary[0:-1])) <= boundary[-1]: return obs[0] elif obs[1] == 'p': dictator = True for i in range(len(boundary)): if np.dot(x, boundary[i][0:-1]) + boundary[i][-1] > 0: dictator = False break if dictator == True: return obs[0] # whether x lies within regions for (regions, boundary) in iter(self.ts['region'].items()): if regions[1] == 'b' and np.linalg.norm( x - np.asarray(boundary[0:-1])) <= boundary[-1]: return regions[0] elif regions[1] == 'p': dictator = True for i in range(len(boundary)): if np.dot(x, np.asarray( boundary[i][0:-1])) + boundary[i][-1] > 0: dictator = False break if dictator == True: return regions[0] return '' def checkTranB(self, b_state, x_label, q_b_new): """ decide valid transition, whether b_state --L(x)---> q_b_new Algorithm2 in Chapter 2 Motion and Task Planning :param b_state: buchi state :param x_label: label of x :param q_b_new buchi state :return True satisfied """ b_state_succ = self.buchi_graph.succ[b_state] # q_b_new is not the successor of b_state if q_b_new not in b_state_succ: return False b_label = self.buchi_graph.edges[(b_state, q_b_new)]['label'] if self.t_satisfy_b(x_label, b_label): return True def t_satisfy_b(self, x_label, b_label): """ decide whether label of self.ts_graph can satisfy label of self.buchi_graph :param x_label: label of x :param b_label: label of buchi state :return t_s_b: true if satisfied """ t_s_b = True # split label with || b_label = b_label.split('||') for label in b_label: t_s_b = True # spit label with && atomic_label = label.split('&&') for a in atomic_label: a = a.strip() a = a.strip('(') a = a.strip(')') if a == '1': continue # whether ! in an atomic proposition if '!' in a: if a[1:] in x_label: t_s_b = False break else: if not a in x_label: t_s_b = False break # either one of || holds if t_s_b: return t_s_b return t_s_b def findpath(self, goals): """ find the path backwards :param goal: goal state :return: dict path : cost """ paths = OrderedDict() for i in range(len(goals)): goal = goals[i] path = [goal] s = goal while s != self.init: s = list(self.tree.pred[s].keys())[0] path.insert(0, s) if self.seg == 'pre': paths[i] = [self.tree.nodes[goal]['cost'], path] elif self.seg == 'suf': # path.append(self.init) paths[i] = [ self.tree.nodes[goal]['cost'] + np.linalg.norm(np.subtract(goal[0], self.init[0])), path ] return paths def mulp2sglp(self, point): """ convert multiple form point ((),(),(),...) to single form point () :param point: multiple points ((),(),(),...) :return: signle point () """ sp = [] for p in point: sp = sp + list(p) return tuple(sp) def sglp2mulp(self, point): """ convert single form point () to multiple form point ((), (), (), ...) :param point: single form point () :return: multiple form point ((), (), (), ...) """ mp = [] for i in range(self.robot): mp.append(point[i * self.dim:(i + 1) * self.dim]) return tuple(mp)
def dp_dag_general(G, r, U, cost_func, node_reward_key='r', debug=False): """ cost_func(node, D table, graph, [(cost at child , child)]) It should return cost as integer type(fixed point is used when appropriate) """ ns = G.nodes() if debug: print("total #nodes {}".format(len(ns))) A, D, BP = {}, {}, {} for n in ns: A[n] = {} # maximum sum of node u at a cost i A[n][0] = G.node[n][node_reward_key] D[n] = {} # set of nodes included corresponding to A[u][i] D[n][0] = {n} BP[n] = defaultdict(list) # backpointer corresponding to A[u][i] for n_i, n in enumerate( topological_sort(G, reverse=True)): # leaves come first if debug: print("#nodes processed {}".format(n_i)) children = G.neighbors(n) if debug: print('{}\'s children={}'.format(n, children)) reward = G.node[n][node_reward_key] if len(children) == 1: child = children[0] if debug: print('child={}'.format(child)) for i in A[child]: c = cost_func(n, D, G, [(i, child)]) assert isinstance(c, int) if c <= U: A[n][c] = A[child][i] + reward D[n][c] = D[child][i] | {n} BP[n][c] = [(child, i)] elif len(children) > 1: assert len(children) == 2 lchild, rchild = children for i in A[lchild]: c = cost_func(n, D, G, [(i, lchild)]) assert isinstance(c, int) if debug: print('n={}, D={}, cost_child_tuples={}'.format( n, D, [(i, lchild)]) ) print('c={}'.format(c)) if c <= U: if A[n].get(c) is None or A[lchild][i] + reward > A[n][c]: A[n][c] = A[lchild][i] + reward D[n][c] = D[lchild][i] | {n} BP[n][c] = [(lchild, i)] for i in A[rchild]: c = cost_func(n, D, G, [(i, rchild)]) assert isinstance(c, int) if c <= U: if A[n].get(c) is None or A[rchild][i] + reward > A[n][c]: A[n][c] = A[rchild][i] + reward D[n][c] = D[rchild][i] | {n} BP[n][c] = [(rchild, i)] for i in A[lchild]: for j in A[rchild]: c = cost_func(n, D, G, [(i, lchild), (j, rchild)]) assert isinstance(c, int) lset, rset = D[lchild][i], D[rchild][j] if c <= U: if (A[n].get(c) is None or A[lchild][i] + A[rchild][j] + reward > A[n][c]) and \ len(lset & rset) == 0: A[n][c] = A[lchild][i] + A[rchild][j] + reward D[n][c] = D[lchild][i] | D[rchild][j] | {n} BP[n][c] = [(lchild, i), (rchild, j)] if n == r: # no need to continue once we processed root break best_cost = max(xrange(U + 1), key=lambda i: A[r][i] if i in A[r] else float('-inf')) tree = DiGraph() tree.add_node(r) stack = [] if debug and len(stack) == 0: print('stack empty') print(A) for n, cost in BP[r][best_cost]: stack.append((r, n, cost)) while len(stack) > 0: if debug: print('stack size: {}'.format(len(stack))) print('stack: {}'.format(stack)) parent, child, cost = stack.pop(0) tree.add_edge(parent, child) # copy the attributes tree[parent][child] = G[parent][child] tree.node[parent] = G.node[parent] tree.node[child] = G.node[child] for grandchild, cost2 in BP[child][cost]: if debug: print(grandchild, cost2) stack.append((child, grandchild, cost2)) return tree
class unbiasedTree(object): """ unbiased tree for prefix and suffix parts """ def __init__(self, workspace, buchi, init_state, init_label, segment, para): """ initialization of the tree :param workspace: workspace :param buchi: buchi automaton :param init_state: initial location of the robots :param init_label: label generated by the initial location :param segment: prefix or suffix part :param para: parameters regarding unbiased-sampling method """ # parameters regarding workspace self.workspace = workspace.workspace self.dim = len(self.workspace) self.regions = workspace.regions self.obstacles = workspace.obs self.robot = buchi.number_of_robots # parameters regarding task self.buchi = buchi self.accept = self.buchi.buchi_graph.graph['accept'] self.init = init_state # initlizing the tree self.unbiased_tree = DiGraph(type='PBA', init=self.init) self.unbiased_tree.add_node(self.init, cost=0, label=init_label) # parameters regarding TL-RRT* algorithm self.goals = set() self.step_size = para['step_size'] self.segment = segment self.lite = para['is_lite'] # size of the ball used in function near uni_v = np.power(np.pi, self.robot * self.dim / 2) / math.gamma(self.robot * self.dim / 2 + 1) # self.gamma = np.ceil(4 * np.power(1 / uni_v, 1. / (self.dim * self.robot))) # unit workspace self.gamma = (2 + 1 / 4) * np.power( (1 + 0.0 / 4) * 2.5 / (self.dim * self.robot + 1) / (1 / 4) / (1 - 0.0) * 0.84 / uni_v, 1. / (self.dim * self.robot + 1)) # select final buchi states if self.segment == 'prefix': self.b_final = self.buchi.buchi_graph.graph['accept'][0] else: self.b_final = self.buchi.buchi_graph.graph['accept'] # threshold for collision avoidance self.threshold = para['threshold'] def sample(self): """ sample point from the workspace :return: sampled point, tuple """ x_rand = [] for i in range(self.dim): x_rand.append(uniform(0, self.workspace[i])) return tuple(x_rand) def collision_avoidance(self, x, robot_index): """ check whether robots with smaller index than robot_index collide with the robot of index robot_index :param x: position of robots :param robot_index: index of the specific robot :return: true if collision free """ for i in range(len(x)): if i != robot_index and np.fabs(x[i][0] - x[robot_index][0]) <= self.threshold and \ np.fabs(x[i][1] - x[robot_index][1]) <= self.threshold: return False return True def nearest(self, x_rand): """ find the nearest class of vertices in the tree :param: x_rand randomly sampled point form: single point () :return: nearest class of vertices form: single point () """ min_dis = math.inf q_p_nearest = [] for node in self.unbiased_tree.nodes: x = self.mulp2single(node[0]) dis = np.linalg.norm(np.subtract(x_rand, x)) if dis < min_dis: q_p_nearest = [node] min_dis = dis elif dis == min_dis: q_p_nearest.append(node) return q_p_nearest def steer(self, x_rand, x_nearest): """ steer :param: x_rand randomly sampled point form: single point () :param: x_nearest nearest point in the tree form: single point () :return: new point single point () """ if np.linalg.norm(np.subtract(x_rand, x_nearest)) <= self.step_size: return x_rand else: return tuple( map( tuple, np.asarray(x_nearest) + self.step_size * (np.subtract(x_rand, x_nearest)) / np.linalg.norm(np.subtract(x_rand, x_nearest)))) def extend(self, q_new, near_nodes, label, obs_check): """ add the new sate q_new to the tree :param: q_new: new state :param: near_nodes: near state :param: obs_check: check the line connecting two points are inside the freespace :return: the tree after extension """ added = False cost = np.inf q_min = () # loop over all nodes in near_nodes for node in near_nodes: if q_new != node and obs_check[(q_new[0], node[0])] and \ self.check_transition_b(node[1], self.unbiased_tree.nodes[node]['label'], q_new[1]): c = self.unbiased_tree.nodes[node]['cost'] \ + np.linalg.norm(np.subtract(self.mulp2single(q_new[0]), self.mulp2single(node[0]))) if c < cost: added = True q_min = node cost = c if added: self.unbiased_tree.add_node(q_new, cost=cost, label=label) self.unbiased_tree.add_edge(q_min, q_new) if self.segment == 'prefix' and q_new[1] in self.accept: q_n = list(list(self.unbiased_tree.pred[q_new].keys())[0]) cost = self.unbiased_tree.nodes[tuple(q_n)]['cost'] label = self.unbiased_tree.nodes[tuple(q_n)]['label'] q_n[1] = q_new[1] q_n = tuple(q_n) if q_n != q_min: self.unbiased_tree.add_node(q_n, cost=cost, label=label) self.unbiased_tree.add_edge(q_min, q_n) self.goals.add(q_n) # if self.segment == 'suffix' and \ # self.obstacle_check([self.init], q_new[0], label)[(q_new[0], self.init[0])] \ # and self.check_transition_b(q_new[1], label, self.init[1]): # self.goals.add(q_new) elif self.segment == 'suffix' and self.init[1] == q_new[1]: self.goals.add(q_new) return added def rewire(self, q_new, near_nodes, obs_check): """ :param: q_new: new state :param: near_nodes: states returned near :param: obs_check: check whether obstacle-free :return: the tree after rewiring """ for node in near_nodes: if obs_check[(q_new[0], node[0])] \ and self.check_transition_b(q_new[1], self.unbiased_tree.nodes[q_new]['label'], node[1]): c = self.unbiased_tree.nodes[q_new]['cost'] \ + np.linalg.norm(np.subtract(self.mulp2single(q_new[0]), self.mulp2single(node[0]))) delta_c = self.unbiased_tree.nodes[node]['cost'] - c # update the cost of node in the subtree rooted at the rewired node if delta_c > 0: self.unbiased_tree.remove_edge( list(self.unbiased_tree.pred[node].keys())[0], node) self.unbiased_tree.add_edge(q_new, node) edges = dfs_labeled_edges(self.unbiased_tree, source=node) for _, v, d in edges: if d == 'forward': self.unbiased_tree.nodes[v][ 'cost'] = self.unbiased_tree.nodes[v][ 'cost'] - delta_c def near(self, x_new): """ find the states in the near ball :param x_new: new point form: single point :return: p_near: near state, form: tuple (mulp, buchi) """ near_nodes = [] radius = min( self.gamma * np.power( np.log(self.unbiased_tree.number_of_nodes() + 1) / self.unbiased_tree.number_of_nodes(), 1. / (self.dim * self.robot)), self.step_size) for node in self.unbiased_tree.nodes: if np.linalg.norm(np.subtract(x_new, self.mulp2single( node[0]))) <= radius: near_nodes.append(node) return near_nodes def obstacle_check(self, near_node, x_new, label): """ check whether line from x_near to x_new is obstacle-free :param near_node: nodes returned by near function :param x_new: new position component :param label: label of x_new :return: a dictionary indicating whether the line connecting two points are obstacle-free """ obs_check = {} checked = set() for node in near_node: # whether the position component of nodes has been checked if node[0] in checked: continue checked.add(node[0]) obs_check[(x_new, node[0])] = True flag = True # indicate whether break and jump to outer loop for r in range(self.robot): # the line connecting two points crosses an obstacle for (obs, boundary) in iter(self.obstacles.items()): if LineString([Point(node[0][r]), Point(x_new[r])]).intersects(boundary): obs_check[(x_new, node[0])] = False flag = False break # no need to check further if not flag: break for (region, boundary) in iter(self.regions.items()): if LineString([Point(node[0][r]), Point(x_new[r])]).intersects(boundary) \ and region + '_' + str(r + 1) != label[r] \ and region + '_' + str(r + 1) != self.unbiased_tree.nodes[node]['label'][r]: obs_check[(x_new, node[0])] = False flag = False break # no need to check further if not flag: break return obs_check def get_label(self, x): """ generating the label of position component :param x: position :return: label """ point = Point(x) # whether x lies within obstacle for (obs, boundary) in iter(self.obstacles.items()): if point.within(boundary): return obs # whether x lies within regions for (region, boundary) in iter(self.regions.items()): if point.within(boundary): return region # x lies within unlabeled region return '' def check_transition_b(self, q_b, x_label, q_b_new): """ check whether q_b -- x_label ---> q_b_new :param q_b: buchi state :param x_label: label of x :param q_b_new: buchi state :return True if satisfied """ b_state_succ = self.buchi.buchi_graph.succ[q_b] # q_b_new is not the successor of b_state if q_b_new not in b_state_succ: return False # check whether label of x enables the transition truth = self.buchi.buchi_graph.edges[(q_b, q_b_new)]['truth'] if self.check_transition_b_helper(x_label, truth): return True return False def check_transition_b_helper(self, x_label, truth): """ check whether transition enabled with current generated label :param x_label: label of the current position :param truth: symbol enabling the transition :return: true or false """ if truth == '1': return True # all true propositions should be satisdied true_label = [ true_label for true_label in truth.keys() if truth[true_label] ] for label in true_label: if label not in x_label: return False # all fasle propositions should not be satisfied false_label = [ false_label for false_label in truth.keys() if not truth[false_label] ] for label in false_label: if label in x_label: return False return True def find_path(self, goals): """ find the path backwards :param goals: found all goal states :return: the path leading to the goal state and the corresponding cost """ paths = OrderedDict() for i in range(len(goals)): goals = list(goals) goal = goals[i] path = [goal] s = goal while s != self.init: s = list(self.unbiased_tree.pred[s].keys())[0] path.insert(0, s) if self.segment == 'prefix': paths[i] = [self.unbiased_tree.nodes[goal]['cost'], path] elif self.segment == 'suffix': path.append(self.init) paths[i] = [ self.unbiased_tree.nodes[goal]['cost'] + np.linalg.norm( np.subtract(self.mulp2single(goal[0]), self.mulp2single(self.init[0]))), path ] return paths def mulp2single(self, point): """ convert a point, which in the form of a tuple of tuple ((),(),(),...) to point in the form of a flat tuple :param point: point((position of robot 1), (position of robot2), (), ...) :return: point (position of robot1, position of robot2, ...) """ return tuple([p for r in point for p in r]) def single2mulp(self, point): """ convert a point in the form of flat tuple to point in the form of a tuple of tuple ((),(),(),...) :param point: point (position of robot1, position of robot2, ...) :return: point((position of robot 1), (position of robot2), (), ...) """ mp = [ point[i * self.dim:(i + 1) * self.dim] for i in range(self.robot) ] return tuple(mp)
def hoftask(init, buchi_graph, regions, r): h_task = DiGraph(type='subtask') try: label = to_dnf( buchi_graph.edges[(buchi_graph.graph['init'][0], buchi_graph.graph['init'][0])]['label'], True) except KeyError: # no self loop label = to_dnf('0') init_node = State((init, buchi_graph.graph['init'][0]), label) h_task.add_node(init_node) open_set = list() open_set.append((init_node, '0')) explore_set = list() # if q1 --label--> q2, [target(label), q1]:q2 succ_dict = dict() succ_dict[(init_node, '0')] = {buchi_graph.graph['init'][0]} task_group = dict() while open_set: curr = open_set.pop(0) # buchi state that curr corresponds to for q_target in succ_dict[curr]: try: label = to_dnf( buchi_graph.edges[(q_target, q_target)]['label'], True) except KeyError: # no self loop label = to_dnf('0') for q_b in buchi_graph.succ[q_target]: if q_b != q_target: # region corresponding to the label/word edge_label = (buchi_graph.edges[(q_target, q_b)]['label']) if edge_label == '(1)': x_set = [curr[0].x] elif '~' in edge_label and to_dnf(edge_label).nargs == { 1 }: # ~l3_1 if curr[0].x == regions[to_dnf( edge_label).args[0].name.split('_')[0]]: x_set = [(curr[0].x[0], curr[0].x[1] - r)] else: x_set = [curr[0].x] else: x_set = target(to_dnf(edge_label), regions) if x_set: for x in x_set: cand = State((x, q_target), label) # seems no need to check # if curr[0] != cand and not match(h_task, curr[0], cand): if not match(h_task, curr[0], cand): h_task.add_edge(curr[0], cand) try: task_group[curr[0].x].add((curr[0], cand)) except KeyError: task_group[curr[0].x] = {(curr[0], cand)} try: succ_dict[(cand, edge_label)].add(q_b) except KeyError: succ_dict[(cand, edge_label)] = {q_b} if (cand, edge_label) not in open_set and ( cand, edge_label) not in explore_set: open_set.append((cand, edge_label)) h_task.add_node(cand) explore_set.append(curr) return h_task, task_group