def sample_crates(vector, offset, period): """ Returns the consumption rates of actor[offset] if the consuming actor is unfolded period times. NOTE: offset is 0-based """ vector = cyclic(vector) pattern = [None] * (len(vector) // gcd(len(vector), period)) for i in range(len(pattern)): start = offset + 1 + (i - 1) * period end = start + period pattern[i] = vector.sum(start, end) token_delta = pattern[0] - vector.sum(stop=offset + 1) return token_delta, cyclic(pattern)
def sample_prates(vector, offset, period): """ Returns the production rates of unfolded actor [offset] if the producing actor is unfolded period times. NOTE: offset is 0-based """ vector = cyclic(vector) pattern = [None] * (len(vector) // gcd(len(vector), period)) for i in range(len(pattern)): start = offset + i * period end = start + period pattern[i] = vector.sum(start, end) token_delta = vector.sum(stop=offset) return token_delta, cyclic(pattern)
def single_rate_equivalent(sdfg): hsdfg = nx.MultiDiGraph() if sdfg.is_multigraph() else nx.DiGraph() multi = nx.MultiDiGraph(sdfg) q = sdfg.repetition_vector() for v in sdfg: assert 'wcet' in sdfg.node[ v], "Missing 'wcet' attribute for node {}".format(v) wcets = cyclic(sdfg.node[v]['wcet']) for i in range(q[v]): hsdfg.add_node((v, i + 1), wcet=wcets[i % len(wcets)]) for u, _, key, data in multi.in_edges_iter(v, data=True, keys=True): prates = data.get('production') crates = data.get('consumption') tokens = data.get('tokens') for j in range(q[v]): i = predecessor(j + 1, **data) if sdfg.is_multigraph(): hsdfg.add_edge((u, (i - 1) % q[u] + 1), (v, j + 1), key=key, tokens=(q[u] - i) // q[u]) else: hsdfg.add_edge((u, (i - 1) % q[u] + 1), (v, j + 1), tokens=(q[u] - i) // q[u]) assert hsdfg.number_of_nodes() == sum(q.values( )), "Size of HSDF graph must be equal to sum of repetition vector" return hsdfg
def multi_rate_equivalent(sdfg): result = nx.MultiDiGraph() for v, data in sdfg.nodes_iter(data=True): phi = data.get('phases', 1) for i in range(phi): result.add_node((v, i + 1), wcet=data.get('wcet', core.cyclic(0))[i]) for v, w, data in sdfg.edges_iter(data=True): Tv = sdfg.node[v].get('phases', 1) Tw = sdfg.node[w].get('phases', 1) prates = data.get('production') crates = data.get('consumption') tokens = data.get('tokens') plen = len(prates) assert Tv == len(prates) assert Tw == len(crates) # consuming actor in unfolded graph has a single phase # and consumption rate equal to crates.sum() csum = crates.sum() # sum of production rates in multi-rate equivalent of unfolded graph psum = prates.sum() gvw = gcd(csum, psum) # iterate consuming actors for j in range(Tw): # determine which incoming channels must be added # see Algorithm ? in PhD thesis for i in range(plen): delta_i_n = tokens + prates.sum(stop=i + 1) - crates.sum(stop=j + 1) delta_i1_n = tokens + prates.sum(stop=i) - crates.sum(stop=j + 1) sol_min = (gvw - delta_i_n - 1) // gvw sol_max = (gvw - delta_i1_n - 1) // gvw if sol_min < sol_max: # add channel extra_data = dict() if 'var' in data: extra_data['var'] = data['var'] result.add_edge((v, i + 1), (w, j + 1), production=psum, consumption=csum, tokens=tokens + prates.sum(stop=i) + crates.sum(start=j + 1), **extra_data) return core.SDFGraph(result)
def vectorize( ps, t, cs ): periods = cs.sum() // gcd( ps.sum(), cs.sum() ) vs = list() for p in ps * periods: t += p if t >= cs[0]: # consume nops =-1 csum = 0 while t >= cs[0]: t = t - cs[ 0 ] csum += cs[ 0 ] cs = cs[1:] nops += 1 vs.append( csum ) for _ in range( nops ): vs.append( 0 ) return core.cyclic( vs )
def unfold(sdfg, dct=None, **periods): if dct is not None: periods.update(dct) result = nx.MultiDiGraph() for v, data in sdfg.nodes_iter(data=True): Tv = periods.get(v, 1) for i in range(Tv): result.add_node((v, i + 1) if Tv > 1 else v, wcet=data.get('wcet', core.cyclic(0))[i::Tv]) for v, w, data in sdfg.edges_iter(data=True): Tv = periods.get(v, 1) Tw = periods.get(w, 1) prates = data.get('production', core.cyclic(1)) crates = data.get('consumption', core.cyclic(1)) tokens = data.get('tokens', 0) plen = len(prates) # consuming actor in unfolded graph has a single phase # and consumption rate equal to periods_w * crates.sum() csum = crates.sum() * Tw // gcd(Tw, len(crates)) # sum of production rates in multi-rate equivalent of unfolded graph psum = prates.sum() * Tv // gcd(Tv, len(prates)) gm = gcd(csum, psum) gvw = gcd(csum, prates.sum()) # multiplicative inverse of (psum // gvw) modulo (gm // gvw) g_cd, mulinv, _ = xgcd(prates.sum() // gvw, gm // gvw) assert g_cd == 1 # iterate consuming actors for j in range(Tw): # determine which incoming channels must be added incoming = set() # see Algorithm ? in PhD thesis sols = set() for n0 in range(len(crates) // gcd(len(crates), Tw)): for i0 in range(plen): delta_i_n = tokens + prates.sum(stop=i0 + 1) - crates.sum( stop=j + 1 + n0 * Tw) delta_i1_n = tokens + prates.sum(stop=i0) - crates.sum( stop=j + 1 + n0 * Tw) sol_min = (gvw - delta_i_n - 1) // gvw sol_max = (gvw - delta_i1_n - 1) // gvw for sol in range(sol_min, sol_max): s = (i0 + sol * mulinv * plen) % gcd( Tv, plen * gm // gvw) sols.add(s) for i_residue in sols: for i0 in range(i_residue, Tv, gcd(Tv, plen * gm // gvw)): incoming.add(i0) # compute consumption rates for incoming channels tokens_c, incoming_crates = sample_crates(crates, j, Tw) # add incoming channels: for i in incoming: # compute production rates tokens_p, incoming_prates = sample_prates(prates, i, Tv) extra_data = dict() if 'var' in data: extra_data['var'] = data['var'] # add channel vi = (v, i + 1) if Tv > 1 else v wj = (w, j + 1) if Tw > 1 else w result.add_edge(vi, wj, production=incoming_prates, consumption=incoming_crates, tokens=tokens + tokens_c + tokens_p, **extra_data) return core.SDFGraph(result)