simulation.py 6.6KB

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