process-bigraph 0.0.27__tar.gz → 0.0.28__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. {process-bigraph-0.0.27/process_bigraph.egg-info → process-bigraph-0.0.28}/PKG-INFO +1 -1
  2. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/__init__.py +2 -1
  3. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/composite.py +12 -518
  4. process-bigraph-0.0.28/process_bigraph/emitter.py +406 -0
  5. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/process_types.py +139 -21
  6. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/processes/growth_division.py +2 -2
  7. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/processes/parameter_scan.py +2 -1
  8. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/protocols.py +0 -1
  9. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/tests.py +78 -17
  10. {process-bigraph-0.0.27 → process-bigraph-0.0.28/process_bigraph.egg-info}/PKG-INFO +1 -1
  11. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph.egg-info/SOURCES.txt +1 -0
  12. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/setup.py +1 -1
  13. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/AUTHORS.md +0 -0
  14. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/LICENSE +0 -0
  15. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/README.md +0 -0
  16. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/experiments/__init__.py +0 -0
  17. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/experiments/minimal_gillespie.py +0 -0
  18. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph/processes/__init__.py +0 -0
  19. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph.egg-info/dependency_links.txt +0 -0
  20. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph.egg-info/requires.txt +0 -0
  21. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/process_bigraph.egg-info/top_level.txt +0 -0
  22. {process-bigraph-0.0.27 → process-bigraph-0.0.28}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: process-bigraph
3
- Version: 0.0.27
3
+ Version: 0.0.28
4
4
  Summary: UNKNOWN
5
5
  Home-page: https://github.com/vivarium-collective/process-bigraph
6
6
  Author: Ryan Spangler, Eran Agmon
@@ -1,7 +1,8 @@
1
1
  import pprint
2
2
  from bigraph_schema.registry import deep_merge, default
3
3
  from process_bigraph.processes import register_processes
4
- from process_bigraph.composite import Process, Step, Composite, ProcessTypes, interval_time_precision
4
+ from process_bigraph.composite import Process, Step, Composite, interval_time_precision
5
+ from process_bigraph.process_types import ProcessTypes
5
6
 
6
7
 
7
8
  pretty = pprint.PrettyPrinter(indent=2)
@@ -10,12 +10,14 @@ import math
10
10
  import collections
11
11
  from typing import Dict
12
12
 
13
- from bigraph_schema import Edge, TypeSystem, get_path, set_path, deep_merge, is_schema_key, strip_schema_keys, Registry, hierarchy_depth, visit_method
13
+ from bigraph_schema import (
14
+ Edge, Registry,
15
+ get_path, resolve_path, hierarchy_depth, deep_merge,
16
+ is_schema_key, strip_schema_keys)
14
17
 
15
18
  from process_bigraph.protocols import local_lookup, local_lookup_module
16
19
 
17
20
 
18
-
19
21
  # =========================
20
22
  # Process Utility Functions
21
23
  # =========================
@@ -24,7 +26,8 @@ def assert_interface(interface: Dict):
24
26
  """Ensure that an interface dict has the required keys"""
25
27
  required_keys = ['inputs', 'outputs']
26
28
  existing_keys = set(interface.keys())
27
- assert existing_keys == set(required_keys), f"every interface requires an inputs schema and an outputs schema, not {existing_keys}"
29
+ assert existing_keys == set(required_keys), \
30
+ f"every interface requires an inputs schema and an outputs schema, not {existing_keys}"
28
31
 
29
32
 
30
33
  def find_instances(state, instance_type='process_bigraph.composite.Process'):
@@ -57,7 +60,7 @@ def find_step_triggers(path, step):
57
60
  step['inputs'])
58
61
 
59
62
  for wire in wire_paths:
60
- trigger_path = tuple(prefix) + tuple(wire)
63
+ trigger_path = resolve_path(tuple(prefix) + tuple(wire))
61
64
  if trigger_path not in triggers:
62
65
  triggers[trigger_path] = []
63
66
  triggers[trigger_path].append(path)
@@ -241,324 +244,6 @@ def interval_time_precision(timestep):
241
244
  return global_time_precision
242
245
 
243
246
 
