Exemple #1
0
    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 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
Exemple #3
0
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
Exemple #4
0
    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
Exemple #7
0
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
Exemple #8
0
 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
Exemple #9
0
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)
Exemple #10
0
    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
Exemple #12
0
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)