CBDsimulator.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. import math
  2. from cbd.CBDBlocks import *
  3. #the old CBD formalism simulator
  4. #topological sorting needs to be extracted from this
  5. class CBDsimulator:
  6. def __init__(self, model, curIteration=0):
  7. flattened = False
  8. self.__model = model
  9. self.__depGraph = None
  10. self.sortedGraph = None
  11. for block in model.getBlocks():
  12. if isinstance(block,CBD) and flattened == False:
  13. model.flatten()
  14. flattened = True
  15. self.__curIteration = curIteration
  16. #get de sortedlist of components to use for constant folding optimization
  17. """self.__depGraph = self.__createDepGraph()
  18. self.sortedGraph = self.__depGraph.getStrongComponents()
  19. model.foldConstants(self.sortedGraph)
  20. model.dump()"""
  21. self.__depGraph = None
  22. self.sortedGraph = None
  23. #return the dep graph
  24. #TODO: make sure this is safe to do
  25. def getDepGraph(self):
  26. if self.__depGraph == None:
  27. self.__depGraph = self.__createDepGraph()
  28. return self.__depGraph
  29. # to add: flattening of hierarchical CBDs before simulation starts
  30. def getModel(self):
  31. return self.__model
  32. def step(self):
  33. #print("Iteration" + str(self.__curIteration))
  34. if (self.__curIteration < 2):
  35. self.__depGraph = self.__createDepGraph()
  36. """print "----------------- Dependency Graph ---------------------"
  37. print self.__depGraph
  38. print "--------------------------------------------------------"""""
  39. self.sortedGraph = self.__depGraph.getStrongComponents()
  40. #print "-- Sorted collection of Strongly Connected Components --"
  41. #print self.sortedGraph # a list of lists
  42. #print "--------------------------------------------------------"
  43. self.__computeBlocks()
  44. self.__curIteration += 1
  45. # Constructs the dependency graph of the current state of the CBD
  46. def __createDepGraph(self):
  47. #self.__model.dump()
  48. blocks = self.__model.getBlocks()
  49. depGraph = DepGraph()
  50. for block in blocks:
  51. #print "Block before Depgraph", block
  52. depGraph.addMember(block)
  53. for block in blocks:
  54. if not block.Trigger == None:
  55. depGraph.setDependency(block,block.Trigger)
  56. if isinstance(block, DelayBlock):
  57. if (self.__curIteration == 0):
  58. if block.IC in blocks:
  59. depGraph.setDependency(block,block.IC)
  60. else:
  61. raise ValueError("No block linked to IC port of: ", block.getBlockName())
  62. else:
  63. pass
  64. else:
  65. for influencer in block.linksIN:
  66. if influencer in blocks:
  67. depGraph.setDependency(block, influencer)
  68. else:
  69. raise ValueError("No block linked to port of: ", block.getBlockName())
  70. if isinstance(block,TestBlock):
  71. if block.TRUE_in in blocks:
  72. depGraph.setDependency(block,block.TRUE_in)
  73. else:
  74. raise ValueError("No block linked to TRUE port of: ", block.getBlockName())
  75. if block.FALSE_in in blocks:
  76. depGraph.setDependency(block,block.FALSE_in)
  77. else:
  78. raise ValueError("No block linked to FALSE port of: ", block.getBlockName())
  79. # TO IMPLEMENT
  80. # Treat dependencies on memory blocks (Delay, Integrator, Derivative) differently:
  81. # During the first iteration (time == 0.0), the block only depends on the IC;
  82. # During later iterations, it depends on the block type.
  83. #
  84. # use depGraph.setDependency(block, block_it_depends_on)
  85. #
  86. return depGraph
  87. def __computeGeneric(self, block):
  88. operator = getattr(math, block.getBlockOperator())
  89. output = operator(block.linksIN[0].getSignal()[self.__curIteration])
  90. block.appendToSignal(output)
  91. def __computeConstant(self, block):
  92. # TO IMPLEMENT
  93. block.appendToSignal(block.getValue())
  94. def __computeScope(self,block):
  95. influentBlock = block.linksIN[0]
  96. block.appendToSignal(influentBlock.getSignal()[self.__curIteration])
  97. def __computeNegator(self, block):
  98. if 1:
  99. #if block.Trigger.getSignal()[self.__curIteration]:
  100. influentBlock = block.linksIN[0]
  101. block.appendToSignal(-influentBlock.getSignal()[self.__curIteration])
  102. else:
  103. block.appendToSignal(block.getSignal()[self.__curIteration-1])
  104. def __computeGain(self, block):
  105. if 1:
  106. #if block.Trigger.getSignal()[self.__curIteration]:
  107. influentBlock = block.linksIN[0]
  108. block.appendToSignal(influentBlock.getSignal()[self.__curIteration]*block.gain)
  109. else:
  110. block.appendToSignal(block.getSignal()[self.__curIteration-1])
  111. def __computeInverter(self, block):
  112. if 1:
  113. #if block.Trigger.getSignal()[self.__curIteration]:
  114. influentBlock = block.linksIN[0]
  115. if influentBlock.getSignal()[self.__curIteration] == 0:
  116. raise ValueError("Can't divide by 0: ", block.getBlockName())
  117. else:
  118. block.appendToSignal(1/influentBlock.getSignal()[self.__curIteration])
  119. else:
  120. block.appendToSignal(block.getSignal()[self.__curIteration-1])
  121. def __computeAdder(self, block):
  122. if 1:
  123. #if block.Trigger.getSignal()[self.__curIteration]:
  124. sum = 0
  125. for influentBlock in block.linksIN:
  126. sum += influentBlock.getSignal()[self.__curIteration]
  127. block.appendToSignal(sum)
  128. else:
  129. block.appendToSignal(block.getSignal()[self.__curIteration-1])
  130. def __computeProduct(self, block):
  131. if 1:
  132. #if block.Trigger.getSignal()[self.__curIteration]:
  133. prod = 1
  134. for influentBlock in block.linksIN:
  135. prod *= influentBlock.getSignal()[self.__curIteration]
  136. block.appendToSignal(prod)
  137. else:
  138. block.appendToSignal(block.getSignal()[self.__curIteration-1])
  139. def __computePower(self, block):
  140. if 1:
  141. #if block.Trigger.getSignal()[self.__curIteration]:
  142. influentBlock = block.linksIN[0]
  143. block.appendToSignal(influentBlock.getSignal()[self.__curIteration] ** block.power)
  144. else:
  145. block.appendToSignal(block.getSignal()[self.__curIteration-1])
  146. def __computeModulo(self, block):
  147. if 1:
  148. #if block.Trigger.getSignal()[self.__curIteration]:
  149. influentBlock = block.linksIN[0]
  150. if block.div == 0:
  151. raise ValueError("Can't divide by 0: ", block.getBlockName())
  152. else:
  153. block.appendToSignal(influentBlock.getSignal()[self.__curIteration] % block.div)
  154. else:
  155. block.appendToSignal(block.getSignal()[self.__curIteration-1])
  156. def __computeDelay(self, block):
  157. if 1:
  158. #if block.Trigger.getSignal()[self.__curIteration]:
  159. if self.__curIteration == 0:
  160. block.appendToSignal(block.IC.getSignal()[self.__curIteration])
  161. else:
  162. block.appendToSignal(block.linksIN[0].getSignal()[self.__curIteration - 1])
  163. else:
  164. block.appendToSignal(block.getSignal()[self.__curIteration-1])
  165. def __computeDerivative(self, block):
  166. # Should never be used. Derivative blocks should be replaced
  167. # by block diagrams using only Delay blocks
  168. if (self.__curIteration == 0):
  169. output = block.IC.getSignal()[self.__curIteration]
  170. else:
  171. influent_block = block.linksIN[0] # only one input
  172. influent_signal = influent_block.getSignal()
  173. output = (influent_signal[self.__curIteration] - \
  174. influent_signal[self.__curIteration-1]) / \
  175. block.getTimeIncrement()
  176. block.appendToSignal(output)
  177. def __computeIntegrator(self, block):
  178. # Should never be used. Integrator blocks should be replaced
  179. # by block diagrams using only Delay blocks
  180. if (self.__curIteration == 0):
  181. output = block.IC.getSignal()[self.__curIteration]
  182. else:
  183. if block.reset and block.reset.getSignal()[self.__curIteration-1]:
  184. #print("Resetting")
  185. #print(block.getBlockName())
  186. influent_block = block.init_val
  187. output = influent_block.getSignal()[self.__curIteration - 1]
  188. else:
  189. #print("Not resetting")
  190. influent_block = block.linksIN[0] # only one input
  191. influent_signal = influent_block.getSignal()
  192. output = block.getSignal()[self.__curIteration-1] + \
  193. influent_signal[self.__curIteration-1] * \
  194. block.getTimeIncrement()
  195. block.appendToSignal(output)
  196. def __computeTest(self, block):
  197. if 1:
  198. #if block.Trigger.getSignal()[self.__curIteration]:
  199. influentBlock = block.linksIN[0]
  200. if block.expr == "=":
  201. if influentBlock.getSignal()[self.__curIteration] == block.switch_value:
  202. block.appendToSignal(block.TRUE_in.getSignal()[self.__curIteration])
  203. else:
  204. block.appendToSignal(block.FALSE_in.getSignal()[self.__curIteration])
  205. elif block.expr == ">":
  206. if influentBlock.getSignal()[self.__curIteration] > block.switch_value:
  207. block.appendToSignal(block.TRUE_in.getSignal()[self.__curIteration])
  208. else:
  209. block.appendToSignal(block.FALSE_in.getSignal()[self.__curIteration])
  210. elif block.expr == "<":
  211. if influentBlock.getSignal()[self.__curIteration] < block.switch_value:
  212. block.appendToSignal(block.TRUE_in.getSignal()[self.__curIteration])
  213. else:
  214. block.appendToSignal(block.FALSE_in.getSignal()[self.__curIteration])
  215. elif block.expr == "<=":
  216. if influentBlock.getSignal()[self.__curIteration] < block.switch_value or influentBlock.getSignal()[self.__curIteration] == block.switch_value:
  217. block.appendToSignal(block.TRUE_in.getSignal()[self.__curIteration])
  218. else:
  219. block.appendToSignal(block.FALSE_in.getSignal()[self.__curIteration])
  220. elif block.expr == ">=":
  221. if influentBlock.getSignal()[self.__curIteration] > block.switch_value or influentBlock.getSignal()[self.__curIteration] == block.switch_value:
  222. block.appendToSignal(block.TRUE_in.getSignal()[self.__curIteration])
  223. else:
  224. block.appendToSignal(block.FALSE_in.getSignal()[self.__curIteration])
  225. else:
  226. raise ValueError("Expression "+block.expr+" isn't a legal expression. Allowed expression are: = > < >= <=")
  227. else:
  228. block.appendToSignal(block.getSignal()[self.__curIteration-1])
  229. def __computeSequence(self, block):
  230. if 1:
  231. #if block.Trigger.getSignal()[self.__curIteration]:
  232. block.appendToSignal(block.seq[block.iteration])
  233. if self.__curIteration != 0:
  234. block.iteration += 1
  235. if block.iteration == len(block.seq):
  236. block.iteration = 0
  237. else:
  238. block.appendToSignal(block.getSignal()[self.__curIteration-1])
  239. def __computeFloor(self, block):
  240. if 1:
  241. #if block.Trigger.getSignal()[self.__curIteration]:
  242. influentBlock = block.linksIN[0]
  243. block.appendToSignal(math.floor(influentBlock.getSignal()[self.__curIteration]))
  244. else:
  245. block.appendToSignal(block.getSignal()[self.__curIteration-1])
  246. def __computeBlocks(self):
  247. for component in self.sortedGraph:
  248. if (not self.__hasCycle(component)):
  249. block = component[0] # the strongly connected component has a single element
  250. #print "Block type + name:", block.getBlockType(),":",block.getBlockName()
  251. if isinstance(block, GenericBlock):
  252. self.__computeGeneric(block)
  253. elif isinstance(block, ConstantBlock):
  254. self.__computeConstant(block)
  255. elif isinstance(block, ScopeBlock):
  256. self.__computeScope(block)
  257. elif isinstance(block, NegatorBlock):
  258. self.__computeNegator(block)
  259. elif isinstance(block, InverterBlock):
  260. self.__computeInverter(block)
  261. elif isinstance(block, AdderBlock):
  262. self.__computeAdder(block)
  263. elif isinstance(block, ProductBlock):
  264. self.__computeProduct(block)
  265. elif isinstance(block, GainBlock):
  266. self.__computeGain(block)
  267. elif isinstance(block, FloorBlock):
  268. self.__computeFloor(block)
  269. elif isinstance(block, PowerBlock):
  270. self.__computePower(block)
  271. elif isinstance(block, ModuloBlock):
  272. self.__computeModulo(block)
  273. elif isinstance(block, DerivativeBlock):
  274. self.__computeDerivative(block)
  275. elif isinstance(block, IntegratorBlock):
  276. self.__computeIntegrator(block)
  277. elif isinstance(block, DelayBlock): # note that this needs to come AFTER Derivative and Integrator !
  278. # as they are sub-classes of DelayBlock
  279. self.__computeDelay(block)
  280. elif isinstance(block, TestBlock):
  281. self.__computeTest(block)
  282. elif isinstance(block,SequenceRepeater):
  283. self.__computeSequence(block)
  284. elif isinstance(block, CBD):
  285. raise ValueError("Hierachical blocks should have been flattened by now")
  286. else:
  287. raise ValueError("Unknown block type")
  288. else: #TODO what happens when blocks in loop have different rates?
  289. print("Component" + str(component))
  290. # Detected a strongly connected component
  291. assert self.__isLinear(component), "Cannot solve non-linear algebraic loop"
  292. solverInput = self.__constructLinearInput(component)
  293. print("########### Input matrix for linear solver -> ###########")
  294. self.__printLinearInputMatrices(component, solverInput)
  295. self.__gaussjLinearSolver(solverInput)
  296. solutionVector = solverInput[1]
  297. print("########### Solution from linear solver -> ###########")
  298. for block in component:
  299. blockIndex = component.index(block)
  300. block.appendToSignal(solutionVector[blockIndex])
  301. print(block.getBlockName() + "=", solutionVector[blockIndex] + "\n")
  302. # Determine whether a component is cyclic
  303. def __hasCycle(self, component):
  304. assert len(component)>=1, "A component should have at least one element"
  305. if len(component)>1:
  306. return True
  307. else: # a strong component of size one may still have a cycle: a self-loop
  308. if self.__depGraph.hasDependency(component[0], component[0]):
  309. return True
  310. else:
  311. return False
  312. # Determines if an algebraic loop describes a linear equation or not
  313. def __isLinear(self, strongComponent):
  314. # TO IMPLEMENT
  315. for block in strongComponent:
  316. if (isinstance(block, NegatorBlock) or isinstance(block, AdderBlock) or isinstance(block,GainBlock) ):
  317. pass
  318. elif isinstance(block, ProductBlock):
  319. strongInputs = 0
  320. for influentBlock in block.linksIN:
  321. if (influentBlock in strongComponent):
  322. strongInputs += 1
  323. if (strongInputs >= 2):
  324. return False
  325. else:
  326. return False
  327. print("Linear algebraic loop detected")
  328. return True
  329. # Constructs input for a solver of systems of linear equations
  330. # Input consists of two matrices:
  331. # M1: coefficient matrix, where each row represents an equation of the system
  332. # M2: result matrix, where each element is the result for the corresponding equation in M1
  333. def __constructLinearInput(self, strongComponent):
  334. size = len(strongComponent)
  335. row = []
  336. M1 = []
  337. M2 = []
  338. # Initialize matrices with zeros
  339. i = 0
  340. while (i < size):
  341. j = 0
  342. row = []
  343. while (j < size):
  344. row.append(0)
  345. j += 1
  346. M1.append(row)
  347. M2.append(0)
  348. i += 1
  349. # TO IMPLEMENT
  350. for index in range(0, len(strongComponent)):
  351. print("index = ", index)
  352. block = strongComponent[index]
  353. print("strongComponent.index = " + str(strongComponent.index(block)))
  354. blockType = block.getBlockType()
  355. if isinstance(block, AdderBlock):
  356. for influentblock in block.linksIN:
  357. if (not(influentblock in strongComponent)):
  358. M2[index] = -influentblock.getSignal()[self.__curIteration]
  359. print("I'm here " + str(influentblock.getSignal()[self.__curIteration]) +
  360. str(M2))
  361. else:
  362. print("influentblock index = " + str(strongComponent.index(influentblock)))
  363. print("M1 = " + str(M1))
  364. M1[index][index] = -1
  365. M1[index][strongComponent.index(influentblock)] = 1
  366. elif isinstance(block, NegatorBlock):
  367. influentblock = block.linksIN[0]
  368. M2[index] = 0
  369. M1[index][index] = -1
  370. M1[index][strongComponent.index(influentblock)] = -1
  371. elif isinstance(block, ProductBlock):
  372. factor = 1
  373. infuentindex = 0
  374. for influentblock in block.linksIN:
  375. if (not(influentblock in strongComponent)):
  376. factor = influentblock.getSignal()[self.__curIteration]
  377. else:
  378. infuentindex = strongComponent.index(influentblock)
  379. M2[index] = 0
  380. M1[index][index] = -1
  381. M1[index][infuentindex] = factor
  382. else:
  383. print("Fault in check linear")
  384. return [M1, M2]
  385. def __swap(self, a, b):
  386. t = a
  387. a = b
  388. b = t
  389. return (a, b)
  390. def __ivector(self, n):
  391. v = []
  392. for i in range(n):
  393. v.append(0)
  394. return v
  395. # Linear equation solution by Gauss-Jordan elimination
  396. def __gaussjLinearSolver(self, solverInput):
  397. M1 = solverInput[0]
  398. M2 = solverInput[1]
  399. n = len(M1)
  400. indxc = self.__ivector(n)
  401. indxr = self.__ivector(n)
  402. ipiv = self.__ivector(n)
  403. icol = 0
  404. irow = 0
  405. for i in range(n):
  406. big = 0.0
  407. for j in range(n):
  408. if (ipiv[j] != 1):
  409. for k in range(n):
  410. if (ipiv[k] == 0):
  411. if (math.fabs(M1[j][k]) >= big):
  412. big = math.fabs(M1[j][k])
  413. irow = j
  414. icol = k
  415. elif (ipiv[k] > 1):
  416. raise ValueError("GAUSSJ: Singular Matrix-1")
  417. ipiv[icol] += 1
  418. if (irow != icol):
  419. for l in range(n):
  420. (M1[irow][l], M1[icol][l]) = self.__swap(M1[irow][l], M1[icol][l])
  421. (M2[irow], M2[icol]) = self.__swap(M2[irow], M2[icol])
  422. indxr[i] = irow
  423. indxc[i] = icol
  424. if (M1[icol][icol] == 0.0):
  425. raise ValueError("GAUSSJ: Singular Matrix-2")
  426. pivinv = 1.0/M1[icol][icol]
  427. M1[icol][icol] = 1.0
  428. for l in range(n):
  429. M1[icol][l] *= pivinv
  430. M2[icol] *= pivinv
  431. for ll in range(n):
  432. if (ll != icol):
  433. dum = M1[ll][icol]
  434. M1[ll][icol] = 0.0
  435. for l in range(n):
  436. M1[ll][l] -= M1[icol][l] * dum
  437. M2[ll] -= M2[icol] * dum
  438. l = n
  439. while (l > 0):
  440. l -= 1
  441. if (indxr[l] != indxc[l]):
  442. for k in range(n):
  443. (M1[k][indxr[l]], M1[k][indxc[l]]) = self.__swap(M1[k][indxr[l]], M1[k][indxc[l]])
  444. # Print out the input matrices
  445. def __printLinearInputMatrices(self, strongComponent, solverInput):
  446. print("Indices:")
  447. i = 0
  448. for block in strongComponent:
  449. print(str(i) + ":" + block.getBlockName() + block.getBlockType())
  450. i += 1
  451. print("Matrices:")
  452. M1 = solverInput[0]
  453. M2 = solverInput[1]
  454. for row in M1:
  455. print(row + "=" + M2[M1.index(row)])
  456. """ This module implements a dependency graph
  457. @author: Marc Provost
  458. @organization: McGill University
  459. @license: GNU General Public License
  460. @contact: marc.provost@mail.mcgill.ca
  461. """
  462. import copy
  463. class DepNode:
  464. """ Class implementing a node in the dependency graph.
  465. """
  466. def __init__(self, object):
  467. """ DepNode's constructor.
  468. @param object: Reference to a semantic object identifying the node
  469. @type object: Object
  470. """
  471. self.__object = object
  472. self.__isMarked = False
  473. def mark(self):
  474. self.__isMarked = True
  475. def unMark(self):
  476. self.__isMarked = False
  477. def isMarked(self):
  478. return self.__isMarked
  479. def getMappedObj(self):
  480. return self.__object
  481. def __repr__(self):
  482. return "DepNode :: "+str(self.__object)
  483. class DepGraph:
  484. """ Class implementing dependency graph.
  485. """
  486. def __init__(self):
  487. """ DepGraph's constructor.
  488. """
  489. #Dict holding a mapping "Object -> DepNode"
  490. self.__semanticMapping = {}
  491. #map object->list of objects depending on object
  492. self.__dependents = {}
  493. #map object->list of objects that influences object
  494. self.__influencers = {}
  495. def __repr__(self):
  496. repr = "Dependents: \n"
  497. for dep in self.__dependents:
  498. repr += dep.getBlockName() + ":" + str(self.__dependents[dep]) + "\n"
  499. repr += "Influencers: \n"
  500. for infl in self.__influencers:
  501. repr += infl.getBlockName() + ":" + str(self.__influencers[infl]) + "\n"
  502. return repr
  503. def addMember(self, object):
  504. """ Add an object mapped to this graph.
  505. @param object: the object to be added
  506. @type object: Object
  507. @raise ValueError: If object is already in the graph
  508. """
  509. if not self.hasMember(object):
  510. node = DepNode(object)
  511. self.__dependents[object] = []
  512. self.__influencers[object] = []
  513. self.__semanticMapping[object] = node
  514. else:
  515. raise ValueError("Specified object is already member of this graph")
  516. def hasMember(self, object):
  517. return object in self.__semanticMapping
  518. def removeMember(self, object):
  519. """ Remove a object from this graph.
  520. @param object: the object to be removed
  521. @type object: Object
  522. @raise ValueError: If object is not in the graph
  523. """
  524. if self.hasMember(object):
  525. for dependent in self.getDependents(object):
  526. self.__influencers[dependent].remove(object)
  527. for influencer in self.getInfluencers(object):
  528. self.__dependents[influencer].remove(object)
  529. del self.__dependents[object]
  530. del self.__influencers[object]
  531. del self.__semanticMapping[object]
  532. else:
  533. raise ValueError("Specified object is not member of this graph")
  534. def setDependency(self, dependent, influencer):
  535. """ Creates a dependency between two objects.
  536. @param dependent: The object which depends on the other
  537. @param influencer: The object which influences the other
  538. @type dependent: Object
  539. @type dependent: Object
  540. @raise ValueError: if dependent or influencer is not member of this graph
  541. @raise ValueError: if the dependency already exists
  542. """
  543. if self.hasMember(dependent) and self.hasMember(influencer):
  544. if not influencer in self.__influencers[dependent] and\
  545. not dependent in self.__dependents[influencer]:
  546. self.__influencers[dependent].append(influencer)
  547. self.__dependents[influencer].append(dependent)
  548. else:
  549. print("DEPENDENT" + str(dependent))
  550. print("influencer" + str(influencer))
  551. raise ValueError("Specified dependency already exists")
  552. else:
  553. if not self.hasMember(dependent):
  554. raise ValueError("Specified dependent object is not member of this graph")
  555. if not self.hasMember(influencer):
  556. raise ValueError("Specified influencer object is not member of this graph")
  557. def hasDependency(self, dependent, influencer):
  558. if self.hasMember(dependent) and self.hasMember(influencer):
  559. return influencer in self.__influencers[dependent] and\
  560. dependent in self.__dependents[influencer]
  561. else:
  562. if not self.hasMember(dependent):
  563. raise ValueError("Specified dependent object is not member of this graph")
  564. if not self.hasMember(influencer):
  565. raise ValueError("Specified influencer object is not member of this graph")
  566. def unsetDependency(self, dependent, influencer):
  567. """ Removes a dependency between two objects.
  568. @param dependent: The object which depends on the other
  569. @param influencer: The object which influences the other
  570. @type dependent: Object
  571. @type dependent: Object
  572. @raise ValueError: if dependent or influencer is not member of this graph
  573. @raise ValueError: if the dependency does not exists
  574. """
  575. if self.hasMember(dependent) and self.hasMember(influencer):
  576. if influencer in self.__influencers[dependent] and\
  577. dependent in self.__dependents[influencer]:
  578. self.__influencers[dependent].remove(influencer)
  579. self.__dependents[influencer].remove(dependent)
  580. else:
  581. raise ValueError("Specified dependency does not exists")
  582. else:
  583. if not self.hasMember(dependent):
  584. raise ValueError("Specified dependent object is not member of this graph")
  585. if not self.hasMember(influencer):
  586. raise ValueError("Specified influencer object is not member of this graph")
  587. def getDependents(self, object):
  588. if self.hasMember(object):
  589. return copy.copy(self.__dependents[object])
  590. else:
  591. raise ValueError("Specified object is not member of this graph")
  592. def getInfluencers(self, object):
  593. if self.hasMember(object):
  594. return copy.copy(self.__influencers[object])
  595. else:
  596. raise ValueError("Specified object is not member of this graph")
  597. def getStrongComponents(self):
  598. return self.__strongComponents()
  599. def __getDepNode(self, object):
  600. if self.hasMember(object):
  601. return self.__semanticMapping[object]
  602. else:
  603. raise ValueError("Specified object is not a member of this graph")
  604. def __mark(self, object):
  605. self.__getDepNode(object).mark()
  606. def __unMark(self, object):
  607. self.__getDepNode(object).unMark()
  608. def __isMarked(self, object):
  609. return self.__getDepNode(object).isMarked()
  610. def __topoSort(self):
  611. """ Performs a topological sort on the graph.
  612. """
  613. for object in self.__semanticMapping.keys():
  614. self.__unMark(object)
  615. sortedList = []
  616. for object in self.__semanticMapping.keys():
  617. if not self.__isMarked(object):
  618. self.__dfsSort(object, sortedList)
  619. return sortedList
  620. def __dfsSort(self, object, sortedList):
  621. """ Performs a depth first search collecting
  622. the objects in topological order.
  623. @param object: the currently visited object.
  624. @param sortedList: partial sorted list of objects
  625. @type object: Object
  626. @type sortedList: list Of Object
  627. """
  628. if not self.__isMarked(object):
  629. self.__mark(object)
  630. for influencer in self.getInfluencers(object):
  631. self.__dfsSort(influencer, sortedList)
  632. sortedList.append(object)
  633. def __strongComponents(self):
  634. """ Determine the strong components of the graph
  635. @rtype: list of list of Object
  636. """
  637. strongComponents = []
  638. sortedList = self.__topoSort()
  639. for object in self.__semanticMapping.keys():
  640. self.__unMark(object)
  641. sortedList.reverse()
  642. for object in sortedList:
  643. if not self.__isMarked(object):
  644. component = []
  645. self.__dfsCollect(object, component)
  646. strongComponents.append(component)
  647. strongComponents.reverse()
  648. return strongComponents
  649. def __dfsCollect(self, object, component):
  650. """ Collects objects member of a strong component.
  651. @param object: Node currently visited
  652. @param component: current component
  653. @type object: Object
  654. @type component: List of Object
  655. """
  656. if not self.__isMarked(object):
  657. self.__mark(object)
  658. for dependent in self.getDependents(object):
  659. self.__dfsCollect(dependent, component)
  660. component.append(object)