randomgml 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. #!/usr/bin/python
  2. import sys, random
  3. def usage():
  4. s = ''
  5. s += '\n'
  6. s += 'USAGE:\n'
  7. s += 'To create a random connected graph, use\n'
  8. s += ' ./randomgml OPTION n m\n'
  9. s += 'where n is the number of nodes you want,\n'
  10. s += 'and m is the number of edges you want,\n'
  11. s += '(must have n-1 <= m <= n(n-1)/2)\n'
  12. s += 'and where OPTION is:\n'
  13. s += ' -a: automatic filename v%de%d.tgf%(n,m)\n'
  14. s += ' -s: write to stdout\n'
  15. s += './randomgml -s 10 15\n'
  16. return s
  17. class Node:
  18. def __init__(self,graph,index):
  19. self.graph = graph
  20. self.index = index
  21. self.friends = []
  22. self.strangers = range(index)
  23. def randomStranger(self):
  24. s = len(self.strangers)
  25. if s == 0: return None
  26. j = random.randint(0,s-1)
  27. return self.strangers[j]
  28. def hasFriend(self,i):
  29. return i in self.friends
  30. def addStranger(self,i):
  31. self.strangers.append(i)
  32. self.graph.markNodeIndexUnsat(self.index)
  33. def addFriend(self,i):
  34. if self.hasFriend(i): return
  35. self.friends.append(i)
  36. self.strangers.remove(i)
  37. if not self.strangers:
  38. self.graph.markNodeIndexSat(self.index)
  39. class Graph:
  40. def __init__(self):
  41. self.size = 0
  42. self.nodes = []
  43. self.edges = []
  44. self.unsatNI = [] # unsaturated node indices
  45. def newNode(self):
  46. "Create and return a new node for this graph."
  47. n = self.size
  48. N = Node(self,n)
  49. if n > 0:
  50. self.unsatNI = range(n+1)
  51. # Tell old nodes about new stranger.
  52. for M in self.nodes:
  53. M.strangers.append(n)
  54. self.nodes.append(N)
  55. self.size = n + 1
  56. return N
  57. def markNodeIndexSat(self,i):
  58. self.unsatNI.remove(i)
  59. def markNodeIndexUnsat(self,i):
  60. self.unsatNI.append(i)
  61. def edgeMayExist(self,si,ti):
  62. "Check whether the indices are within bounds."
  63. n = self.size
  64. return si < n and ti < n
  65. def edgeExists(self,si,ti):
  66. "Say whether nodes si, ti yet share an edge."
  67. if not self.edgeMayExist(si,ti): return False
  68. sn = self.nodes[si]
  69. return sn.hasFriend(ti)
  70. def isComplete(self):
  71. "Is this graph complete?"
  72. return len(self.unsatNI) == 0
  73. def addEdge(self,si,ti):
  74. "Add the edge (si,ti) if possible."
  75. if self.edgeMayExist(si,ti) and not self.edgeExists(si,ti):
  76. sn = self.nodes[si]
  77. tn = self.nodes[ti]
  78. # Inform the nodes
  79. sn.addFriend(ti)
  80. tn.addFriend(si)
  81. # Record edge locally
  82. self.edges.append( (si,ti) )
  83. def addRandomEdge(self):
  84. "Add a random edge if possible, and return it."
  85. if self.isComplete(): return None
  86. # Choose an unsaturated node at random.
  87. u = len(self.unsatNI)
  88. j = random.randint(0,u-1)
  89. si = self.unsatNI[j]
  90. sn = self.nodes[si]
  91. ti = sn.randomStranger()
  92. self.addEdge(si,ti)
  93. return (si,ti)
  94. def addRandomConnectedNode(self):
  95. "Create a new node and attach it to a random existing node."
  96. sn = self.newNode()
  97. if self.size > 1:
  98. si = sn.index
  99. ti = sn.randomStranger()
  100. self.addEdge(si,ti)
  101. def gml(self):
  102. t = '# gml graph generated with randomgml script\ngraph [\n'
  103. for n in self.nodes:
  104. t += ' node [ id %d ]\n'%n.index
  105. for e in self.edges:
  106. t += ' edge [ source %d target %d ]\n'%e
  107. t += ']\n'
  108. return t
  109. def genRandGraph(n,m):
  110. """
  111. Generate a random connected graph of n nodes and m edges.
  112. """
  113. if m < n - 1 or m > n*(n - 1)/2:
  114. print 'ERROR: m must be between n-1 and n(n-1)/2 inclusive.'
  115. sys.exit(1)
  116. G = Graph()
  117. # First create n nodes, connected by a spanning tree.
  118. for i in range(n):
  119. G.addRandomConnectedNode()
  120. # There are now n - 1 edges in the graph.
  121. # Add m - n + 1 more.
  122. for j in range(m - n + 1):
  123. G.addRandomEdge()
  124. return G
  125. def genRandGraphByDensity(n,d):
  126. G = Graph()
  127. N = 0
  128. if d < 1 or d > (n-1)/2.0:
  129. print 'ERROR: d must be between 1 and (n-1)/2, inclusive'
  130. sys.exit(1)
  131. p = (d-1)/d
  132. while N < n:
  133. r = random.random()
  134. if r < p:
  135. G.addRandomEdge()
  136. else:
  137. G.addRandomConnectedNode()
  138. N += 1
  139. return G
  140. def readCmdLine():
  141. try:
  142. opt = sys.argv[1]
  143. if opt not in ['-a','-s']: raise Exception()
  144. n = int(sys.argv[2])
  145. m = int(sys.argv[3])
  146. if m < n-1 or m > n*(n-1)/2.0: raise Exception()
  147. return (opt,n,m)
  148. except:
  149. print usage()
  150. sys.exit(1)
  151. def main():
  152. opt,n,m = readCmdLine()
  153. G = genRandGraph(n,m)
  154. gml = G.gml()
  155. if opt == '-a':
  156. fn = 'v%de%d.gml'%(n,m)
  157. open(fn,'w').write(gml)
  158. elif opt == '-s':
  159. sys.stdout.write(gml)
  160. if __name__ == '__main__':
  161. main()