244
- # ======================
245
- # Process Type Functions
246
- # ======================
247
-
248
- def apply_process(schema, current, update, core):
249
- """Apply an update to a process."""
250
- process_schema = schema.copy()
251
- process_schema.pop('_apply')
252
- return core.apply(
253
- process_schema,
254
- current,
255
- update)
256
-
257
-
258
- def check_process(schema, state, core):
259
- """Check if this is a process."""
260
- return 'instance' in state and isinstance(
261
- state['instance'],
262
- Edge)
263
-
264
-
265
- def fold_visit(schema, state, method, values, core):
266
- visit = visit_method(
267
- schema,
268
- state,
269
- method,
270
- values,
271
- core)
272
-
273
- return visit
274
-
275
-
276
- def divide_process(schema, state, values, core):
277
- # daughter_configs must have a config per daughter
278
-
279
- daughter_configs = values.get(
280
- 'daughter_configs',
281
- [{} for index in range(values['divisions'])])
282
-
283
- if 'config' not in state:
284
- return daughter_configs
285
-
286
- existing_config = state['config']
287
-
288
- divisions = []
289
- for index in range(values['divisions']):
290
- daughter_config = copy.deepcopy(
291
- existing_config)
292
- daughter_config = deep_merge(
293
- daughter_config,
294
- daughter_configs[index])
295
-
296
- # TODO: provide a way to override inputs and outputs
297
- daughter_state = {
298
- 'address': state['address'],
299
- 'config': daughter_config,
300
- 'inputs': copy.deepcopy(state['inputs']),
301
- 'outputs': copy.deepcopy(state['outputs'])}
302
-
303
- if 'interval' in state:
304
- daughter_state['interval'] = state['interval']
305
-
306
- divisions.append(daughter_state)
307
-
308
- return divisions
309
-
310
-
311
- def serialize_process(schema, value, core):
312
- """Serialize a process to a JSON-safe representation."""
313
- # TODO -- need to get back the protocol: address and the config
314
- process = value.copy()
315
- process['config'] = core.serialize(
316
- process['instance'].config_schema,
317
- process['config'])
318
- del process['instance']
319
- return process
320
-
321
-
322
- def deserialize_process(schema, encoded, core):
323
- """Deserialize a process from a serialized state.
324
-
325
- This function is used by the type system to deserialize a process.
326
-
327
- :param encoded: A JSON-safe representation of the process.
328
- :param bindings: The bindings to use for deserialization.
329
- :param core: The type system to use for deserialization.
330
-
331
- :returns: The deserialized state with an instantiated process.
332
- """
333
- encoded = encoded or {}
334
- schema = schema or {}
335
-
336
- if not encoded:
337
- deserialized = core.default(schema)
338
- else:
339
- deserialized = encoded
340
-
341
- if not deserialized.get('address'):
342
- return deserialized
343
-
344
- protocol, address = deserialized['address'].split(':', 1)
345
-
346
- if 'instance' in deserialized:
347
- instantiate = type(deserialized['instance'])
348
- else:
349
- process_lookup = core.protocol_registry.access(protocol)
350
- if not process_lookup:
351
- raise Exception(f'protocol "{protocol}" not implemented')
352
-
353
- instantiate = process_lookup(core, address)
354
- if not instantiate:
355
- raise Exception(f'process "{address}" not found')
356
-
357
- config = core.deserialize(
358
- instantiate.config_schema,
359
- deserialized.get('config', {}))
360
-
361
- interval = core.deserialize(
362
- 'interval',
363
- deserialized.get('interval'))
364
-
365
- if interval is None:
366
- interval = core.default(
367
- schema.get(
368
- 'interval',
369
- 'interval'))
370
-
371
- if not 'instance' in deserialized:
372
- process = instantiate(
373
- config,
374
- core=core)
375
-
376
- deserialized['instance'] = process
377
-
378
- deserialized['config'] = config
379
- deserialized['interval'] = interval
380
- deserialized['_inputs'] = copy.deepcopy(
381
- deserialized['instance'].inputs())
382
- deserialized['_outputs'] = copy.deepcopy(
383
- deserialized['instance'].outputs())
384
-
385
- return deserialized
386
-
387
-
388
- def deserialize_step(schema, encoded, core):
389
- if not encoded:
390
- deserialized = core.default(schema)
391
- else:
392
- deserialized = copy.deepcopy(encoded)
393
-
394
- if not deserialized['address']:
395
- return deserialized
396
-
397
- protocol, address = deserialized['address'].split(':', 1)
398
-
399
- if 'instance' in deserialized:
400
- instantiate = type(deserialized['instance'])
401
- else:
402
- process_lookup = core.protocol_registry.access(protocol)
403
- if not process_lookup:
404
- raise Exception(f'protocol "{protocol}" not implemented')
405
-
406
- instantiate = process_lookup(core, address)
407
- if not instantiate:
408
- raise Exception(f'process "{address}" not found')
409
-
410
- config = core.deserialize(
411
- instantiate.config_schema,
412
- deserialized.get('config', {}))
413
-
414
- if not 'instance' in deserialized:
415
- process = instantiate(config, core=core)
416
- deserialized['instance'] = process
417
-
418
- deserialized['config'] = config
419
- deserialized['_inputs'] = copy.deepcopy(
420
- deserialized['instance'].inputs())
421
- deserialized['_outputs'] = copy.deepcopy(
422
- deserialized['instance'].outputs())
423
-
424
- return deserialized
425
-
426
-
427
- PROCESS_TYPES = {
428
- 'protocol': {
429
- '_type': 'protocol',
430
- '_inherit': 'string'},
431
-
432
- 'emitter_mode': 'enum[none,all,stores,bridge,paths,ports]',
433
-
434
- 'interval': {
435
- '_type': 'interval',
436
- '_inherit': 'float',
437
- '_apply': 'set',
438
- '_default': '1.0'},
439
-
440
- 'step': {
441
- '_type': 'step',
442
- '_inherit': 'edge',
443
- '_apply': apply_process,
444
- '_serialize': serialize_process,
445
- '_deserialize': deserialize_step,
446
- '_check': check_process,
447
- '_fold': fold_visit,
448
- '_divide': divide_process,
449
- '_description': '',
450
- # TODO: support reference to type parameters from other states
451
- 'address': 'protocol',
452
- 'config': 'quote'},
453
-
454
- # TODO: slice process to allow for navigating through a port
455
- 'process': {
456
- '_type': 'process',
457
- '_inherit': 'edge',
458
- '_apply': apply_process,
459
- '_serialize': serialize_process,
460
- '_deserialize': deserialize_process,
461
- '_check': check_process,
462
- '_fold': fold_visit,
463
- '_divide': divide_process,
464
- '_description': '',
465
- # TODO: support reference to type parameters from other states
466
- 'interval': 'interval',
467
- 'address': 'protocol',
468
- 'config': 'quote'}}
469
-
470
-
471
- BASE_PROTOCOLS = {
472
- 'local': local_lookup}
473
-
474
-
475
- # ===================
476
- # Process Type System
477
- # ===================
478
-
479
- class ProcessTypes(TypeSystem):
480
- """
481
- ProcessTypes class extends the TypeSystem class to include process types.
482
- It maintains a registry of process types and provides methods to register
483
- new process types, protocols, and emitters.
484
- """
485
-
486
- def __init__(self):
487
- super().__init__()
488
- self.process_registry = Registry()
489
- self.protocol_registry = Registry()
490
-
491
- self.update_types(PROCESS_TYPES)
492
- self.register_protocols(BASE_PROTOCOLS)
493
- self.register_processes(BASE_EMITTERS)
494
-
495
- self.register_process('composite', Composite)
496
-
497
-
498
- def register_protocols(self, protocols):
499
- """Register protocols with the core"""
500
- self.protocol_registry.register_multiple(protocols)
501
-
502
-
503
- def register_process(
504
- self,
505
- name,
506
- process_data
507
- ):
508
- """
509
- Registers a new process type in the process registry.
510
-
511
- Args:
512
- name (str): The name of the process type.
513
- process_data: The data associated with the process type.
514
- """
515
- self.process_registry.register(name, process_data)
516
-
517
-
518
- def register_processes(self, processes):
519
- for process_key, process_data in processes.items():
520
- self.register_process(
521
- process_key,
522
- process_data)
523
-
524
-
525
- def initialize_edge_state(self, schema, path, edge):
526
- """
527
- Initialize the state for an edge based on the schema and the edge.
528
- """
529
- initial_state = edge['instance'].initial_state()
530
- if not initial_state:
531
- return initial_state
532
-
533
- input_ports = copy.deepcopy(get_path(schema, path + ('_inputs',)))
534
- output_ports = copy.deepcopy(get_path(schema, path + ('_outputs',)))
535
- ports = {
536
- '_inputs': input_ports,
537
- '_outputs': output_ports}
538
-
539
- input_state = {}
540
- if input_ports:
541
- input_state = self.project_edge(
542
- ports,
543
- edge,
544
- path[:-1],
545
- initial_state,
546
- ports_key='inputs')
547
-
548
- output_state = {}
549
- if output_ports:
550
- output_state = self.project_edge(
551
- ports,
552
- edge,
553
- path[:-1],
554
- initial_state,
555
- ports_key='outputs')
556
-
557
- state = deep_merge(input_state, output_state)
558
-
559
- return state
560
-
561
-
562
247
  # ===============
