simulation.py 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. # coding: utf-8
  2. import collections
  3. import typing
  4. from synergine2.base import BaseObject
  5. from synergine2.config import Config
  6. from synergine2.share import shared
  7. from synergine2.utils import get_mechanisms_classes
  8. class Intention(object):
  9. pass
  10. class IntentionManager(object):
  11. def __init__(self) -> None:
  12. self.intentions = {} # type: typing.Dict[typing.Type[Intention], Intention]
  13. def set(self, intention: Intention) -> None:
  14. self.intentions[type(intention)] = intention
  15. def get(self, intention_type: typing.Type[Intention]) -> Intention:
  16. return self.intentions[intention_type]
  17. class Subject(BaseObject):
  18. collections = []
  19. behaviours_classes = []
  20. behaviour_selector_class = None # type: typing.Type[SubjectBehaviourSelector]
  21. intention_manager_class = None # type: typing.Type[IntentionManager]
  22. def __init__(
  23. self,
  24. config: Config,
  25. simulation: 'Simulation',
  26. ):
  27. # TODO: Bannir les attribut de classe passé en reference ! Et meme virer les attr de classe tout court.
  28. self.collections = self.collections[:]
  29. self.config = config
  30. self._id = id(self) # We store object id because it's lost between process
  31. self.simulation = simulation
  32. self.intentions = None
  33. if self.behaviour_selector_class:
  34. self.behaviour_selector = self.behaviour_selector_class()
  35. else:
  36. self.behaviour_selector = SubjectBehaviourSelector()
  37. if self.intention_manager_class:
  38. self.intentions = self.intention_manager_class()
  39. else:
  40. self.intentions = IntentionManager()
  41. # TODO: Revoir le mechanisme de collection: utilité, usage avec les process, etc
  42. # for collection in self.collections:
  43. # self.simulation.collections[collection].append(self)
  44. @property
  45. def id(self) -> int:
  46. return self._id
  47. def change_id(self, id_: int) -> None:
  48. self._id = id_
  49. def expose(self) -> None:
  50. subject_behaviours_index = shared.get('subject_behaviours_index').setdefault(self._id, [])
  51. subject_mechanisms_index = shared.get('subject_mechanisms_index').setdefault(self._id, [])
  52. subject_classes = shared.get('subject_classes')
  53. for behaviour_class in self.behaviours_classes:
  54. subject_behaviours_index.append(id(behaviour_class))
  55. for mechanism_class in behaviour_class.use:
  56. subject_mechanisms_index.append(id(mechanism_class))
  57. subject_classes[self._id] = id(type(self))
  58. def __str__(self):
  59. return self.__repr__()
  60. def __repr__(self):
  61. return '{}({})'.format(
  62. type(self).__name__,
  63. self.id,
  64. )
  65. class Subjects(list):
  66. """
  67. TODO: Manage other list methods
  68. """
  69. subject_ids = shared.create('subject_ids', [])
  70. def __init__(self, *args, **kwargs):
  71. self.simulation = kwargs.pop('simulation')
  72. self.removes = []
  73. self.adds = []
  74. self.track_changes = False
  75. self.index = {}
  76. self._auto_expose = True
  77. super().__init__(*args, **kwargs)
  78. @property
  79. def auto_expose(self) -> bool:
  80. return self._auto_expose
  81. @auto_expose.setter
  82. def auto_expose(self, value: bool) -> None:
  83. assert self._auto_expose
  84. self._auto_expose = value
  85. def remove(self, value: Subject):
  86. # Remove from index
  87. del self.index[value.id]
  88. self.subject_ids.remove(value.id)
  89. # Remove from subjects list
  90. super().remove(value)
  91. # Remove from collections
  92. for collection_name in value.collections:
  93. self.simulation.collections[collection_name].remove(value)
  94. # Add to removed listing
  95. if self.track_changes:
  96. self.removes.append(value)
  97. # TODO: Supprimer des choses du shared ! Sinon fuite mémoire dans la bdd
  98. def append(self, p_object):
  99. # Add to index
  100. self.index[p_object.id] = p_object
  101. self.subject_ids.append(p_object.id)
  102. # Add to subjects list
  103. super().append(p_object)
  104. # Add to adds list
  105. if self.track_changes:
  106. self.adds.append(p_object)
  107. if self.auto_expose:
  108. p_object.expose()
  109. class Simulation(BaseObject):
  110. accepted_subject_class = Subjects
  111. behaviours_classes = []
  112. subject_behaviours_index = shared.create('subject_behaviours_index', {})
  113. subject_mechanisms_index = shared.create('subject_mechanisms_index', {})
  114. subject_classes = shared.create('subject_classes', {})
  115. def __init__(
  116. self,
  117. config: Config,
  118. ):
  119. self.config = config
  120. self.collections = collections.defaultdict(list)
  121. self._subjects = None # type: Subjects
  122. # Should contain all usable class of Behaviors, Mechanisms, SubjectBehaviourSelectors,
  123. # IntentionManagers, Subject
  124. self._index = {} # type: typing.Dict[int, type]
  125. self._index_locked = False
  126. self.behaviours = {}
  127. self.mechanisms = {}
  128. for mechanism_class in get_mechanisms_classes(self):
  129. self.mechanisms[mechanism_class.__name__] = mechanism_class(
  130. config=self.config,
  131. simulation=self,
  132. )
  133. for behaviour_class in self.behaviours_classes:
  134. self.behaviours[behaviour_class.__name__] = behaviour_class(
  135. config=self.config,
  136. simulation=self,
  137. )
  138. def add_to_index(self, class_: type) -> None:
  139. assert not self._index_locked
  140. self._index[id(class_)] = class_
  141. @property
  142. def index(self) -> typing.Dict[int, type]:
  143. return self._index
  144. def lock_index(self) -> None:
  145. self._index_locked = True
  146. @property
  147. def subjects(self):
  148. return self._subjects
  149. @subjects.setter
  150. def subjects(self, value: 'Subjects'):
  151. if not isinstance(value, self.accepted_subject_class):
  152. raise Exception('Simulation.subjects must be {0} type'.format(
  153. self.accepted_subject_class,
  154. ))
  155. self._subjects = value
  156. def get_or_create_subject(self, subject_id: int) -> Subject:
  157. try:
  158. return self._subjects.index[subject_id]
  159. except KeyError:
  160. # We should be in process context and subject have to been created
  161. subject_class_id = shared.get('subject_classes')[subject_id]
  162. subject_class = self.index[subject_class_id]
  163. subject = subject_class(self.config, self)
  164. subject.change_id(subject_id)
  165. self.subjects.append(subject)
  166. return subject
  167. class Mechanism(BaseObject):
  168. pass
  169. class SubjectMechanism(Mechanism):
  170. def __init__(
  171. self,
  172. config: Config,
  173. simulation: Simulation,
  174. subject: Subject,
  175. ):
  176. self.config = config
  177. self.simulation = simulation
  178. self.subject = subject
  179. def run(self):
  180. raise NotImplementedError()
  181. class SimulationMechanism(Mechanism):
  182. """If parallelizable behaviour, call """
  183. parallelizable = False
  184. def __init__(
  185. self,
  186. config: Config,
  187. simulation: Simulation,
  188. ):
  189. self.config = config
  190. self.simulation = simulation
  191. def repr_debug(self) -> str:
  192. return self.__class__.__name__
  193. def run(self, process_id: int=None, process_count: int=None):
  194. raise NotImplementedError()
  195. class Event(BaseObject):
  196. def __init__(self, *args, **kwargs):
  197. pass
  198. def repr_debug(self) -> str:
  199. return self.__class__.__name__
  200. class Behaviour(BaseObject):
  201. def run(self, data):
  202. raise NotImplementedError()
  203. class SubjectBehaviour(Behaviour):
  204. frequency = 1
  205. use = [] # type: typing.List[typing.Type[SubjectMechanism]]
  206. def __init__(
  207. self,
  208. config: Config,
  209. simulation: Simulation,
  210. subject: Subject,
  211. ):
  212. self.config = config
  213. self.simulation = simulation
  214. self.subject = subject
  215. def run(self, data):
  216. """
  217. Method called in subprocess.
  218. If return equivalent to False, behaviour produce nothing.
  219. If return equivalent to True, action bahaviour method
  220. will be called with these data
  221. Note: Returned data will be transfered from sub processes.
  222. Prefer scalar types.
  223. """
  224. raise NotImplementedError() # TODO Test it and change to strictly False
  225. def action(self, data) -> [Event]:
  226. """
  227. Method called in main process
  228. Return events will be give to terminals
  229. """
  230. raise NotImplementedError()
  231. class SimulationBehaviour(Behaviour):
  232. frequency = 1
  233. use = []
  234. def __init__(
  235. self,
  236. config: Config,
  237. simulation: Simulation,
  238. ):
  239. self.config = config
  240. self.simulation = simulation
  241. def run(self, data):
  242. """
  243. Method called in subprocess if mechanisms are
  244. parallelizable, in main process if not.
  245. """
  246. raise NotImplementedError()
  247. @classmethod
  248. def merge_data(cls, new_data, start_data=None):
  249. """
  250. Called if behaviour executed in subprocess
  251. """
  252. raise NotImplementedError()
  253. def action(self, data) -> [Event]:
  254. """
  255. Method called in main process
  256. Return events will be give to terminals
  257. """
  258. raise NotImplementedError()
  259. class SubjectBehaviourSelector(BaseObject):
  260. def reduce_behaviours(
  261. self,
  262. behaviours: typing.Dict[typing.Type[SubjectBehaviour], object],
  263. ) -> typing.Dict[typing.Type[SubjectBehaviour], object]:
  264. return behaviours