simulation.py 6.8KB

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