563
248
  # Process Classes
564
249
  # ===============
@@ -586,6 +271,10 @@ class Step(Edge):
586
271
  return sync
587
272
 
588
273
 
274
+ def register_shared(self, instance):
275
+ self.instance = instance
276
+
277
+
589
278
  def update(self, state):
590
279
  return {}
591
280
 
@@ -703,16 +392,6 @@ class Composite(Process):
703
392
  'bridge': {
704
393
  'inputs': 'wires',
705
394
  'outputs': 'wires'},
706
- 'emitter': {
707
- 'path': {
708
- '_type': 'path',
709
- '_default': ['emitter']},
710
- 'address': {
711
- '_type': 'string',
712
- '_default': 'local:ram-emitter'},
713
- 'config': 'tree[any]',
714
- 'mode': 'emitter_mode',
715
- 'emit': 'wires'},
716
395
  'global_time_precision': 'maybe[float]'}
717
396
 
718
397
 
@@ -744,9 +423,6 @@ class Composite(Process):
744
423
  initial_composition,
745
424
  initial_state)
746
425
 
747
- # self.composition = copy.deepcopy(
748
- # self.core.access(composition))
749
-
750
426
  # TODO: add flag to self.core.access(copy=True)
751
427
  self.bridge = self.config.get('bridge', {})
