simulation.py 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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.utils import get_mechanisms_classes
  7. class Intention(object):
  8. pass
  9. class IntentionManager(object):
  10. def __init__(self) -> None:
  11. self.intentions = {} # type: typing.Dict[typing.Type[Intention], Intention]
  12. def set(self, intention: Intention) -> None:
  13. self.intentions[type(intention)] = intention
  14. def get(self, intention_type: typing.Type[Intention]) -> Intention:
  15. return self.intentions[intention_type]
  16. class Subject(BaseObject):
  17. collections = []
  18. behaviours_classes = []
  19. behaviour_selector_class = None # type: typing.Type[SubjectBehaviourSelector]
  20. def __init__(
  21. self,
  22. config: Config,
  23. simulation: 'Simulation',
  24. ):
  25. # TODO: Bannir les attribut de classe passé en reference ! Et meme virer les attr de classe tout court.
  26. self.collections = self.collections[:]
  27. self.config = config
  28. self.id = id(self) # We store object id because it's lost between process
  29. self.simulation = simulation
  30. self.behaviours = {}
  31. self.mechanisms = {}
  32. self.intentions = IntentionManager()
  33. self.behaviour_selector = None # type: SubjectBehaviourSelector
  34. if self.behaviour_selector_class:
  35. self.behaviour_selector = self.behaviour_selector_class()
  36. for collection in self.collections:
  37. self.simulation.collections[collection].append(self)
  38. self.initialize()
  39. def __str__(self):
  40. return self.__repr__()
  41. def __repr__(self):
  42. return '{}({})'.format(
  43. type(self).__name__,
  44. self.id,
  45. )
  46. def initialize(self):
  47. for mechanism_class in get_mechanisms_classes(self):
  48. self.mechanisms[mechanism_class] = mechanism_class(
  49. config=self.config,
  50. simulation=self.simulation,
  51. subject=self,
  52. )
  53. for behaviour_class in self.behaviours_classes:
  54. self.behaviours[behaviour_class] = behaviour_class(
  55. config=self.config,
  56. simulation=self.simulation,
  57. subject=self,
  58. )
  59. class Subjects(list):
  60. """
  61. TODO: Manage other list methods
  62. """
  63. def __init__(self, *args, **kwargs):
  64. self.simulation = kwargs.pop('simulation')
  65. self.removes = []
  66. self.adds = []
  67. self.track_changes = False
  68. self.index = {}
  69. super().__init__(*args, **kwargs)
  70. def remove(self, value: Subject):
  71. # Remove from index
  72. del self.index[value.id]
  73. # Remove from subjects list
  74. super().remove(value)
  75. # Remove from collections
  76. for collection_name in value.collections:
  77. self.simulation.collections[collection_name].remove(value)
  78. # Add to removed listing
  79. if self.track_changes:
  80. self.removes.append(value)
  81. def append(self, p_object):
  82. # Add to index
  83. self.index[p_object.id] = p_object
  84. # Add to subjects list
  85. super().append(p_object)
  86. # Add to adds list
  87. if self.track_changes:
  88. self.adds.append(p_object)
  89. class Simulation(BaseObject):
  90. accepted_subject_class = Subjects
  91. behaviours_classes = []
  92. def __init__(
  93. self,
  94. config: Config,
  95. ):
  96. self.config = config
  97. self.collections = collections.defaultdict(list)
  98. self._subjects = None
  99. self.behaviours = {}
  100. self.mechanisms = {}
  101. self.initialize()
  102. @property
  103. def subjects(self):
  104. return self._subjects
  105. @subjects.setter
  106. def subjects(self, value: 'Subjects'):
  107. if not isinstance(value, self.accepted_subject_class):
  108. raise Exception('Simulation.subjects must be {0} type'.format(
  109. self.accepted_subject_class,
  110. ))
  111. self._subjects = value
  112. def initialize(self):
  113. for mechanism_class in get_mechanisms_classes(self):
  114. self.mechanisms[mechanism_class] = mechanism_class(
  115. config=self.config,
  116. simulation=self,
  117. )
  118. for behaviour_class in self.behaviours_classes:
  119. self.behaviours[behaviour_class] = behaviour_class(
  120. config=self.config,
  121. simulation=self,
  122. )
  123. class SubjectMechanism(BaseObject):
  124. def __init__(
  125. self,
  126. config: Config,
  127. simulation: Simulation,
  128. subject: Subject,
  129. ):
  130. self.config = config
  131. self.simulation = simulation
  132. self.subject = subject
  133. def run(self):
  134. raise NotImplementedError()
  135. class SimulationMechanism(BaseObject):
  136. """If parallelizable behaviour, call """
  137. parallelizable = False
  138. def __init__(
  139. self,
  140. config: Config,
  141. simulation: Simulation,
  142. ):
  143. self.config = config
  144. self.simulation = simulation
  145. def repr_debug(self) -> str:
  146. return self.__class__.__name__
  147. def run(self, process_id: int=None, process_count: int=None):
  148. raise NotImplementedError()
  149. class Event(BaseObject):
  150. def __init__(self, *args, **kwargs):
  151. pass
  152. def repr_debug(self) -> str:
  153. return self.__class__.__name__
  154. class SubjectBehaviour(BaseObject):
  155. frequency = 1
  156. use = [] # type: typing.List[typing.Type[SubjectMechanism]]
  157. def __init__(
  158. self,
  159. config: Config,
  160. simulation: Simulation,
  161. subject: Subject,
  162. ):
  163. self.config = config
  164. self.simulation = simulation
  165. self.subject = subject
  166. def run(self, data):
  167. """
  168. Method called in subprocess.
  169. If return equivalent to False, behaviour produce nothing.
  170. If return equivalent to True, action bahaviour method
  171. will be called with these data
  172. Note: Returned data will be transfered from sub processes.
  173. Prefer scalar types.
  174. """
  175. raise NotImplementedError() # TODO Test it and change to strictly False
  176. def action(self, data) -> [Event]:
  177. """
  178. Method called in main process
  179. Return events will be give to terminals
  180. """
  181. raise NotImplementedError()
  182. class SimulationBehaviour(BaseObject):
  183. frequency = 1
  184. use = []
  185. def __init__(
  186. self,
  187. config: Config,
  188. simulation: Simulation,
  189. ):
  190. self.config = config
  191. self.simulation = simulation
  192. def run(self, data):
  193. """
  194. Method called in subprocess if mechanisms are
  195. parallelizable, in main process if not.
  196. """
  197. raise NotImplementedError()
  198. @classmethod
  199. def merge_data(cls, new_data, start_data=None):
  200. """
  201. Called if behaviour executed in subprocess
  202. """
  203. raise NotImplementedError()
  204. def action(self, data) -> [Event]:
  205. """
  206. Method called in main process
  207. Return events will be give to terminals
  208. """
  209. raise NotImplementedError()
  210. class SubjectBehaviourSelector(BaseObject):
  211. def reduce_behaviours(
  212. self,
  213. behaviours: typing.Dict[typing.Type[SubjectBehaviour], object],
  214. ) -> typing.Dict[typing.Type[SubjectBehaviour], object]:
  215. raise NotImplementedError()