def add_node(self, n, attr_dict=None, check=True, **attr): """Use a L{TypedDict} as attribute dict. Overrides C{networkx.MultiDiGraph.add_node}, see that for details. Log warning if node already exists. All other functionality remains the same. @param check: if True and untyped keys are passed, then raise C{AttributeError}. """ # avoid multiple additions if n in self: logger.debug('Graph already has node: ' + str(n)) attr_dict = self._update_attr_dict_with_attr(attr_dict, attr) # define typed dict typed_attr = TypedDict() typed_attr.set_types(self._node_label_types) typed_attr.update(copy.deepcopy(self._node_label_defaults)) # type checking happens here typed_attr.update(attr_dict) logger.debug('node typed_attr: ' + str(typed_attr)) self._check_for_untyped_keys(typed_attr, self._node_label_types, check) nx.MultiDiGraph.add_node(self, n, attr_dict=typed_attr)
def add_edge(self, u, v, key=None, attr_dict=None, check=True, **attr): """Use a L{TypedDict} as attribute dict. Overrides C{networkx.MultiDiGraph.add_edge}, see that for details. - Raise ValueError if C{u} or C{v} are not already nodes. - Raise Exception if edge (u, v, {}) exists. - Log warning if edge (u, v, attr_dict) exists. - Raise ValueError if C{attr_dict} contains typed key with invalid value. - Raise AttributeError if C{attr_dict} contains untyped keys, unless C{check=False}. Each label defines a different labeled edge. So to "change" the label, either: - remove the edge with this label, then add a new one, or - find the edge key, then use subscript notation: C{G[i][j][key]['attr_name'] = attr_value} Notes ===== 1. Argument C{key} has been removed compared to C{networkx.MultiDiGraph.add_edge}, because edges are defined by their labeling, i.e., multiple edges with same labeling are not allowed. @param check: raise C{AttributeError} if C{attr_dict} has untyped attribute keys, otherwise warn """ # legacy if 'check_states' in attr: msg = 'saw keyword argument: check_states ' +\ 'which is no longer available, ' +\ 'firstly add the new nodes.' logger.warning(msg) # check nodes exist if u not in self.succ: raise ValueError('Graph does not have node u: ' + str(u)) if v not in self.succ: raise ValueError('Graph does not have node v: ' + str(v)) attr_dict = self._update_attr_dict_with_attr(attr_dict, attr) # define typed dict typed_attr = TypedDict() typed_attr.set_types(self._edge_label_types) typed_attr.update(copy.deepcopy(self._edge_label_defaults)) # type checking happens here typed_attr.update(attr_dict) logger.debug('Given: attr_dict = ' + str(attr_dict)) logger.debug('Stored in: typed_attr = ' + str(typed_attr)) # may be possible to speedup using .succ existing_u_v = self.get_edge_data(u, v, default={}) if dict() in existing_u_v.values(): msg = ( 'Unlabeled transition: ' 'from_state-> to_state already exists,\n' 'where:\t from_state = ' + str(u) + '\n' 'and:\t to_state = ' + str(v) + '\n') raise Exception(msg) # check if same labeled transition exists if attr_dict in existing_u_v.values(): msg = ( 'Same labeled transition:\n' 'from_state---[label]---> to_state\n' 'already exists, where:\n' '\t from_state = ' + str(u) + '\n' '\t to_state = ' + str(v) + '\n' '\t label = ' + str(typed_attr) + '\n') warnings.warn(msg) logger.warning(msg) return # self._breaks_determinism(from_state, labels) self._check_for_untyped_keys(typed_attr, self._edge_label_types, check) # the only change from nx in this clause is using TypedDict logger.debug('adding edge: ' + str(u) + ' ---> ' + str(v)) if v in self.succ[u]: msg = 'there already exist directed edges with ' +\ 'same end-points' logger.debug(msg) keydict = self.adj[u][v] # find a unique integer key if key is None: key = len(keydict) while key in keydict: key -= 1 datadict = keydict.get(key, typed_attr) datadict.update(typed_attr) keydict[key] = datadict else: logger.debug('first directed edge between these nodes') # selfloops work this way without special treatment key = 0 keydict = {key: typed_attr} self.succ[u][v] = keydict self.pred[v][u] = keydict
def add_edge(self, u, v, key=None, attr_dict=None, check=True, **attr): """Use a L{TypedDict} as attribute dict. Overrides C{networkx.MultiDiGraph.add_edge}, see that for details. - Raise ValueError if C{u} or C{v} are not already nodes. - Raise Exception if edge (u, v, {}) exists. - Log warning if edge (u, v, attr_dict) exists. - Raise ValueError if C{attr_dict} contains typed key with invalid value. - Raise AttributeError if C{attr_dict} contains untyped keys, unless C{check=False}. Each label defines a different labeled edge. So to "change" the label, either: - remove the edge with this label, then add a new one, or - find the edge key, then use subscript notation: C{G[i][j][key]['attr_name'] = attr_value} Notes ===== @param check: raise C{AttributeError} if C{attr_dict} has untyped attribute keys, otherwise warn """ # legacy if 'check_states' in attr: msg = 'saw keyword argument: check_states ' +\ 'which is no longer available, ' +\ 'firstly add the new nodes.' logger.warning(msg) # check nodes exist if u not in self._succ: raise ValueError('Graph does not have node u: ' + str(u)) if v not in self._succ: raise ValueError('Graph does not have node v: ' + str(v)) attr_dict = self._update_attr_dict_with_attr(attr_dict, attr) # define typed dict typed_attr = TypedDict() typed_attr.set_types(self._edge_label_types) typed_attr.update(copy.deepcopy(self._edge_label_defaults)) # type checking happens here typed_attr.update(attr_dict) existing_u_v = self.get_edge_data(u, v, default={}) if dict() in existing_u_v.values(): msg = ( 'Unlabeled transition: ' 'from_state-> to_state already exists,\n' 'where:\t from_state = ' + str(u) + '\n' 'and:\t to_state = ' + str(v) + '\n') raise Exception(msg) # check if same labeled transition exists if attr_dict in existing_u_v.values(): msg = ( 'Same labeled transition:\n' 'from_state---[label]---> to_state\n' 'already exists, where:\n' '\t from_state = ' + str(u) + '\n' '\t to_state = ' + str(v) + '\n' '\t label = ' + str(typed_attr) + '\n') logger.warning(msg) return # self._breaks_determinism(from_state, labels) self._check_for_untyped_keys(typed_attr, self._edge_label_types, check) # the only change from nx in this clause is using TypedDict logger.debug('adding edge: ' + str(u) + ' ---> ' + str(v)) if key is None: key = self.new_edge_key(u, v) if v in self._succ[u]: keydict = self._adj[u][v] datadict = keydict.get(key, typed_attr) datadict.update(typed_attr) nx.MultiDiGraph.add_edge(self, u, v, key, **datadict) else: # selfloops work this way without special treatment nx.MultiDiGraph.add_edge(self, u, v, **typed_attr)
def find(self, from_states=None, to_states=None, with_attr_dict=None, typed_only=False, **with_attr): """Find all edges between given states with given labels. Instead of having two separate methods to: - find all labels of edges between given states (s1, s2) - find all transitions (s1, s2, L) with given label L, possibly from some given state s1, i.e., the edges leading to the successor states Post(s1, a) = Post(s1) restricted by action a this method provides both functionalities. Preimage under edge labeling function L of given label, intersected with given subset of edges:: L^{-1}(desired_label) \\cap (from_states x to_states) See Also ======== L{add}, L{add_adj} @param from_states: edges must start from this subset of states @type from_states: - iterable of existing states, or - None (no constraint, default) @param to_states: edges must end in this subset of states @type to_states: - iterable of existing states, or - None (no constraint, default) @param with_attr_dict: edges must be annotated with these labels @type with_attr_dict: - {label_type : desired_label_value, ...}, or - None (no constraint, default) @param with_attr: label type-value pairs, take precedence over C{desired_label}. @return: set of transitions = labeled edges:: (from_state, to_state, label) such that:: (from_state, to_state ) in from_states x to_states @rtype: list of transitions:: = list of labeled edges = [(from_state, to_state, label),...] where: - C{from_state} in C{from_states} - C{to_state} in C{to_states} - C{label}: dict """ if with_attr_dict is None: with_attr_dict = with_attr try: with_attr_dict.update(with_attr) except: raise TypeError('with_attr_dict must be a dict') found_transitions = [] u_v_edges = self.graph.edges(nbunch=from_states, data=True) if to_states is not None: u_v_edges = [(u, v, d) for u, v, d in u_v_edges if v in to_states] for u, v, attr_dict in u_v_edges: ok = True if not with_attr_dict: logger.debug('Any label is allowed.') elif not attr_dict: logger.debug('No labels defined.') else: logger.debug('Checking guard.') typed_attr = TypedDict() typed_attr.set_types(self.graph._edge_label_types) typed_attr.update(attr_dict) ok = label_is_desired(typed_attr, with_attr_dict) if ok: logger.debug('Transition label matched desired label.') transition = (u, v, dict(attr_dict)) found_transitions.append(transition) return found_transitions
def find(self, states=None, with_attr_dict=None, **with_attr): """Filter by desired states and by desired state labels. Examples ======== Assume that the system is: >>> import transys as trs >>> ts = trs.FTS() >>> ts.atomic_propositions.add('p') >>> ts.states.add('s0', ap={'p'}) - To find the label of a single state C{'s0'}: >>> a = ts.states.find(['s0'] ) >>> (s0_, label) = a[0] >>> print(label) {'ap': set(['p'])} - To find all states with a specific label C{{'p'}}: >>> ts.states.add('s1', ap={'p'}) >>> b = ts.states.find(with_attr_dict={'ap':{'p'} } ) >>> states = [state for (state, label_) in b] >>> print(set(states) ) {'s0', 's1'} - To find all states in subset C{M} labeled with C{{'p'}}: >>> ts.states.add('s2', ap={'p'}) >>> M = {'s0', 's2'} >>> b = ts.states.find(M, {'ap': {'p'} } ) >>> states = [state for (state, label_) in b] >>> print(set(states) ) {'s0', 's2'} @param states: subset of states over which to search @type states: 'any' (default) | iterable of valid states | single valid state @param with_attr_dict: label with which to filter the states @type with_attr_dict: {sublabel_type : desired_sublabel_value, ...} | leave empty, to allow any state label (default) @param with_attr: label key-value pairs which take precedence over C{with_attr_dict}. @rtype: list of labeled states @return: [(C{state}, C{label}),...] where: - C{state} \\in C{states} - C{label}: dict """ if with_attr_dict is None: with_attr_dict = with_attr else: try: with_attr_dict.update(with_attr) except AttributeError: raise Exception('with_attr_dict must be a dict') if states is not None: # singleton check if states in self: state = states msg = ( 'LabeledStates.find got single state: ' + str(state) + '\n' 'instead of Iterable of states.\n') states = [state] msg += 'Replaced given states = ' + str(state) msg += ' with states = ' + str(states) logger.debug(msg) found_state_label_pairs = [] for state, attr_dict in self.graph.nodes(data=True): logger.debug('Checking state_id = ' + str(state) + ', with attr_dict = ' + str(attr_dict)) if states is not None: if state not in states: logger.debug('state_id = ' + str(state) + ', not desired.') continue msg = ( 'Checking state label:\n\t attr_dict = ' + str(attr_dict) + '\n vs:\n\t desired_label = ' + str(with_attr_dict)) logger.debug(msg) if not with_attr_dict: logger.debug('Any label acceptable.') ok = True else: typed_attr = TypedDict() typed_attr.set_types(self.graph._node_label_types) typed_attr.update(attr_dict) ok = label_is_desired(typed_attr, with_attr_dict) if ok: logger.debug('Label Matched:\n\t' + str(attr_dict) + ' == ' + str(with_attr_dict)) state_label_pair = (state, dict(attr_dict)) found_state_label_pairs.append(state_label_pair) else: logger.debug('No match for label---> state discarded.') return found_state_label_pairs
def setUp(self): d = TypedDict() d.set_types({'animal': {'dog', 'cat'}}) self.d = d
def setUp(self): d = TypedDict() d.set_types({"animal": {"dog", "cat"}}) self.d = d