cycle.py 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. # coding: utf-8
  2. import multiprocessing
  3. from synergine2.base import BaseObject
  4. from synergine2.config import Config
  5. from synergine2.log import SynergineLogger
  6. from synergine2.processing import ProcessManager
  7. from synergine2.simulation import SimulationMechanism
  8. from synergine2.simulation import SimulationBehaviour
  9. from synergine2.simulation import Simulation
  10. from synergine2.simulation import SubjectBehaviour
  11. from synergine2.simulation import SubjectMechanism
  12. from synergine2.simulation import Event
  13. from synergine2.utils import ChunkManager, time_it
  14. class CycleManager(BaseObject):
  15. def __init__(
  16. self,
  17. config: Config,
  18. logger: SynergineLogger,
  19. simulation: Simulation,
  20. process_manager: ProcessManager=None,
  21. ):
  22. if process_manager is None:
  23. process_manager = ProcessManager(
  24. # TODO: Changer de config de merde (core.use_x_cores)
  25. process_count=config.get('core', {}).get('use_x_cores', multiprocessing.cpu_count()),
  26. chunk_manager=ChunkManager(multiprocessing.cpu_count()),
  27. )
  28. self.config = config
  29. self.logger = logger
  30. self.simulation = simulation
  31. self.process_manager = process_manager
  32. self.current_cycle = -1
  33. self.first_cycle = True
  34. def next(self) -> [Event]:
  35. if self.first_cycle:
  36. # To dispatch subjects add/removes, enable track on them
  37. self.simulation.subjects.track_changes = True
  38. self.first_cycle = False
  39. self.current_cycle += 1
  40. self.logger.info('Process cycle {}'.format(self.current_cycle))
  41. events = []
  42. # TODO: gestion des behaviours non parallelisables
  43. # TODO: Proposer des ordres d'execution
  44. with time_it() as elapsed_time:
  45. events.extend(self._get_subjects_events())
  46. print('Cycle subjects events duration: {}s'.format(elapsed_time.get_final_time()))
  47. with time_it() as elapsed_time:
  48. events.extend(self._get_simulation_events())
  49. print('Cycle simulation events duration: {}s'.format(elapsed_time.get_final_time()))
  50. self.logger.info('Cycle {} generate {} events'.format(
  51. str(self.current_cycle),
  52. str(len(events)),
  53. ))
  54. return events
  55. def _get_simulation_events(self) -> [Event]:
  56. events = []
  57. results = {}
  58. self.logger.info('Process simulation events')
  59. results_by_processes = self.process_manager.execute_jobs(
  60. data=self.simulation,
  61. job_maker=self.simulation_computing,
  62. )
  63. for process_result in results_by_processes:
  64. for behaviour_class, behaviour_result in process_result.items():
  65. results[behaviour_class] = behaviour_class.merge_data(
  66. behaviour_result,
  67. results.get(behaviour_class),
  68. )
  69. self.logger.info('Simulation generate {} behaviours'.format(len(results)))
  70. # Make events
  71. for behaviour_class, behaviour_data in results.items():
  72. behaviour_events = self.simulation.behaviours[behaviour_class].action(behaviour_data)
  73. self.logger.info('{} behaviour generate {} events'.format(
  74. str(behaviour_class),
  75. str(len(behaviour_events)),
  76. ))
  77. if self.logger.is_debug:
  78. self.logger.debug('{} behaviour generated events: {}'.format(
  79. str(behaviour_class),
  80. str([e.repr_debug() for e in behaviour_events]),
  81. ))
  82. events.extend(behaviour_events)
  83. self.logger.info('Simulation behaviours generate {} events'.format(len(events)))
  84. return events
  85. def _get_subjects_events(self) -> [Event]:
  86. events = []
  87. results = {}
  88. self.logger.info('Process subjects events')
  89. results_by_processes = self.process_manager.chunk_and_execute_jobs(
  90. data=self.simulation.subjects,
  91. job_maker=self.subjects_computing,
  92. )
  93. for process_results in results_by_processes:
  94. results.update(process_results)
  95. # Duplicate list to prevent conflicts with behaviours subjects manipulations
  96. for subject in self.simulation.subjects[:]:
  97. subject_behaviours = results.get(subject.id, {})
  98. if subject.behaviour_selector:
  99. # TODO: Looging
  100. subject_behaviours = subject.behaviour_selector.reduce_behaviours(dict(subject_behaviours))
  101. for behaviour_class, behaviour_data in subject_behaviours.items():
  102. # TODO: Ajouter une etape de selection des actions a faire (genre neuronnal)
  103. # (genre se cacher et fuir son pas compatibles)
  104. behaviour_events = subject.behaviours[behaviour_class].action(behaviour_data)
  105. self.logger.info('{} behaviour for subject {} generate {} events'.format(
  106. str(behaviour_class),
  107. str(subject.id),
  108. str(len(behaviour_events)),
  109. ))
  110. if self.logger.is_debug:
  111. self.logger.debug('{} behaviour for subject {} generated events: {}'.format(
  112. str(behaviour_class),
  113. str(subject.id),
  114. str([e.repr_debug() for e in behaviour_events]),
  115. ))
  116. events.extend(behaviour_events)
  117. self.logger.info('Subjects behaviours generate {} events'.format(len(events)))
  118. return events
  119. def simulation_computing(
  120. self,
  121. simulation,
  122. process_number,
  123. process_count,
  124. ):
  125. self.logger.info('Simulation computing')
  126. # TODO: necessaire de passer simulation ?
  127. mechanisms = self.get_mechanisms_to_compute(simulation)
  128. mechanisms_data = {}
  129. behaviours_data = {}
  130. self.logger.info('{} mechanisms to compute'.format(str(len(mechanisms))))
  131. if self.logger.is_debug:
  132. self.logger.debug('Mechanisms are: {}'.format(
  133. str([m.repr_debug() for m in mechanisms])
  134. ))
  135. for mechanism in mechanisms:
  136. mechanism_data = mechanism.run(
  137. process_number=process_number,
  138. process_count=process_count,
  139. )
  140. if self.logger.is_debug:
  141. self.logger.debug('{} mechanism product data: {}'.format(
  142. type(mechanism).__name__,
  143. str(mechanism_data),
  144. ))
  145. mechanisms_data[type(mechanism)] = mechanism_data
  146. behaviours = self.get_behaviours_to_compute(simulation)
  147. self.logger.info('{} behaviours to compute'.format(str(len(behaviours))))
  148. if self.logger.is_debug:
  149. self.logger.debug('Behaviours are: {}'.format(
  150. str([b.repr_debug() for b in behaviours])
  151. ))
  152. for behaviour in behaviours:
  153. behaviour_data = behaviour.run(mechanisms_data) # TODO: Behaviours dependencies
  154. if self.logger.is_debug:
  155. self.logger.debug('{} behaviour produce data: {}'.format(
  156. type(behaviour).__name__,
  157. behaviour_data,
  158. ))
  159. if behaviour_data:
  160. behaviours_data[type(behaviour)] = behaviour_data
  161. return behaviours_data
  162. def subjects_computing(
  163. self,
  164. subjects,
  165. process_number=None,
  166. process_count=None,
  167. ):
  168. results = {}
  169. self.logger.info('Subjects computing: {} subjects to compute'.format(str(len(subjects))))
  170. for subject in subjects:
  171. mechanisms = self.get_mechanisms_to_compute(subject)
  172. if mechanisms:
  173. self.logger.info('Subject {}: {} mechanisms'.format(
  174. str(subject.id),
  175. str(len(mechanisms)),
  176. ))
  177. if self.logger.is_debug:
  178. self.logger.info('Subject {}: mechanisms are: {}'.format(
  179. str(subject.id),
  180. str([m.repr_debug for m in mechanisms])
  181. ))
  182. mechanisms_data = {}
  183. behaviours_data = {}
  184. for mechanism in mechanisms:
  185. with time_it() as elapsed_time:
  186. mechanism_data = mechanism.run()
  187. if self.logger.is_debug:
  188. self.logger.debug('Subject {}: {} mechanisms produce data: {} in {}s'.format(
  189. str(subject.id),
  190. type(mechanism).__name__,
  191. str(mechanism_data),
  192. elapsed_time.get_final_time(),
  193. ))
  194. mechanisms_data[type(mechanism)] = mechanism_data
  195. if mechanisms:
  196. if self.logger.is_debug:
  197. self.logger.info('Subject {}: mechanisms data are: {}'.format(
  198. str(subject.id),
  199. str(mechanisms_data),
  200. ))
  201. subject_behaviours = self.get_behaviours_to_compute(subject)
  202. if not subject_behaviours:
  203. break
  204. self.logger.info('Subject {}: have {} behaviours'.format(
  205. str(subject.id),
  206. str(len(subject_behaviours)),
  207. ))
  208. for behaviour in subject_behaviours:
  209. self.logger.info('Subject {}: run {} behaviour'.format(
  210. str(subject.id),
  211. str(type(behaviour)),
  212. ))
  213. # We identify behaviour data with it's class to be able to intersect it after subprocess data collect
  214. with time_it() as elapsed_time:
  215. behaviour_data = behaviour.run(mechanisms_data) # TODO: Behaviours dependencies
  216. if self.logger.is_debug:
  217. self.logger.debug('Subject {}: behaviour {} produce data: {} in {}s'.format(
  218. str(type(behaviour)),
  219. str(subject.id),
  220. str(behaviour_data),
  221. elapsed_time.get_final_time(),
  222. ))
  223. if behaviour_data:
  224. behaviours_data[type(behaviour)] = behaviour_data
  225. results[subject.id] = behaviours_data
  226. return results
  227. def get_mechanisms_to_compute(self, mechanisable) -> [SubjectMechanism, SimulationMechanism]:
  228. # TODO: Implementer un systeme qui inhibe des mechanisme (ex. someil inhibe l'ouie)
  229. return mechanisable.mechanisms.values()
  230. def get_behaviours_to_compute(self, mechanisable) -> [SubjectBehaviour, SimulationBehaviour]:
  231. # TODO: Implementer un systeme qui inhibe des behaviours (ex. someil inhibe avoir faim)
  232. behaviours = list(mechanisable.behaviours.values())
  233. for behaviour in behaviours[:]:
  234. if behaviour.frequency != 1:
  235. if self.current_cycle % behaviour.frequency:
  236. behaviours.remove(behaviour)
  237. return behaviours
  238. def apply_actions(
  239. self,
  240. simulation_actions: [tuple]=None,
  241. subject_actions: [tuple]=None,
  242. ) -> [Event]:
  243. """
  244. TODO: bien specifier la forme des parametres
  245. simulation_actions = [(class, {'data': 'foo'})]
  246. subject_actions = [(subject_id, [(class, {'data': 'foo'}])]
  247. """
  248. simulation_actions = simulation_actions or []
  249. subject_actions = subject_actions or []
  250. events = []
  251. self.logger.info('Apply {} simulation_actions and {} subject_actions'.format(
  252. len(simulation_actions),
  253. len(subject_actions),
  254. ))
  255. for subject_id, behaviours_and_data in subject_actions:
  256. subject = self.simulation.subjects.index.get(subject_id)
  257. for behaviour_class, behaviour_data in behaviours_and_data:
  258. behaviour = behaviour_class(
  259. simulation=self.simulation,
  260. subject=subject,
  261. )
  262. self.logger.info('Apply {} behaviour on subject {}'.format(
  263. str(behaviour_class),
  264. str(subject_id),
  265. ))
  266. if self.logger.is_debug:
  267. self.logger.debug('{} behaviour data is {}'.format(
  268. str(behaviour_class),
  269. str(behaviour_data),
  270. ))
  271. behaviour_events = behaviour.action(behaviour_data)
  272. self.logger.info('{} events from behaviour {} from subject {}'.format(
  273. len(behaviour_events),
  274. str(behaviour_class),
  275. str(subject_id),
  276. ))
  277. if self.logger.is_debug:
  278. self.logger.debug('Events from behaviour {} from subject {} are: {}'.format(
  279. str(behaviour_class),
  280. str(subject_id),
  281. str([e.repr_debug() for e in behaviour_events])
  282. ))
  283. events.extend(behaviour_events)
  284. for behaviour_class, behaviour_data in simulation_actions:
  285. behaviour = behaviour_class(
  286. config=self.config,
  287. simulation=self.simulation,
  288. )
  289. self.logger.info('Apply {} simulation behaviour'.format(
  290. str(behaviour_class),
  291. ))
  292. behaviour_events = behaviour.action(behaviour_data)
  293. if self.logger.is_debug:
  294. self.logger.debug('Events from behaviour {} are: {}'.format(
  295. str(behaviour_class),
  296. str([e.repr_debug() for e in behaviour_events])
  297. ))
  298. events.extend(behaviour_events)
  299. self.logger.info('{} events generated'.format(len(events)))
  300. return events