From 7de1759a3e0b92ca1c8e711f8df9f8289f731842 Mon Sep 17 00:00:00 2001 From: Ryan Spangler Date: Thu, 7 Apr 2022 00:00:38 +0000 Subject: [PATCH 1/5] trying out a few approaches to having processes accept keyword arguments --- vivarium/core/process.py | 94 ++++++++++++++++++++----------- vivarium/processes/growth_rate.py | 28 ++++++--- 2 files changed, 81 insertions(+), 41 deletions(-) diff --git a/vivarium/core/process.py b/vivarium/core/process.py index bcd5bcf8..65bca293 100644 --- a/vivarium/core/process.py +++ b/vivarium/core/process.py @@ -82,43 +82,47 @@ def _get_parameters( class Process(metaclass=abc.ABCMeta): - defaults: Dict[str, Any] = {} - - def __init__(self, parameters: Optional[dict] = None) -> None: - """Process parent class. + """Process parent class. + + All :term:`process` classes must inherit from this class. Each + class can provide a ``defaults`` class variable to specify the + process defaults as a dictionary. + + Note that subclasses should call the superclass init function + first. This allows the superclass to correctly save the initial + parameters before they are mutated by subclass constructor code. + We need access to the original parameters for serialization to + work properly. + + Args: + parameters: Override the class defaults. This dictionary may + also contain the following special keys: + + * ``name``: Saved to ``self.name``. + * ``_original_parameters``: Returned by + ``__getstate__()`` for serialization. + * ``_no_original_parameters``: If specified with a value + of ``True``, original parameters will not be copied + during initialization, and ``__getstate__()`` will + instead return ``self.parameters``. This puts the + responsibility on the user to not mutate process + parameters. + * ``_schema``: Overrides the schema. + * ``_parallel``: Indicates that the process should be + parallelized. ``self.parallel`` will be set to True. + * ``_condition``: Path to a variable whose value will be + returned by + :py:meth:`vivarium.core.process.Process.update_condition`. + * ``time_step``: Returned by + :py:meth:`vivarium.core.process.Process.calculate_timestep`. + """ - All :term:`process` classes must inherit from this class. Each - class can provide a ``defaults`` class variable to specify the - process defaults as a dictionary. + defaults: Dict[str, Any] = {} - Note that subclasses should call the superclass init function - first. This allows the superclass to correctly save the initial - parameters before they are mutated by subclass constructor code. - We need access to the original parameters for serialization to - work properly. + def __init__(self, parameters=None): + self.initialize_parameters(parameters) - Args: - parameters: Override the class defaults. This dictionary may - also contain the following special keys: - - * ``name``: Saved to ``self.name``. - * ``_original_parameters``: Returned by - ``__getstate__()`` for serialization. - * ``_no_original_parameters``: If specified with a value - of ``True``, original parameters will not be copied - during initialization, and ``__getstate__()`` will - instead return ``self.parameters``. This puts the - responsibility on the user to not mutate process - parameters. - * ``_schema``: Overrides the schema. - * ``_parallel``: Indicates that the process should be - parallelized. ``self.parallel`` will be set to True. - * ``_condition``: Path to a variable whose value will be - returned by - :py:meth:`vivarium.core.process.Process.update_condition`. - * ``time_step``: Returned by - :py:meth:`vivarium.core.process.Process.calculate_timestep`. - """ + def initialize_parameters(self, parameters: Optional[dict] = None, **kwargs) -> None: parameters = parameters or {} if '_original_parameters' in parameters: original_parameters = parameters.pop('_original_parameters') @@ -158,6 +162,28 @@ class can provide a ``defaults`` class variable to specify the self.parameters.setdefault('time_step', DEFAULT_TIME_STEP) self.schema: Optional[dict] = None + def initialize(self, parameters, base_parameters): + # parameters = local_parameters.copy() + # base_parameters = parameters.pop('base_parameters') + + # # use schema to check base parameters + # base_parameters_schema.validate(base_parameters) + + for local in ['self', '__class__', 'ipdb']: + if local in parameters: + del parameters[local] + + parameters.update(base_parameters) + self.initialize_parameters(parameters) + + def _set_timestep(self): + self.parameters.setdefault('timestep', DEFAULT_TIME_STEP) + if self.parameters.get('time_step'): + self.parameters['timestep'] = self.parameters['time_step'] + # else: + # # setting 'time_step' for backwards compatibility (!) + # self.parameters['time_step'] = self.parameters['timestep'] + def __getstate__(self) -> dict: """Return parameters diff --git a/vivarium/processes/growth_rate.py b/vivarium/processes/growth_rate.py index ff4700c0..64df4648 100644 --- a/vivarium/processes/growth_rate.py +++ b/vivarium/processes/growth_rate.py @@ -28,6 +28,17 @@ class GrowthRate(Process): 'variables': ['mass'] } + def __init__( + self, + default_growth_rate=0.0005, + default_growth_noise=0.0, + variables=('mass',), + **base_parameters): + + parameters = locals().copy() + base_parameters = parameters.pop('base_parameters') + self.initialize(parameters, base_parameters) + def ports_schema(self): return { 'variables': { @@ -73,13 +84,16 @@ def next_update(self, timestep, states): def test_growth_rate(total_time=1350): initial_mass = 100 growth_rate = 0.0005 - config = { - 'variables': ['mass'], - 'default_growth_rate': growth_rate, - 'time_step': 2, - } - - growth_rate_process = GrowthRate(config) + # config = { + # 'variables': ['mass'], + # 'default_growth_rate': growth_rate, + # 'timestep': 2, + # } + + growth_rate_process = GrowthRate( + default_growth_rate=growth_rate, + timestep=5.0, + variables=('mass',)) initial_state = {'variables': {'mass': initial_mass}} experiment = process_in_experiment( growth_rate_process, From 70f7c7263e1180449eb3715b66b7a68e36a2cc15 Mon Sep 17 00:00:00 2001 From: Ryan Spangler Date: Thu, 24 Mar 2022 16:21:27 -0700 Subject: [PATCH 2/5] providing backwards compatibility for time_step --> timestep --- vivarium/core/process.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vivarium/core/process.py b/vivarium/core/process.py index 65bca293..f89b838b 100644 --- a/vivarium/core/process.py +++ b/vivarium/core/process.py @@ -159,7 +159,8 @@ def initialize_parameters(self, parameters: Optional[dict] = None, **kwargs) -> '_emit': True, '_updater': 'set'})) - self.parameters.setdefault('time_step', DEFAULT_TIME_STEP) + self._set_timestep() + self.schema: Optional[dict] = None def initialize(self, parameters, base_parameters): @@ -301,10 +302,10 @@ def calculate_timestep(self, states: Optional[State]) -> Union[float, int]: """Return the next process time step A process subclass may override this method to implement - adaptive timesteps. By default it returns self.parameters['time_step']. + adaptive timesteps. By default it returns self.parameters['timestep']. """ _ = states - return self.parameters['time_step'] + return self.parameters['timestep'] def default_state(self) -> State: """Get the default values of the variables in each port. From 8c2ef72d9815bfb0173d692011dd1518347ac3e3 Mon Sep 17 00:00:00 2001 From: Eran Agmon Date: Wed, 6 Apr 2022 18:36:41 -0700 Subject: [PATCH 3/5] minor clen up --- vivarium/core/process.py | 24 ++++++++++++------------ vivarium/processes/growth_rate.py | 10 +++++----- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/vivarium/core/process.py b/vivarium/core/process.py index f89b838b..83945ffa 100644 --- a/vivarium/core/process.py +++ b/vivarium/core/process.py @@ -119,10 +119,14 @@ class can provide a ``defaults`` class variable to specify the defaults: Dict[str, Any] = {} - def __init__(self, parameters=None): + def __init__(self, parameters: Optional[dict] = None) -> None: self.initialize_parameters(parameters) - def initialize_parameters(self, parameters: Optional[dict] = None, **kwargs) -> None: + def initialize_parameters( + self, + parameters: Optional[dict] = None, + **kwargs + ) -> None: parameters = parameters or {} if '_original_parameters' in parameters: original_parameters = parameters.pop('_original_parameters') @@ -163,27 +167,23 @@ def initialize_parameters(self, parameters: Optional[dict] = None, **kwargs) -> self.schema: Optional[dict] = None - def initialize(self, parameters, base_parameters): - # parameters = local_parameters.copy() + def initialize( + self, + parameters: Optional[dict] = None, + base_parameters: Optional[dict] = None + ) -> None: # base_parameters = parameters.pop('base_parameters') - # # use schema to check base parameters - # base_parameters_schema.validate(base_parameters) - for local in ['self', '__class__', 'ipdb']: if local in parameters: del parameters[local] - parameters.update(base_parameters) self.initialize_parameters(parameters) - def _set_timestep(self): + def _set_timestep(self) -> None: self.parameters.setdefault('timestep', DEFAULT_TIME_STEP) if self.parameters.get('time_step'): self.parameters['timestep'] = self.parameters['time_step'] - # else: - # # setting 'time_step' for backwards compatibility (!) - # self.parameters['time_step'] = self.parameters['timestep'] def __getstate__(self) -> dict: """Return parameters diff --git a/vivarium/processes/growth_rate.py b/vivarium/processes/growth_rate.py index 64df4648..1ff5a2c1 100644 --- a/vivarium/processes/growth_rate.py +++ b/vivarium/processes/growth_rate.py @@ -22,11 +22,11 @@ class GrowthRate(Process): """ A Vivarium process that models exponential growth of biomass """ name = NAME - defaults = { - 'default_growth_rate': 0.0005, - 'default_growth_noise': 0.0, - 'variables': ['mass'] - } + # defaults = { + # 'default_growth_rate': 0.0005, + # 'default_growth_noise': 0.0, + # 'variables': ['mass'] + # } def __init__( self, From 34f843a5c5850206c586b2189b08d654406fd9d9 Mon Sep 17 00:00:00 2001 From: Eran Agmon Date: Wed, 6 Apr 2022 18:44:35 -0700 Subject: [PATCH 4/5] fix pytests --- vivarium/composites/toys.py | 10 +++++----- vivarium/core/serialize_test.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/vivarium/composites/toys.py b/vivarium/composites/toys.py index 04df8635..3b1ec45e 100644 --- a/vivarium/composites/toys.py +++ b/vivarium/composites/toys.py @@ -263,7 +263,7 @@ def next_update(self, timestep, states): class Proton(Process): name = 'proton' defaults = { - 'time_step': 1.0, + 'timestep': 1.0, 'radius': 0.0} def ports_schema(self): @@ -314,7 +314,7 @@ def next_update(self, timestep, states): class Electron(Process): name = 'electron' defaults = { - 'time_step': 1.0, + 'timestep': 1.0, 'spin': electron_spins[0]} def ports_schema(self): @@ -667,10 +667,10 @@ def test_composite_parameters() -> None: composer_parameters = bb_composer.get_parameters() composite_parameters = bb_composite.get_parameters() expected_parameters = { - 'a1': {'time_step': 1.0}, - 'a2': {'time_step': 1.0}, + 'a1': {'timestep': 1.0}, + 'a2': {'timestep': 1.0}, 'a3_store': { - 'a3': {'time_step': 1.0}}} + 'a3': {'timestep': 1.0}}} assert composite_parameters == composer_parameters assert composite_parameters == expected_parameters diff --git a/vivarium/core/serialize_test.py b/vivarium/core/serialize_test.py index 69f92f44..cb4dcb77 100644 --- a/vivarium/core/serialize_test.py +++ b/vivarium/core/serialize_test.py @@ -115,7 +115,7 @@ def test_serialization_full() -> None: serialized.pop('function')) expected_serialized = { 'process': ( - "!ProcessSerializer[{'time_step': 1.0, '_name': " + "!ProcessSerializer[{'timestep': 1.0, '_name': " "'SerializeProcess'}]" ), '1': True, From 3cdb1e9a9b96a8b50e609d80d1b8018aecea12f7 Mon Sep 17 00:00:00 2001 From: Eran Agmon Date: Tue, 19 Apr 2022 09:00:02 -0700 Subject: [PATCH 5/5] begin work on keyword_init --- vivarium/core/process.py | 29 ++++++++++++++++------------- vivarium/processes/growth_rate.py | 11 ++++++----- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/vivarium/core/process.py b/vivarium/core/process.py index 83945ffa..2ccd287d 100644 --- a/vivarium/core/process.py +++ b/vivarium/core/process.py @@ -120,12 +120,11 @@ class can provide a ``defaults`` class variable to specify the defaults: Dict[str, Any] = {} def __init__(self, parameters: Optional[dict] = None) -> None: - self.initialize_parameters(parameters) + self.initialize(parameters) - def initialize_parameters( + def initialize( self, parameters: Optional[dict] = None, - **kwargs ) -> None: parameters = parameters or {} if '_original_parameters' in parameters: @@ -167,18 +166,22 @@ def initialize_parameters( self.schema: Optional[dict] = None - def initialize( + def config_init( + self, parameters: Optional[dict] = None) -> None: + self.initialize(parameters) + + def keyword_init(self) -> None: + raise Exception( + f'keyword_init not provided for this Process {self.name}') + + def base_keyword_init( self, - parameters: Optional[dict] = None, - base_parameters: Optional[dict] = None + timestep: float = 1, + parallel: bool = False, + condition: Optional[HierarchyPath] = None, + original_parameters: Optional[dict] = None ) -> None: - # base_parameters = parameters.pop('base_parameters') - - for local in ['self', '__class__', 'ipdb']: - if local in parameters: - del parameters[local] - parameters.update(base_parameters) - self.initialize_parameters(parameters) + pass def _set_timestep(self) -> None: self.parameters.setdefault('timestep', DEFAULT_TIME_STEP) diff --git a/vivarium/processes/growth_rate.py b/vivarium/processes/growth_rate.py index 1ff5a2c1..bac7e86b 100644 --- a/vivarium/processes/growth_rate.py +++ b/vivarium/processes/growth_rate.py @@ -28,16 +28,17 @@ class GrowthRate(Process): # 'variables': ['mass'] # } - def __init__( + def keyword_init( self, default_growth_rate=0.0005, default_growth_noise=0.0, variables=('mass',), - **base_parameters): + # **base_parameters + ): parameters = locals().copy() - base_parameters = parameters.pop('base_parameters') - self.initialize(parameters, base_parameters) + # base_parameters = parameters.pop('base_parameters') + self.initialize() def ports_schema(self): return { @@ -90,7 +91,7 @@ def test_growth_rate(total_time=1350): # 'timestep': 2, # } - growth_rate_process = GrowthRate( + growth_rate_process = GrowthRate.keyword_init( default_growth_rate=growth_rate, timestep=5.0, variables=('mass',))