def lcm_list( xs ): l = 1 for x in xs: l = lcm( l, x ) return l
def add_edge(self, u, v, attr_dict=None, **attr): assert attr_dict is None, "FIXME: add_edge does not deal with attr_dict" production = attr['production'] = SDFGraph.validate_vector( (u, v), attr.get('production', 1), 'production rate') assert type(production) is Cyclic # update phases try: phases_u = self.nodes[u].get('phases', 1) self.nodes[u]['phases'] = lcm(len(production), phases_u) except KeyError: super().add_node(u, wcet=Cyclic(0), phases=len(production), period=1) consumption = attr['consumption'] = SDFGraph.validate_vector( (u, v), attr.get('consumption', 1), 'consumption rates') assert type(consumption) is Cyclic # update phases try: phases_v = self.nodes[v].get('phases', 1) self.nodes[v]['phases'] = lcm(len(consumption), phases_v) except KeyError: super().add_node(v, wcet=Cyclic(0), phases=len(consumption), period=1) try: tokens = int(attr.get('tokens', 0)) assert tokens >= 0 attr['tokens'] = tokens except ValueError: raise SDFParseError( "Channel {} has an invalid number of tokens: {}".format( (u, v), attr['tokens'])) if (self.has_edge(u, v)): # FIXME: replace parallel edges by single edge # insert auxilary node pr = attr.get('production') cr = attr.get('consumption') toks = attr.get('tokens') aux, c = "aux_0", 0 while aux in self.nodes: c += 1 aux = "aux_{}".format(c) self.add_node(aux, wcet=0) self.add_edge(u, aux, production=pr, consumption=1, tokens=toks) self.add_edge(aux, v, production=1, consumption=cr) print("WARNING: added node {} to prevent parallel edge ({}, {})". format(aux, u, v)) else: super().add_edge(u, v, attr_dict, **attr) if 'capacity' in attr: try: capacity = int(attr['capacity']) if capacity < tokens: raise Exception( "Tokens on channel {} violates specified capacity". format((u, v))) del self.get_edge_data(u, v)['capacity'] # create reverse channel (w, v) self.add_edge(v, u, production=attr['consumption'], consumption=attr['production'], tokens=capacity - tokens) except ValueError: del self.get_edge_data(u, v)['capacity'] # create reverse channel (w, v) self.add_edge(v, u, production=attr['consumption'], consumption=attr['production'], var=attr['capacity'])
def __check_consistency(self): node_lcm_rates = {} for v, w, data in self.edges(data=True): prates = data.get('production') crates = data.get('consumption') if prates.sum() <= 0 or len(prates) == 0: raise Exception( "({}, {}) has an invalid production rate vector (sum = {})" .format(v, w, prates.sum())) if crates.sum() <= 0 or len(crates) == 0: raise Exception( "({}, {}) has an invalid consumption rate vector (sum = {})" .format(v, w, crates.sum())) avg_prate = Fraction(prates.sum(), len(prates)) avg_crate = Fraction(crates.sum(), len(crates)) node_lcm_rates[v] = lcm( lcm(node_lcm_rates.get(v, 1), prates.sum()), avg_prate.denominator) if node_lcm_rates[v] == 0: raise Exception( "LCM of rates associated with actor {} is zero", v) node_lcm_rates[w] = lcm( lcm(node_lcm_rates.get(w, 1), crates.sum()), avg_crate.denominator) if node_lcm_rates[w] == 0: raise Exception( "LCM of rates associated with actor {} is zero", w) fractional_q = {} undirected = self.to_undirected() for v, w, key in dfs_edges(undirected): # if self does not cantain (v, w), it must contains (w, v) if not self.has_edge(v, w, key): v, w = w, v # get rates of channel (v, w) data = self.get_edge_data(v, w, key) p_vw, c_vw = data.get('production'), data.get('consumption') v_period = self.nodes[v]['period'] w_period = self.nodes[w]['period'] # if there is an edge (w, v) in sdfg, check whether it's consistent with (v, w) if v != w and self.has_edge(w, v): data_reverse = self.get_edge_data(w, v, 0) p_wv, c_wv = data_reverse.get('production'), data_reverse.get( 'consumption') if p_vw.sum(0, v_period) * p_wv.sum(0, w_period) != c_vw.sum( 0, w_period) * c_wv.sum(0, v_period): raise Exception("Inconsistent cycle: [{},{},{}]".format( v, w, v)) # check consistency of (v, w) with rest of the self if (v in fractional_q) and (w in fractional_q): if p_vw.sum(0, v_period) * fractional_q[v] != c_vw.sum( 0, w_period) * fractional_q[w]: raise Exception("Inconsistent edge: ({},{})".format(v, w)) elif (v in fractional_q): fractional_q[w] = fractional_q[v] * Fraction( p_vw.sum(0, v_period), c_vw.sum(0, w_period)) elif (w in fractional_q): fractional_q[v] = fractional_q[w] * Fraction( c_vw.sum(0, w_period), p_vw.sum(0, v_period)) else: fractional_q[v] = 1 fractional_q[w] = Fraction(p_vw.sum(0, v_period), c_vw.sum(0, w_period)) # compute the LCM of the denominators in the fractional repetition vector m = 1 for f in fractional_q.values(): m = lcm(m, Fraction(f).denominator) # multiply all fractions with the LCM of their denominators to obtain the repetition vector q = {} tpi = 1 for k in fractional_q: f = fractional_q[k] q[k] = int(f * m * self.phases(k)) assert (node_lcm_rates[k] * f * m).denominator == 1 tpi = lcm(tpi, (node_lcm_rates[k] * f * m).numerator) s = {} for v, w, data in self.edges(data=True): prate, crate = data.get('production'), data.get('consumption') s[(v, w)] = (tpi * len(prate)) // (q[v] * prate.sum()) return q, s, tpi
def __build(self, graph): for v, data in graph.nodes(data=True): wcet = SDFGraph.__validate_vector(v, data.get('wcet', 0), 'wcet') super().add_node(v, wcet=wcet, phases=len(wcet)) for u, v, data in graph.edges(data=True): production = SDFGraph.__validate_vector((u, v), data.get('production', 1), 'production rate') consumption = SDFGraph.__validate_vector( (u, v), data.get('consumption', 1), 'consumption rates') g_uv = gcd(production.sum(), consumption.sum()) try: tokens = int(data.get('tokens', 0)) except ValueError: raise SDFParseError( "Channel {} has an invalid token specification: {}".format( (u, v), data['tokens'])) super().add_edge(u, v, production=production, consumption=consumption, tokens=tokens, gcd=g_uv, pred=SDFGraph.__predecessor_fun( production, consumption, tokens)) if 'capacity' in data: try: capacity = int(data['capacity']) if capacity < tokens: raise Exception( "More tokens on channel {} than specified capacity" .format((u, v))) # create reverse channel (w, v) super().add_edge(v, u, production=consumption, consumption=production, tokens=capacity - tokens, gcd=g_uv, pred=SDFGraph.__predecessor_fun( consumption, production, capacity - tokens)) # succ = SDFGraph.__successor_fun( consumption, production, capacity - tokens )) except ValueError: # create reverse channel (w, v) raise SDFParseError( "Channel {} has an invalid capacity specification: {}". format((u, v), data['capacity'])) # update phases for v, data in self.nodes(data=True): phases = data['phases'] for u, _, edge_data in self.in_edges(v, True): phases = lcm(phases, len(edge_data['consumption'])) for _, w, edge_data in self.out_edges(v, True): phases = lcm(phases, len(edge_data['production'])) data['period'] = data['phases'] = phases