petrinet.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. '''This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
  2. Copyright 2011 by the AToMPM team and licensed under the LGPL
  3. See COPYING.lesser and README.md in the root of this project for full details'''
  4. #from numpy import *
  5. import igraph as ig
  6. import datetime
  7. #import pydot
  8. from threading import *
  9. from Queue import *
  10. from barrier import *
  11. from random import choice
  12. from sets import *
  13. class PnModule(Thread):
  14. def __init__(self, pnet, que, bar, full=False):
  15. Thread.__init__(self)
  16. '''Our graph'''
  17. self.pngraph = pnet;
  18. P = self.pngraph.vs.select(type_eq = 'P')
  19. key = P[0]['name'][:1]
  20. self.key = key
  21. #index in matrix to graph id
  22. self.mx_to_gr_indexT = {}
  23. self.mx_to_gr_indexP = {}
  24. self.mx_to_gr_indexTn = {}
  25. self.mx_to_gr_indexPn = {}
  26. self.que = que
  27. self.barrier = bar
  28. self.TFtoRun = {}
  29. self.ssc = 0
  30. self.full = full
  31. self.result={}
  32. self.xxx = 0
  33. def repstr(self,string, length):
  34. return (string * length)[0:length]
  35. def printMatrix(self,M):
  36. lenr = len(M[0])
  37. lenc = len(M)
  38. first = True
  39. i = 0
  40. j = 0
  41. text = ''
  42. colwidth = []
  43. for x in range(lenr):
  44. text+=self.pngraph.vs[self.mx_to_gr_indexT[x]]['name']+' '
  45. colwidth.append(len(self.pngraph.vs[self.mx_to_gr_indexT[x]]['name'])+1)
  46. text+='\n'
  47. for row in M:
  48. for el in row:
  49. text += '%d'%el+self.repstr(' ', colwidth[j]-len(str(int(el))))
  50. j+=1
  51. text += self.pngraph.vs[self.mx_to_gr_indexP[i]]['name']
  52. text+='\n'
  53. j=0
  54. i+=1
  55. print text
  56. def summary(self):
  57. ig.summary(self.reachability)
  58. def getKey(self):
  59. return self.key
  60. def next(self):
  61. self.ssc += 1
  62. return self.ssc
  63. def DminPlusMatrix(self):
  64. i = 0
  65. j = 0
  66. self.incidence = zeros((self.numP,self.numT,))
  67. for p in self.P:
  68. self.mx_to_gr_indexP[i]=p.index
  69. self.mx_to_gr_indexPn[p['name']]=i
  70. self.M0[i]=p['nbTokens']
  71. totrans = self.pngraph.successors(p.index)
  72. fromtrans = self.pngraph.predecessors(p.index)
  73. toweight = 0
  74. fromweight = 0
  75. for t in self.T:
  76. if t.index in totrans:
  77. self.dminus[i][j] = 1
  78. toweight = int(self.pngraph.es[self.pngraph.get_eid(p.index,t.index)]['weight'])
  79. self.dminus[i][j] = toweight
  80. if t.index in fromtrans:
  81. self.dplus[i][j] = 1
  82. fromweight = int(self.pngraph.es[self.pngraph.get_eid(t.index,p.index)]['weight'])
  83. self.dplus[i][j] = fromweight
  84. #print t['name']
  85. # if t.index in totrans:
  86. # self.dminus[i][j] = 1
  87. # elif t.index in fromtrans:
  88. # self.dplus[i][j] = 1
  89. # else:
  90. # pass#row.append(0)
  91. self.mx_to_gr_indexTn[t['name']] = j
  92. self.mx_to_gr_indexT[j] = t.index
  93. j+=1
  94. i+=1
  95. j = 0
  96. # self.printMatrix(self.dminus)
  97. # self.printMatrix(self.incidence)
  98. # self.printMatrix(self.dplus)
  99. #get Strongly connected components, used in modular analysis
  100. def getSCC(self,M):
  101. vindex = statePresent(M)
  102. if not vindex == -1:
  103. return self.reachability.vs[vindex]['SCC']
  104. else:
  105. return -1
  106. def getSCCvid(self,id):
  107. P = self.reachability.vs[int(id)]
  108. return self.reachability.vs[int(id)]['SCC']
  109. def statePresent(self, M):
  110. for v in self.reachability.vs:
  111. if all(v['M'] == M):
  112. return v.index
  113. return -1
  114. #get all enabled transitions for state exploration
  115. def enabledT(self,M):
  116. enabled=[]
  117. for j in range(self.numT):
  118. good = True
  119. tcol = self.dminus[0:self.numP,j] #Ti column over P as rows
  120. for i in range(self.numP):
  121. if tcol[i] > 0:
  122. t = self.mx_to_gr_indexT[j]
  123. p = self.mx_to_gr_indexP[i]
  124. weight = int(self.pngraph.es[self.pngraph.get_eid(p,t)]['weight'])
  125. if not M[i] >= weight :
  126. good = False
  127. break
  128. if good:
  129. # print 'Enabled trans %s'%self.pngraph.vs[self.mx_to_gr_indexT[j]]['name']
  130. enabled.append(j)
  131. return enabled
  132. #produce new marking
  133. def fire(self,j,M,fusion=False):
  134. if fusion:
  135. empty = []
  136. empty.append(j)
  137. return empty
  138. else:
  139. i = 0
  140. marking = ''
  141. for value in M:
  142. marking+=' %s-%d'%(self.pngraph.vs[self.mx_to_gr_indexP[i]]['name'],value)
  143. i+=1
  144. #print 'Old marking %s'%marking
  145. ts = zeros(self.numT) #array of Ts to fire, fire one transition indexed by j
  146. ts[j] = 1
  147. Mnew = M + dot(self.incidence,ts)
  148. #print 'Fire %s'%self.pngraph.vs[self.mx_to_gr_indexT[j]]['name']
  149. marking = ''
  150. i=0
  151. for value in Mnew:
  152. marking+=' %s-%d'%(self.pngraph.vs[self.mx_to_gr_indexP[i]]['name'],value)
  153. i+=1
  154. #print 'New marking %s'%marking
  155. return Mnew
  156. #create reachability graph
  157. def reachabilityG(self):
  158. work=[]
  159. work.append(self.M0)
  160. self.reachability.add_vertices(1)
  161. self.reachability.vs[0]['M'] = self.M0
  162. while work:
  163. M = work.pop()
  164. #del(work[-1])
  165. fromID = self.statePresent(M)
  166. enabledTs = self.enabledT(M)
  167. for i in enabledTs:
  168. Mnew = self.fire(i,M)
  169. idFound = self.statePresent(Mnew)
  170. if idFound == -1:
  171. self.reachability.add_vertices(1)
  172. newID = self.reachability.vcount()-1
  173. self.reachability.vs[newID]['M'] = Mnew
  174. #print fromID
  175. self.reachability.add_edges([(fromID,newID)])
  176. self.reachability.es[self.reachability.get_eid(fromID, newID)]['T'] = self.pngraph.vs[self.mx_to_gr_indexT[i]]['name']
  177. work.append(Mnew)
  178. else:
  179. self.reachability.add_edges([(fromID,idFound)])
  180. self.reachability.es[self.reachability.get_eid(fromID, idFound)]['T'] = self.pngraph.vs[self.mx_to_gr_indexT[i]]['name']
  181. self.barrier.wait() #several modules can run in parallel (comes from modular analysis) wait for all to finish.
  182. #mark strong components of a graph
  183. def SC(self):
  184. if not 'SCC' in self.reachability.vs.attribute_names():
  185. self.reachability.vs[0]['SCC'] = self.next()
  186. components = self.reachability.clusters(mode=ig.STRONG)
  187. for i in range(len(components)):
  188. ssc = self.next()
  189. changed = False
  190. partial = False
  191. for s in components[i]:
  192. if self.reachability.vs[s]['SCC']>=0:
  193. pass
  194. else:
  195. self.reachability.vs[s]['SCC'] = ssc
  196. changed = True
  197. if not changed:
  198. self.ssc -= 1
  199. #Transition fusion sets, modular analysys
  200. def addToEnabledTF(self,TF,M):
  201. if not TF in self.TFtoRun:
  202. self.TFtoRun[TF]=[]
  203. for m in self.TFtoRun[TF]:
  204. if all(m==M) :
  205. #print' duplicate'
  206. return
  207. self.TFtoRun[TF].append(M)
  208. # vattr = ''
  209. # j =0
  210. # for id in M:
  211. # vattr += '%s-%s,'%(self.pngraph.vs[self.mx_to_gr_indexP[j]]['name'],int(id))
  212. # j+=1
  213. #print 'adding %s to enabled Fusion transitions'%vattr
  214. #for modular analysis
  215. def explore(self,id):
  216. work = []
  217. work.append(id)
  218. while work:
  219. vid = work.pop()
  220. #print 'explore name %s'%self.reachability.vs[vid]['M']
  221. M = self.reachability.vs[vid]['M']
  222. enabledTs = self.enabledT(M)
  223. for i in enabledTs:
  224. if self.pngraph.vs[self.mx_to_gr_indexT[i]]['fusion'] == True: #new
  225. self.addToEnabledTF(self.pngraph.vs[self.mx_to_gr_indexT[i]]['name'],M) #new
  226. continue #noted the shared transition continue exploring.
  227. self.reachability.vs[vid]['visited'] = True
  228. succ = self.reachability.successors(vid)
  229. for i in succ:
  230. if not self.reachability.vs[i]['visited']:
  231. work.append(i)
  232. self.reset()
  233. def reset(self):
  234. for p in self.reachability.vs:
  235. p['visited']= False
  236. def reachabilityModular(self):
  237. work=[]
  238. sgwork=[]
  239. work.append(self.M0)
  240. self.reachability.add_vertices(1)
  241. self.reachability.vs[0]['M'] = self.M0
  242. xxx = 0;
  243. exp = False
  244. while self.run:
  245. while work:
  246. M = work.pop()
  247. fromID = self.statePresent(M)
  248. enabledTs = self.enabledT(M)
  249. from_M = []
  250. for i in enabledTs:
  251. if self.pngraph.vs[self.mx_to_gr_indexT[i]]['fusion'] == True: #new
  252. self.addToEnabledTF(self.pngraph.vs[self.mx_to_gr_indexT[i]]['name'],M) #new
  253. continue #noted the shared transition continue exploring.
  254. else:
  255. Mnew = self.fire(i,M)
  256. idFound = self.statePresent(Mnew)
  257. if idFound == -1:
  258. self.reachability.add_vertices(1)
  259. newID = self.reachability.vcount()-1
  260. self.reachability.vs[newID]['M'] = Mnew
  261. #print 'add edge src %d-dest %d name %s'%(fromID,newID,self.pngraph.vs[self.mx_to_gr_indexT[i]]['name'])
  262. self.reachability.add_edges([(fromID,newID)])
  263. self.reachability.es[self.reachability.get_eid(fromID, newID)]['T'] = self.pngraph.vs[self.mx_to_gr_indexT[i]]['name']
  264. #if not self.pngraph.vs[self.mx_to_gr_indexT[i]]['fusion'] == True:
  265. work.append(Mnew)
  266. #if self.pngraph.vs[self.mx_to_gr_indexT[i]]['fusion'] == True:
  267. #self.reachability.es[self.reachability.get_eid(fromID, newID)]['fusion'] = True
  268. else:
  269. if exp:
  270. pass
  271. #BACKWARDS
  272. #self.explore(idFound)
  273. self.reachability.add_edges([(fromID,idFound)])
  274. self.reachability.es[self.reachability.get_eid(fromID, idFound)]['T'] = self.pngraph.vs[self.mx_to_gr_indexT[i]]['name']
  275. if self.pngraph.vs[self.mx_to_gr_indexT[i]]['fusion'] == True:
  276. self.reachability.es[self.reachability.get_eid(fromID, idFound)]['fusion'] = True
  277. self.barrier.wait()
  278. exp = False
  279. self.graph(xxx);
  280. xxx+=1;
  281. ts = self.que.get(block=True,timeout=None)
  282. self.result={}
  283. res = {}
  284. if '@exit' in ts:
  285. self.run = False
  286. break
  287. for t in ts:
  288. self.result[t]=[]
  289. j = self.mx_to_gr_indexTn[t]
  290. # Mse = self.reachability.es.select(T_eq=t)
  291. from_M = []
  292. to_M = []
  293. for M in self.TFtoRun[t]: #loop over synchtransition edges
  294. exp = True
  295. Mnew = self.fire(j, M) #use marking at the source of the edge
  296. if self.statePresent(Mnew) == -1:
  297. self.reachability.add_vertices(1) #add this new marking
  298. newID = self.reachability.vcount()-1 # to the reachability
  299. self.reachability.vs[newID]['M'] = Mnew
  300. work.append(Mnew)
  301. from_M.append(self.statePresent(M))
  302. to_M.append(self.statePresent(Mnew))
  303. self.result[t].append(from_M)
  304. self.result[t].append(to_M)
  305. for ftlists in self.result:
  306. for from_to in self.result[ftlists]:
  307. for i in range(len(from_to)):
  308. from_to[i] = '%s-%d'%(self.key,from_to[i])
  309. for t in ts:
  310. del self.TFtoRun[t] #remove processed maybe not yet?
  311. def rnode(self,id,state):
  312. vattr = ""
  313. j=0
  314. i = len(state)
  315. for value in state:
  316. vattr += '%s-%s'%(self.pngraph.vs[self.mx_to_gr_indexP[j]]['name'],int(value))
  317. if not i-1 == 0:
  318. vattr+=','
  319. i -=1
  320. j+=1
  321. return "<node id=\"%s\"><marking>%s"%(id,vattr)+"</marking></node>\n"
  322. #export reachability graph to xml
  323. def reachtoxml(self,fname='',key=''):
  324. header = "<rgraph>\n"
  325. end = "</rgraph>"
  326. for v in self.reachability.vs:
  327. header+=self.rnode(v.index,v['M'])
  328. for e in self.reachability.es:
  329. header+="<edge source=\"%s\" target=\"%s\"><transition>%s</transition></edge>\n"%(e.source,e.target,e['T'])
  330. dateTag = datetime.datetime.now().strftime("%Y-%b-%d_%H-%M-%S")
  331. f = open('%s.%s.reachability.xml'%(fname,key),'w')
  332. header+=end
  333. f.write(header)
  334. f.close()
  335. def graph(self,key,fname='',id=None):
  336. vattr=''
  337. eattr = ''
  338. nodes = {}
  339. graph = pydot.Dot(self.key, graph_type='digraph')
  340. dateTag = datetime.datetime.now().strftime("%Y-%b-%d_%H-%M-%S")
  341. for v in self.reachability.vs:
  342. i = len(v['M'])
  343. leng = i
  344. j=0
  345. if 'SCC' in self.reachability.vs.attribute_names():
  346. vattr +='SCC-%s\n'%v['SCC']
  347. for value in v['M']:
  348. if leng == 1:
  349. vattr = 'fstate%d'%choice(range(100))
  350. else:
  351. if int(value) > 0:
  352. vattr += '%s-%s '%(self.pngraph.vs[self.mx_to_gr_indexP[j]]['name'],int(value))
  353. if not i-1 == 0:
  354. pass#vattr+=','
  355. i -=1
  356. j+=1
  357. #vattr+='\nid-%d'%v.index;
  358. #vattr +=')'
  359. nodes[v.index] = pydot.Node(vattr)
  360. graph.add_node(nodes[v.index])
  361. vattr = ''
  362. for e in self.reachability.es:
  363. graph.add_edge(pydot.Edge(nodes[e.source],nodes[e.target],label=e['T']))
  364. #graph.write_svg('graphs/STATE%s%d%s.svg'%(self.key,choice(range(100)),dateTag))
  365. if not fname:
  366. graph.write_svg('graphs/STATE%s%d%s.svg'%(self.key,choice(range(100)),dateTag))
  367. else:
  368. graph.write_svg('%s.%s.reachability.svg'%(fname,key))
  369. #the thread per module
  370. def run(self):
  371. self.reachability = ig.Graph(0,directed=True)
  372. TF = self.pngraph.vs.select(fusion_eq = True)
  373. self.TFS = []
  374. for tf in TF:
  375. self.TFS.append(tf['name'])
  376. self.P = self.pngraph.vs.select(type_eq = 'P')
  377. self.key = self.P[0]['name'][:1]
  378. self.T = self.pngraph.vs.select(type_eq = 'T')
  379. self.numP = len(self.P.indices)
  380. self.numT = len(self.T.indices)
  381. #Get places and transitions
  382. self.M0 = zeros(self.numP)
  383. self.dminus = zeros((self.numP,self.numT,))
  384. self.dplus = zeros((self.numP,self.numT,))
  385. self.DminPlusMatrix()
  386. self.incidence = self.dplus - self.dminus
  387. self.printMatrix(self.incidence)
  388. if self.full:
  389. self.reachabilityG()
  390. else:
  391. self.reachabilityModular()
  392. def reachable(self, state):
  393. P = zeros(self.numP)
  394. for ps in state:
  395. for key,value in ps.items():
  396. index = self.mx_to_gr_indexPn[key]
  397. P[index] = value
  398. id = self.statePresent(P)
  399. if not id==-1:
  400. return True
  401. else:
  402. return False
  403. def reachableMod(self,state):
  404. P = zeros(self.numP)
  405. for ps in state:
  406. for key,value in ps.items():
  407. index = self.mx_to_gr_indexPn[key]
  408. P[index] = value
  409. id = self.statePresent(P)
  410. components = self.markAncestors(id)
  411. return Set(components)
  412. def markAncestors(self,id):
  413. work = []
  414. result = []
  415. work.append(id)
  416. while work:
  417. vid = work.pop()
  418. result.append(self.reachability.vs[vid]['SCC'])
  419. self.reachability.vs[vid]['visitedd'] = True
  420. ancestors = self.reachability.predecessors(vid)
  421. for i in ancestors:
  422. if not self.reachability.vs[i]['visitedd']:
  423. work.append(i)
  424. #self.reset()
  425. return result
  426. def getEnabledTFs(self):
  427. return self.TFtoRun.keys()