def get_fbvs_max_size(self, g: MultiGraph, k: int) -> set:
        if len(g) <= k + 2:
            return set(g.nodes()[:k])

        # Construct a trivial FVS of size k + 1 on the first k + 3 vertices of G.
        nodes = g.nodes()

        # The set of nodes currently under consideration.
        node_set = set(nodes[:(k + 2)])

        # The current best solution, of size (k + 1) before each compression step,
        # and size <= k at the end.
        soln = set(nodes[:k])

        for i in range(k + 2, len(nodes)):
            soln.add(nodes[i])
            node_set.add(nodes[i])

            if len(soln) < k + 1:
                continue

            assert (len(soln) == (k + 1))
            assert (len(node_set) == (i + 1))

            new_soln = self.ic_compression(g.subgraph(node_set), soln, k)

            if new_soln is None:
                return None

            soln = new_soln
            assert (len(soln) <= k)

        return soln
Exemple #2
0
def reduction2(g: MultiGraph, w: set, h: MultiGraph, k: int) -> (int, int, bool):
	for v in h.nodes():
		# Check if G[W ∪ {v}] contains a cycle.
		if not is_forest(g.subgraph(w.union({v}))):
			g.remove_node(v)
			h.remove_nodes_from([v])
			return (k - 1, v, True)
	return (k, None, False)
 def preprocess_1(self, g: MultiGraph, f: set, active_v) -> set:
     if number_connected_components(g) >= 2:
         mif_set = set()
         for component in connected_components(g):
             f_i = component.intersection(f)
             gx = g.subgraph(component)
             component_mif_set = self.preprocess_2(gx, f_i, active_v)
             if component_mif_set:
                 mif_set = mif_set.union(component_mif_set)
         return mif_set
     return self.preprocess_2(g, f, active_v)
Exemple #4
0
def generate_snapshots_over_time(G: nx.MultiGraph,
                                 minutes=0,
                                 hours=0,
                                 days=0,
                                 max_snapshots=None,
                                 interval=None,
                                 include_final=False,
                                 cummulative=True):
    """
	:param G: MultiGraph you want to watch over time, with "created_at" edge attribute
	:param minutes: Number of minutes between two snapshots (Default 0)
	:param hours: Number of hours between two snapshots (Default 0)
	:param days: Number of days between two snapshots (Default 0)
	:param max_snapshots: Maximum number of generated snapshots (Default None - all snapshots are created)
	:param interval: Tuple (start, end) specifying in which interval to generate snapshots (Default None - takes min, max created_at date of G)
	:param include_final: If True, set G as value for max_date (Default False)
	:return: A dict with timestamps
	"""
    if nx.get_edge_attributes(G, "created_at") == {}:
        raise Exception("Graph needs 'created_at' edge attribute")
    if minutes < 0 or hours < 0 or days < 0 or minutes + hours + days <= 0:
        raise Exception("Illegal minutes, hours or days values")

    edges = G.edges(data=True)
    created_ats = [attr["created_at"] for (_, _, attr) in edges]
    if interval is None:
        date = str_to_datetime(min(created_ats))
        max_date = str_to_datetime(max(created_ats))
    else:
        date = str_to_datetime(interval[0])
        max_date = str_to_datetime(interval[1])
    snapshots = {}
    while date <= max_date and (max_snapshots is None
                                or len(snapshots) < max_snapshots):
        if cummulative:
            edges_snapshot = [(a, b) for (a, b, attr) in edges
                              if str_to_datetime(attr["created_at"]) <= date]
        else:
            edges_snapshot = [
                (a, b) for (a, b, attr) in edges
                if (date <= str_to_datetime(attr["created_at"]) <= date +
                    timedelta(minutes=minutes, hours=hours, days=days))
            ]
        nodes_snapshot = list(sum(edges_snapshot, ()))
        G_snapshot = G.subgraph(nodes_snapshot)
        snapshots[date] = reduce_multi_graph(G_snapshot)

        date += timedelta(minutes=minutes, hours=hours, days=days)

    if include_final:
        snapshots[max_date] = reduce_multi_graph(G)
    return snapshots
    def fvs_disjoint(self, g: MultiGraph, w: set, k: int) -> set:
        """
        Given an undirected graph G and a fbvs W in G of size at least (k + 1), is it possible to construct
        a fbvs X of size at most k using only the nodes of G - W?

        :return: The set X, or `None` if it's not possible to construct X
        """

        # If G[W] isn't a forest, then a solution X not using W can't remove W's cycles.
        if not is_acyclic(g.subgraph(w)):
            return None

        # Apply reductions exhaustively.
        k, soln_redux = self.apply_reductions(g, w, k)

        # If k becomes negative, it indicates that the reductions included
        # more than k nodes. In other word, reduction 2 shows that there are more than k nodes
        # in G - W that will create cycle in W. Hence, no solution of size <= k exists.
        if k < 0:
            return None

        # From now onwards we assume that k >= 0

        # If G has been reduced to nothing and k is >= 0 then the solution generated by the reductions
        # is already optimal.
        if len(g) == 0:
            return soln_redux

        # Recall that H is a forest as W is a feedback vertex set. Thus H has a node x of degree at most 1.
        # Find an x in H of degree at most 1.
        h = graph_minus(g, w)
        x = None
        for v in h.nodes():
            if h.degree(v) <= 1:
                x = v
                break
        assert x is not None, "There must be at least one node x of degree at most 1"

        # Branch on (G - {x}, W, k−1) and (G, W ∪ {x}, k)
        # G is copied in the left branch (as it is modified), but passed directly in the right.
        soln_left = self.fvs_disjoint(graph_minus(g, {x}), w, k - 1)

        if soln_left is not None:
            return soln_redux.union(soln_left).union({x})

        soln_right = self.fvs_disjoint(g, w.union({x}), k)

        if soln_right is not None:
            return soln_redux.union(soln_right)

        return None
    def reduction2(self, g: MultiGraph, w: set, h: MultiGraph, k: int) -> (int, int, bool):
        """
        If there exists a node v in H such that G[W ∪ {v}]
        contains a cycle, then include v in the solution, delete v and decrease the
        parameter by 1. That is, the new instance is (G - {v}, W, k - 1).

        If v introduces a cycle, it must be part of X as none of the vertices in W
        will be available to neutralise this cycle.
        """
        for v in h.nodes():
            # Check if G[W ∪ {v}] contains a cycle.
            if not is_acyclic(g.subgraph(w.union({v}))):
                g.remove_node(v)
                h.remove_nodes_from([v])
                return k - 1, v, True
        return k, None, False