752
428
 
@@ -778,10 +454,6 @@ class Composite(Process):
778
454
  self.state,
779
455
  edge_state)
780
456
 
781
- # self.state = self.core.deserialize(
782
- # self.composition,
783
- # self.state)
784
-
785
457
  # TODO: call validate on this composite, not just check
786
458
  # assert self.core.validate(
787
459
  # self.composition,
@@ -797,11 +469,6 @@ class Composite(Process):
797
469
  self.global_time_precision = self.config[
798
470
  'global_time_precision']
799
471
 
800
- emitter_config = self.config.get('emitter')
801
- if emitter_config and not emitter_config.get('mode', 'none') == 'none':
802
- self.add_emitter(
803
- emitter_config)
804
-
805
472
  self.front: Dict = {
806
473
  path: empty_front(self.state['global_time'])
807
474
  for path in self.process_paths}
@@ -813,6 +480,7 @@ class Composite(Process):
813
480
 
814
481
  # self.run_steps(self.to_run)
815
482
 
483
+
816
484
  def build_step_network(self):
817
485
  self.step_triggers = {}
818
486
  for step_path, step in self.step_paths.items():
@@ -872,10 +540,6 @@ class Composite(Process):
872
540
  print(f"Created new file: {filename}")
873
541
 
874
542
 
875
- # def __repr__(self):
876
- # return self.core.representation(self.composition)
877
-
878
-
879
543
  def reset_step_state(self, step_paths):
880
544
  self.trigger_state = build_trigger_state(
881
545
  self.node_dependencies)
@@ -900,10 +564,6 @@ class Composite(Process):
900
564
  state,
901
565
  'process_bigraph.composite.Step')
902
566
 
903
- self.emitter_paths = find_instance_paths(
904
- state,
905
- 'process_bigraph.composite.Emitter')
906
-
907
567
 
908
568
  def inputs(self):
909
569
  return self.process_schema.get('inputs', {})
@@ -913,63 +573,6 @@ class Composite(Process):
913
573
  return self.process_schema.get('outputs', {})
914
574
 
915
575
 
916
- def read_emitter_config(self, emitter_config):
917
-
918
- # upcoming deprecation warning
919
- print("Warning: read_emitter_config() is deprecated and will be removed in a future version. "
920
- "Use use Vivarium for managing simulations and emitters instead of Composite.")
921
-
922
- address = emitter_config.get('address', 'local:ram-emitter')
923
- config = emitter_config.get('config', {})
924
- mode = emitter_config.get('mode', 'none')
925
-
926
- if mode == 'all':
927
- inputs = {
928
- key: [emitter_config.get('inputs', {}).get(key, key)]
929
- for key in self.state.keys()
930
- if not is_schema_key(key)}
931
-
932
- elif mode == 'none':
933
- inputs = emitter_config.get('emit', {})
934
-
935
- elif mode == 'bridge':
936
- inputs = {}
937
-
938
- elif mode == 'ports':
939
- inputs = {}
940
-
941
- if not 'emit' in config:
942
- config['emit'] = {
943
- input: 'any'
944
- for input in inputs}
945
-
946
- return {
947
- '_type': 'step',
948
- 'address': address,
949
- 'config': config,
950
- 'inputs': inputs}
951
-
952
-
953
- def add_emitter(self, emitter_config):
954
- path = tuple(emitter_config['path'])
955
-
956
- step_config = self.read_emitter_config(emitter_config)
957
- emitter = set_path(
958
- {}, path, step_config)
959
-
960
- self.merge(
961
- {},
962
- emitter)
963
-
964
- _, instance = self.core.slice(
965
- self.composition,
966
- self.state,
967
- path)
968
-
969
- self.emitter_paths[path] = instance
970
- self.step_paths[path] = instance
971
-
972
-
973
576
  def merge(self, schema, state, path=None):
