def test_single_rate_equivalent_simple_multirate( self ): # create a small simple multirate graph g = nx.DiGraph() g.add_node( 1, wcet = 2 ) g.add_node( 2, wcet = 3 ) g.add_edge( 1, 2, production = 2, consumption = 3 ) g.add_edge( 2, 1, production = 3, consumption = 2, tokens = 4 ) sdfg = core.SDFGraph(g) # transform to single rate equivalent hsdfg = single_rate_equivalent( sdfg ) self.assertIn( (1, 1), hsdfg.nodes() ) self.assertIn( (1, 2), hsdfg.nodes() ) self.assertIn( (1, 3), hsdfg.nodes() ) self.assertIn( (2, 1), hsdfg.nodes() ) self.assertIn( (2, 2), hsdfg.nodes() ) self.assertEqual( hsdfg.number_of_nodes(), 5 ) self.assertEqual( hsdfg.number_of_edges(), 5 ) self.assertIn( ((1, 2), (2, 1)), hsdfg.edges() ) self.assertIn( ((1, 3), (2, 2)), hsdfg.edges() ) self.assertIn( ((2, 2), (1, 1)), hsdfg.edges() ) self.assertIn( ((2, 2), (1, 2)), hsdfg.edges() ) self.assertIn( ((2, 1), (1, 3)), hsdfg.edges() ) toks = lambda a, b: hsdfg.get_edge_data( a, b, 0 ).get('tokens', 0) self.assertEqual( toks( (1, 2), (2, 1) ), 0 ) self.assertEqual( toks( (1, 3), (2, 2) ), 0 ) self.assertEqual( toks( (2, 2), (1, 1) ), 1 ) self.assertEqual( toks( (2, 2), (1, 2) ), 1 ) self.assertEqual( toks( (2, 1), (1, 3) ), 0 )
def test_strictly_periodic_feasible_schedule(self): # create a small simple multirate graph g = nx.DiGraph() g.add_node(1, wcet=2) g.add_node(2, wcet=3) g.add_edge(1, 2, production=2, consumption=3) g.add_edge(2, 1, production=3, consumption=2, tokens=4) sdfg = core.SDFGraph(g)
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.nodes[v].get('phases', 1) Tw = sdfg.nodes[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 test_single_rate_apx_cyclostatic( self ): # create a small simple multirate graph g = nx.DiGraph() g.add_node( 1, wcet = (2, 1) ) g.add_node( 2, wcet = (3, 4) ) g.add_edge( 1, 2, production = (2, 1), consumption = (3, 2) ) g.add_edge( 2, 1, production = (2, 3), consumption = (0, 3), tokens = 4 ) sdfg = core.SDFGraph(g) # transform to single rate equivalent hsdfg = single_rate_apx( sdfg ) toks = lambda a, b: hsdfg.get_edge_data( a, b, 0 ).get('tokens', 0) self.assertEqual( toks( 1, 2 ), -2 ) self.assertEqual( toks( 2, 1 ), 4 )
def generate_cycle(n = 3): # generate a rate-vector zs = generate_canonical_int_vector( n, 2, 10 ) g = nx.MultiDiGraph() for i in range( n ): g.add_node( i + 1, wcet = 1 ) g.add_edge( n, 1, production = zs[ n - 1 ], consumption = zs[ 0 ], gcd = gcd( zs[ n - 1 ], zs[ 0 ]) ) for i in range( 1, n ): prate = zs[ i - 1 ] crate = zs[ i ] g.add_edge( i, i + 1, production = prate, consumption = crate, gcd = gcd( prate, crate )) apx = transform.single_rate_apx( core.SDFGraph( g )) tokens = 0 for v, w, data in apx.edges_iter( data = True ): tokens += data.get( 'tokens', 0 ) add_tokens( g, 1 - tokens ) return zs, g
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 ) if __name__ == '__main__': seed( 42 ) rates, graph = generate_cycle( 9 ) true_period = 1 / sse.find_throughput( core.SDFGraph( graph )) print( "Actual period = {}".format( true_period )) sdfg = core.SDFGraph( graph ) apx = transform.single_rate_apx( sdfg ) for v in sdfg: # measure the impact of vectorizing v _, w, out_data = sdfg.out_edges( v, data = True )[ 0 ] u, _, in_data = sdfg.in_edges( v, data = True )[ 0 ] p_in, c_in, t_in = in_data['production'], in_data['consumption'], in_data.get('tokens', 0) p_out, c_out, t_out = out_data['production'], out_data['consumption'], out_data.get('tokens', 0) assert c_in == p_out, "rates must match" delta_apx( p_in, t_in, c_in, t_out, c_out )
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)
def periodic_state(graph, time, admissible=True): result = nx.MultiDiGraph() schedule = sched.strictly_periodic_schedule(graph, admissible) # compute completed and active firings firings = dict() for v, data in graph.nodes(data=True): start_v, period_v = schedule[v] wcets = data['wcet'] num_phases = data['phases'] assert num_phases == len(wcets) started_firings = math.floor((time - start_v) / period_v) + 1 completed_firings = math.floor( (time - wcets[0] - start_v) / (period_v * num_phases)) + 1 for i in range(1, num_phases): first_firing = start_v + i * period_v completed_firings = min( completed_firings, math.floor((time - wcets[i] - first_firing) / (period_v * num_phases)) + 1) # compute active firings idx = completed_productions future = dict() while start_time <= time: # firing with <0-based index> = idx has started but not completed before time # add it as an active firing future[completion_time] = future.get(completion_time, 0) + 1 idx += 1 start_time += period_v completion_time = max(completion_time, start_time + wcets[idx]) firings[v] = completed_productions, future result.add_node(v, wcet=wcets[completed_productions:], active=future) # compute marking for v, w, key, data in graph.edges(keys=True, data=True): crates = data['consumption'] prates = data['production'] tokens = data['tokens'] v_completed, _ = firings[v] w_completed, w_active = firings[w] completed_productions = v_completed started_consumptions = w_completed + sum(w_active.values()) print( "({}, {}): Production = {}, consumption = {}, tokens = {}".format( v, w, prates, crates, tokens)) if completed_productions > 0: tokens += prates.sum(0, completed_productions) else: tokens -= prates.sum(completed_productions, 0) if started_consumptions > 0: tokens -= crates.sum(0, started_consumptions) else: tokens += crates.sum(started_consumptions, 0) result.add_edge(v, w, key, production=prates[completed_productions:], consumption=crates[started_consumptions:], tokens=tokens) return core.SDFGraph(result)