Exemple #7
0
def mif_preprocess_1(g: MultiGraph, f: set, active_v, k: int) -> set:
	if nxc.number_connected_components(g) >= 2:
		mif_set = set()
		for component in nxc.connected_components(g):
			f_i = component.intersection(f)
			gx = g.subgraph(component)
			component_mif_set = mif_preprocess_2(gx, f_i, active_v, None)
			if component_mif_set:
				mif_set = mif_set.union(component_mif_set)
				if k != None:
					k -= (len(component_mif_set) - len(f_i))
					if k <= 0:
						return mif_set
		if k == None or len(mif_set) >= k:
			return mif_set
		return None
	return mif_preprocess_2(g, f, active_v, k)
Exemple #8
0
def fvs_disjoint(g: MultiGraph, w: set, k: int) -> set:
	# Check that G[W] is a forest.
	# If it isn't, then a solution X not using W can't remove W's cycles.
	if not is_forest(g.subgraph(w)):
		return None

	# Apply reductions exhaustively.
	k, soln_redux = apply_reductions(g, w, k)

	# If k becomes negative, it indicates that the reductions included
	# more than k vertices, hence no solution of size <= k exists.
	if k < 0:
		return None

	# If G has been reduced to nothing and k is >= 0 then the solution generated by the reductions
	# is already optimal.
	if len(g) == 0:
		return soln_redux

	# Find an x in H of degree at most 1.
	h = graph_minus(g, w)
	x = None
	for v in h.nodes():
		if h.degree(v) <= 1:
			x = v
			break
	assert x is not None

	# Branch.
	# G is copied in the left branch (as it is modified), but passed directly in the right.
	soln_left = fvs_disjoint(graph_minus(g, {x}), w, k - 1)

	if soln_left is not None:
		return soln_redux.union(soln_left).union({x})

	soln_right = fvs_disjoint(g, w.union({x}), k)

	if soln_right is not None:
		return soln_redux.union(soln_right)

	return None
Exemple #9
0
def nodes_list_to_edges(graph: nx.MultiGraph, nodes_list: List) -> List:
    """
    Get a graph and a list of nodes - the first node in the list is the source node, and the last is the target node.
    The function calculates for every two adjacent nodes the edge with the minimal base-fee
    (since there might be multiple edges between two nodes - it's a MultiGraph).

    :param graph: The MultiGraph to process.
    :param nodes_list: The list of nodes describing a path.
    :return: A list of edges in the graph, each one is the edge in the path.
    """
    edges_list = list()

    for i in range(len(nodes_list) - 1):
        node1 = nodes_list[i]
        node2 = nodes_list[i + 1]
        subgraph = graph.subgraph(nodes=(node1, node2))
        min_fee_channel = get_channel_with_minimal_fee_base(subgraph,
                                                            source=node1,
                                                            target=node2)
        edges_list.append(min_fee_channel)

    return edges_list
    def preprocess_2(self, g: MultiGraph, f: set, active_v) -> set:
        mif_set = set()
        while not is_independent_set(g, f):
            mif_set = mif_set.union(f)
            for component in connected_components(g.subgraph(f)):
                if len(component) > 1:
                    if active_v in component:
                        active_v = component.pop()
                        compressed_node = active_v
                    else:
                        compressed_node = component.pop()
                    g = self.compress(g, component, compressed_node, False)
                    f = f.intersection(g.nodes())
                    # Maybe faster with
                    # f = f.difference(component)
                    # f.add(compressed_node)
                    mif_set = mif_set.union(component)
                    break
        mif_set2 = self.mif_main(g, f, active_v)
        if mif_set2:
            mif_set = mif_set2.union(mif_set)

        return mif_set
Exemple #11
0
def mif_preprocess_2(g: MultiGraph, f: set, active_v, k: int) -> set:
	mif_set = set()
	while not is_independent_set(g, f):
		mif_set = mif_set.union(f)
		for component in nxc.connected_components(g.subgraph(f)):
			if len(component) > 1:
				if active_v in component:
					active_v = component.pop()
					compressed_node = active_v
				else:
					compressed_node = component.pop()
				g = compress(g, component, compressed_node, True)
				f = f.intersection(g.nodes())
				# Maybe faster with
				# f = f.difference(component)
				# f.add(compressed_node)
				mif_set = mif_set.union(component)
				break
	mif_set2 = mif_main(g, f, active_v, k)
	if mif_set2:
		mif_set = mif_set2.union(mif_set)
	if k == None or len(mif_set) >= k:
		return mif_set
	return None