974
577
  path = path or []
975
578
  self.composition, self.state = self.core.merge(
@@ -1209,16 +812,6 @@ class Composite(Process):
1209
812
  self.expire_process_paths(update_paths)
1210
813
  self.trigger_steps(update_paths)
1211
814
 
1212
- # # display and emit
1213
- # if self.progress_bar:
1214
- # print_progress_bar(self.global_time, end_time)
1215
- # if self.emit_step == 1:
1216
- # self._emit_store_data()
1217
- # elif emit_time <= self.global_time:
1218
- # while emit_time <= self.global_time:
1219
- # self._emit_store_data()
1220
- # emit_time += self.emit_step
1221
-
1222
815
  else:
1223
816
  # all processes have run past the interval
1224
817
  self.state['global_time'] = end_time
@@ -1283,27 +876,6 @@ class Composite(Process):
1283
876
  self.run_steps(to_run)
1284
877
 
1285
878
 
1286
- def gather_results(self, queries=None):
1287
- '''
1288
- a map of paths to emitter --> queries for the emitter at that path
1289
- '''
1290
-
1291
- if queries is None:
1292
- queries = {
1293
- path: None
1294
- for path in self.emitter_paths.keys()}
1295
-
1296
- results = {}
1297
- for path, query in queries.items():
1298
- emitter = get_path(self.state, path)
1299
- results[path] = emitter['instance'].query(query)
1300
-
1301
- # TODO: unnest the results?
1302
- # TODO: allow the results to be transposed
1303
-
1304
- return results
1305
-
1306
-
1307
879
  def update(self, state, interval):
1308
880
  # do everything
1309
881
 
@@ -1325,81 +897,3 @@ class Composite(Process):
1325
897
  return updates
1326
898
 
1327
899
 
1328
- # ========
1329
- # Emitters
1330
- # ========
1331
- # Emitters are steps that observe the state of the system and emit it to an external source.
1332
- # This could be to a database, to a file, or to the console.
1333
-
1334
- class Emitter(Step):
1335
- """Base emitter class.
1336
-
1337
- An `Emitter` implementation instance diverts all querying of data to
1338
- the primary historical collection whose type pertains to Emitter child, i.e:
1339
- database-emitter=>`pymongo.Collection`, ram-emitter=>`.RamEmitter.history`(`List`)
1340
- """
1341
- config_schema = {
1342
- 'emit': 'schema'}
1343
-
1344
- def inputs(self) -> Dict:
1345
- return self.config['emit']
1346
-
1347
- def query(self, query=None):
1348
- return {}
1349
-
1350
- def update(self, state) -> Dict:
1351
- return {}
1352
-
1353
-
1354
- class ConsoleEmitter(Emitter):
1355
- """Console emitter class.
1356
-
1357
- This emitter logs the state to the console.
1358
- """
1359
-
1360
- def update(self, state) -> Dict:
1361
- print(state)
1362
- return {}
1363
-
1364
-
1365
- class RAMEmitter(Emitter):
1366
- """RAM emitter class.
1367
-
1368
- This emitter logs the state to a list in memory.
1369
- """
1370
-
1371
- def __init__(self, config, core):
1372
- super().__init__(config, core)
1373
- self.history = []
1374
-
1375
-
1376
- def update(self, state) -> Dict:
1377
- self.history.append(copy.deepcopy(state))
1378
- return {}
1379
-
1380
-
1381
- def query(self, query=None):
1382
- """
1383
- Query the history of the emitter.
1384
- :param query: a list of paths to query from the history. If None, the entire history is returned.
1385
- :return: results of the query in a list
1386
- """
1387
- if isinstance(query, list):
1388
- results = []
1389
- for t in self.history:
1390
- result = {}
1391
- for path in query:
1392
- element = get_path(t, path)
1393
- result = set_path(result, path, element)
1394
- results.append(result)
1395
- # element = get_path(self.history, path)
1396
- # result = set_path(result, path, element)
1397
- else:
1398
- results = self.history
1399
-
1400
- return results
1401
-
1402
-
1403
- BASE_EMITTERS = {
1404
- 'console-emitter': ConsoleEmitter,
1405
- 'ram-emitter': RAMEmitter}