petrinet.py 15 KB

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