simulation.py 6.9KB

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