def iteration(self): _, unconnected_res = get_missing_connections(self.context) if not unconnected_res: # we are done return True, [] else: actions = self.generate_actions() return False, actions
def cndp_remove_one_child(cndp, to_remove): """ Removes a child from NDP. Assumes ndp connected. Dangling arrows are substituted with top/bottom. """ print('removing %r' % to_remove) cndp.check_fully_connected() name2ndp = cndp.get_name2ndp() assert to_remove in name2ndp connections = cndp.get_connections() fnames = cndp.get_fnames() rnames = cndp.get_rnames() name2ndp_i = dict((k, v) for k, v in name2ndp.items() if k != to_remove) filter_c = lambda c: not c.involves_any_of_these_nodes([to_remove]) connections_i = filter(filter_c, connections) ndp2 = CompositeNamedDP.from_parts(name2ndp=name2ndp_i, connections=connections_i, fnames=fnames, rnames=rnames) unconnected_fun, unconnected_res = get_missing_connections(ndp2.context) for r in [CResource(*_) for _ in unconnected_res]: R = ndp2.context.get_rtype(r) values = R.get_maximal_elements() dp = LimitMaximals(R, values) new_ndp = dpwrap(dp, 'limit', []) name = ndp2.context.new_name('_limit_r') ndp2.context.add_ndp(name, new_ndp) c = Connection(dp2=name, s2='limit', dp1=r.dp, s1=r.s) ndp2.context.add_connection(c) for f in [CFunction(*_) for _ in unconnected_fun]: F = ndp2.context.get_ftype(f) values = F.get_minimal_elements() dp = ConstantMinimals(F, values) new_ndp = dpwrap(dp, [], 'limit') name = ndp2.context.new_name('_limit_f') ndp2.context.add_ndp(name, new_ndp) c = Connection(dp1=name, s1='limit', dp2=f.dp, s2=f.s) ndp2.context.add_connection(c) ndp2.check_fully_connected() return ndp2
def __init__(self, opt, options, context, executed, forbidden, lower_bounds, ur, creation_order): print('CREATED %s' % creation_order) self.opt = opt self.options = options self.context = context self.executed = executed self.forbidden = forbidden self.lower_bounds = lower_bounds self.ur = ur if do_extra_checks(): tu = get_types_universe() # We expect that for each unconnected resource, we have a lower bound _unconnected_fun, unconnected_res = get_missing_connections( self.context) for dp, s in unconnected_res: r = CResource(dp, s) if not r in lower_bounds: msg = 'There is no lower bound for this resource.' raise_desc(ValueError, msg, r=r, lower_bounds=lower_bounds) r_ur = lower_bounds[r] R = self.context.get_rtype(r) tu.check_equal(r_ur.P, R) # make sure we don't have extra for r in lower_bounds: assert isinstance(r, CResource), r R = self.context.get_rtype(r) assert (r.dp, r.s) in unconnected_res, (r, unconnected_res) self.num_connection_options = self._compute_connection_options() self.hash = self._compute_hash() self._msg = "" for r, lb in lower_bounds.items(): R = self.context.get_rtype(r) self.info('lb %s >= %s' % (r, lb), quiet=True) self.num_resources_need_connecting = self.compute_num_resources_need_connecting( ) self.creation_order = creation_order
def _compute_connection_options(self): unconnected_fun, unconnected_res = get_missing_connections(self.context) n = 0 usd = parse_poset('USD') for (dp, s) in unconnected_res: r = CResource(dp, s) R = self.context.get_rtype(r) foptions = get_compatible_unconnected_functions(R, self.context, unconnected_fun) ok = [] for f in foptions: if R == usd and would_introduce_cycle(self.context, r=r, f=f): print('skipping %r - %r because it adds a cycle' % (r, f)) continue ok.append(f) unconnected_fun.remove((f.dp, f.s)) if ok: n += 1 return n
def __init__(self, opt, options, context, executed, forbidden, lower_bounds, ur, creation_order): print('CREATED %s' % creation_order) self.opt = opt self.options = options self.context = context self.executed = executed self.forbidden = forbidden self.lower_bounds = lower_bounds self.ur = ur if do_extra_checks(): tu = get_types_universe() # We expect that for each unconnected resource, we have a lower bound _unconnected_fun, unconnected_res = get_missing_connections(self.context) for dp, s in unconnected_res: r = CResource(dp, s) if not r in lower_bounds: msg = 'There is no lower bound for this resource.' raise_desc(ValueError, msg, r=r, lower_bounds=lower_bounds) r_ur = lower_bounds[r] R = self.context.get_rtype(r) tu.check_equal(r_ur.P, R) # make sure we don't have extra for r in lower_bounds: assert isinstance(r, CResource), r R = self.context.get_rtype(r) assert (r.dp, r.s) in unconnected_res, (r, unconnected_res) self.num_connection_options = self._compute_connection_options() self.hash = self._compute_hash() self._msg = "" for r, lb in lower_bounds.items(): R = self.context.get_rtype(r) self.info('lb %s >= %s' % (r, lb), quiet=True) self.num_resources_need_connecting = self.compute_num_resources_need_connecting() self.creation_order = creation_order
def get_lower_bound_ndp(context): """ We create an NDP where each open resource becomes a new resource. and all the functions are given a lower bound of 0. The new resources are given a name like: dummy<i> """ context = clone_context(context) unconnected_fun, unconnected_res = get_missing_connections(context) # let's remove the new resources that are unconnected for rname, name, _ndp in context.iterate_new_resources(): if (name, rname) in unconnected_fun: unconnected_fun.remove((name, rname)) del context.names[name] context.rnames.remove(rname) # create a new resource for each unconnected resource resource2var = {} # CResource -> str for dp, s in unconnected_res: r = CResource(dp, s) R = context.get_rtype(r) rname = 'dummy%d' % len(resource2var) context.add_ndp_res_node(rname, R) c = Connection(dp1=dp, s1=s, dp2=get_name_for_res_node(rname), s2=rname) context.add_connection(c) resource2var[r] = rname # add a minimal bound for all unconnected functions for dp, s in unconnected_fun: f = CFunction(dp, s) F = context.get_ftype(f) minimals = F.get_minimal_elements() res = get_constant_minimals_as_resources(F, minimals, context) c = Connection(dp1=res.dp, s1=res.s, dp2=dp, s2=s) context.add_connection(c) ndp = CompositeNamedDP.from_context(context) ndp.check_fully_connected() return ndp, resource2var
def _compute_connection_options(self): unconnected_fun, unconnected_res = get_missing_connections( self.context) n = 0 usd = parse_poset('USD') for (dp, s) in unconnected_res: r = CResource(dp, s) R = self.context.get_rtype(r) foptions = get_compatible_unconnected_functions( R, self.context, unconnected_fun) ok = [] for f in foptions: if R == usd and would_introduce_cycle(self.context, r=r, f=f): print('skipping %r - %r because it adds a cycle' % (r, f)) continue ok.append(f) unconnected_fun.remove((f.dp, f.s)) if ok: n += 1 return n
def create_composite_(gdc0, ndp, plotting_info, SKIP_INITIAL): try: assert isinstance(ndp, CompositeNamedDP) # names2functions[name][fn] = item # names2resources[name][rn] = item names2resources = defaultdict(lambda: {}) names2functions = defaultdict(lambda: {}) if gdc0.should_I_enclose(ndp): if gdc0.yourname is None: container_label = '' else: if gdc0.yourname and gdc0.yourname[0] == '_': container_label = '' else: container_label = gdc0.yourname c = gdc0.newItem(container_label) gdc0.styleApply('container', c) gdc = gdc0.child_context(parent=c, yourname=gdc0.yourname) else: gdc = gdc0 for name, value in ndp.context.names.items(): # do not create these edges if SKIP_INITIAL: if is_function_with_one_connection_that_is_not_a_res_one( ndp, name): # print('Skipping extra node for is_function_with_one_connection %r' % name) # warnings.warn('hack') continue if SKIP_INITIAL: if is_resource_with_one_connection_that_is_not_a_fun_one( ndp, name): # print('skipping extra node for %r' % name) # warnings.warn('hack') continue if False: # this makes the nodes appear as red dots if is_function_with_no_connections(ndp, name): # only draw the balloon item = gdc.newItem("%s" % name) gdc.styleApply('unconnected', item) for fn in value.get_fnames(): names2functions[name][fn] = item for rn in value.get_rnames(): names2resources[name][rn] = item continue if is_resource_with_no_connections(ndp, name): # only draw the balloon instead of "Identity" node item = gdc.newItem("%s" % name) gdc.styleApply('unconnected', item) for fn in value.get_fnames(): names2functions[name][fn] = item for rn in value.get_rnames(): names2resources[name][rn] = item continue with gdc.child_context_yield(yourname=name, parent=gdc.parent) as child: plotting_info2 = RecursiveEdgeLabeling(plotting_info, name) f, r = create(child, value, plotting_info=plotting_info2) # print('name %s -> functions %s , resources = %s' % (name, list(f), list(r))) names2resources[name] = r names2functions[name] = f for rn in names2resources[name]: if resource_has_more_than_one_connected(ndp, name, rn): # create new splitter orig = names2resources[name][rn] split = gdc.newItem('') gdc.styleApply('splitter', split) l = gdc.newLink(orig, split) gdc.gg.propertyAppend(l, "constraint", "false") gdc.gg.propertyAppend(l, "weight", "0") gdc.styleApply('splitter_link', l) gdc.decorate_arrow_resource(l) names2resources[name][rn] = split ignore_connections = set() if SKIP_INITIAL: for name, value in ndp.context.names.items(): if is_function_with_one_connection_that_is_not_a_res_one( ndp, name): only_one = get_connections_to_function(ndp, name)[0] ignore_connections.add(only_one) if not only_one.dp2 in names2functions: msg = ( 'Cannot find function node ref for %r' % only_one.dp2 + ' while drawing one connection %s' % str(only_one)) # warnings.warn('giving up') # continue raise_desc(ValueError, msg, names=list(ndp.context.names), names2functions=list(names2functions)) node = names2functions[only_one.dp2][only_one.s2] names2functions[name][only_one.s1] = node # XXX: not really sure names2resources[name][only_one.s1] = node for name, value in ndp.context.names.items(): if is_resource_with_one_connection_that_is_not_a_fun_one( ndp, name): only_one = get_connections_to_resource(ndp, name)[0] ignore_connections.add(only_one) if not only_one.dp1 in names2resources: # warnings.warn('giving up') # continue raise ValueError( 'Cannot find function node ref for %r' % only_one.dp1 + ' while drawing one connection %s' % str(only_one)) node = names2resources[only_one.dp1][only_one.s1] names2resources[name][only_one.s2] = node # XXX: not really sure names2functions[name][only_one.s2] = node for c in ndp.context.connections: if c in ignore_connections: # print('ignoring connection %s' % str(c)) continue dpa = names2functions[c.dp2] n_a = dpa[c.s2] dpb = names2resources[c.dp1] n_b = dpb[c.s1] skip = gdc.should_I_skip_leq(ndp.context, c) ndp_first = ndp.context.names[c.dp1] ndp_second = ndp.context.names[c.dp2] second_simple = is_simple(ndp_second) first_simple = is_simple(ndp_first) any_simple = second_simple or first_simple both_simple = second_simple and first_simple ua = ndp.context.names[c.dp2].get_ftype(c.s2) ub = ndp.context.names[c.dp1].get_rtype(c.s1) if skip: label = get_signal_label(c.s1, ub) l1 = gdc.newLink(n_b, n_a, label=label) else: box = gdc.newItem('') # '≼') # LEQ rel_to_8 = MCDPConstants.diagrams_fontsize / 8 diagrams_leqimagesize = MCDPConstants.diagrams_leqimagesize_rel * \ rel_to_8 gdc.gg.propertyAppend(box, 'height', diagrams_leqimagesize) gdc.styleApply("leq", box) l1_label = get_signal_label(c.s2, ua) dec = plotting_info.get_fname_label(ndp_name=(c.dp2, ), fname=c.s2) if dec is not None: l1_label = get_signal_label_namepart(c.s2) + '\n' + dec if isinstance(ndp_second, SimpleWrap) and isinstance( ndp_second.dp, ResourceNode): l1_label = 'required ' + l1_label # print('Creating label with %r %s' % l1_label) l1 = gdc.newLink(box, n_a, label=l1_label) # if False: # gdc.gg.propertyAppend(l1, "headport", "w") l2_label = get_signal_label(c.s1, ub) if isinstance(ndp_first, SimpleWrap) and isinstance( ndp_first.dp, FunctionNode): l2_label = 'provided ' + l2_label dec = plotting_info.get_rname_label(ndp_name=(c.dp1, ), rname=c.s1) if dec is not None: l2_label = get_signal_label_namepart(c.s1) + '\n' + dec l2 = gdc.newLink(n_b, box, label=l2_label) # if False: # gdc.gg.propertyAppend(l2, "tailport", "e") # # if False: # gdc.gg.propertyAppend(l1, 'constraint', 'false') # gdc.gg.propertyAppend(l2, 'constraint', 'false') if both_simple: weight = 0 elif any_simple: weight = 0.5 else: weight = 1 if any_simple: gdc.gg.propertyAppend(l2, 'weight', '%s' % weight) gdc.gg.propertyAppend(l1, 'weight', '%s' % weight) # gdc.gg.propertyAppend(l2, 'color', 'blue') # gdc.gg.propertyAppend(l1, 'color', 'blue') gdc.decorate_arrow_function(l1) gdc.decorate_arrow_resource(l2) unconnected_fun, unconnected_res = get_missing_connections(ndp.context) for (dp, fn) in unconnected_fun: x = gdc.newItem('') gdc.styleApply("unconnected_node", x) n = names2functions[dp][fn] F = ndp.context.names[dp].get_ftype(fn) label = get_signal_label(fn, F) it_is, _ = is_res_node_name(dp) if it_is: label = 'required ' + label l = gdc.newLink(x, n, label=label) gdc.decorate_arrow_function(l) # XXX? gdc.styleApply('unconnected_link', l) for (dp, rn) in unconnected_res: x = gdc.newItem('') gdc.styleApply("unconnected_node", x) n = names2resources[dp][rn] R = ndp.context.names[dp].get_rtype(rn) label = get_signal_label(rn, R) it_is, _ = is_fun_node_name(dp) if it_is: label = 'provided ' + label l = gdc.newLink(n, x, label=label) gdc.decorate_arrow_resource(l) # XXX? gdc.styleApply('unconnected_link', l) functions = {} resources = {} for rname in ndp.get_rnames(): name = get_name_for_res_node(rname) resources[rname] = list(names2resources[name].values())[0] for fname in ndp.get_fnames(): name = get_name_for_fun_node(fname) functions[fname] = list(names2functions[name].values())[0] if not (gdc is gdc0): gdc0.all_nodes.extend(gdc.all_nodes) return functions, resources except BaseException as e: raise msg = 'Could not draw diagram.' raise_wrapped(Exception, e, msg, names2functions=names2functions, names2resources=names2resources, ndp=ndp)
def generate_actions(self): """ Returns a list of actions. Actions are hashable and given an OptimizationState they return another """ self.info('generating actions') unconnected_fun, unconnected_res = get_missing_connections( self.context) unconnected = [CResource(*u) for u in unconnected_res] r2actions = {} # list of list for r in unconnected: r_actions = [] R = self.context.get_rtype(r) # print('need to look for somebody implementing %s' % R) lb = self.lower_bounds[r] options = self.opt.get_providers(R=R, lb=lb) if not options: # self.info('No new providers can provide for %s %s' % (r, lb)) pass else: for id_ndp, fname in options: action = ActionAddNDP(id_ndp, fname, r) r_actions.append(action) # connecting one available resource foptions = get_compatible_unconnected_functions( R, self.context, unconnected_fun) usd = parse_poset('USD') for f in foptions: if R == usd and would_introduce_cycle(self.context, r=r, f=f): print('skipping %r - %r because it adds a cycle' % (r, f)) continue action = ActionConnect(r=r, f=f) r_actions.append(action) r2actions[r] = r_actions # all the actions above should be mutually exclusive. # e.g. if actions = {a1, a2, a3} # then if we apply a1 to s to obtain a1(s), # we commit to never use a1 again if we # If there are no options for r, r_actions in r2actions.items(): lb = self.lower_bounds[r] if not r_actions: # this is a resource that cannot be satisfied... msg = ( 'Nobody can provide for resource %s %s and no unconnected compatible' % (r, lb)) msg += '\n unconn: %s' % unconnected_fun self.info(msg) return [] # If one option is obligated, go for it. # Give precedence to actions that close an edge for r, r_actions in r2actions.items(): if len(r_actions) == 1: action = r_actions[0] if isinstance(action, ActionConnect): # If there is only one available we do that first self.info('There is only one action available for %s' % (r.__str__())) return r_actions for r, r_actions in r2actions.items(): if len(r_actions) == 1: # If there is only one available we do that first self.info('There is only one action available for %s' % (r.__str__())) return r_actions # concatenate all actions = [] for r_actions in r2actions.values(): actions.extend(r_actions) return actions
def generate_actions(self): """ Returns a list of actions. Actions are hashable and given an OptimizationState they return another """ self.info('generating actions') unconnected_fun, unconnected_res = get_missing_connections(self.context) unconnected = [CResource(*u) for u in unconnected_res] r2actions = {} # list of list for r in unconnected: r_actions = [] R = self.context.get_rtype(r) # print('need to look for somebody implementing %s' % R) lb = self.lower_bounds[r] options = self.opt.get_providers(R=R, lb=lb) if not options: # self.info('No new providers can provide for %s %s' % (r, lb)) pass else: for id_ndp, fname in options: action = ActionAddNDP(id_ndp, fname, r) r_actions.append(action) # connecting one available resource foptions = get_compatible_unconnected_functions(R, self.context, unconnected_fun) usd = parse_poset('USD') for f in foptions: if R == usd and would_introduce_cycle(self.context, r=r, f=f): print('skipping %r - %r because it adds a cycle' % (r, f)) continue action = ActionConnect(r=r, f=f) r_actions.append(action) r2actions[r] = r_actions # all the actions above should be mutually exclusive. # e.g. if actions = {a1, a2, a3} # then if we apply a1 to s to obtain a1(s), # we commit to never use a1 again if we # If there are no options for r, r_actions in r2actions.items(): lb = self.lower_bounds[r] if not r_actions: # this is a resource that cannot be satisfied... msg = ('Nobody can provide for resource %s %s and no unconnected compatible' % (r, lb)) msg += '\n unconn: %s' % unconnected_fun self.info(msg) return [] # If one option is obligated, go for it. # Give precedence to actions that close an edge for r, r_actions in r2actions.items(): if len(r_actions) == 1: action = r_actions[0] if isinstance(action, ActionConnect): # If there is only one available we do that first self.info('There is only one action available for %s' % (r.__str__())) return r_actions for r, r_actions in r2actions.items(): if len(r_actions) == 1: # If there is only one available we do that first self.info('There is only one action available for %s' % (r.__str__())) return r_actions # concatenate all actions = [] for r_actions in r2actions.values(): actions.extend(r_actions) return actions
def create_composite_(gdc0, ndp, plotting_info, SKIP_INITIAL): try: assert isinstance(ndp, CompositeNamedDP) # names2functions[name][fn] = item # names2resources[name][rn] = item names2resources = defaultdict(lambda: {}) names2functions = defaultdict(lambda: {}) if gdc0.should_I_enclose(ndp): if gdc0.yourname is None: container_label = '' else: if gdc0.yourname and gdc0.yourname[0] == '_': container_label = '' else: container_label = gdc0.yourname c = gdc0.newItem(container_label) gdc0.styleApply('container', c) gdc = gdc0.child_context(parent=c, yourname=gdc0.yourname) else: gdc = gdc0 for name, value in ndp.context.names.items(): # do not create these edges if SKIP_INITIAL: if is_function_with_one_connection_that_is_not_a_res_one(ndp, name): # print('Skipping extra node for is_function_with_one_connection %r' % name) # warnings.warn('hack') continue if SKIP_INITIAL: if is_resource_with_one_connection_that_is_not_a_fun_one(ndp, name): # print('skipping extra node for %r' % name) # warnings.warn('hack') continue if False: # this makes the nodes appear as red dots if is_function_with_no_connections(ndp, name): # only draw the balloon item = gdc.newItem("%s" % name) gdc.styleApply('unconnected', item) for fn in value.get_fnames(): names2functions[name][fn] = item for rn in value.get_rnames(): names2resources[name][rn] = item continue if is_resource_with_no_connections(ndp, name): # only draw the balloon instead of "Identity" node item = gdc.newItem("%s" % name) gdc.styleApply('unconnected', item) for fn in value.get_fnames(): names2functions[name][fn] = item for rn in value.get_rnames(): names2resources[name][rn] = item continue with gdc.child_context_yield(yourname=name, parent=gdc.parent) as child: plotting_info2 = RecursiveEdgeLabeling(plotting_info, name) f, r = create(child, value, plotting_info=plotting_info2) # print('name %s -> functions %s , resources = %s' % (name, list(f), list(r))) names2resources[name] = r names2functions[name] = f for rn in names2resources[name]: if resource_has_more_than_one_connected(ndp, name, rn): # create new splitter orig = names2resources[name][rn] split = gdc.newItem('') gdc.styleApply('splitter', split) l = gdc.newLink(orig, split) gdc.gg.propertyAppend(l, "constraint", "false") gdc.gg.propertyAppend(l, "weight", "0") gdc.styleApply('splitter_link', l) gdc.decorate_arrow_resource(l) names2resources[name][rn] = split ignore_connections = set() if SKIP_INITIAL: for name, value in ndp.context.names.items(): if is_function_with_one_connection_that_is_not_a_res_one(ndp, name): only_one = get_connections_to_function(ndp, name)[0] ignore_connections.add(only_one) if not only_one.dp2 in names2functions: msg = ('Cannot find function node ref for %r' % only_one.dp2 + ' while drawing one connection %s' % str(only_one)) # warnings.warn('giving up') # continue raise_desc(ValueError, msg, names=list(ndp.context.names), names2functions=list(names2functions)) node = names2functions[only_one.dp2][only_one.s2] names2functions[name][only_one.s1] = node # XXX: not really sure names2resources[name][only_one.s1] = node for name, value in ndp.context.names.items(): if is_resource_with_one_connection_that_is_not_a_fun_one(ndp, name): only_one = get_connections_to_resource(ndp, name)[0] ignore_connections.add(only_one) if not only_one.dp1 in names2resources: # warnings.warn('giving up') # continue raise ValueError('Cannot find function node ref for %r' % only_one.dp1 + ' while drawing one connection %s' % str(only_one)) node = names2resources[only_one.dp1][only_one.s1] names2resources[name][only_one.s2] = node # XXX: not really sure names2functions[name][only_one.s2] = node for c in ndp.context.connections: if c in ignore_connections: # print('ignoring connection %s' % str(c)) continue dpa = names2functions[c.dp2] n_a = dpa[c.s2] dpb = names2resources[c.dp1] n_b = dpb[c.s1] skip = gdc.should_I_skip_leq(ndp.context, c) ndp_first = ndp.context.names[c.dp1] ndp_second = ndp.context.names[c.dp2] second_simple = is_simple(ndp_second) first_simple = is_simple(ndp_first) any_simple = second_simple or first_simple both_simple = second_simple and first_simple ua = ndp.context.names[c.dp2].get_ftype(c.s2) ub = ndp.context.names[c.dp1].get_rtype(c.s1) if skip: l1 = gdc.newLink(n_b, n_a , label=get_signal_label(c.s1, ub)) else: box = gdc.newItem('') # '≼') gdc.styleApply("leq", box) l1_label = get_signal_label(c.s2, ua) dec = plotting_info.get_fname_label(ndp_name=(c.dp2,), fname=c.s2) if dec is not None: l1_label = get_signal_label_namepart(c.s2) + '\n' + dec if isinstance(ndp_second, SimpleWrap) and isinstance(ndp_second.dp, ResourceNode): l1_label = 'required ' + l1_label # print('Creating label with %r %s' % l1_label) l1 = gdc.newLink(box, n_a , label=l1_label) # if False: # gdc.gg.propertyAppend(l1, "headport", "w") l2_label = get_signal_label(c.s1, ub) if isinstance(ndp_first, SimpleWrap) and isinstance(ndp_first.dp, FunctionNode): l2_label = 'provided ' + l2_label dec = plotting_info.get_rname_label(ndp_name=(c.dp1,), rname=c.s1) if dec is not None: l2_label = get_signal_label_namepart(c.s1) + '\n' + dec l2 = gdc.newLink(n_b, box, label=l2_label) # if False: # gdc.gg.propertyAppend(l2, "tailport", "e") # # if False: # gdc.gg.propertyAppend(l1, 'constraint', 'false') # gdc.gg.propertyAppend(l2, 'constraint', 'false') if both_simple: weight = 0 elif any_simple: weight = 0.5 else: weight = 1 if any_simple: gdc.gg.propertyAppend(l2, 'weight', '%s' % weight) gdc.gg.propertyAppend(l1, 'weight', '%s' % weight) # gdc.gg.propertyAppend(l2, 'color', 'blue') # gdc.gg.propertyAppend(l1, 'color', 'blue') gdc.decorate_arrow_function(l1) gdc.decorate_arrow_resource(l2) unconnected_fun, unconnected_res = get_missing_connections(ndp.context) for (dp, fn) in unconnected_fun: x = gdc.newItem('') gdc.styleApply("unconnected_node", x) n = names2functions[dp][fn] F = ndp.context.names[dp].get_ftype(fn) label = get_signal_label(fn, F) it_is, _ = is_res_node_name(dp) if it_is: label = 'required ' + label l = gdc.newLink(x, n, label=label) gdc.decorate_arrow_function(l) # XXX? gdc.styleApply('unconnected_link', l) for (dp, rn) in unconnected_res: x = gdc.newItem('') gdc.styleApply("unconnected_node", x) n = names2resources[dp][rn] R = ndp.context.names[dp].get_rtype(rn) label = get_signal_label(rn, R) it_is, _ = is_fun_node_name(dp) if it_is: label = 'provided ' + label l = gdc.newLink(n, x, label=label) gdc.decorate_arrow_resource(l) # XXX? gdc.styleApply('unconnected_link', l) functions = {} resources = {} for rname in ndp.get_rnames(): name = get_name_for_res_node(rname) resources[rname] = list(names2resources[name].values())[0] for fname in ndp.get_fnames(): name = get_name_for_fun_node(fname) functions[fname] = list(names2functions[name].values())[0] if not (gdc is gdc0): gdc0.all_nodes.extend(gdc.all_nodes) return functions, resources except BaseException as e: raise msg = 'Could not draw diagram.' raise_wrapped(Exception, e, msg, names2functions=names2functions, names2resources=names2resources, ndp=ndp)