Problem collecting pebbles. Given a directed graph
.
There are pebbles on a subset of vertives
.
We can move pebbles along the directed edges. Once we move a pebble
along some edge, this edge is immediately inverted. The problem is can
we move
pebbles to a special vertex
.
I think this should be NP-Hard but I don’t know how to prove it.
I found this problem in a paper “Pebble
game algorithms and sparse graphs”. On
-sparse
graphs with special rules for putting initial pebbles this problem can
be solved through simple depth-first-search.
There are two interesting ideas in the paper. The relation of sparsity
and pebble games and why the above problem can be solved with simple
dfs.
1
-sparsity
-sparsity
is a quantitative way to say how sparse an undirected graph is.
and
.
A graph
is
-sparse
if
holds for any subgraph
of
.
One can see that
since otherwise there must be no edge between any two vertices. The
-sparse
subgraphs(edge set) of
form the independent sets of a matroid. For more on matroid and
especially sparsity matroid, read matroid and sparsity
matroid. The Pairs (k,l) that form a matroid part in
the sparsity matroid link is misleading. Matroids with
and
satisfying those condition will have a spanning tight graph as their
bases; those who don’t satisfying conditions are still matroids but
their bases will never be
-tight
graphs.
Actually more general things are also matroids. They are called count
matroids. read Andras Frank’s “Connections
in Combinatorial Optimization” Theorem 13.5.5 for proofs.
In terms of sparsity, the proof in the book basically shows
is a matroid rank function and the independnet sets of our sparsity
matroid defined above are exactly the independent sets of the matroid
defined by rank function
.
If you want to prove the independnet set exchange property directed, it
seems a little harder. We need to show for any two
-sparse
subgraph
and
s.t.
,
there exists
s.t.
is
-sparse.
We only consider connnected graphs. If
is not a subset of
,
there would be
and some edge
connecting
and
.
can be added to
since
.
On the other hand if
is a subset of
how to find such an edge? Suppose such an edge does not exist. Then for
any
,
we can find a tight subgraph
of
containing
and
but not the edge
.(see
Theorem 5 in the pebble game paper). Also there exists at least one edge
in
but not in
.
(I think the contradiction is
but don’t know what to do next…) This method doesn’t work
sparsity proofs
Googling ‘graph sparsity’ normally returns sparsity measurement like
upperbounds on average degree or max degree. There are many literature
about bounded max(average) vertex degree graphs(see this lecture from
mim_uw for example). However these definitions don’t imply matroids.
For example graph with max degree 4.
counterexample
One can see that the two graphs do not satisfy the independent set
exchange property.
2 pebble game
How to decide whether a given graph is
-sparse?
“Pebble
game algorithms and sparse graphs” provides an algorithm solving the
problem in polynomial
time(,
is the number of vertices) and a proof of equivalence of pebble game and
graph
-sparsity.
The algorithm described in the paper basically finds a base of
-sparsity
matorid defined on the input graph. Note that finding a base of any
matroid
can be easily done with
independence oracle calls. If we want a polynomial time alg then the
independence oracle must be fast. A bruteforce method is to check every
subgraph. We can certainly not afford that.
In the paper the authors designed a nice way to check independence. The
idea is instead of comparing
and
,
they compare
and
.
Then
is fixed and checking every subgraph to compute
is still needed. They manage to count
by counting pebbles on vertices instead of counting edges. Thus the
rules of pebble games are quite straightforward(but still some ambiguous
rules). Initially we have an empty graph and
vertices. On each vertex there are
pebbles(for
).
We consider edges one by one in arbitrary order. Once we added an edge
to the graph, we should remove one pebble from one of the edge’s
endpoints. We need to design rules for accepting or rejecting edges.
Astute readers may find that simply removing pebbles while adding new
edges is completely not working 😅. We want the remaining pebbles on any
subgraph to be
but the method we try to use doesn’t guarantee this since we may remove
a pebble on either endpoint of the added edge…
In the paper they use directed graph. When considering an undirected
edge
,
they make it
directed(i.e. )
and then remove a pebble from the
source().
An edge is accepted if and only the endpoints can collect
pebbles in total. Collecting pebbles is just searching paths from
or
to some vertex with pebbles and moving one pebble from that vertex to
(or
)
and reversing the edges on the path. For any vertex, if an out edge is
added, an pebble is removed(accepting edge); if one pebble is removed,
an out edge is added(pebble collecting vertex); if one pebble is added,
one out edge is removed(reverse pebble collecting path).
One can see that for any subgraph
this pebble collection and edge adding operation preserve the sum of
total number of pebbles on
.
see the paper for detailed proof.
proof in the paper
The last question is can we do every operations in polynomial time? or
in other words why collecting pebbles can be done in polynomial time? In
the paper there is a lemma saying that if adding an edge
does not break sparsity and there are not enough pebbles on
and
(we
need to do pebble collection), then we can always find a pebble
collecting path without changing the pebble count of other vertices.
Thus we can collect pebbles by simple dfs.
python code for hypergraph sparsity.
from networkx import Graphfrom itertools import chain, combinations'''pebble game algorithm for checking hypergraph sparsity.`hyperedges` is a list of frozensets of non-number objects.for example, `[frozenset({'1', '0'})]`'''def pebblegame(k,l,hyperedges:list): vertices=frozenset().union(*hyperedges) n=len(vertices) pebs={v:k for v in vertices} H_tail=Graph() H_tail.add_nodes_from(range(len(hyperedges)),bipartite=0) H_tail.add_nodes_from(vertices,bipartite=1)def add_edge(i): H=hyperedges[i] v=next(filter(lambda v:pebs[v]>0,H)) pebs[v]=pebs[v]-1 H_tail.add_edge(i,v)returnNonedef collect_pebble(v,forbiddenset): # collect 1 peb to v. visited=set() path=[]# use dfs to collect pebsdef dfs(node,node_H):if node in visited: returnFalse# don't use nodes in Hif node_H !=-1and node in forbiddenset: returnFalse visited.add(node) path.append((node,node_H))if node notin forbiddenset and pebs[node]>0:# pebs[node]=pebs[node]-1# pebs[v]=pebs[v]+1returnTruefor H in H_tail.neighbors(node):for nxt in hyperedges[H]:if nxt!=node and dfs(nxt,H): returnTrue path.pop()returnFalseif dfs(v,-1): # find a pebble# collect the pebble t,_=path[-1] pebs[t]=pebs[t]-1 pebs[v]=pebs[v]+1# reverse hyperedges last_u,last_H=path.pop()whilelen(path): top_u,top_H=path.pop() H_tail.remove_edge(last_H,top_u) H_tail.add_edge(last_H,last_u) last_u,last_H=top_u,top_HreturnTrueelse: returnFalse# try to add every hyperedgefor i inrange(len(hyperedges)): H=hyperedges[i] demand=l+1-sum([pebs[v] for v in H])if demand<=0: add_edge(i)else: # collect pebswhileTrue: collected=Falsefor u in H:if H_tail.degree(u)>0:if collect_pebble(u,forbiddenset=H): demand=demand-1 collected=Trueif demand==0: breakifnot collected: # can not add this edgereturn"dependent"if demand==0: break add_edge(i)ifsum([pebs[v] for v in vertices])==l: return"tight"else: return"sparse"# # tests# import random# def bruteforce(k,l,hyperedges):# def non_empty_subsets(s):# return list(chain.from_iterable(combinations(s, r) # for r in range(1, len(s) + 1)))# for U in non_empty_subsets(hyperedges):# vertices=frozenset().union(*U)# if len(vertices)*k-l<len(U):# return "dependent"# return "sparse"# def generate_random_subsets(n, k):# # Define the set [n]# full_set = list(range(1, n + 1))# # Generate k random subsets# subsets = []# for _ in range(k):# # Randomly choose a subset size# subset_size = random.randint(1, n)# # Randomly select subset_size elements from the set# subset = random.sample(full_set, subset_size)# subsets.append(frozenset(map(str,subset)))# return subsets# while True:# n_v=100# n_H=10# k=2# l=2# hyperedges=generate_random_subsets(n_v,n_H)# pebble_res=pebblegame(k,l,hyperedges)# bf_res=bruteforce(k,l,hyperedges)# print(pebble_res,bf_res)# if pebble_res!=bf_res:# print(hyperedges)# input()
sage code for an straightforward
implementation
def is_kl_sparse(g,k:int,l:int):r''' pebble game algorithm for deciding if `g` is (k,l)-sparse. `g` may have multiedges and loops use `g=Graph(multiedges=True,loops=True)` from sagemath graph https://linkinghub.elsevier.com/retrieve/pii/S0012365X07005602 '''assert(k>0and l>=0and l<=2*k-1) d=DiGraph(multiedges=True,loops=True) d.add_vertices(g.vertex_iterator()) peb=[k for _ in d.vertex_iterator()] edges=g.edges(labels=False)def dfs(u,v): # returns if u can get pebble. dfs should not touch v vis=[Falsefor _ in d.vertex_iterator()] pre=[-1for _ in d.vertex_iterator()] st=[] vis[u]=vis[v]=True st.append(u)whilelen(st): h=st.pop()if(peb[h]>0): peb[h]=peb[h]-1while pre[h]!=-1: d.reverse_edge((pre[h],h)) h=pre[h] peb[h]=peb[h]+1returnTruefor c in d.neighbor_out_iterator(h):ifnot vis[c]: vis[c]=True st.append(c) pre[c]=hreturnFalsedef edgeinsertion(u,v):if peb[u]==0: u,v=v,u d.add_edge(u,v) peb[u]=peb[u]-1 rej=0for (u,v) in edges:if peb[u]+peb[v]>=l+1: edgeinsertion(u,v)else:whileTrue: tryu=tryv=Falseif peb[u]<k: tryu=dfs(u,v)if peb[u]+peb[v]==l+1: edgeinsertion(u,v)breakif peb[v]<k: tryv=dfs(v,u)if peb[u]+peb[v]==l+1: edgeinsertion(u,v)breakifnot tryu andnot tryv: rej=rej+1break# d.plot().show()# input() pebsum=sum(peb)if pebsum==l:if rej==0: return"tight"else: return"spanning"elif pebsum>l and rej==0: return"sparse"else: return"other"# d=Graph(multiedges=True,loops=True)# d.add_edges([(0,2),(1,2),(1,3),(2,4),(2,5),(4,6),(4,7),(5,8)])# # d.plot().show()# # print(is_kl_sparse(d,1,1))# d.add_edge(4,6)# d.plot().show()# print(is_kl_sparse(d,1,1))# print(sum(peb))