From 3cf3f45c083a7d10ae77a73824c8e022c3d2e436 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Wed, 6 Mar 2019 14:50:46 -0500 Subject: [PATCH 01/19] udc workarround --- cadCAD/engine/simulation.py | 17 ++++++ simulations/az_run.py | 25 ++++++++ simulations/validation/config1.py | 7 +-- simulations/validation/config_az.py | 91 +++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 simulations/az_run.py create mode 100644 simulations/validation/config_az.py diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index a62d1bb..104e8d2 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -5,6 +5,7 @@ from fn.op import foldr, call from cadCAD.engine.utils import engine_exception from cadCAD.utils import flatten +import pprint as pp id_exception: Callable = engine_exception(KeyError, KeyError, None) @@ -72,6 +73,7 @@ class Executor: _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sL, last_in_obj, policy_funcs)) # ToDo: add env_proc generator to `last_in_copy` iterator as wrapper function + # ToDo: Can be multithreaded ?? last_in_copy: Dict[str, Any] = dict( [ self.state_update_exception(f(var_dict, sub_step, sL, last_in_obj, _input)) for f in state_funcs @@ -86,6 +88,7 @@ class Executor: self.apply_env_proc(env_processes, last_in_copy, last_in_copy['timestep']) + # ToDo: make 'substep' & 'timestep' reserve fields last_in_copy['substep'], last_in_copy['timestep'], last_in_copy['run'] = sub_step, time_step, run sL.append(last_in_copy) @@ -106,7 +109,16 @@ class Executor: sub_step = 0 states_list_copy: List[Dict[str, Any]] = deepcopy(states_list) + # for d1 in states_list: + # for d2 in states_list_copy: + # d2['classX'] = d1['classX'] + # + # print() + # pp.pprint(states_list_copy) + # print() + genesis_states: Dict[str, Any] = states_list_copy[-1] + del states_list_copy genesis_states['substep'], genesis_states['timestep'] = sub_step, time_step states_list: List[Dict[str, Any]] = [genesis_states] @@ -158,6 +170,11 @@ class Executor: def execute_run(var_dict, states_list, configs, env_processes, time_seq, run) -> List[Dict[str, Any]]: run += 1 states_list_copy: List[Dict[str, Any]] = deepcopy(states_list) + + # for d1 in states_list: + # for d2 in states_list_copy: + # d2['classX'] = d1['classX'] + head, *tail = self.run_pipeline(var_dict, states_list_copy, configs, env_processes, time_seq, run) del states_list_copy diff --git a/simulations/az_run.py b/simulations/az_run.py new file mode 100644 index 0000000..8c199ce --- /dev/null +++ b/simulations/az_run.py @@ -0,0 +1,25 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.validation import config_az +from cadCAD import configs + +import pprint as pp + +exec_mode = ExecutionMode() + +print("Simulation Execution: Single Configuration") +print() +first_config = configs # only contains config1 +single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) +run = Executor(exec_context=single_proc_ctx, configs=first_config) + +raw_result, tensor_field = run.main() +result = pd.DataFrame(raw_result) +print() +print("Tensor Field: config1") +print(tabulate(tensor_field, headers='keys', tablefmt='psql')) +print("Output:") +print(tabulate(result, headers='keys', tablefmt='psql')) +print() \ No newline at end of file diff --git a/simulations/validation/config1.py b/simulations/validation/config1.py index 2c26f91..70d1da3 100644 --- a/simulations/validation/config1.py +++ b/simulations/validation/config1.py @@ -6,7 +6,6 @@ from cadCAD.configuration import append_configs from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step from cadCAD.configuration.utils.parameterSweep import config_sim - seeds = { 'z': np.random.RandomState(1), 'a': np.random.RandomState(2), @@ -37,7 +36,7 @@ def p2m3(_g, step, sL, s): # Internal States per Mechanism def s1m1(_g, step, sL, s, _input): y = 's1' - x = _input['param1'] + x = s['s1'] + 1 return (y, x) def s2m1(_g, step, sL, s, _input): y = 's2' @@ -46,7 +45,7 @@ def s2m1(_g, step, sL, s, _input): def s1m2(_g, step, sL, s, _input): y = 's1' - x = _input['param1'] + x = s['s1'] + 1 return (y, x) def s2m2(_g, step, sL, s, _input): y = 's2' @@ -55,7 +54,7 @@ def s2m2(_g, step, sL, s, _input): def s1m3(_g, step, sL, s, _input): y = 's1' - x = _input['param1'] + x = s['s1'] + 1 return (y, x) def s2m3(_g, step, sL, s, _input): y = 's2' diff --git a/simulations/validation/config_az.py b/simulations/validation/config_az.py new file mode 100644 index 0000000..02ff2f3 --- /dev/null +++ b/simulations/validation/config_az.py @@ -0,0 +1,91 @@ +from copy import deepcopy +from datetime import timedelta +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import ep_time_step +from cadCAD.configuration.utils.parameterSweep import config_sim + + +class MyClass: + def __init__(self): + self.x = 0 + print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") + + def update(self): + # res = deepcopy(self) + self.x += 1 + # print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") + return self #res + + def __str__(self): + return f"PRINT MyClass: @ {hex(id(self))}: value {self.x}" + +# genesis state +state_dict = { + 'classX': MyClass(), + 'a': 0, + 'b': 0, + 'timestamp': '2019-01-01 00:00:00' +} + + +timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. +ts_format = '%Y-%m-%d %H:%M:%S' +def time_model(_g, step, sL, s, _input): + y = 'timestamp' + x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) + return (y, x) + + +def updateClassX(_g, step, sL, s, _input): + y = 'classX' + x = s['classX'] + return (y, x) + + +def updateA(_g, step, sL, s, _input): + y = 'a' + x = s['a'] + 1 + return (y, x) + + +def updateB(_g, step, sL, s, _input): + s['classX'].update() + y = 'b' + x = s['classX'].x + return (y, x) + + +partial_state_update_blocks = { + 'PSUB1': { + 'behaviors': { + }, + 'states': { + 'timestamp': time_model + } + }, + 'PSUB2': { + 'behaviors': { + }, + 'states': { + 'classX': updateClassX, + 'a': updateA, + 'b': updateB + } + }, + 'PSUB3': { + 'behaviors': { + }, + 'states': { + 'classX': updateClassX, + 'a': updateA, + 'b': updateB + } + } +} + +sim_config = config_sim({ + "N": 2, + "T": range(4) +}) + +append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) From ac44a7bee8b1ab884c8410422065e8ea8b11d827 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Mon, 18 Mar 2019 11:42:37 -0400 Subject: [PATCH 02/19] checkpoint --- cadCAD/configuration/utils/__init__.py | 22 ++- cadCAD/configuration/utils/parameterSweep.py | 20 --- cadCAD/engine/simulation.py | 45 ++++- simulations/az_run_a.py | 24 +++ simulations/az_run_b.py | 24 +++ simulations/az_run_udc.py | 24 +++ simulations/az_run_udc_json.py | 24 +++ simulations/validation/config1.py | 3 +- simulations/validation/config2.py | 3 +- simulations/validation/config4.py | 3 +- simulations/validation/config_az.py | 6 +- simulations/validation/config_az_a.py | 113 +++++++++++++ simulations/validation/config_az_b.py | 86 ++++++++++ simulations/validation/config_udc.py | 169 +++++++++++++++++++ simulations/validation/config_udc_json.py | 150 ++++++++++++++++ simulations/validation/sweep_config.py | 11 +- 16 files changed, 687 insertions(+), 40 deletions(-) delete mode 100644 cadCAD/configuration/utils/parameterSweep.py create mode 100644 simulations/az_run_a.py create mode 100644 simulations/az_run_b.py create mode 100644 simulations/az_run_udc.py create mode 100644 simulations/az_run_udc_json.py create mode 100644 simulations/validation/config_az_a.py create mode 100644 simulations/validation/config_az_b.py create mode 100644 simulations/validation/config_udc.py create mode 100644 simulations/validation/config_udc_json.py diff --git a/cadCAD/configuration/utils/__init__.py b/cadCAD/configuration/utils/__init__.py index d0d9aec..ee121ad 100644 --- a/cadCAD/configuration/utils/__init__.py +++ b/cadCAD/configuration/utils/__init__.py @@ -6,7 +6,7 @@ import pandas as pd # Temporary from cadCAD.configuration.utils.depreciationHandler import sanitize_partial_state_updates -from cadCAD.utils import dict_filter, contains_type +from cadCAD.utils import dict_filter, contains_type, flatten_tabulated_dict, tabulate_dict # ToDo: Fix - Returns empty when partial_state_update is missing in Configuration @@ -122,4 +122,22 @@ def exo_update_per_ts(ep): else: return y, s[y] - return {es: ep_decorator(f, es) for es, f in ep.items()} \ No newline at end of file + return {es: ep_decorator(f, es) for es, f in ep.items()} + + +# param sweep enabling middleware +def config_sim(d): + def process_variables(d): + return flatten_tabulated_dict(tabulate_dict(d)) + if "M" in d: + return [ + { + "N": d["N"], + "T": d["T"], + "M": M + } + for M in process_variables(d["M"]) + ] + else: + d["M"] = [{}] + return d \ No newline at end of file diff --git a/cadCAD/configuration/utils/parameterSweep.py b/cadCAD/configuration/utils/parameterSweep.py deleted file mode 100644 index a91e938..0000000 --- a/cadCAD/configuration/utils/parameterSweep.py +++ /dev/null @@ -1,20 +0,0 @@ -from cadCAD.utils import flatten_tabulated_dict, tabulate_dict - - -def process_variables(d): - return flatten_tabulated_dict(tabulate_dict(d)) - - -def config_sim(d): - if "M" in d: - return [ - { - "N": d["N"], - "T": d["T"], - "M": M - } - for M in process_variables(d["M"]) - ] - else: - d["M"] = [{}] - return d \ No newline at end of file diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 104e8d2..b55d0bf 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -68,18 +68,47 @@ class Executor: run: int ) -> List[Dict[str, Any]]: - last_in_obj: Dict[str, Any] = sL[-1] + last_in_obj: Dict[str, Any] = deepcopy(sL[-1]) + # last_in_obj: Dict[str, Any] = sL[-1] _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sL, last_in_obj, policy_funcs)) # ToDo: add env_proc generator to `last_in_copy` iterator as wrapper function # ToDo: Can be multithreaded ?? + + # ToDo: Create Separate past state paradigm for which users specify the use of identity / past function + # ToDo: UDC / any class must be deepcopy before every update + # vs an assumed update + + # last_class = deepcopy(last_in_obj['classX']) + + # incoming + + # past_attr_dict = {k: v for k, v in last_in_obj.items() if + # hasattr(v, 'past_attr') and k == v.past_attr} + # incoming_attr_dict = {k: deepcopy(v) for k, v in last_in_obj.items() if + # hasattr(v, 'past_attr') and k != v.past_attr} + + # udcs = {k: deepcopy(v) for k, v in last_in_obj.items() if hasattr(v, 'class_id')} + # non_udcs = {k: deepcopy(v) for k, v in last_in_obj.items() if not hasattr(v, 'class_id')} + + + # past_attr_dict = {k: v for k, v in last_in_obj.items() if 'past' in v.keys()} + # incoming_attr_dict = {k: v for k, v in last_in_obj.items() if 'current' in v.keys()} + + # ToDo: Previous Record Cache + # last_in_copy_staging = deepcopy(last_in_obj) + + # past_udc = deepcopy(last_in_obj['classX']['current']) + last_in_copy: Dict[str, Any] = dict( [ self.state_update_exception(f(var_dict, sub_step, sL, last_in_obj, _input)) for f in state_funcs ] ) + # a b c d e f g + for k in last_in_obj: if k not in last_in_copy: last_in_copy[k] = last_in_obj[k] @@ -91,6 +120,16 @@ class Executor: # ToDo: make 'substep' & 'timestep' reserve fields last_in_copy['substep'], last_in_copy['timestep'], last_in_copy['run'] = sub_step, time_step, run + # # ToDo: Handle conditions + # for k_past, _ in past_attr_dict.items(): + # for _, v_current in incoming_attr_dict.items(): + # last_in_copy[k_past] = v_current + + # last_in_copy['pastX'] = last_class + + # last_in_copy['classX']['past'] = past_udc + # last_in_copy['pastX_str'] = past_udc + sL.append(last_in_copy) del last_in_copy @@ -109,10 +148,11 @@ class Executor: sub_step = 0 states_list_copy: List[Dict[str, Any]] = deepcopy(states_list) + # for d1 in states_list: # for d2 in states_list_copy: # d2['classX'] = d1['classX'] - # + # print() # pp.pprint(states_list_copy) # print() @@ -125,6 +165,7 @@ class Executor: sub_step += 1 for config in configs: s_conf, p_conf = config[0], config[1] + # states_list["classX"] = deepcopy(classX) states_list: List[Dict[str, Any]] = self.partial_state_update( var_dict, sub_step, states_list, s_conf, p_conf, env_processes, time_step, run ) diff --git a/simulations/az_run_a.py b/simulations/az_run_a.py new file mode 100644 index 0000000..7fe6f84 --- /dev/null +++ b/simulations/az_run_a.py @@ -0,0 +1,24 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.validation import config_az_a +from cadCAD import configs + + +exec_mode = ExecutionMode() + +print("Simulation Execution: Single Configuration") +print() +first_config = configs # only contains config1 +single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) +run = Executor(exec_context=single_proc_ctx, configs=first_config) + +raw_result, tensor_field = run.main() +result = pd.DataFrame(raw_result) +print() +print("Tensor Field: config1") +print(tabulate(tensor_field, headers='keys', tablefmt='psql')) +print("Output:") +print(tabulate(result, headers='keys', tablefmt='psql')) +print() \ No newline at end of file diff --git a/simulations/az_run_b.py b/simulations/az_run_b.py new file mode 100644 index 0000000..483677e --- /dev/null +++ b/simulations/az_run_b.py @@ -0,0 +1,24 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.validation import config_az_b +from cadCAD import configs + + +exec_mode = ExecutionMode() + +print("Simulation Execution: Single Configuration") +print() +first_config = configs # only contains config1 +single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) +run = Executor(exec_context=single_proc_ctx, configs=first_config) + +raw_result, tensor_field = run.main() +result = pd.DataFrame(raw_result) +print() +print("Tensor Field: config1") +print(tabulate(tensor_field, headers='keys', tablefmt='psql')) +print("Output:") +print(tabulate(result, headers='keys', tablefmt='psql')) +print() \ No newline at end of file diff --git a/simulations/az_run_udc.py b/simulations/az_run_udc.py new file mode 100644 index 0000000..0406896 --- /dev/null +++ b/simulations/az_run_udc.py @@ -0,0 +1,24 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.validation import config_udc +from cadCAD import configs + + +exec_mode = ExecutionMode() + +print("Simulation Execution: Single Configuration") +print() +first_config = configs # only contains config1 +single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) +run = Executor(exec_context=single_proc_ctx, configs=first_config) + +raw_result, tensor_field = run.main() +result = pd.DataFrame(raw_result) +print() +print("Tensor Field: config1") +print(tabulate(tensor_field, headers='keys', tablefmt='psql')) +print("Output:") +print(tabulate(result, headers='keys', tablefmt='psql')) +print() \ No newline at end of file diff --git a/simulations/az_run_udc_json.py b/simulations/az_run_udc_json.py new file mode 100644 index 0000000..81190e3 --- /dev/null +++ b/simulations/az_run_udc_json.py @@ -0,0 +1,24 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.validation import config_udc_json +from cadCAD import configs + + +exec_mode = ExecutionMode() + +print("Simulation Execution: Single Configuration") +print() +first_config = configs # only contains config1 +single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) +run = Executor(exec_context=single_proc_ctx, configs=first_config) + +raw_result, tensor_field = run.main() +result = pd.DataFrame(raw_result) +print() +print("Tensor Field: config1") +print(tabulate(tensor_field, headers='keys', tablefmt='psql')) +print("Output:") +print(tabulate(result, headers='keys', tablefmt='psql')) +print() \ No newline at end of file diff --git a/simulations/validation/config1.py b/simulations/validation/config1.py index 70d1da3..9f0fb64 100644 --- a/simulations/validation/config1.py +++ b/simulations/validation/config1.py @@ -3,8 +3,7 @@ import numpy as np from datetime import timedelta from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step -from cadCAD.configuration.utils.parameterSweep import config_sim +from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step, config_sim seeds = { 'z': np.random.RandomState(1), diff --git a/simulations/validation/config2.py b/simulations/validation/config2.py index 0247c9b..a75b22a 100644 --- a/simulations/validation/config2.py +++ b/simulations/validation/config2.py @@ -3,8 +3,7 @@ import numpy as np from datetime import timedelta from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step -from cadCAD.configuration.utils.parameterSweep import config_sim +from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step, config_sim seeds = { 'z': np.random.RandomState(1), diff --git a/simulations/validation/config4.py b/simulations/validation/config4.py index 62f626f..164df26 100644 --- a/simulations/validation/config4.py +++ b/simulations/validation/config4.py @@ -3,8 +3,7 @@ import numpy as np from datetime import timedelta from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step -from cadCAD.configuration.utils.parameterSweep import config_sim +from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step, config_sim seeds = { diff --git a/simulations/validation/config_az.py b/simulations/validation/config_az.py index 02ff2f3..746df2d 100644 --- a/simulations/validation/config_az.py +++ b/simulations/validation/config_az.py @@ -1,8 +1,6 @@ -from copy import deepcopy from datetime import timedelta from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import ep_time_step -from cadCAD.configuration.utils.parameterSweep import config_sim +from cadCAD.configuration.utils import ep_time_step, config_sim class MyClass: @@ -60,7 +58,7 @@ partial_state_update_blocks = { 'behaviors': { }, 'states': { - 'timestamp': time_model + 'timestamp': time_model, } }, 'PSUB2': { diff --git a/simulations/validation/config_az_a.py b/simulations/validation/config_az_a.py new file mode 100644 index 0000000..153d883 --- /dev/null +++ b/simulations/validation/config_az_a.py @@ -0,0 +1,113 @@ +from datetime import timedelta +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import ep_time_step, config_sim + + +class MyClass: + def __init__(self): + self.x = 0 + print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") + + def update(self): + self.x += 1 + print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") + return self + + def __str__(self): + return f"PRINT MyClass: @ {hex(id(self))}: value {self.x}" + +# a is Correct, and classX's value is Incorrect +# Expected: a == classX's value +# b should be tracking classX's value and a: +# b should be the same value as the previous classX value and the previous a value + +udc = MyClass() +# z = MyClass() +# pointer(z) +# separate thread/process for UCD with async calls to this thread/process + +# genesis state +state_dict = { + 'classX': udc, + 'c_udc': udc, + 'a': 0, + 'b': 0, + 'c': "", + 'd': None, + 'timestamp': '2019-01-01 00:00:00' +} + +timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. +ts_format = '%Y-%m-%d %H:%M:%S' +def time_model(_g, step, sL, s, _input): + y = 'timestamp' + x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) + return (y, x) + +def updateClassX(_g, step, sL, s, _input): + y = 'classX' + x = s['classX'].update() + return (y, x) + +def updateA(_g, step, sL, s, _input): + y = 'a' + x = s['a'] + 1 + return (y, x) + +def updateB(_g, step, sL, s, _input): + y = 'b' + x = s['classX'].x + return (y, x) + +def updateC(_g, step, sL, s, _input): + y = 'c' + x = f"PRINT MyClass: @ {hex(id(s['classX']))}: value {s['classX'].x}" + return (y, x) + +def updateD(_g, step, sL, s, _input): + y = 'd' + x = s['classX'] + return (y, x) + + +partial_state_update_blocks = { + 'PSUB1': { + 'behaviors': { + }, + 'states': { + 'timestamp': time_model, + 'b': updateB, + 'c': updateC, + # 'd': updateD + } + }, + 'PSUB2': { + 'behaviors': { + }, + 'states': { + 'classX': updateClassX, + 'a': updateA, + 'b': updateB, + 'c': updateC, + # 'd': updateD + } + }, + 'PSUB3': { + 'behaviors': { + }, + 'states': { + 'classX': updateClassX, + 'a': updateA, + 'b': updateB, + 'c': updateC, + # 'd': updateD + } + } +} + +sim_config = config_sim({ + "N": 2, + "T": range(4) +}) + +append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) diff --git a/simulations/validation/config_az_b.py b/simulations/validation/config_az_b.py new file mode 100644 index 0000000..462b40b --- /dev/null +++ b/simulations/validation/config_az_b.py @@ -0,0 +1,86 @@ +from copy import deepcopy +from datetime import timedelta +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import ep_time_step, config_sim + +class MyClass: + def __init__(self): + self.x = 0 + print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") + + def update(self): + res = deepcopy(self) + res.x += 1 + print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") + return res + + def __str__(self): + return f"PRINT MyClass: @ {hex(id(self))}: value {self.x}" + + +# genesis state +state_dict = { + 'classX': MyClass(), + 'a': 0, + 'b': 0, + 'timestamp': '2019-01-01 00:00:00' +} + +timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. +ts_format = '%Y-%m-%d %H:%M:%S' +def time_model(_g, step, sL, s, _input): + y = 'timestamp' + x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) + return (y, x) + +def updateClassX(_g, step, sL, s, _input): + y = 'classX' + x = s['classX'].update() + return (y, x) + +def updateA(_g, step, sL, s, _input): + y = 'a' + x = s['a'] + 1 + return (y, x) + +def updateB(_g, step, sL, s, _input): + y = 'b' + # x = s['classX'].x + x = s['a'] + return (y, x) + +partial_state_update_blocks = { + 'PSUB1': { + 'behaviors': { + }, + 'states': { + 'timestamp': time_model, + 'b': updateB + } + }, + 'PSUB2': { + 'behaviors': { + }, + 'states': { + 'classX': updateClassX, + 'a': updateA, + 'b': updateB + } + }, + 'PSUB3': { + 'behaviors': { + }, + 'states': { + 'classX': updateClassX, + 'a': updateA, + 'b': updateB + } + } +} + +sim_config = config_sim({ + "N": 2, + "T": range(4) +}) + +append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) diff --git a/simulations/validation/config_udc.py b/simulations/validation/config_udc.py new file mode 100644 index 0000000..6904766 --- /dev/null +++ b/simulations/validation/config_udc.py @@ -0,0 +1,169 @@ +from datetime import timedelta +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import ep_time_step, config_sim +from copy import deepcopy + + +# ToDo: Create member for past value +class MyClass: + def __init__(self, past_attr): + self.past_self = self + self.past_attr = past_attr + self.class_id = None + self.x = 0 + + print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") + + def update(self): + # self.past_self = deepcopy(self) + self.x += 1 + print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") + return self.x #self #old_self #self.x + + def past(self): + return self.past_self + + def getMemID(self): + return str(hex(id(self))) + + # can be accessed after an update within the same substep and timestep + # ToDo: id sensitive to lineage, rerepresent + def __str__(self): + # return str(self.x) + return f"{hex(id(self))} - {self.x}" + + +# a is Correct, and classX's value is Incorrect +# Expected: a == classX's value +# b should be tracking classX's value and a: +# b should be the same value as the previous classX value and the previous a value + +udc = MyClass('pastX') + +# z = MyClass() +# pointer(z) +# separate thread/process for UCD with async calls to this thread/process + +# genesis state + +# udc_json = {'udc': udc, 'udc-1': udc} +state_dict = { + 'classX': udc, + 'classX_MemID': udc.getMemID(), + # 'pastX': udc, + # 'pastX_MemID': udc.getMemID(), + 'a': 0, + 'b': udc.x, + 'c': udc.x, + 'c2': udc.x, + 'z': udc.x, + 'timestamp': '2019-01-01 00:00:00' +} + +timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. +ts_format = '%Y-%m-%d %H:%M:%S' +def time_model(_g, step, sL, s, _input): + y = 'timestamp' + x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) + return (y, x) + +def trackClassX(_g, step, sL, s, _input): + y = 'classX' + x = s['classX'] + return (y, x) + +def trackClassX_str(_g, step, sL, s, _input): + y = 'classX_MemID' + x = s['classX'].getMemID() + return (y, x) + +def updatePastX(_g, step, sL, s, _input): + y = 'pastX' + x = s['pastX'] + return (y, x) + +def updatePastX_str(_g, step, sL, s, _input): + y = 'pastX_MemID' + x = s['pastX'].getMemID() + return (y, x) + +def updateA(_g, step, sL, s, _input): + y = 'a' + x = s['a'] + 1 + return (y, x) + +def updateB(_g, step, sL, s, _input): + y = 'b' + x = s['classX'].x + return (y, x) + +def updateC(_g, step, sL, s, _input): + y = 'c' + x = s['classX'].update() + return (y, x) + +def updateZ(_g, step, sL, s, _input): + y = 'z' + x = s['classX'].x + return (y, x) + +def updateC2(_g, step, sL, s, _input): + y = 'c2' + x = s['classX'].x + return (y, x) + +partial_state_update_blocks = { + 'PSUB1': { + 'behaviors': { + }, + 'states': { + 'a': updateA, + 'b': updateB, + 'c': updateC, + 'c2': updateC2, + 'classX': trackClassX, + 'timestamp': time_model, + 'classX_MemID': trackClassX_str, + # 'pastX': updatePastX, + # 'pastX_MemID': updatePastX_str, + 'z': updateZ + } + }, + 'PSUB2': { + 'behaviors': { + }, + 'states': { + 'a': updateA, + 'b': updateB, + 'c': updateC, + 'c2': updateC2, + 'classX': trackClassX, + 'classX_MemID': trackClassX_str, + # 'pastX': updatePastX, + # 'pastX_MemID': updatePastX_str, + 'z': updateZ + } + }, + 'PSUB3': { + 'behaviors': { + }, + 'states': { + 'a': updateA, + 'b': updateB, + 'c': updateC, + 'c2': updateC2, + 'classX': trackClassX, + 'classX_MemID': trackClassX_str, + # 'pastX': updatePastX, + # 'pastX_MemID': updatePastX_str, + 'z': updateZ + } + } +} + +sim_config = config_sim({ + "N": 2, + "T": range(4) +}) + +append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) diff --git a/simulations/validation/config_udc_json.py b/simulations/validation/config_udc_json.py new file mode 100644 index 0000000..8aa8d85 --- /dev/null +++ b/simulations/validation/config_udc_json.py @@ -0,0 +1,150 @@ +from datetime import timedelta +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import ep_time_step, config_sim +import inspect + + +# ToDo: Create member for past value +class MyClass: + def __init__(self, past_attr): + self.past_attr = past_attr + self.x = 0 + + print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") + + def update(self): + self.x += 1 + print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") + return self.x + + def getMemID(self): + return str(hex(id(self))) + + # can be accessed after an update within the same substep and timestep + def __str__(self): + # return str(self.x) + return f"{hex(id(self))} - {self.x}" + + +# a is Correct, and classX's value is Incorrect +# Expected: a == classX's value +# b should be tracking classX's value and a: +# b should be the same value as the previous classX value and the previous a value + +udc = MyClass('pastX') + +# z = MyClass() +# pointer(z) +# separate thread/process for UCD with async calls to this thread/process + +# genesis state +# seperate process +# staging + +udc_json = {'current': udc, 'past': udc} +state_dict = { + 'classX': udc_json, + 'classX_str': udc_json['current'], + # 'pastX': udc, + 'pastX_str': f"{hex(id(udc_json['past']))} - {udc_json['past'].x}", + 'a': 0, + 'b': udc_json['current'].x, + 'c': udc_json['current'].x, + 'timestamp': '2019-01-01 00:00:00' +} + +timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. +ts_format = '%Y-%m-%d %H:%M:%S' +def time_model(_g, step, sL, s, _input): + y = 'timestamp' + x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) + return (y, x) + +def trackClassX(_g, step, sL, s, _input): + y = 'classX' + x = s['classX'] + return (y, x) + +def trackClassX_str(_g, step, sL, s, _input): + y = 'classX_str' + # x = s['classX']['current'] + x = s['classX_str'] + return (y, x) + +def updatePastX(_g, step, sL, s, _input): + y = 'pastX' + x = s['pastX'] + return (y, x) + +def updatePastX_str(_g, step, sL, s, _input): + y = 'pastX_str' + x = s['classX']['past'] + # x = f"{hex(id(s['classX']['past']))} - {s['classX']['past'].x}" + # x = s['pastX_str'] + return (y, x) + +def updateA(_g, step, sL, s, _input): + y = 'a' + x = s['a'] + 1 + return (y, x) + +def updateB(_g, step, sL, s, _input): + y = 'b' + x = s['classX']['current'].x + return (y, x) + +def updateC(_g, step, sL, s, _input): + y = 'c' + x = s['classX']['current'].update() + return (y, x) + + + +partial_state_update_blocks = { + 'PSUB1': { + 'behaviors': { + }, + 'states': { + 'a': updateA, + 'b': updateB, + 'c': updateC, + 'timestamp': time_model, + 'classX_str': trackClassX_str, + # 'pastX': updatePastX, + 'pastX_str': updatePastX_str + } + }, + 'PSUB2': { + 'behaviors': { + }, + 'states': { + 'a': updateA, + 'b': updateB, + 'c': updateC, + 'classX': trackClassX, + 'classX_str': trackClassX_str, + # 'pastX': updatePastX, + 'pastX_str': updatePastX_str, + } + }, + 'PSUB3': { + 'behaviors': { + }, + 'states': { + 'a': updateA, + 'b': updateB, + 'c': updateC, + 'classX': trackClassX, + 'classX_str': trackClassX_str, + # 'pastX': updatePastX, + 'pastX_str': updatePastX_str, + } + } +} + +sim_config = config_sim({ + "N": 2, + "T": range(4) +}) + +append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) diff --git a/simulations/validation/sweep_config.py b/simulations/validation/sweep_config.py index 2350d95..40071b6 100644 --- a/simulations/validation/sweep_config.py +++ b/simulations/validation/sweep_config.py @@ -4,8 +4,7 @@ from datetime import timedelta import pprint from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import proc_trigger, ep_time_step -from cadCAD.configuration.utils.parameterSweep import config_sim +from cadCAD.configuration.utils import proc_trigger, ep_time_step, config_sim from typing import Dict, List @@ -18,7 +17,7 @@ seeds = { 'c': np.random.RandomState(3) } - +# Optional g: Dict[str, List[int]] = { 'alpha': [1], 'beta': [2, 5], @@ -178,16 +177,16 @@ partial_state_update_block = { } } - +# config_sim Necessary sim_config = config_sim( { "N": 2, "T": range(5), - "M": g + "M": g # Optional } ) - +# New Convention append_configs( sim_configs=sim_config, initial_state=genesis_states, From d56d60d7a3e33c5f8c6a09ee8de8cbb11fc7735c Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Fri, 29 Mar 2019 09:10:31 -0400 Subject: [PATCH 03/19] hydra --- cadCAD/engine/simulation.py | 93 +++----- cadCAD/utils/__init__.py | 38 ++++ simulations/az_run_udc.py | 2 +- simulations/sweep_udc_hack_run.py | 24 +++ simulations/validation/config_udc.py | 54 ++--- simulations/validation/config_udc3.py | 210 ++++++++++++++++++ simulations/validation/config_udc_json2.py | 235 +++++++++++++++++++++ 7 files changed, 573 insertions(+), 83 deletions(-) create mode 100644 simulations/sweep_udc_hack_run.py create mode 100644 simulations/validation/config_udc3.py create mode 100644 simulations/validation/config_udc_json2.py diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index b55d0bf..d9b4823 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -1,22 +1,22 @@ +from collections import namedtuple from typing import Any, Callable, Dict, List, Tuple from pathos.pools import ThreadPool as TPool from copy import deepcopy from fn.op import foldr, call from cadCAD.engine.utils import engine_exception -from cadCAD.utils import flatten -import pprint as pp +from cadCAD.utils import flatten, UDC_Wrapper id_exception: Callable = engine_exception(KeyError, KeyError, None) class Executor: def __init__( - self, - policy_ops: List[Callable], - policy_update_exception: Callable = id_exception, - state_update_exception: Callable = id_exception - ) -> None: + self, + policy_ops: List[Callable], + policy_update_exception: Callable = id_exception, + state_update_exception: Callable = id_exception + ) -> None: # behavior_ops self.policy_ops = policy_ops @@ -69,6 +69,7 @@ class Executor: ) -> List[Dict[str, Any]]: last_in_obj: Dict[str, Any] = deepcopy(sL[-1]) + udc = var_dict[0]['udc'] # last_in_obj: Dict[str, Any] = sL[-1] _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sL, last_in_obj, policy_funcs)) @@ -76,38 +77,37 @@ class Executor: # ToDo: add env_proc generator to `last_in_copy` iterator as wrapper function # ToDo: Can be multithreaded ?? - # ToDo: Create Separate past state paradigm for which users specify the use of identity / past function - # ToDo: UDC / any class must be deepcopy before every update - # vs an assumed update + def generate_record(state_funcs, alt_udc_dict): + for k, v in last_in_obj.items(): + if isinstance(v, dict) and hasattr(v, 'class_id'): + del last_in_obj[k] - # last_class = deepcopy(last_in_obj['classX']) + # def HydraObj(_g, step, sL, s, _input): + # y = 'hydra_obj' + # # x = s['hydra_obj'] + # x = namedtuple("Hydra", s['hydra_members'].keys())(*s['hydra_members'].values()) + # return (y, x) - # incoming - - # past_attr_dict = {k: v for k, v in last_in_obj.items() if - # hasattr(v, 'past_attr') and k == v.past_attr} - # incoming_attr_dict = {k: deepcopy(v) for k, v in last_in_obj.items() if - # hasattr(v, 'past_attr') and k != v.past_attr} - - # udcs = {k: deepcopy(v) for k, v in last_in_obj.items() if hasattr(v, 'class_id')} - # non_udcs = {k: deepcopy(v) for k, v in last_in_obj.items() if not hasattr(v, 'class_id')} + new_last_in_obj = dict(list(last_in_obj.items()) + list(alt_udc_dict.items())) + # for f in state_funcs + [HydraObj]: + for f in state_funcs: + # ToDo: Create Named Tuple Here + y, x = f(var_dict, sub_step, sL, new_last_in_obj, _input) + # if isinstance(x, dict) and x['hydra_type'] == Dict and 'class_id' in x.keys(): + # x = namedtuple("Hydra", x.keys())(*x.values()) + yield self.state_update_exception((y, x)) - # past_attr_dict = {k: v for k, v in last_in_obj.items() if 'past' in v.keys()} - # incoming_attr_dict = {k: v for k, v in last_in_obj.items() if 'current' in v.keys()} - - # ToDo: Previous Record Cache - # last_in_copy_staging = deepcopy(last_in_obj) - - # past_udc = deepcopy(last_in_obj['classX']['current']) - - last_in_copy: Dict[str, Any] = dict( - [ - self.state_update_exception(f(var_dict, sub_step, sL, last_in_obj, _input)) for f in state_funcs - ] - ) - - # a b c d e f g + udc_dict = { + k: UDC_Wrapper( + v['current'], + udc(**v['current'].__dict__), + current_functions=['update'] + ).get_hybrid_members() + for k, v in last_in_obj.items() if isinstance(v, dict) and 'current' in v.keys() + } + last_in_copy: Dict[str, Any] = dict(generate_record(state_funcs, udc_dict)) + del udc_dict for k in last_in_obj: if k not in last_in_copy: @@ -120,16 +120,6 @@ class Executor: # ToDo: make 'substep' & 'timestep' reserve fields last_in_copy['substep'], last_in_copy['timestep'], last_in_copy['run'] = sub_step, time_step, run - # # ToDo: Handle conditions - # for k_past, _ in past_attr_dict.items(): - # for _, v_current in incoming_attr_dict.items(): - # last_in_copy[k_past] = v_current - - # last_in_copy['pastX'] = last_class - - # last_in_copy['classX']['past'] = past_udc - # last_in_copy['pastX_str'] = past_udc - sL.append(last_in_copy) del last_in_copy @@ -149,14 +139,6 @@ class Executor: sub_step = 0 states_list_copy: List[Dict[str, Any]] = deepcopy(states_list) - # for d1 in states_list: - # for d2 in states_list_copy: - # d2['classX'] = d1['classX'] - - # print() - # pp.pprint(states_list_copy) - # print() - genesis_states: Dict[str, Any] = states_list_copy[-1] del states_list_copy genesis_states['substep'], genesis_states['timestep'] = sub_step, time_step @@ -165,7 +147,6 @@ class Executor: sub_step += 1 for config in configs: s_conf, p_conf = config[0], config[1] - # states_list["classX"] = deepcopy(classX) states_list: List[Dict[str, Any]] = self.partial_state_update( var_dict, sub_step, states_list, s_conf, p_conf, env_processes, time_step, run ) @@ -212,10 +193,6 @@ class Executor: run += 1 states_list_copy: List[Dict[str, Any]] = deepcopy(states_list) - # for d1 in states_list: - # for d2 in states_list_copy: - # d2['classX'] = d1['classX'] - head, *tail = self.run_pipeline(var_dict, states_list_copy, configs, env_processes, time_seq, run) del states_list_copy diff --git a/cadCAD/utils/__init__.py b/cadCAD/utils/__init__.py index 8e1220c..68aa4ef 100644 --- a/cadCAD/utils/__init__.py +++ b/cadCAD/utils/__init__.py @@ -2,6 +2,44 @@ from typing import Dict, List from collections import defaultdict from itertools import product import warnings +from inspect import getmembers, ismethod +from copy import deepcopy +from collections import namedtuple + + +class objectview(object): + def __init__(self, d): + self.__dict__ = d + + +class UDC_Wrapper(object): + def __init__(self, current, past, current_functions, past_functions=[]): + current_funcs = dict(getmembers(current, ismethod)) + # current_funcs['object'] = current + filtered_current_funcs = {k: v for k, v in current_funcs.items() if k in current_functions} + # current_members = filtered_current_funcs.update(vars(current)) + + # past_funcs = dict(getmembers(past, ismethod)) + # past_funcs['object'] = past + # filtered_past_funcs = {k: v for k, v in past_funcs.items() if k in past_functions} + # past_members = filtered_past_funcs.update(vars(past)) + + filtered_current_funcs['hydra_type'] = Dict + filtered_current_funcs.update(vars(past)) + # print(filtered_current_funcs) + filtered_current_funcs['current'] = current + filtered_current_funcs['past'] = past + + self.hybrid_members = filtered_current_funcs + + def get_hybrid_members(self): + return self.hybrid_members + + def get_namedtuple(self): + return namedtuple("Hydra", self.hybrid_members.keys())(*self.hybrid_members.values()) + + # def hybrid_members_values(self): + # return [v for k, v in self.hybrid_members.keys()] def pipe(x): diff --git a/simulations/az_run_udc.py b/simulations/az_run_udc.py index 0406896..de7abb5 100644 --- a/simulations/az_run_udc.py +++ b/simulations/az_run_udc.py @@ -2,7 +2,7 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import config_udc +from simulations.validation import config_udc_json2 from cadCAD import configs diff --git a/simulations/sweep_udc_hack_run.py b/simulations/sweep_udc_hack_run.py new file mode 100644 index 0000000..dd33c16 --- /dev/null +++ b/simulations/sweep_udc_hack_run.py @@ -0,0 +1,24 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.validation import config_udc_json2 +from cadCAD import configs + +exec_mode = ExecutionMode() + +print("Simulation Execution: Concurrent Execution") +multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) +run = Executor(exec_context=multi_proc_ctx, configs=configs) + +i = 0 +config_names = ['sweep_config_A', 'sweep_config_B'] +for raw_result, tensor_field in run.main(): + result = pd.DataFrame(raw_result) + print() + print("Tensor Field: " + config_names[i]) + print(tabulate(tensor_field, headers='keys', tablefmt='psql')) + print("Output:") + print(tabulate(result, headers='keys', tablefmt='psql')) + print() + i += 1 diff --git a/simulations/validation/config_udc.py b/simulations/validation/config_udc.py index 6904766..5c98b69 100644 --- a/simulations/validation/config_udc.py +++ b/simulations/validation/config_udc.py @@ -1,27 +1,26 @@ from datetime import timedelta from cadCAD.configuration import append_configs from cadCAD.configuration.utils import ep_time_step, config_sim -from copy import deepcopy +from copy import deepcopy, copy # ToDo: Create member for past value class MyClass: def __init__(self, past_attr): - self.past_self = self + # self.past = self self.past_attr = past_attr self.class_id = None self.x = 0 print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") + def update(self): - # self.past_self = deepcopy(self) + # self.past = copy(self) self.x += 1 print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") return self.x #self #old_self #self.x - def past(self): - return self.past_self def getMemID(self): return str(hex(id(self))) @@ -37,8 +36,8 @@ class MyClass: # Expected: a == classX's value # b should be tracking classX's value and a: # b should be the same value as the previous classX value and the previous a value - -udc = MyClass('pastX') +# https://pymotw.com/2/multiprocessing/communication.html +udc = MyClass(0) # z = MyClass() # pointer(z) @@ -49,8 +48,9 @@ udc = MyClass('pastX') # udc_json = {'udc': udc, 'udc-1': udc} state_dict = { 'classX': udc, - 'classX_MemID': udc.getMemID(), - # 'pastX': udc, + # 'classX_MemID': udc.getMemID(), + 'pastX': udc, + 'otherX': udc, # 'pastX_MemID': udc.getMemID(), 'a': 0, 'b': udc.x, @@ -72,21 +72,26 @@ def trackClassX(_g, step, sL, s, _input): x = s['classX'] return (y, x) -def trackClassX_str(_g, step, sL, s, _input): - y = 'classX_MemID' - x = s['classX'].getMemID() - return (y, x) +# def trackClassX_str(_g, step, sL, s, _input): +# y = 'classX_MemID' +# x = s['classX'].getMemID() +# return (y, x) def updatePastX(_g, step, sL, s, _input): y = 'pastX' x = s['pastX'] return (y, x) -def updatePastX_str(_g, step, sL, s, _input): - y = 'pastX_MemID' - x = s['pastX'].getMemID() +def updateOtherX(_g, step, sL, s, _input): + y = 'otherX' + x = s['otherX'] return (y, x) +# def updatePastX_str(_g, step, sL, s, _input): +# y = 'pastX_MemID' +# x = s['pastX'].getMemID() +# return (y, x) + def updateA(_g, step, sL, s, _input): y = 'a' x = s['a'] + 1 @@ -109,7 +114,7 @@ def updateZ(_g, step, sL, s, _input): def updateC2(_g, step, sL, s, _input): y = 'c2' - x = s['classX'].x + x = s['pastX'].x return (y, x) partial_state_update_blocks = { @@ -122,10 +127,9 @@ partial_state_update_blocks = { 'c': updateC, 'c2': updateC2, 'classX': trackClassX, + 'otherX': updateOtherX, 'timestamp': time_model, - 'classX_MemID': trackClassX_str, - # 'pastX': updatePastX, - # 'pastX_MemID': updatePastX_str, + 'pastX': updatePastX, 'z': updateZ } }, @@ -138,8 +142,9 @@ partial_state_update_blocks = { 'c': updateC, 'c2': updateC2, 'classX': trackClassX, - 'classX_MemID': trackClassX_str, - # 'pastX': updatePastX, + 'otherX': updateOtherX, + # 'classX_MemID': trackClassX_str, + 'pastX': updatePastX, # 'pastX_MemID': updatePastX_str, 'z': updateZ } @@ -153,8 +158,9 @@ partial_state_update_blocks = { 'c': updateC, 'c2': updateC2, 'classX': trackClassX, - 'classX_MemID': trackClassX_str, - # 'pastX': updatePastX, + 'otherX': updateOtherX, + # 'classX_MemID': trackClassX_str, + 'pastX': updatePastX, # 'pastX_MemID': updatePastX_str, 'z': updateZ } diff --git a/simulations/validation/config_udc3.py b/simulations/validation/config_udc3.py new file mode 100644 index 0000000..c4fc321 --- /dev/null +++ b/simulations/validation/config_udc3.py @@ -0,0 +1,210 @@ +from datetime import timedelta +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import ep_time_step, config_sim + + +# ToDo: Create member for past value +class MyClassA: + def __init__(self): + self.class_id = None + self.x = 0 + + print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") + + + def update(self): + # self.past = copy(self) + self.x += 1 + print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") + return self.x #self #old_self #self.x + + + def getMemID(self): + return str(hex(id(self))) + + # can be accessed after an update within the same substep and timestep + # ToDo: id sensitive to lineage, rerepresent + def __str__(self): + # return str(self.x) + return f"{hex(id(self))} - {self.x}" + + +class MyClassB: + def __init__(self): + self.class_id = None + self.x = 5 + + print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") + + + def update(self): + # self.past = copy(self) + self.x += 1 + print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") + return self.x #self #old_self #self.x + + + def getMemID(self): + return str(hex(id(self))) + + # can be accessed after an update within the same substep and timestep + # ToDo: id sensitive to lineage, rerepresent + def __str__(self): + # return str(self.x) + return f"{hex(id(self))} - {self.x}" + +# a is Correct, and classX's value is Incorrect +# Expected: a == classX's value +# b should be tracking classX's value and a: +# b should be the same value as the previous classX value and the previous a value +# https://pymotw.com/2/multiprocessing/communication.html +udcA = MyClassA() +udcB = MyClassB() + +# z = MyClass() +# pointer(z) +# separate thread/process for UCD with async calls to this thread/process + +# genesis state + +# udc_json = {'udc': udc, 'udc-1': udc} +state_dict = { + 'ca': 0, + 'cb': udcA.x, + 'cblassX': udcA, + 'cc': udcA.x, + 'cz': udcA.x, + 'da': 5, + 'db': udcB.x, + 'dblassX': udcB, + 'dc': udcB.x, + 'dz': udcB.x, + 'timestamp': '2019-01-01 00:00:00' +} + +timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. +ts_format = '%Y-%m-%d %H:%M:%S' +def time_model(_g, step, sL, s, _input): + y = 'timestamp' + x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) + return (y, x) + +def CBlassX(_g, step, sL, s, _input): + y = 'cblassX' + # x = s['cblassX'] + x = _g['cblassX'] + return (y, x) + +def DBlassX(_g, step, sL, s, _input): + y = 'dblassX' + # x = s['dblassX'] + x = _g['dblassX'] + return (y, x) + + +def CA(_g, step, sL, s, _input): + y = 'ca' + x = s['ca'] + 1 + return (y, x) + +def DA(_g, step, sL, s, _input): + y = 'da' + x = s['da'] + 1 + return (y, x) + +def CB(_g, step, sL, s, _input): + y = 'cb' + x = _g['cblassX'].x + # x = s['cblassX'].x + return (y, x) + +def DB(_g, step, sL, s, _input): + y = 'db' + x = _g['dblassX'].x + # x = s['dblassX'].x + return (y, x) + +def CC(_g, step, sL, s, _input): + y = 'cc' + # x = s['cblassX'].update() + x = _g['cblassX'].update() + return (y, x) + +def DC(_g, step, sL, s, _input): + # s['dblassX'] = _g['dblassX'].update() + + y = 'dc' + # x = s['dblassX'].update() + x = _g['dblassX'].update() + return (y, x) + +def CZ(_g, step, sL, s, _input): + y = 'cz' + x = _g['cblassX'].x + # x = s['cblassX'].x + return (y, x) + +def DZ(_g, step, sL, s, _input): + y = 'dz' + x = _g['dblassX'].x + # x = s['dblassX'].x + return (y, x) + +partial_state_update_blocks = { + 'PSUB1': { + 'behaviors': { + }, + 'states': { + 'ca': CA, + 'cb': CB, + 'cblassX': CBlassX, + 'cc': CC, + 'cz': CZ, + 'da': DA, + 'db': DB, + 'dblassX': DBlassX, + 'dc': DC, + 'dz': DZ, + 'timestamp': time_model, + } + }, + 'PSUB2': { + 'behaviors': { + }, + 'states': { + 'ca': CA, + 'cb': CB, + 'cblassX': CBlassX, + 'cc': CC, + 'cz': CZ, + 'da': DA, + 'db': DB, + 'dblassX': DBlassX, + 'dc': DC, + 'dz': DZ, + } + }, + 'PSUB3': { + 'behaviors': { + }, + 'states': { + 'ca': CA, + 'cb': CB, + 'cblassX': CBlassX, + 'cc': CC, + 'cz': CZ, + 'da': DA, + 'db': DB, + 'dblassX': DBlassX, + 'dc': DC, + 'dz': DZ, + } + } +} + +sim_config = config_sim({ + "N": 2, + "T": range(4) +}) + +append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) diff --git a/simulations/validation/config_udc_json2.py b/simulations/validation/config_udc_json2.py new file mode 100644 index 0000000..0169e26 --- /dev/null +++ b/simulations/validation/config_udc_json2.py @@ -0,0 +1,235 @@ +from copy import deepcopy, copy +from datetime import timedelta +from cadCAD.utils import UDC_Wrapper, objectview +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import ep_time_step, config_sim +from typing import Dict, List, Any + +from collections import namedtuple + + +# ToDo: Create member for past value +class MyClassA(object): + def __init__(self, x, class_id=None): + self.class_id = class_id + self.x = x + print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") + + def update(self): + # self.past = copy(self) + self.x += 1 + print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") + return self.x #self #old_self #self.x + + def getMemID(self): + return str(hex(id(self))) + + # can be accessed after an update within the same substep and timestep + # ToDo: id sensitive to lineage, rerepresent + def __str__(self): + # return str(self.x) + return f"{hex(id(self))} - {self.x}" + + +class MyClassB: + def __init__(self, x): + self.class_id = None + self.x = x + + print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") + + + def update(self): + # self.past = copy(self) + self.x += 1 + print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") + return self.x #self #old_self #self.x + + + def getMemID(self): + return str(hex(id(self))) + + # can be accessed after an update within the same substep and timestep + # ToDo: id sensitive to lineage, rerepresent + def __str__(self): + # return str(self.x) + return f"{hex(id(self))} - {self.x}" + +# a is Correct, and classX's value is Incorrect +# Expected: a == classX's value +# b should be tracking classX's value and a: +# b should be the same value as the previous classX value and the previous a value +# https://pymotw.com/2/multiprocessing/communication.html +# ccc = MyClassA +# udc = ccc(0) +# print(MyClassA(**udc.__dict__).__dict__) + +g: Dict[str, List[MyClassA]] = {'udc': [MyClassA]} + +# udcB = MyClassB() + +# z = MyClass() +# pointer(z) +# separate thread/process for UCD with async calls to this thread/process + +# genesis state +udc = MyClassA(0) +# namedtuple("Hydra", self.hybrid_members.keys())(*self.hybrid_members.values()) +udc_json = {'current': udc, 'past': udc} +hydra = UDC_Wrapper(udc, udc, current_functions=['update']) +hydra_members = hydra.get_hybrid_members() +hydra_obj = namedtuple("Hydra", hydra_members.keys())(*hydra_members.values()) + + +state_dict = { + 'a': 0, + 'b': 0, + 'i': 0, + 'j': 0, + 'k': 0, + # "hydra": hydra, + "hydra_members": hydra_members, + "hydra_obj": hydra_obj, + 'hydra_view': objectview(hydra_members), + 'timestamp': '2019-01-01 00:00:00' +} + +timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. +ts_format = '%Y-%m-%d %H:%M:%S' +def time_model(_g, step, sL, s, _input): + y = 'timestamp' + x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) + return (y, x) + +def Hydra(_g, step, sL, s, _input): + y = 'hydra' + x = s['hydra'] + return (y, x) + +def HydraMembers(_g, step, sL, s, _input): + y = 'hydra_members' + x = s['hydra_members'] #.get_hybrid_members() + return (y, x) + +def HydraObj(_g, step, sL, s, _input): + y = 'hydra_obj' + # x = s['hydra_obj'] + # v = list(map(lambda x: copy(x), list(s['hydra_members'].values()))) + + # hydra_members = s['hydra_members'] + # def generate_var_deepcopy(hydra_members): + # for k, v in hydra_members.items(): + # if k == 'x': + # yield k, deepcopy(v) + # else: + # yield k, v + # + # hydra_nt = namedtuple("Hydra", s['hydra_members'].keys())(*s['hydra_members'].values()) + # new_hydra = dict(generate_var_deepcopy(hydra_nt)) + + # new_hydra_members = dict(generate_var_deepcopy(hydra_members)) + x = namedtuple("Hydra", s['hydra_members'].keys())(*s['hydra_members'].values()) + # x = namedtuple("Hydra", new_hydra.keys())(*new_hydra.values()) + + # print(x.x) + return (y, x) + +def HydraView(_g, step, sL, s, _input): + y = 'hydra_view' + x = objectview(s['hydra_members']) + return (y, x) + +def A(_g, step, sL, s, _input): + y = 'a' + x = s['a'] + 1 + return (y, x) + +def B(_g, step, sL, s, _input): + y = 'b' + x = s['hydra_members']['x'] + # x = s['hydra_members'].x + # x = s['hydra_obj'].x + return (y, x) + +def I(_g, step, sL, s, _input): + y = 'i' + # x = s['hydra_members']['update']() + + # Either works + # x = s['hydra_members'].update() + x = s['hydra_obj'].update() + return (y, x) + +def J(_g, step, sL, s, _input): + y = 'j' + x = s['hydra_members']['x'] + # x = s['hydra_members'].x + # x = s['hydra_obj'].x + return (y, x) + + +def K(_g, step, sL, s, _input): + y = 'k' + # x = s['hydra_view'].x + x = s['hydra_obj'].x + return (y, x) + + +partial_state_update_blocks = { + 'PSUB1': { + 'behaviors': { + }, + 'states': { + # 'ca': CA, + 'a': A, + 'b': B, + # 'hydra': Hydra, + 'hydra_members': HydraMembers, + 'hydra_obj': HydraObj, + 'hydra_view': HydraView, + 'i': I, + 'j': J, + 'k': K, + 'timestamp': time_model, + } + }, + 'PSUB2': { + 'behaviors': { + }, + 'states': { + # 'ca': CA, + 'a': A, + 'b': B, + # 'hydra': Hydra, + 'hydra_members': HydraMembers, + 'hydra_obj': HydraObj, + 'hydra_view': HydraView, + 'i': I, + 'j': J, + 'k': K, + } + }, + 'PSUB3': { + 'behaviors': { + }, + 'states': { + 'a': A, + 'b': B, + # 'hydra': Hydra, + 'hydra_members': HydraMembers, + 'hydra_obj': HydraObj, + 'hydra_view': HydraView, + 'i': I, + 'j': J, + 'k': K, + } + } +} + +sim_config = config_sim({ + "N": 2, + "T": range(4), + "M": g +}) + +append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) From a57e9d5ea3a06b1402f9434695dca7ff192ed847 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Wed, 3 Apr 2019 10:59:45 -0400 Subject: [PATCH 04/19] json udc working but not spec --- cadCAD/engine/simulation.py | 15 +------ cadCAD/utils/__init__.py | 16 ++------ simulations/validation/config_udc_json2.py | 48 ++++++++++++---------- 3 files changed, 31 insertions(+), 48 deletions(-) diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index d9b4823..73fc690 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -5,7 +5,7 @@ from copy import deepcopy from fn.op import foldr, call from cadCAD.engine.utils import engine_exception -from cadCAD.utils import flatten, UDC_Wrapper +from cadCAD.utils import flatten, UDC_Wrapper, objectview id_exception: Callable = engine_exception(KeyError, KeyError, None) @@ -70,7 +70,6 @@ class Executor: last_in_obj: Dict[str, Any] = deepcopy(sL[-1]) udc = var_dict[0]['udc'] - # last_in_obj: Dict[str, Any] = sL[-1] _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sL, last_in_obj, policy_funcs)) @@ -82,20 +81,10 @@ class Executor: if isinstance(v, dict) and hasattr(v, 'class_id'): del last_in_obj[k] - # def HydraObj(_g, step, sL, s, _input): - # y = 'hydra_obj' - # # x = s['hydra_obj'] - # x = namedtuple("Hydra", s['hydra_members'].keys())(*s['hydra_members'].values()) - # return (y, x) - new_last_in_obj = dict(list(last_in_obj.items()) + list(alt_udc_dict.items())) - # for f in state_funcs + [HydraObj]: for f in state_funcs: # ToDo: Create Named Tuple Here - y, x = f(var_dict, sub_step, sL, new_last_in_obj, _input) - # if isinstance(x, dict) and x['hydra_type'] == Dict and 'class_id' in x.keys(): - # x = namedtuple("Hydra", x.keys())(*x.values()) - yield self.state_update_exception((y, x)) + yield self.state_update_exception(f(var_dict, sub_step, sL, new_last_in_obj, _input)) udc_dict = { diff --git a/cadCAD/utils/__init__.py b/cadCAD/utils/__init__.py index 68aa4ef..5379e88 100644 --- a/cadCAD/utils/__init__.py +++ b/cadCAD/utils/__init__.py @@ -3,7 +3,7 @@ from collections import defaultdict from itertools import product import warnings from inspect import getmembers, ismethod -from copy import deepcopy +from copy import deepcopy, copy from collections import namedtuple @@ -15,18 +15,11 @@ class objectview(object): class UDC_Wrapper(object): def __init__(self, current, past, current_functions, past_functions=[]): current_funcs = dict(getmembers(current, ismethod)) - # current_funcs['object'] = current + filtered_current_funcs = {k: v for k, v in current_funcs.items() if k in current_functions} - # current_members = filtered_current_funcs.update(vars(current)) - # past_funcs = dict(getmembers(past, ismethod)) - # past_funcs['object'] = past - # filtered_past_funcs = {k: v for k, v in past_funcs.items() if k in past_functions} - # past_members = filtered_past_funcs.update(vars(past)) - - filtered_current_funcs['hydra_type'] = Dict filtered_current_funcs.update(vars(past)) - # print(filtered_current_funcs) + filtered_current_funcs['hydra_type'] = Dict filtered_current_funcs['current'] = current filtered_current_funcs['past'] = past @@ -38,9 +31,6 @@ class UDC_Wrapper(object): def get_namedtuple(self): return namedtuple("Hydra", self.hybrid_members.keys())(*self.hybrid_members.values()) - # def hybrid_members_values(self): - # return [v for k, v in self.hybrid_members.keys()] - def pipe(x): return x diff --git a/simulations/validation/config_udc_json2.py b/simulations/validation/config_udc_json2.py index 0169e26..1f34b4b 100644 --- a/simulations/validation/config_udc_json2.py +++ b/simulations/validation/config_udc_json2.py @@ -28,7 +28,7 @@ class MyClassA(object): # ToDo: id sensitive to lineage, rerepresent def __str__(self): # return str(self.x) - return f"{hex(id(self))} - {self.x}" + return f"{self.__class__.__name__} - {hex(id(self))} - {self.__dict__}" class MyClassB: @@ -75,10 +75,11 @@ g: Dict[str, List[MyClassA]] = {'udc': [MyClassA]} # genesis state udc = MyClassA(0) # namedtuple("Hydra", self.hybrid_members.keys())(*self.hybrid_members.values()) -udc_json = {'current': udc, 'past': udc} +# udc_json = {'current': udc, 'past': udc} hydra = UDC_Wrapper(udc, udc, current_functions=['update']) hydra_members = hydra.get_hybrid_members() -hydra_obj = namedtuple("Hydra", hydra_members.keys())(*hydra_members.values()) +# hydra_obj = namedtuple("Hydra", hydra_members.keys())(*hydra_members.values()) +hydra_view = objectview(hydra_members) state_dict = { @@ -86,11 +87,11 @@ state_dict = { 'b': 0, 'i': 0, 'j': 0, - 'k': 0, + # 'k': 0, # "hydra": hydra, "hydra_members": hydra_members, - "hydra_obj": hydra_obj, - 'hydra_view': objectview(hydra_members), + # "hydra_obj": hydra_obj, + # 'hydra_view': hydra_view, 'timestamp': '2019-01-01 00:00:00' } @@ -128,7 +129,8 @@ def HydraObj(_g, step, sL, s, _input): # new_hydra = dict(generate_var_deepcopy(hydra_nt)) # new_hydra_members = dict(generate_var_deepcopy(hydra_members)) - x = namedtuple("Hydra", s['hydra_members'].keys())(*s['hydra_members'].values()) + hm = copy(s['hydra_members']) + x = namedtuple("Hydra", hm.keys())(*hm.values()) # x = namedtuple("Hydra", new_hydra.keys())(*new_hydra.values()) # print(x.x) @@ -136,7 +138,8 @@ def HydraObj(_g, step, sL, s, _input): def HydraView(_g, step, sL, s, _input): y = 'hydra_view' - x = objectview(s['hydra_members']) + # x = objectview(s['hydra_members']) + x = s['hydra_view'].update() return (y, x) def A(_g, step, sL, s, _input): @@ -147,17 +150,17 @@ def A(_g, step, sL, s, _input): def B(_g, step, sL, s, _input): y = 'b' x = s['hydra_members']['x'] - # x = s['hydra_members'].x + # x = s['hydra_view'].x # x = s['hydra_obj'].x return (y, x) def I(_g, step, sL, s, _input): y = 'i' - # x = s['hydra_members']['update']() + x = s['hydra_members']['update']() # Either works - # x = s['hydra_members'].update() - x = s['hydra_obj'].update() + # x = s['hydra_obj'].update() + # x = s['hydra_view'].x return (y, x) def J(_g, step, sL, s, _input): @@ -165,13 +168,14 @@ def J(_g, step, sL, s, _input): x = s['hydra_members']['x'] # x = s['hydra_members'].x # x = s['hydra_obj'].x + # x = s['hydra_view'].x return (y, x) def K(_g, step, sL, s, _input): y = 'k' - # x = s['hydra_view'].x x = s['hydra_obj'].x + # x = s['hydra_view'].x return (y, x) @@ -185,11 +189,11 @@ partial_state_update_blocks = { 'b': B, # 'hydra': Hydra, 'hydra_members': HydraMembers, - 'hydra_obj': HydraObj, - 'hydra_view': HydraView, + # 'hydra_obj': HydraObj, + # 'hydra_view': HydraView, 'i': I, 'j': J, - 'k': K, + # 'k': K, 'timestamp': time_model, } }, @@ -202,11 +206,11 @@ partial_state_update_blocks = { 'b': B, # 'hydra': Hydra, 'hydra_members': HydraMembers, - 'hydra_obj': HydraObj, - 'hydra_view': HydraView, + # 'hydra_obj': HydraObj, + # 'hydra_view': HydraView, 'i': I, 'j': J, - 'k': K, + # 'k': K, } }, 'PSUB3': { @@ -217,11 +221,11 @@ partial_state_update_blocks = { 'b': B, # 'hydra': Hydra, 'hydra_members': HydraMembers, - 'hydra_obj': HydraObj, - 'hydra_view': HydraView, + # 'hydra_obj': HydraObj, + # 'hydra_view': HydraView, 'i': I, 'j': J, - 'k': K, + # 'k': K, } } } From 875f370c5e6ba6bfa1ada41e6193424b0e361e2f Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Wed, 3 Apr 2019 15:33:38 -0400 Subject: [PATCH 05/19] json udc working - meets spec --- cadCAD/engine/simulation.py | 27 +--- cadCAD/utils/__init__.py | 36 +++-- simulations/az_run_udc.py | 2 +- simulations/validation/config_udc_json2.py | 4 +- simulations/validation/config_udc_json3.py | 157 +++++++++++++++++++++ 5 files changed, 186 insertions(+), 40 deletions(-) create mode 100644 simulations/validation/config_udc_json3.py diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 73fc690..aa8e10c 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -1,11 +1,10 @@ -from collections import namedtuple from typing import Any, Callable, Dict, List, Tuple from pathos.pools import ThreadPool as TPool from copy import deepcopy from fn.op import foldr, call from cadCAD.engine.utils import engine_exception -from cadCAD.utils import flatten, UDC_Wrapper, objectview +from cadCAD.utils import flatten id_exception: Callable = engine_exception(KeyError, KeyError, None) @@ -69,34 +68,16 @@ class Executor: ) -> List[Dict[str, Any]]: last_in_obj: Dict[str, Any] = deepcopy(sL[-1]) - udc = var_dict[0]['udc'] _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sL, last_in_obj, policy_funcs)) # ToDo: add env_proc generator to `last_in_copy` iterator as wrapper function # ToDo: Can be multithreaded ?? - - def generate_record(state_funcs, alt_udc_dict): - for k, v in last_in_obj.items(): - if isinstance(v, dict) and hasattr(v, 'class_id'): - del last_in_obj[k] - - new_last_in_obj = dict(list(last_in_obj.items()) + list(alt_udc_dict.items())) + def generate_record(state_funcs): for f in state_funcs: - # ToDo: Create Named Tuple Here - yield self.state_update_exception(f(var_dict, sub_step, sL, new_last_in_obj, _input)) + yield self.state_update_exception(f(var_dict, sub_step, sL, last_in_obj, _input)) - - udc_dict = { - k: UDC_Wrapper( - v['current'], - udc(**v['current'].__dict__), - current_functions=['update'] - ).get_hybrid_members() - for k, v in last_in_obj.items() if isinstance(v, dict) and 'current' in v.keys() - } - last_in_copy: Dict[str, Any] = dict(generate_record(state_funcs, udc_dict)) - del udc_dict + last_in_copy: Dict[str, Any] = dict(generate_record(state_funcs)) for k in last_in_obj: if k not in last_in_copy: diff --git a/cadCAD/utils/__init__.py b/cadCAD/utils/__init__.py index 5379e88..c9fe37b 100644 --- a/cadCAD/utils/__init__.py +++ b/cadCAD/utils/__init__.py @@ -2,8 +2,6 @@ from typing import Dict, List from collections import defaultdict from itertools import product import warnings -from inspect import getmembers, ismethod -from copy import deepcopy, copy from collections import namedtuple @@ -11,25 +9,35 @@ class objectview(object): def __init__(self, d): self.__dict__ = d + def __str__(self): + filtered_members = {k: v for k, v in self.__dict__.items() if k != 'obj'} + return f"{filtered_members}" -class UDC_Wrapper(object): - def __init__(self, current, past, current_functions, past_functions=[]): - current_funcs = dict(getmembers(current, ismethod)) - filtered_current_funcs = {k: v for k, v in current_funcs.items() if k in current_functions} +class UDC(object): + def __init__(self, obj): + d = vars(obj) # somehow is enough + d['obj'] = obj - filtered_current_funcs.update(vars(past)) - filtered_current_funcs['hydra_type'] = Dict - filtered_current_funcs['current'] = current - filtered_current_funcs['past'] = past + self.members_dict = d - self.hybrid_members = filtered_current_funcs + def get_members(self): + return self.members_dict - def get_hybrid_members(self): - return self.hybrid_members + def get_object(self): + return objectview(self.members_dict) def get_namedtuple(self): - return namedtuple("Hydra", self.hybrid_members.keys())(*self.hybrid_members.values()) + return namedtuple("Hydra", self.members_dict.keys())(*self.members_dict.values()) + + +# class UDC_Wrapper2(object): +# def __init__(self, obj, functions): +# +# self.obj = obj +# +# def get_object(self): +# return objectview(self.obj.__dict__) def pipe(x): diff --git a/simulations/az_run_udc.py b/simulations/az_run_udc.py index de7abb5..88d04d1 100644 --- a/simulations/az_run_udc.py +++ b/simulations/az_run_udc.py @@ -2,7 +2,7 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import config_udc_json2 +from simulations.validation import config_udc_json3 from cadCAD import configs diff --git a/simulations/validation/config_udc_json2.py b/simulations/validation/config_udc_json2.py index 1f34b4b..4f9221e 100644 --- a/simulations/validation/config_udc_json2.py +++ b/simulations/validation/config_udc_json2.py @@ -19,7 +19,7 @@ class MyClassA(object): # self.past = copy(self) self.x += 1 print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") - return self.x #self #old_self #self.x + return self #.x #self #old_self #self.x def getMemID(self): return str(hex(id(self))) @@ -75,7 +75,7 @@ g: Dict[str, List[MyClassA]] = {'udc': [MyClassA]} # genesis state udc = MyClassA(0) # namedtuple("Hydra", self.hybrid_members.keys())(*self.hybrid_members.values()) -# udc_json = {'current': udc, 'past': udc} +udc_json = {'current': udc, 'past': udc} hydra = UDC_Wrapper(udc, udc, current_functions=['update']) hydra_members = hydra.get_hybrid_members() # hydra_obj = namedtuple("Hydra", hydra_members.keys())(*hydra_members.values()) diff --git a/simulations/validation/config_udc_json3.py b/simulations/validation/config_udc_json3.py new file mode 100644 index 0000000..8b1e0d8 --- /dev/null +++ b/simulations/validation/config_udc_json3.py @@ -0,0 +1,157 @@ +from datetime import timedelta +from cadCAD.utils import UDC +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import ep_time_step, config_sim +from typing import Dict, List + + +# ToDo: Create member for past value +class MyClassA(object): + def __init__(self, x): + self.x = x + print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") + + def update(self): + self.x += 1 + print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") + # return self.x + return self + + def getMemID(self): + return str(hex(id(self))) + + # can be accessed after an update within the same substep and timestep + # ToDo: id sensitive to lineage, rerepresent + def __str__(self): + # return f"{self.__class__.__name__} - {hex(id(self))} - {self.__dict__}" + return f"{self.__dict__}" + + +# a is Correct, and classX's value is Incorrect +# Expected: a == classX's value +# b should be tracking classX's value and a: +# b should be the same value as the previous classX value and the previous a value +# https://pymotw.com/2/multiprocessing/communication.html +# ccc = MyClassA +# udc = ccc(0) +# print(MyClassA(**udc.__dict__).__dict__) + +g: Dict[str, List[MyClassA]] = {'udc': [MyClassA]} + +# udcB = MyClassB() + +# z = MyClass() +# pointer(z) +# separate thread/process for UCD with async calls to this thread/process + +# genesis state +# udc_obj = MyClassA(0) +# hydra = UDC_Wrapper(udc, udc, current_functions=['update']) +# hydra = UDC_Wrapper(udc_obj, functions=['update']) +hydra = UDC(MyClassA(0)) +hydra_members = hydra.get_object() + +state_dict = { + 'a': 0, + 'b': 0, + 'j': 0, + "hydra_members": hydra_members, + 'timestamp': '2019-01-01 00:00:00' +} + +timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. +ts_format = '%Y-%m-%d %H:%M:%S' +def time_model(_g, step, sL, s, _input): + y = 'timestamp' + x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) + return (y, x) + + +def HydraMembers(_g, step, sL, s, _input): + y = 'hydra_members' + obj = s['hydra_members'].obj + obj.update() + x = UDC(obj).get_object() + return (y, x) + + +def A(_g, step, sL, s, _input): + y = 'a' + x = s['a'] + 1 + return (y, x) + +def B(_g, step, sL, s, _input): + y = 'b' + # x = s['hydra_members']['x'] + x = s['hydra_members'].x + # x = s['hydra_obj'].x + return (y, x) + + +def J(_g, step, sL, s, _input): + y = 'j' + # x = s['hydra_members']['x'] + x = s['hydra_members'].x + # x = s['hydra_obj'].x + # x = s['hydra_view'].x + return (y, x) + + +partial_state_update_blocks = { + 'PSUB1': { + 'behaviors': { + }, + 'states': { + # 'ca': CA, + 'a': A, + 'b': B, + # 'hydra': Hydra, + 'hydra_members': HydraMembers, + # 'hydra_obj': HydraObj, + # 'hydra_view': HydraView, + # 'i': I, + 'j': J, + # 'k': K, + 'timestamp': time_model, + } + }, + 'PSUB2': { + 'behaviors': { + }, + 'states': { + # 'ca': CA, + 'a': A, + 'b': B, + # 'hydra': Hydra, + 'hydra_members': HydraMembers, + # 'hydra_obj': HydraObj, + # 'hydra_view': HydraView, + # 'i': I, + 'j': J, + # 'k': K, + } + }, + 'PSUB3': { + 'behaviors': { + }, + 'states': { + 'a': A, + 'b': B, + # 'hydra': Hydra, + 'hydra_members': HydraMembers, + # 'hydra_obj': HydraObj, + # 'hydra_view': HydraView, + # 'i': I, + 'j': J, + # 'k': K, + } + } +} + +sim_config = config_sim({ + "N": 2, + "T": range(4), + "M": g +}) + +append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) From c4863a838d5e3fba2f9643445c8b191355a0d5fd Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Fri, 5 Apr 2019 13:38:55 -0400 Subject: [PATCH 06/19] udc w/ policies --- cadCAD/configuration/__init__.py | 8 +- .../configuration/utils/policyAggregation.py | 1 - cadCAD/configuration/utils/udc.py | 47 +++++ cadCAD/engine/simulation.py | 11 +- cadCAD/utils/__init__.py | 41 +---- simulations/az_run_udc.py | 4 +- simulations/validation/config_udc_json3.py | 164 +++++++++--------- 7 files changed, 148 insertions(+), 128 deletions(-) create mode 100644 cadCAD/configuration/utils/udc.py diff --git a/cadCAD/configuration/__init__.py b/cadCAD/configuration/__init__.py index 8c04248..3802772 100644 --- a/cadCAD/configuration/__init__.py +++ b/cadCAD/configuration/__init__.py @@ -28,7 +28,7 @@ class Configuration(object): def append_configs(sim_configs={}, initial_state={}, seeds={}, raw_exogenous_states={}, env_processes={}, - partial_state_update_blocks={}, _exo_update_per_ts: bool = True) -> None: + partial_state_update_blocks={}, policy_ops=[foldr(dict_elemwise_sum())], _exo_update_per_ts: bool = True) -> None: if _exo_update_per_ts is True: exogenous_states = exo_update_per_ts(raw_exogenous_states) else: @@ -42,7 +42,8 @@ def append_configs(sim_configs={}, initial_state={}, seeds={}, raw_exogenous_sta seeds=seeds, exogenous_states=exogenous_states, env_processes=env_processes, - partial_state_update_blocks=partial_state_update_blocks + partial_state_update_blocks=partial_state_update_blocks, + policy_ops=policy_ops ) configs.append(config) elif isinstance(sim_configs, dict): @@ -52,7 +53,8 @@ def append_configs(sim_configs={}, initial_state={}, seeds={}, raw_exogenous_sta seeds=seeds, exogenous_states=exogenous_states, env_processes=env_processes, - partial_state_update_blocks=partial_state_update_blocks + partial_state_update_blocks=partial_state_update_blocks, + policy_ops=policy_ops ) configs.append(config) diff --git a/cadCAD/configuration/utils/policyAggregation.py b/cadCAD/configuration/utils/policyAggregation.py index 68535a3..6a03db6 100644 --- a/cadCAD/configuration/utils/policyAggregation.py +++ b/cadCAD/configuration/utils/policyAggregation.py @@ -1,7 +1,6 @@ from fn.op import foldr from fn.func import curried - def get_base_value(x): if isinstance(x, str): return '' diff --git a/cadCAD/configuration/utils/udc.py b/cadCAD/configuration/utils/udc.py new file mode 100644 index 0000000..fe3790a --- /dev/null +++ b/cadCAD/configuration/utils/udc.py @@ -0,0 +1,47 @@ +from collections import namedtuple +from inspect import getmembers, ismethod + + +class udcView(object): + def __init__(self, d): + self.__dict__ = d + + # returns dict to dataframe + # def __repr__(self): + def __repr__(self): + members = {} + functionless = {k: v for k, v in self.__dict__.items() if str(type(v)) != "" and k != 'obj'} + members['functions'] = [k for k, v in self.__dict__.items() if str(type(v)) == ""] + members.update(functionless) + return f"{members}" + + +class udcBroker(object): + def __init__(self, obj, function_filter=['__init__']): + d = {} + funcs = dict(getmembers(obj, ismethod)) + filtered_functions = {k: v for k, v in funcs.items() if k not in function_filter} + d['obj'] = obj + d.update(vars(obj)) # somehow is enough + d.update(filtered_functions) + + self.members_dict = d + + def get_members(self): + return self.members_dict + + def get_view(self): + return udcView(self.members_dict) + + def get_namedtuple(self): + return namedtuple("Hydra", self.members_dict.keys())(*self.members_dict.values()) + + +def generate_udc_view(udc): + return udcBroker(udc).get_view() + + +def next_udc_view(obj_view): + return generate_udc_view(obj_view.obj) + + diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index aa8e10c..8e6edf4 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -1,7 +1,7 @@ from typing import Any, Callable, Dict, List, Tuple from pathos.pools import ThreadPool as TPool from copy import deepcopy -from fn.op import foldr, call +from functools import reduce from cadCAD.engine.utils import engine_exception from cadCAD.utils import flatten @@ -38,7 +38,10 @@ class Executor: def get_col_results(var_dict, sub_step, sL, s, funcs): return list(map(lambda f: f(var_dict, sub_step, sL, s), funcs)) - return foldr(call, get_col_results(var_dict, sub_step, sL, s, funcs))(ops) + # return foldr(call, get_col_results(var_dict, sub_step, sL, s, funcs))(ops) + + col_results = get_col_results(var_dict, sub_step, sL, s, funcs) + return reduce(lambda a, b: {**a, **b}, col_results) def apply_env_proc( self, @@ -68,6 +71,7 @@ class Executor: ) -> List[Dict[str, Any]]: last_in_obj: Dict[str, Any] = deepcopy(sL[-1]) + # last_in_obj: Dict[str, Any] = sL[-1] _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sL, last_in_obj, policy_funcs)) @@ -115,8 +119,7 @@ class Executor: states_list: List[Dict[str, Any]] = [genesis_states] sub_step += 1 - for config in configs: - s_conf, p_conf = config[0], config[1] + for [s_conf, p_conf] in configs: states_list: List[Dict[str, Any]] = self.partial_state_update( var_dict, sub_step, states_list, s_conf, p_conf, env_processes, time_step, run ) diff --git a/cadCAD/utils/__init__.py b/cadCAD/utils/__init__.py index c9fe37b..e07792d 100644 --- a/cadCAD/utils/__init__.py +++ b/cadCAD/utils/__init__.py @@ -1,43 +1,16 @@ from typing import Dict, List -from collections import defaultdict +from collections import defaultdict, Counter from itertools import product import warnings -from collections import namedtuple -class objectview(object): - def __init__(self, d): - self.__dict__ = d +class IndexCounter: + def __init__(self): + self.i = 0 - def __str__(self): - filtered_members = {k: v for k, v in self.__dict__.items() if k != 'obj'} - return f"{filtered_members}" - - -class UDC(object): - def __init__(self, obj): - d = vars(obj) # somehow is enough - d['obj'] = obj - - self.members_dict = d - - def get_members(self): - return self.members_dict - - def get_object(self): - return objectview(self.members_dict) - - def get_namedtuple(self): - return namedtuple("Hydra", self.members_dict.keys())(*self.members_dict.values()) - - -# class UDC_Wrapper2(object): -# def __init__(self, obj, functions): -# -# self.obj = obj -# -# def get_object(self): -# return objectview(self.obj.__dict__) + def __call__(self): + self.i += 1 + return self.i def pipe(x): diff --git a/simulations/az_run_udc.py b/simulations/az_run_udc.py index 88d04d1..7f1d44f 100644 --- a/simulations/az_run_udc.py +++ b/simulations/az_run_udc.py @@ -5,6 +5,7 @@ from cadCAD.engine import ExecutionMode, ExecutionContext, Executor from simulations.validation import config_udc_json3 from cadCAD import configs +import pprint as pp exec_mode = ExecutionMode() @@ -21,4 +22,5 @@ print("Tensor Field: config1") print(tabulate(tensor_field, headers='keys', tablefmt='psql')) print("Output:") print(tabulate(result, headers='keys', tablefmt='psql')) -print() \ No newline at end of file +print() +print(result.info(verbose=True)) \ No newline at end of file diff --git a/simulations/validation/config_udc_json3.py b/simulations/validation/config_udc_json3.py index 8b1e0d8..8133c78 100644 --- a/simulations/validation/config_udc_json3.py +++ b/simulations/validation/config_udc_json3.py @@ -1,64 +1,58 @@ from datetime import timedelta -from cadCAD.utils import UDC + from cadCAD.configuration import append_configs from cadCAD.configuration.utils import ep_time_step, config_sim -from typing import Dict, List +from cadCAD.configuration.utils.policyAggregation import dict_op, dict_elemwise_sum +from cadCAD.configuration.utils.udc import udcBroker, next_udc_view, generate_udc_view # ToDo: Create member for past value -class MyClassA(object): +class MyClass(object): def __init__(self, x): self.x = x - print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") def update(self): self.x += 1 - print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") - # return self.x return self def getMemID(self): return str(hex(id(self))) - # can be accessed after an update within the same substep and timestep - # ToDo: id sensitive to lineage, rerepresent - def __str__(self): - # return f"{self.__class__.__name__} - {hex(id(self))} - {self.__dict__}" - return f"{self.__dict__}" + pass -# a is Correct, and classX's value is Incorrect -# Expected: a == classX's value -# b should be tracking classX's value and a: -# b should be the same value as the previous classX value and the previous a value -# https://pymotw.com/2/multiprocessing/communication.html -# ccc = MyClassA -# udc = ccc(0) -# print(MyClassA(**udc.__dict__).__dict__) +# can be accessed after an update within the same substep and timestep -g: Dict[str, List[MyClassA]] = {'udc': [MyClassA]} +# udc = MyClassA(0) +# wrapped_udc = UDC(udc) +# hydra_members = wrapped_udc.get_object() +hydra_state_view = generate_udc_view(MyClass(0)) +udc_view_B = generate_udc_view(MyClass(0)) +udc_view_C = generate_udc_view(MyClass(0)) -# udcB = MyClassB() - -# z = MyClass() -# pointer(z) -# separate thread/process for UCD with async calls to this thread/process - -# genesis state -# udc_obj = MyClassA(0) -# hydra = UDC_Wrapper(udc, udc, current_functions=['update']) -# hydra = UDC_Wrapper(udc_obj, functions=['update']) -hydra = UDC(MyClassA(0)) -hydra_members = hydra.get_object() +# g: Dict[str, List[int]] = {'MyClassB'} state_dict = { - 'a': 0, - 'b': 0, - 'j': 0, - "hydra_members": hydra_members, + 'a': 0, 'b': 0, 'j': 0, + 'k': (0, 0), 'q': (0, 0), + 'hydra_state': hydra_state_view, + 'policies': {'hydra_B': udc_view_B, 'hydra_C': udc_view_C}, 'timestamp': '2019-01-01 00:00:00' } +def p1(_g, step, sL, s): + s['policies']['hydra_B'].update() + return {'hydra_B': next_udc_view(s['policies']['hydra_B'])} + +def p2(_g, step, sL, s): + s['policies']['hydra_C'].update() + return {'hydra_C': next_udc_view(s['policies']['hydra_C'])} + +def policies(_g, step, sL, s, _input): + y = 'policies' + x = _input + return (y, x) + timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. ts_format = '%Y-%m-%d %H:%M:%S' def time_model(_g, step, sL, s, _input): @@ -68,90 +62,90 @@ def time_model(_g, step, sL, s, _input): def HydraMembers(_g, step, sL, s, _input): - y = 'hydra_members' - obj = s['hydra_members'].obj - obj.update() - x = UDC(obj).get_object() + y = 'hydra_state' + # PROBLEM: + # s['hydra_members'].update() + # x = s['hydra_members'] + + # SOLUTION: + s['hydra_state'].update() + x = next_udc_view(s['hydra_state']) return (y, x) +def repr(_g, step, sL, s, _input): + y = 'z' + x = s['hydra_members'].__repr__() + return (y, x) def A(_g, step, sL, s, _input): y = 'a' x = s['a'] + 1 return (y, x) -def B(_g, step, sL, s, _input): - y = 'b' - # x = s['hydra_members']['x'] - x = s['hydra_members'].x - # x = s['hydra_obj'].x - return (y, x) +def hydra_state_tracker(y): + return lambda _g, step, sL, s, _input: (y, s['hydra_state'].x) -def J(_g, step, sL, s, _input): - y = 'j' - # x = s['hydra_members']['x'] - x = s['hydra_members'].x - # x = s['hydra_obj'].x - # x = s['hydra_view'].x - return (y, x) +def hydra_policy_tracker(y): + return lambda _g, step, sL, s, _input: (y, tuple(v.x for k, v in s['policies'].items())) +# needs M1&2 need behaviors partial_state_update_blocks = { 'PSUB1': { - 'behaviors': { + 'policies': { + "b1": p1, + "b2": p2 }, 'states': { - # 'ca': CA, 'a': A, - 'b': B, - # 'hydra': Hydra, - 'hydra_members': HydraMembers, - # 'hydra_obj': HydraObj, - # 'hydra_view': HydraView, - # 'i': I, - 'j': J, - # 'k': K, + 'b': hydra_state_tracker('b'), + 'j': hydra_state_tracker('j'), + 'k': hydra_policy_tracker('k'), + 'q': hydra_policy_tracker('q'), + 'hydra_state': HydraMembers, 'timestamp': time_model, + 'policies': policies } }, 'PSUB2': { - 'behaviors': { + 'policies': { + "b1": p1, + "b2": p2 }, 'states': { - # 'ca': CA, 'a': A, - 'b': B, - # 'hydra': Hydra, - 'hydra_members': HydraMembers, - # 'hydra_obj': HydraObj, - # 'hydra_view': HydraView, - # 'i': I, - 'j': J, - # 'k': K, + 'b': hydra_state_tracker('b'), + 'j': hydra_state_tracker('j'), + 'k': hydra_policy_tracker('k'), + 'q': hydra_policy_tracker('q'), + 'hydra_state': HydraMembers, + 'policies': policies } }, 'PSUB3': { - 'behaviors': { + 'policies': { + "b1": p1, + "b2": p2 }, 'states': { 'a': A, - 'b': B, - # 'hydra': Hydra, - 'hydra_members': HydraMembers, - # 'hydra_obj': HydraObj, - # 'hydra_view': HydraView, - # 'i': I, - 'j': J, - # 'k': K, + 'b': hydra_state_tracker('b'), + 'j': hydra_state_tracker('j'), + 'k': hydra_policy_tracker('k'), + 'q': hydra_policy_tracker('q'), + 'hydra_state': HydraMembers, + 'policies': policies } } } sim_config = config_sim({ "N": 2, - "T": range(4), - "M": g + "T": range(4) }) -append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) +append = lambda a, b: [a, b] +update_dict = lambda a, b: a.update(b) +take_first = lambda a, b: [a, b] +append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks)#, policy_ops=[foldr(dict_op(take_first))]) From 9dbb866bd01242a0da235d5f11f5e51e2d835fbc Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Tue, 16 Apr 2019 20:16:40 -0400 Subject: [PATCH 07/19] agent perception --- cadCAD/configuration/__init__.py | 2 + .../configuration/utils/policyAggregation.py | 40 ++++- cadCAD/configuration/utils/{udc.py => udo.py} | 28 +++- cadCAD/engine/simulation.py | 71 ++++++--- cadCAD/utils/__init__.py | 20 +++ simulations/az_run.py | 25 --- simulations/az_run_a.py | 24 --- simulations/az_run_b.py | 24 --- simulations/az_run_udc.py | 15 +- .../{az_run_udc_json.py => externalds_run.py} | 14 +- simulations/single_config_run.py | 4 +- simulations/validation/config1.py | 19 ++- simulations/validation/config1_test_pipe.py | 106 +++++++++++++ simulations/validation/config_udc_json3.py | 89 +++++++---- simulations/validation/config_udc_json4.py | 146 ++++++++++++++++++ simulations/validation/externalds.py | 118 ++++++++++++++ 16 files changed, 603 insertions(+), 142 deletions(-) rename cadCAD/configuration/utils/{udc.py => udo.py} (57%) delete mode 100644 simulations/az_run.py delete mode 100644 simulations/az_run_a.py delete mode 100644 simulations/az_run_b.py rename simulations/{az_run_udc_json.py => externalds_run.py} (68%) create mode 100644 simulations/validation/config1_test_pipe.py create mode 100644 simulations/validation/config_udc_json4.py create mode 100644 simulations/validation/externalds.py diff --git a/cadCAD/configuration/__init__.py b/cadCAD/configuration/__init__.py index 3802772..fd8d551 100644 --- a/cadCAD/configuration/__init__.py +++ b/cadCAD/configuration/__init__.py @@ -10,6 +10,8 @@ from cadCAD.configuration.utils import exo_update_per_ts from cadCAD.configuration.utils.policyAggregation import dict_elemwise_sum from cadCAD.configuration.utils.depreciationHandler import sanitize_partial_state_updates, sanitize_config +# policy_ops=[foldr(dict_elemwise_sum())] +# policy_ops=[reduce, lambda a, b: {**a, **b}] class Configuration(object): def __init__(self, sim_config={}, initial_state={}, seeds={}, env_processes={}, diff --git a/cadCAD/configuration/utils/policyAggregation.py b/cadCAD/configuration/utils/policyAggregation.py index 6a03db6..c43de03 100644 --- a/cadCAD/configuration/utils/policyAggregation.py +++ b/cadCAD/configuration/utils/policyAggregation.py @@ -1,5 +1,6 @@ from fn.op import foldr from fn.func import curried +from collections import Counter def get_base_value(x): if isinstance(x, str): @@ -17,7 +18,7 @@ def policy_to_dict(v): add = lambda a, b: a + b - +# df_union = lambda a, b: ... @curried def foldr_dict_vals(f, d): @@ -38,8 +39,43 @@ def dict_op(f, d1, d2): key_set = set(list(d1.keys()) + list(d2.keys())) - return {k: f(set_base_value(d1, d2, k), set_base_value(d2, d1, k)) for k in key_set} + return {k: f(set_base_value(d1, d2, k), set_base_value(d2, d1, k)) for k in key_set} +# +# @curried +# def dict_op(f, d1, d2): +# def set_base_value(target_dict, source_dict, key): +# if key not in target_dict: +# return get_base_value(source_dict[key]) +# else: +# return target_dict[key] +# +# key_set = set(list(d1.keys()) + list(d2.keys())) +# +# norm_d1 = {k: set_base_value(d1, d2, k) for k in key_set} +# norm_d2 = {k: set_base_value(d2, d1, k) for k in key_set} +# +# return {k: f(set_base_value(d1, d2, k), set_base_value(d2, d1, k)) for k in key_set} + + +# @curried +# def dict_op(f, d1, d2): +# # d1C = Counter(d1) +# # d2C = Counter(d2) +# def set_base_value(target_dict, source_dict, key): +# if key not in target_dict: +# return get_base_value(source_dict[key]) +# else: +# return target_dict[key] +# key_set = set(list(d1.keys()) + list(d2.keys())) +# norm_d1 = Counter({k: set_base_value(d1, d2, k) for k in key_set}) +# norm_d2 = Counter({k: set_base_value(d2, d1, k) for k in key_set}) +# # print(norm_d1) +# # print(norm_d2) +# print(norm_d1 + norm_d2) +# # print(f(norm_d1, norm_d2)) +# print() +# return f(norm_d1, norm_d2) def dict_elemwise_sum(): return dict_op(add) diff --git a/cadCAD/configuration/utils/udc.py b/cadCAD/configuration/utils/udo.py similarity index 57% rename from cadCAD/configuration/utils/udc.py rename to cadCAD/configuration/utils/udo.py index fe3790a..96a9a86 100644 --- a/cadCAD/configuration/utils/udc.py +++ b/cadCAD/configuration/utils/udo.py @@ -1,6 +1,16 @@ from collections import namedtuple +from copy import deepcopy from inspect import getmembers, ismethod +from pandas.core.frame import DataFrame +from cadCAD.utils import SilentDF + + +def val_switch(v): + if isinstance(v, DataFrame) is True: + return SilentDF(v) + else: + return v class udcView(object): def __init__(self, d): @@ -10,9 +20,12 @@ class udcView(object): # def __repr__(self): def __repr__(self): members = {} - functionless = {k: v for k, v in self.__dict__.items() if str(type(v)) != "" and k != 'obj'} - members['functions'] = [k for k, v in self.__dict__.items() if str(type(v)) == ""] - members.update(functionless) + variables = { + k: val_switch(v) for k, v in self.__dict__.items() + if str(type(v)) != "" and k != 'obj' # and isinstance(v, DataFrame) is not True + } + members['methods'] = [k for k, v in self.__dict__.items() if str(type(v)) == ""] + members.update(variables) return f"{members}" @@ -22,7 +35,7 @@ class udcBroker(object): funcs = dict(getmembers(obj, ismethod)) filtered_functions = {k: v for k, v in funcs.items() if k not in function_filter} d['obj'] = obj - d.update(vars(obj)) # somehow is enough + d.update(deepcopy(vars(obj))) # somehow is enough d.update(filtered_functions) self.members_dict = d @@ -37,11 +50,12 @@ class udcBroker(object): return namedtuple("Hydra", self.members_dict.keys())(*self.members_dict.values()) -def generate_udc_view(udc): + +def UDO(udc): return udcBroker(udc).get_view() -def next_udc_view(obj_view): - return generate_udc_view(obj_view.obj) +def udoPipe(obj_view): + return UDO(obj_view.obj) diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 8e6edf4..426b1f3 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -1,4 +1,5 @@ from typing import Any, Callable, Dict, List, Tuple + from pathos.pools import ThreadPool as TPool from copy import deepcopy from functools import reduce @@ -33,22 +34,50 @@ class Executor: funcs: List[Callable] ) -> Dict[str, Any]: - ops = self.policy_ops[::-1] + # ops = self.policy_ops[::-1] + ops = self.policy_ops + def get_col_results(var_dict, sub_step, sL, s, funcs): return list(map(lambda f: f(var_dict, sub_step, sL, s), funcs)) - # return foldr(call, get_col_results(var_dict, sub_step, sL, s, funcs))(ops) + def compose(init_reduction_funct, funct_list, val_list): + result, i = None, 0 + composition = lambda x: [reduce(init_reduction_funct, x)] + funct_list + for g in composition(val_list): + if i == 0: + result = g + i = 1 + else: + result = g(result) + return result col_results = get_col_results(var_dict, sub_step, sL, s, funcs) - return reduce(lambda a, b: {**a, **b}, col_results) + key_set = list(set(list(reduce(lambda a, b: a + b, list(map(lambda x: list(x.keys()), col_results)))))) + new_dict = {k: [] for k in key_set} + for d in col_results: + for k in d.keys(): + new_dict[k].append(d[k]) + + ops_head, *ops_tail = ops + return { + k: compose( + init_reduction_funct=ops_head, # func executed on value list + funct_list=ops_tail, + val_list=val_list + ) for k, val_list in new_dict.items() + } + + # [f1] = ops + # return {k: reduce(f1, val_list) for k, val_list in new_dict.items()} + # return foldr(call, col_results)(ops) def apply_env_proc( self, env_processes: Dict[str, Callable], state_dict: Dict[str, Any], sub_step: int - ) -> None: + ) -> Dict[str, Any]: for state in state_dict.keys(): if state in list(env_processes.keys()): env_state: Callable = env_processes[state] @@ -57,6 +86,8 @@ class Executor: else: state_dict[state] = env_state(state_dict[state]) + return state_dict + # mech_step def partial_state_update( self, @@ -81,16 +112,15 @@ class Executor: for f in state_funcs: yield self.state_update_exception(f(var_dict, sub_step, sL, last_in_obj, _input)) - last_in_copy: Dict[str, Any] = dict(generate_record(state_funcs)) - - for k in last_in_obj: - if k not in last_in_copy: - last_in_copy[k] = last_in_obj[k] - - del last_in_obj - - self.apply_env_proc(env_processes, last_in_copy, last_in_copy['timestep']) + def transfer_missing_fields(source, destination): + for k in source: + if k not in destination: + destination[k] = source[k] + del source # last_in_obj + return destination + last_in_copy: Dict[str, Any] = transfer_missing_fields(last_in_obj, dict(generate_record(state_funcs))) + last_in_copy: Dict[str, Any] = self.apply_env_proc(env_processes, last_in_copy, last_in_copy['timestep']) # ToDo: make 'substep' & 'timestep' reserve fields last_in_copy['substep'], last_in_copy['timestep'], last_in_copy['run'] = sub_step, time_step, run @@ -164,15 +194,18 @@ class Executor: def execute_run(var_dict, states_list, configs, env_processes, time_seq, run) -> List[Dict[str, Any]]: run += 1 - states_list_copy: List[Dict[str, Any]] = deepcopy(states_list) - head, *tail = self.run_pipeline(var_dict, states_list_copy, configs, env_processes, time_seq, run) + def generate_init_sys_metrics(genesis_states_list): + for d in genesis_states_list: + d['run'], d['substep'], d['timestep'] = run, int(0), int(0) + yield d + + states_list_copy: List[Dict[str, Any]] = list(generate_init_sys_metrics(deepcopy(states_list))) + + first_timestep_per_run: List[Dict[str, Any]] = self.run_pipeline(var_dict, states_list_copy, configs, env_processes, time_seq, run) del states_list_copy - genesis: Dict[str, Any] = head.pop() - genesis['substep'], genesis['timestep'], genesis['run'] = 0, 0, run - first_timestep_per_run: List[Dict[str, Any]] = [genesis] + tail.pop(0) - return [first_timestep_per_run] + tail + return first_timestep_per_run pipe_run: List[List[Dict[str, Any]]] = flatten( TPool().map( diff --git a/cadCAD/utils/__init__.py b/cadCAD/utils/__init__.py index e07792d..802bb9d 100644 --- a/cadCAD/utils/__init__.py +++ b/cadCAD/utils/__init__.py @@ -1,8 +1,23 @@ +from functools import reduce from typing import Dict, List from collections import defaultdict, Counter from itertools import product import warnings +from pandas import DataFrame + + +class SilentDF(DataFrame): + def __repr__(self): + return f"{hex(id(DataFrame))})" #"pandas.core.frame.DataFrame" + + +def val_switch(v): + if isinstance(v, DataFrame) is True or isinstance(v, SilentDF) is True: + return SilentDF(v) + else: + return v.x + class IndexCounter: def __init__(self): @@ -12,6 +27,11 @@ class IndexCounter: self.i += 1 return self.i +# def compose(*functions): +# return reduce(lambda f, g: lambda x: f(g(x)), functions, lambda x: x) + +def compose(*functions): + return reduce(lambda f, g: lambda x: f(g(x)), functions, lambda x: x) def pipe(x): return x diff --git a/simulations/az_run.py b/simulations/az_run.py deleted file mode 100644 index 8c199ce..0000000 --- a/simulations/az_run.py +++ /dev/null @@ -1,25 +0,0 @@ -import pandas as pd -from tabulate import tabulate -# The following imports NEED to be in the exact order -from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import config_az -from cadCAD import configs - -import pprint as pp - -exec_mode = ExecutionMode() - -print("Simulation Execution: Single Configuration") -print() -first_config = configs # only contains config1 -single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) -run = Executor(exec_context=single_proc_ctx, configs=first_config) - -raw_result, tensor_field = run.main() -result = pd.DataFrame(raw_result) -print() -print("Tensor Field: config1") -print(tabulate(tensor_field, headers='keys', tablefmt='psql')) -print("Output:") -print(tabulate(result, headers='keys', tablefmt='psql')) -print() \ No newline at end of file diff --git a/simulations/az_run_a.py b/simulations/az_run_a.py deleted file mode 100644 index 7fe6f84..0000000 --- a/simulations/az_run_a.py +++ /dev/null @@ -1,24 +0,0 @@ -import pandas as pd -from tabulate import tabulate -# The following imports NEED to be in the exact order -from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import config_az_a -from cadCAD import configs - - -exec_mode = ExecutionMode() - -print("Simulation Execution: Single Configuration") -print() -first_config = configs # only contains config1 -single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) -run = Executor(exec_context=single_proc_ctx, configs=first_config) - -raw_result, tensor_field = run.main() -result = pd.DataFrame(raw_result) -print() -print("Tensor Field: config1") -print(tabulate(tensor_field, headers='keys', tablefmt='psql')) -print("Output:") -print(tabulate(result, headers='keys', tablefmt='psql')) -print() \ No newline at end of file diff --git a/simulations/az_run_b.py b/simulations/az_run_b.py deleted file mode 100644 index 483677e..0000000 --- a/simulations/az_run_b.py +++ /dev/null @@ -1,24 +0,0 @@ -import pandas as pd -from tabulate import tabulate -# The following imports NEED to be in the exact order -from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import config_az_b -from cadCAD import configs - - -exec_mode = ExecutionMode() - -print("Simulation Execution: Single Configuration") -print() -first_config = configs # only contains config1 -single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) -run = Executor(exec_context=single_proc_ctx, configs=first_config) - -raw_result, tensor_field = run.main() -result = pd.DataFrame(raw_result) -print() -print("Tensor Field: config1") -print(tabulate(tensor_field, headers='keys', tablefmt='psql')) -print("Output:") -print(tabulate(result, headers='keys', tablefmt='psql')) -print() \ No newline at end of file diff --git a/simulations/az_run_udc.py b/simulations/az_run_udc.py index 7f1d44f..5cefaed 100644 --- a/simulations/az_run_udc.py +++ b/simulations/az_run_udc.py @@ -5,7 +5,6 @@ from cadCAD.engine import ExecutionMode, ExecutionContext, Executor from simulations.validation import config_udc_json3 from cadCAD import configs -import pprint as pp exec_mode = ExecutionMode() @@ -17,10 +16,22 @@ run = Executor(exec_context=single_proc_ctx, configs=first_config) raw_result, tensor_field = run.main() result = pd.DataFrame(raw_result) +result = pd.concat([result.drop(['c'], axis=1), result['c'].apply(pd.Series)], axis=1) + +# print(list(result['c'])) + +# print(tabulate(result['c'].apply(pd.Series), headers='keys', tablefmt='psql')) + print() print("Tensor Field: config1") print(tabulate(tensor_field, headers='keys', tablefmt='psql')) print("Output:") print(tabulate(result, headers='keys', tablefmt='psql')) print() -print(result.info(verbose=True)) \ No newline at end of file +print(result.info(verbose=True)) + +# def f(df, col): +# for k in df[col].iloc[0].keys(): +# df[k] = None +# for index, row in df.iterrows(): +# # df.apply(lambda row:, axis=1) \ No newline at end of file diff --git a/simulations/az_run_udc_json.py b/simulations/externalds_run.py similarity index 68% rename from simulations/az_run_udc_json.py rename to simulations/externalds_run.py index 81190e3..569d432 100644 --- a/simulations/az_run_udc_json.py +++ b/simulations/externalds_run.py @@ -2,10 +2,11 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import config_udc_json +# from simulations.validation import config1_test_pipe +# from simulations.validation import config1 +from simulations.validation import externalds from cadCAD import configs - exec_mode = ExecutionMode() print("Simulation Execution: Single Configuration") @@ -14,11 +15,10 @@ first_config = configs # only contains config1 single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) run = Executor(exec_context=single_proc_ctx, configs=first_config) -raw_result, tensor_field = run.main() +raw_result, _ = run.main() result = pd.DataFrame(raw_result) -print() -print("Tensor Field: config1") -print(tabulate(tensor_field, headers='keys', tablefmt='psql')) +result.to_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/output.csv', index=False) + print("Output:") print(tabulate(result, headers='keys', tablefmt='psql')) -print() \ No newline at end of file +print() diff --git a/simulations/single_config_run.py b/simulations/single_config_run.py index 6326c52..4d2e9f1 100644 --- a/simulations/single_config_run.py +++ b/simulations/single_config_run.py @@ -2,7 +2,9 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import config1 +# from simulations.validation import config1_test_pipe +# from simulations.validation import config1 +from simulations.validation import externalds from cadCAD import configs exec_mode = ExecutionMode() diff --git a/simulations/validation/config1.py b/simulations/validation/config1.py index 9f0fb64..c822296 100644 --- a/simulations/validation/config1.py +++ b/simulations/validation/config1.py @@ -1,7 +1,11 @@ from decimal import Decimal +from functools import reduce + import numpy as np from datetime import timedelta +from cadCAD.configuration.utils.policyAggregation import get_base_value + from cadCAD.configuration import append_configs from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step, config_sim @@ -17,7 +21,7 @@ seeds = { def p1m1(_g, step, sL, s): return {'param1': 1} def p2m1(_g, step, sL, s): - return {'param2': 4} + return {'param1': 1, 'param2': 4} # [] @@ -60,6 +64,13 @@ def s2m3(_g, step, sL, s, _input): x = _input['param2'] return (y, x) +def policies(_g, step, sL, s, _input): + y = 'policies' + x = _input + return (y, x) + + + # Exogenous States proc_one_coef_A = 0.7 @@ -97,7 +108,7 @@ genesis_states = { 's1': Decimal(0.0), 's2': Decimal(0.0), 's3': Decimal(1.0), - 's4': Decimal(1.0), + 's4': Decimal(1.0) # 'timestep': '2018-10-01 15:16:24' } @@ -156,12 +167,12 @@ sim_config = config_sim( } ) - append_configs( sim_configs=sim_config, initial_state=genesis_states, seeds=seeds, raw_exogenous_states=raw_exogenous_states, env_processes=env_processes, - partial_state_update_blocks=partial_state_update_block + partial_state_update_blocks=partial_state_update_block, + policy_ops=[lambda a, b: a + b] ) \ No newline at end of file diff --git a/simulations/validation/config1_test_pipe.py b/simulations/validation/config1_test_pipe.py new file mode 100644 index 0000000..2295f0d --- /dev/null +++ b/simulations/validation/config1_test_pipe.py @@ -0,0 +1,106 @@ +from decimal import Decimal +from functools import reduce + +import numpy as np +from datetime import timedelta + +from cadCAD.configuration.utils.policyAggregation import get_base_value + +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step, config_sim + +seeds = { + 'z': np.random.RandomState(1), + 'a': np.random.RandomState(2), + 'b': np.random.RandomState(3), + 'c': np.random.RandomState(3) +} + + +# Policies per Mechanism +def p1m1(_g, step, sL, s): + return {'param1': 1} +def p2m1(_g, step, sL, s): + return {'param2': 2} + +# [] + +def p1m2(_g, step, sL, s): + return {'param1': 2, 'param2': 2} +def p2m2(_g, step, sL, s): + return {'param1': 2, 'param2': 2} + +def p1m3(_g, step, sL, s): + return {'param1': 1, 'param2': 2, 'param3': 3} +def p2m3(_g, step, sL, s): + return {'param1': 1, 'param2': 2, 'param3': 3} + +def test_pipeline(_g, step, sL, s): + return {'test': 2, 'param2': 2} + + +# Internal States per Mechanism +def policies(_g, step, sL, s, _input): + y = 'policies' + x = _input + return (y, x) + +# Genesis States +genesis_states = { + 'policies': {} +} + + +raw_exogenous_states = {} + + +env_processes = {} + + +partial_state_update_block = { + "m1": { + "policies": { + "b1": p1m1, + "b2": p2m1 + }, + "variables": { + "policies": policies + } + }, + "m2": { + "policies": { + "b1": p1m2, + "b2": p2m2 + }, + "variables": { + "policies": policies + } + }, + "m3": { + "policies": { + "b1": p1m3, + "b2": p2m3 + }, + "variables": { + "policies": policies + } + } +} + + +sim_config = config_sim( + { + "N": 2, + "T": range(5), + } +) + +append_configs( + sim_configs=sim_config, + initial_state=genesis_states, + seeds=seeds, + raw_exogenous_states=raw_exogenous_states, + env_processes=env_processes, + partial_state_update_blocks=partial_state_update_block, + policy_ops=[lambda a, b: a + b] # , lambda y: y + 100, lambda y: y + 300 +) \ No newline at end of file diff --git a/simulations/validation/config_udc_json3.py b/simulations/validation/config_udc_json3.py index 8133c78..95e5bd9 100644 --- a/simulations/validation/config_udc_json3.py +++ b/simulations/validation/config_udc_json3.py @@ -2,19 +2,30 @@ from datetime import timedelta from cadCAD.configuration import append_configs from cadCAD.configuration.utils import ep_time_step, config_sim -from cadCAD.configuration.utils.policyAggregation import dict_op, dict_elemwise_sum -from cadCAD.configuration.utils.udc import udcBroker, next_udc_view, generate_udc_view +# from cadCAD.configuration.utils.policyAggregation import dict_op, dict_elemwise_sum +from cadCAD.configuration.utils.udo import udcBroker, udoPipe, UDO +import pandas as pd +from cadCAD.utils import SilentDF, val_switch + +ds = SilentDF(pd.read_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/output.csv')) -# ToDo: Create member for past value class MyClass(object): - def __init__(self, x): + def __init__(self, x, ds=None): self.x = x + self.ds = ds # for setting ds initially or querying def update(self): self.x += 1 return self + def read(self, ds_uri): + self.ds = SilentDF(pd.read_csv(ds_uri)) + return self + + def write(self, ds_uri): + pd.to_csv(ds_uri) + def getMemID(self): return str(hex(id(self))) @@ -23,12 +34,11 @@ class MyClass(object): # can be accessed after an update within the same substep and timestep -# udc = MyClassA(0) -# wrapped_udc = UDC(udc) -# hydra_members = wrapped_udc.get_object() -hydra_state_view = generate_udc_view(MyClass(0)) -udc_view_B = generate_udc_view(MyClass(0)) -udc_view_C = generate_udc_view(MyClass(0)) +hydra_state_view = UDO(MyClass(0, ds)) +udc_view_A = UDO(MyClass(0, ds)) +udc_view_B = UDO(MyClass(0, ds)) + +print(udc_view_A) # g: Dict[str, List[int]] = {'MyClassB'} @@ -36,17 +46,33 @@ state_dict = { 'a': 0, 'b': 0, 'j': 0, 'k': (0, 0), 'q': (0, 0), 'hydra_state': hydra_state_view, - 'policies': {'hydra_B': udc_view_B, 'hydra_C': udc_view_C}, - 'timestamp': '2019-01-01 00:00:00' + 'policies': {'hydra_A': udc_view_A, 'hydra_B': udc_view_B}, + 'timestamp': '2019-01-01 00:00:00', + 'c': {"ds1": None, "ds2": None, "ds3": None, "timestep": None} } def p1(_g, step, sL, s): - s['policies']['hydra_B'].update() - return {'hydra_B': next_udc_view(s['policies']['hydra_B'])} + s['policies']['hydra_A'].update() + return {'hydra_A': udoPipe(s['policies']['hydra_A'])} def p2(_g, step, sL, s): - s['policies']['hydra_C'].update() - return {'hydra_C': next_udc_view(s['policies']['hydra_C'])} + s['policies']['hydra_B'].update() + # df = s['policies']['hydra_B'].ds + return {'hydra_B': udoPipe(s['policies']['hydra_B'])} + +# ToDo: SilentDF(df) wont work +def C(_g, step, sL, s, _input): + y = 'c' + ds = _input['hydra_B'].ds + df = ds[(ds['run'] == s['run']) & (ds['substep'] == s['substep']) & (ds['timestep'] == s['timestep'])].drop(columns=['run', 'substep']) + def pop_if_not_empty(l): + if len(l) == 0: + return None + else: + return l.pop() + + x = {k: pop_if_not_empty(list(v.values())) for k, v in df.to_dict().items()} # reomve idx + return (y, x) def policies(_g, step, sL, s, _input): y = 'policies' @@ -63,13 +89,8 @@ def time_model(_g, step, sL, s, _input): def HydraMembers(_g, step, sL, s, _input): y = 'hydra_state' - # PROBLEM: - # s['hydra_members'].update() - # x = s['hydra_members'] - - # SOLUTION: s['hydra_state'].update() - x = next_udc_view(s['hydra_state']) + x = udoPipe(s['hydra_state']) return (y, x) def repr(_g, step, sL, s, _input): @@ -77,6 +98,9 @@ def repr(_g, step, sL, s, _input): x = s['hydra_members'].__repr__() return (y, x) +def incriment(y, incr_val): + return lambda _g, step, sL, s, _input: (y, s[y] + incr_val) + def A(_g, step, sL, s, _input): y = 'a' x = s['a'] + 1 @@ -87,7 +111,7 @@ def hydra_state_tracker(y): def hydra_policy_tracker(y): - return lambda _g, step, sL, s, _input: (y, tuple(v.x for k, v in s['policies'].items())) + return lambda _g, step, sL, s, _input: (y, tuple(val_switch(v) for k, v in s['policies'].items())) # needs M1&2 need behaviors @@ -100,6 +124,7 @@ partial_state_update_blocks = { 'states': { 'a': A, 'b': hydra_state_tracker('b'), + 'c': C, 'j': hydra_state_tracker('j'), 'k': hydra_policy_tracker('k'), 'q': hydra_policy_tracker('q'), @@ -116,6 +141,7 @@ partial_state_update_blocks = { 'states': { 'a': A, 'b': hydra_state_tracker('b'), + 'c': C, 'j': hydra_state_tracker('j'), 'k': hydra_policy_tracker('k'), 'q': hydra_policy_tracker('q'), @@ -131,6 +157,7 @@ partial_state_update_blocks = { 'states': { 'a': A, 'b': hydra_state_tracker('b'), + 'c': C, 'j': hydra_state_tracker('j'), 'k': hydra_policy_tracker('k'), 'q': hydra_policy_tracker('q'), @@ -144,8 +171,16 @@ sim_config = config_sim({ "N": 2, "T": range(4) }) +z = {'z': 1} -append = lambda a, b: [a, b] -update_dict = lambda a, b: a.update(b) -take_first = lambda a, b: [a, b] -append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks)#, policy_ops=[foldr(dict_op(take_first))]) +def addZ(d, z): + d.update(z) + return d + +append_configs( + sim_config, + state_dict, + {}, {}, {}, + partial_state_update_blocks, + policy_ops=[lambda a, b: {**a, **b}] +) diff --git a/simulations/validation/config_udc_json4.py b/simulations/validation/config_udc_json4.py new file mode 100644 index 0000000..7107dd7 --- /dev/null +++ b/simulations/validation/config_udc_json4.py @@ -0,0 +1,146 @@ +from datetime import timedelta + +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import ep_time_step, config_sim +from cadCAD.configuration.utils.policyAggregation import dict_op, dict_elemwise_sum +from cadCAD.configuration.utils.udo import udcBroker, udoPipe, UDO + + +# ToDo: Create member for past value +class MyClass(object): + def __init__(self, x): + self.x = x + + def update(self): + self.x += 1 + return self + + def getMemID(self): + return str(hex(id(self))) + + pass + + +# can be accessed after an update within the same substep and timestep + +hydra_state_view = UDO(MyClass(0)) +udc_view_B = UDO(MyClass(0)) +udc_view_C = UDO(MyClass(0)) + +# g: Dict[str, List[int]] = {'MyClassB'} + +state_dict = { + 'a': 0, 'b': 0, 'j': 0, + 'k': (0, 0), 'q': (0, 0), + 'hydra_state': hydra_state_view, + 'policies': {'hydra_B': udc_view_B, 'hydra_C': udc_view_C}, + 'timestamp': '2019-01-01 00:00:00' +} + +def p1(_g, step, sL, s): + s['policies']['hydra_B'].update() + return {'hydra_B': udoPipe(s['policies']['hydra_B'])} + +def p2(_g, step, sL, s): + s['policies']['hydra_C'].update() + return {'hydra_C': udoPipe(s['policies']['hydra_C'])} + +def policies(_g, step, sL, s, _input): + y = 'policies' + x = _input + return (y, x) + +timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. +ts_format = '%Y-%m-%d %H:%M:%S' +def time_model(_g, step, sL, s, _input): + y = 'timestamp' + x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) + return (y, x) + + +def HydraMembers(_g, step, sL, s, _input): + y = 'hydra_state' + s['hydra_state'].update() + x = udoPipe(s['hydra_state']) + return (y, x) + +def repr(_g, step, sL, s, _input): + y = 'z' + x = s['hydra_members'].__repr__() + return (y, x) + +def incriment(y, incr_val): + return lambda _g, step, sL, s, _input: (y, s[y] + incr_val) + +def A(_g, step, sL, s, _input): + y = 'a' + x = s['a'] + 1 + return (y, x) + +def hydra_state_tracker(y): + return lambda _g, step, sL, s, _input: (y, s['hydra_state'].x) + + +def hydra_policy_tracker(y): + return lambda _g, step, sL, s, _input: (y, tuple(v.x for k, v in s['policies'].items())) + + +# needs M1&2 need behaviors +partial_state_update_blocks = { + 'PSUB1': { + 'policies': { + "b1": p1, + "b2": p2 + }, + 'states': { + 'a': A, + 'b': hydra_state_tracker('b'), + 'j': hydra_state_tracker('j'), + 'k': hydra_policy_tracker('k'), + 'q': hydra_policy_tracker('q'), + 'hydra_state': HydraMembers, + 'timestamp': time_model, + 'policies': policies + } + }, + 'PSUB2': { + 'policies': { + "b1": p1, + "b2": p2 + }, + 'states': { + 'a': A, + 'b': hydra_state_tracker('b'), + 'j': hydra_state_tracker('j'), + 'k': hydra_policy_tracker('k'), + 'q': hydra_policy_tracker('q'), + 'hydra_state': HydraMembers, + 'policies': policies + } + }, + 'PSUB3': { + 'policies': { + "b1": p1, + "b2": p2 + }, + 'states': { + 'a': A, + 'b': hydra_state_tracker('b'), + 'j': hydra_state_tracker('j'), + 'k': hydra_policy_tracker('k'), + 'q': hydra_policy_tracker('q'), + 'hydra_state': HydraMembers, + 'policies': policies + } + } +} + +sim_config = config_sim({ + "N": 2, + "T": range(4) +}) + +append = lambda a, b: [a, b] +update_dict = lambda a, b: a.update(b) +take_first = lambda a, b: [a, b] +append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks)#, policy_ops=[foldr(dict_op(take_first))]) diff --git a/simulations/validation/externalds.py b/simulations/validation/externalds.py new file mode 100644 index 0000000..859ec22 --- /dev/null +++ b/simulations/validation/externalds.py @@ -0,0 +1,118 @@ +from decimal import Decimal + +import numpy as np + +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import bound_norm_random, config_sim + +seeds = { + 'z': np.random.RandomState(1), + 'a': np.random.RandomState(2), + 'b': np.random.RandomState(3), + 'c': np.random.RandomState(3) +} + + +# Policies per Mechanism +def p1(_g, step, sL, s): + return {'param1': 10} +def p2(_g, step, sL, s): + return {'param1': 10, 'param2': 40} + + +# Internal States per Mechanism +def s1(_g, step, sL, s, _input): + y = 'ds1' + x = s['ds1'] + 1 + return (y, x) +def s2(_g, step, sL, s, _input): + y = 'ds2' + x = _input['param2'] + return (y, x) + + +# Exogenous States +proc_one_coef_A = 0.7 +proc_one_coef_B = 1.3 + +def es(_g, step, sL, s, _input): + y = 'ds3' + x = s['ds3'] * bound_norm_random(seeds['a'], proc_one_coef_A, proc_one_coef_B) + return (y, x) + + +# Environment States +def env_a(x): + return 5 +def env_b(x): + return 10 + + +# Genesis States +genesis_states = { + 'ds1': Decimal(0.0), + 'ds2': Decimal(0.0), + 'ds3': Decimal(1.0) +} + + +raw_exogenous_states = { + "ds3": es +} + + +env_processes = { + "ds3": env_a +} + + +partial_state_update_block = { + "m1": { + "policies": { + "p1": p1, + "p2": p2 + }, + "variables": { + "ds1": s1, + "ds2": s2 + } + }, + "m2": { + "policies": { + "p1": p1, + "p2": p2 + }, + "variables": { + "ds1": s1, + "ds2": s2 + } + }, + "m3": { + "policies": { + "p1": p1, + "p2": p2 + }, + "variables": { + "ds1": s1, + "ds2": s2 + } + } +} + + +sim_config = config_sim( + { + "N": 2, + "T": range(4), + } +) + +append_configs( + sim_configs=sim_config, + initial_state=genesis_states, + seeds=seeds, + raw_exogenous_states=raw_exogenous_states, + env_processes=env_processes, + partial_state_update_blocks=partial_state_update_block, + policy_ops=[lambda a, b: a + b] +) \ No newline at end of file From 30e1c336e6612542c8d30e0a038294245ffd7f3b Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 25 Apr 2019 11:02:27 -0400 Subject: [PATCH 08/19] feature example refactoring pt. 1 --- cadCAD/configuration/__init__.py | 6 +- .../utils/{udo.py => userDefinedObject.py} | 17 +- cadCAD/engine/simulation.py | 51 +++-- cadCAD/utils/__init__.py | 16 +- cadCAD/utils/sys_config.py | 26 +++ simulations/single_config_run.py | 3 +- simulations/{az_run_udc.py => udo_run.py} | 16 +- simulations/validation/config1_test_pipe.py | 44 ++++- simulations/validation/config_udc_json3.py | 186 ------------------ simulations/validation/config_udc_json4.py | 2 +- .../validation/incr_external_dataset.py | 69 +++++++ simulations/validation/udo.py | 159 +++++++++++++++ 12 files changed, 365 insertions(+), 230 deletions(-) rename cadCAD/configuration/utils/{udo.py => userDefinedObject.py} (74%) create mode 100644 cadCAD/utils/sys_config.py rename simulations/{az_run_udc.py => udo_run.py} (66%) delete mode 100644 simulations/validation/config_udc_json3.py create mode 100644 simulations/validation/incr_external_dataset.py create mode 100644 simulations/validation/udo.py diff --git a/cadCAD/configuration/__init__.py b/cadCAD/configuration/__init__.py index fd8d551..0bae909 100644 --- a/cadCAD/configuration/__init__.py +++ b/cadCAD/configuration/__init__.py @@ -1,6 +1,5 @@ from typing import Dict, Callable, List, Tuple from functools import reduce -from fn.op import foldr import pandas as pd from pandas.core.frame import DataFrame @@ -13,9 +12,10 @@ from cadCAD.configuration.utils.depreciationHandler import sanitize_partial_stat # policy_ops=[foldr(dict_elemwise_sum())] # policy_ops=[reduce, lambda a, b: {**a, **b}] + class Configuration(object): def __init__(self, sim_config={}, initial_state={}, seeds={}, env_processes={}, - exogenous_states={}, partial_state_update_blocks={}, policy_ops=[foldr(dict_elemwise_sum())], + exogenous_states={}, partial_state_update_blocks={}, policy_ops=[lambda a, b: a + b], **kwargs) -> None: self.sim_config = sim_config self.initial_state = initial_state @@ -30,7 +30,7 @@ class Configuration(object): def append_configs(sim_configs={}, initial_state={}, seeds={}, raw_exogenous_states={}, env_processes={}, - partial_state_update_blocks={}, policy_ops=[foldr(dict_elemwise_sum())], _exo_update_per_ts: bool = True) -> None: + partial_state_update_blocks={}, policy_ops=[lambda a, b: a + b], _exo_update_per_ts: bool = True) -> None: if _exo_update_per_ts is True: exogenous_states = exo_update_per_ts(raw_exogenous_states) else: diff --git a/cadCAD/configuration/utils/udo.py b/cadCAD/configuration/utils/userDefinedObject.py similarity index 74% rename from cadCAD/configuration/utils/udo.py rename to cadCAD/configuration/utils/userDefinedObject.py index 96a9a86..c013952 100644 --- a/cadCAD/configuration/utils/udo.py +++ b/cadCAD/configuration/utils/userDefinedObject.py @@ -13,8 +13,9 @@ def val_switch(v): return v class udcView(object): - def __init__(self, d): + def __init__(self, d, masked_members): self.__dict__ = d + self.masked_members = masked_members # returns dict to dataframe # def __repr__(self): @@ -22,7 +23,7 @@ class udcView(object): members = {} variables = { k: val_switch(v) for k, v in self.__dict__.items() - if str(type(v)) != "" and k != 'obj' # and isinstance(v, DataFrame) is not True + if str(type(v)) != "" and k not in self.masked_members # and isinstance(v, DataFrame) is not True } members['methods'] = [k for k, v in self.__dict__.items() if str(type(v)) == ""] members.update(variables) @@ -43,19 +44,17 @@ class udcBroker(object): def get_members(self): return self.members_dict - def get_view(self): - return udcView(self.members_dict) + def get_view(self, masked_members): + return udcView(self.members_dict, masked_members) def get_namedtuple(self): return namedtuple("Hydra", self.members_dict.keys())(*self.members_dict.values()) - -def UDO(udc): - return udcBroker(udc).get_view() +def UDO(udo, masked_members=['obj']): + return udcBroker(udo).get_view(masked_members) def udoPipe(obj_view): - return UDO(obj_view.obj) - + return UDO(obj_view.obj, obj_view.masked_members) diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 426b1f3..0eb8605 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -94,6 +94,7 @@ class Executor: var_dict: Dict[str, List[Any]], sub_step: int, sL: Any, + sH: Any, state_funcs: List[Callable], policy_funcs: List[Callable], env_processes: Dict[str, Callable], @@ -101,16 +102,19 @@ class Executor: run: int ) -> List[Dict[str, Any]]: - last_in_obj: Dict[str, Any] = deepcopy(sL[-1]) - # last_in_obj: Dict[str, Any] = sL[-1] + # last_in_obj: Dict[str, Any] = deepcopy(sL[-1]) + last_in_obj: Dict[str, Any] = sL[-1] + # last_in_obj: Dict[str, Any] = sH[-1] + # print(last_in_obj) + # print(sH[-1]) - _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sL, last_in_obj, policy_funcs)) + _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sH, last_in_obj, policy_funcs)) # ToDo: add env_proc generator to `last_in_copy` iterator as wrapper function # ToDo: Can be multithreaded ?? def generate_record(state_funcs): for f in state_funcs: - yield self.state_update_exception(f(var_dict, sub_step, sL, last_in_obj, _input)) + yield self.state_update_exception(f(var_dict, sub_step, sH, last_in_obj, _input)) def transfer_missing_fields(source, destination): for k in source: @@ -127,13 +131,16 @@ class Executor: sL.append(last_in_copy) del last_in_copy + # print(sL) + # print() + return sL # mech_pipeline - state_update_block def state_update_pipeline( self, var_dict: Dict[str, List[Any]], - states_list: List[Dict[str, Any]], + simulation_list, #states_list: List[Dict[str, Any]], configs: List[Tuple[List[Callable], List[Callable]]], env_processes: Dict[str, Callable], time_step: int, @@ -141,19 +148,33 @@ class Executor: ) -> List[Dict[str, Any]]: sub_step = 0 - states_list_copy: List[Dict[str, Any]] = deepcopy(states_list) + # states_list_copy: List[Dict[str, Any]] = deepcopy(states_list) + # states_list_copy: List[Dict[str, Any]] = states_list + # ToDo: flatten first + states_list_copy: List[Dict[str, Any]] = simulation_list[-1] + # print(states_list_copy) + # ToDo: Causes Substep repeats in sL: genesis_states: Dict[str, Any] = states_list_copy[-1] + + if len(states_list_copy) == 1: + genesis_states['substep'] = sub_step + # genesis_states['timestep'] = 0 + # else: + # genesis_states['timestep'] = time_step + del states_list_copy - genesis_states['substep'], genesis_states['timestep'] = sub_step, time_step states_list: List[Dict[str, Any]] = [genesis_states] + # ToDo: Causes Substep repeats in sL, use for yield sub_step += 1 for [s_conf, p_conf] in configs: states_list: List[Dict[str, Any]] = self.partial_state_update( - var_dict, sub_step, states_list, s_conf, p_conf, env_processes, time_step, run + var_dict, sub_step, states_list, simulation_list, s_conf, p_conf, env_processes, time_step, run ) - + # print(sub_step) + # print(simulation_list) + # print(flatten(simulation_list)) sub_step += 1 time_step += 1 @@ -173,12 +194,20 @@ class Executor: time_seq: List[int] = [x + 1 for x in time_seq] simulation_list: List[List[Dict[str, Any]]] = [states_list] + + # print(simulation_list[-1]) + # print() + # pipe_run = simulation_list[-1] + # print(simulation_list) for time_step in time_seq: pipe_run: List[Dict[str, Any]] = self.state_update_pipeline( - var_dict, simulation_list[-1], configs, env_processes, time_step, run + var_dict, simulation_list, configs, env_processes, time_step, run ) + _, *pipe_run = pipe_run simulation_list.append(pipe_run) + # print(simulation_list) + # print() return simulation_list @@ -197,7 +226,7 @@ class Executor: def generate_init_sys_metrics(genesis_states_list): for d in genesis_states_list: - d['run'], d['substep'], d['timestep'] = run, int(0), int(0) + d['run'], d['substep'], d['timestep'] = run, 0, 0 yield d states_list_copy: List[Dict[str, Any]] = list(generate_init_sys_metrics(deepcopy(states_list))) diff --git a/cadCAD/utils/__init__.py b/cadCAD/utils/__init__.py index 802bb9d..44717b5 100644 --- a/cadCAD/utils/__init__.py +++ b/cadCAD/utils/__init__.py @@ -9,14 +9,18 @@ from pandas import DataFrame class SilentDF(DataFrame): def __repr__(self): - return f"{hex(id(DataFrame))})" #"pandas.core.frame.DataFrame" + return str(hex(id(DataFrame))) #"pandas.core.frame.DataFrame" + +def append_dict(dict, new_dict): + dict.update(new_dict) + return dict -def val_switch(v): - if isinstance(v, DataFrame) is True or isinstance(v, SilentDF) is True: - return SilentDF(v) - else: - return v.x +# def val_switch(v): +# if isinstance(v, DataFrame) is True or isinstance(v, SilentDF) is True: +# return SilentDF(v) +# else: +# return v.x class IndexCounter: diff --git a/cadCAD/utils/sys_config.py b/cadCAD/utils/sys_config.py new file mode 100644 index 0000000..90d3250 --- /dev/null +++ b/cadCAD/utils/sys_config.py @@ -0,0 +1,26 @@ +from cadCAD.configuration.utils import ep_time_step + + +def increment(y, incr_by): + return lambda _g, step, sL, s, _input: (y, s[y] + incr_by) + +def track(y): + return lambda _g, step, sL, s, _input: (y, s[y].x) + +def simple_state_update(y, x): + return lambda _g, step, sH, s, _input: (y, x) + +def simple_policy_update(y): + return lambda _g, step, sH, s: y + +def update_timestamp(y, timedelta, format): + return lambda _g, step, sL, s, _input: ( + y, + ep_time_step(s, dt_str=s[y], fromat_str=format, _timedelta=timedelta) + ) + + +# def repr(_g, step, sL, s, _input): +# y = 'z' +# x = s['state_udo'].__repr__() +# return (y, x) \ No newline at end of file diff --git a/simulations/single_config_run.py b/simulations/single_config_run.py index 4d2e9f1..081d4e1 100644 --- a/simulations/single_config_run.py +++ b/simulations/single_config_run.py @@ -4,7 +4,8 @@ from tabulate import tabulate from cadCAD.engine import ExecutionMode, ExecutionContext, Executor # from simulations.validation import config1_test_pipe # from simulations.validation import config1 -from simulations.validation import externalds +# from simulations.validation import externalds +from simulations.validation import incr_external_dataset from cadCAD import configs exec_mode = ExecutionMode() diff --git a/simulations/az_run_udc.py b/simulations/udo_run.py similarity index 66% rename from simulations/az_run_udc.py rename to simulations/udo_run.py index 5cefaed..bbb07ba 100644 --- a/simulations/az_run_udc.py +++ b/simulations/udo_run.py @@ -2,7 +2,7 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import config_udc_json3 +from simulations.validation import udo from cadCAD import configs @@ -10,13 +10,21 @@ exec_mode = ExecutionMode() print("Simulation Execution: Single Configuration") print() + + first_config = configs # only contains config1 single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) run = Executor(exec_context=single_proc_ctx, configs=first_config) - +# cols = configs[0].initial_state.keys() +cols = [ + 'increment', + 'state_udo_tracker_a', 'state_udo', 'state_udo_perception_tracker', 'state_udo_tracker_b', + 'udo_policy_tracker_a', 'udo_policies', 'udo_policy_tracker_b', + 'timestamp' +] raw_result, tensor_field = run.main() -result = pd.DataFrame(raw_result) -result = pd.concat([result.drop(['c'], axis=1), result['c'].apply(pd.Series)], axis=1) +result = pd.DataFrame(raw_result)[['run', 'substep', 'timestep'] + cols] +# result = pd.concat([result.drop(['c'], axis=1), result['c'].apply(pd.Series)], axis=1) # print(list(result['c'])) diff --git a/simulations/validation/config1_test_pipe.py b/simulations/validation/config1_test_pipe.py index 2295f0d..5d9e3dd 100644 --- a/simulations/validation/config1_test_pipe.py +++ b/simulations/validation/config1_test_pipe.py @@ -40,23 +40,31 @@ def test_pipeline(_g, step, sL, s): # Internal States per Mechanism -def policies(_g, step, sL, s, _input): +def s(y): + return lambda _g, step, sH, s, _input: (y, s[y] + 1) + +def sH(_g, step, sH, s, _input): + y = 'sh' + x = sH + return (y, x) + +def policies(_g, step, sH, s, _input): y = 'policies' x = _input return (y, x) # Genesis States genesis_states = { - 'policies': {} + 'policies': {}, + 's1': 0, + 's2': 0, + # 'sh': [] } - raw_exogenous_states = {} - env_processes = {} - partial_state_update_block = { "m1": { "policies": { @@ -64,6 +72,9 @@ partial_state_update_block = { "b2": p2m1 }, "variables": { + 's1': s('s1'), + 's2': s('s2'), + # 'sh': sH, "policies": policies } }, @@ -73,6 +84,9 @@ partial_state_update_block = { "b2": p2m2 }, "variables": { + 's1': s('s1'), + 's2': s('s2'), + # 'sh': sH, "policies": policies } }, @@ -82,6 +96,9 @@ partial_state_update_block = { "b2": p2m3 }, "variables": { + 's1': s('s1'), + 's2': s('s2'), + # 'sh': sH, "policies": policies } } @@ -90,11 +107,12 @@ partial_state_update_block = { sim_config = config_sim( { - "N": 2, - "T": range(5), + "N": 1, + "T": range(3), } ) + append_configs( sim_configs=sim_config, initial_state=genesis_states, @@ -102,5 +120,13 @@ append_configs( raw_exogenous_states=raw_exogenous_states, env_processes=env_processes, partial_state_update_blocks=partial_state_update_block, - policy_ops=[lambda a, b: a + b] # , lambda y: y + 100, lambda y: y + 300 -) \ No newline at end of file + # policy_ops=[lambda a, b: a + b, lambda y: y + 100, lambda y: y + 300] +) + + +# def p1m3(_g, step, sL, s): +# return {'param1': 1, 'param2': 2, 'param3': 3} +# def p2m3(_g, step, sL, s): +# return {'param1': 1, 'param2': 2, 'param3': 3} +# +# xx = {'param1': [1,1], 'param2': [2,2], 'param3': [3,3]} \ No newline at end of file diff --git a/simulations/validation/config_udc_json3.py b/simulations/validation/config_udc_json3.py deleted file mode 100644 index 95e5bd9..0000000 --- a/simulations/validation/config_udc_json3.py +++ /dev/null @@ -1,186 +0,0 @@ -from datetime import timedelta - -from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import ep_time_step, config_sim -# from cadCAD.configuration.utils.policyAggregation import dict_op, dict_elemwise_sum -from cadCAD.configuration.utils.udo import udcBroker, udoPipe, UDO -import pandas as pd -from cadCAD.utils import SilentDF, val_switch - -ds = SilentDF(pd.read_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/output.csv')) - - -class MyClass(object): - def __init__(self, x, ds=None): - self.x = x - self.ds = ds # for setting ds initially or querying - - def update(self): - self.x += 1 - return self - - def read(self, ds_uri): - self.ds = SilentDF(pd.read_csv(ds_uri)) - return self - - def write(self, ds_uri): - pd.to_csv(ds_uri) - - def getMemID(self): - return str(hex(id(self))) - - pass - - -# can be accessed after an update within the same substep and timestep - -hydra_state_view = UDO(MyClass(0, ds)) -udc_view_A = UDO(MyClass(0, ds)) -udc_view_B = UDO(MyClass(0, ds)) - -print(udc_view_A) - -# g: Dict[str, List[int]] = {'MyClassB'} - -state_dict = { - 'a': 0, 'b': 0, 'j': 0, - 'k': (0, 0), 'q': (0, 0), - 'hydra_state': hydra_state_view, - 'policies': {'hydra_A': udc_view_A, 'hydra_B': udc_view_B}, - 'timestamp': '2019-01-01 00:00:00', - 'c': {"ds1": None, "ds2": None, "ds3": None, "timestep": None} -} - -def p1(_g, step, sL, s): - s['policies']['hydra_A'].update() - return {'hydra_A': udoPipe(s['policies']['hydra_A'])} - -def p2(_g, step, sL, s): - s['policies']['hydra_B'].update() - # df = s['policies']['hydra_B'].ds - return {'hydra_B': udoPipe(s['policies']['hydra_B'])} - -# ToDo: SilentDF(df) wont work -def C(_g, step, sL, s, _input): - y = 'c' - ds = _input['hydra_B'].ds - df = ds[(ds['run'] == s['run']) & (ds['substep'] == s['substep']) & (ds['timestep'] == s['timestep'])].drop(columns=['run', 'substep']) - def pop_if_not_empty(l): - if len(l) == 0: - return None - else: - return l.pop() - - x = {k: pop_if_not_empty(list(v.values())) for k, v in df.to_dict().items()} # reomve idx - return (y, x) - -def policies(_g, step, sL, s, _input): - y = 'policies' - x = _input - return (y, x) - -timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. -ts_format = '%Y-%m-%d %H:%M:%S' -def time_model(_g, step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) - return (y, x) - - -def HydraMembers(_g, step, sL, s, _input): - y = 'hydra_state' - s['hydra_state'].update() - x = udoPipe(s['hydra_state']) - return (y, x) - -def repr(_g, step, sL, s, _input): - y = 'z' - x = s['hydra_members'].__repr__() - return (y, x) - -def incriment(y, incr_val): - return lambda _g, step, sL, s, _input: (y, s[y] + incr_val) - -def A(_g, step, sL, s, _input): - y = 'a' - x = s['a'] + 1 - return (y, x) - -def hydra_state_tracker(y): - return lambda _g, step, sL, s, _input: (y, s['hydra_state'].x) - - -def hydra_policy_tracker(y): - return lambda _g, step, sL, s, _input: (y, tuple(val_switch(v) for k, v in s['policies'].items())) - - -# needs M1&2 need behaviors -partial_state_update_blocks = { - 'PSUB1': { - 'policies': { - "b1": p1, - "b2": p2 - }, - 'states': { - 'a': A, - 'b': hydra_state_tracker('b'), - 'c': C, - 'j': hydra_state_tracker('j'), - 'k': hydra_policy_tracker('k'), - 'q': hydra_policy_tracker('q'), - 'hydra_state': HydraMembers, - 'timestamp': time_model, - 'policies': policies - } - }, - 'PSUB2': { - 'policies': { - "b1": p1, - "b2": p2 - }, - 'states': { - 'a': A, - 'b': hydra_state_tracker('b'), - 'c': C, - 'j': hydra_state_tracker('j'), - 'k': hydra_policy_tracker('k'), - 'q': hydra_policy_tracker('q'), - 'hydra_state': HydraMembers, - 'policies': policies - } - }, - 'PSUB3': { - 'policies': { - "b1": p1, - "b2": p2 - }, - 'states': { - 'a': A, - 'b': hydra_state_tracker('b'), - 'c': C, - 'j': hydra_state_tracker('j'), - 'k': hydra_policy_tracker('k'), - 'q': hydra_policy_tracker('q'), - 'hydra_state': HydraMembers, - 'policies': policies - } - } -} - -sim_config = config_sim({ - "N": 2, - "T": range(4) -}) -z = {'z': 1} - -def addZ(d, z): - d.update(z) - return d - -append_configs( - sim_config, - state_dict, - {}, {}, {}, - partial_state_update_blocks, - policy_ops=[lambda a, b: {**a, **b}] -) diff --git a/simulations/validation/config_udc_json4.py b/simulations/validation/config_udc_json4.py index 7107dd7..29dd353 100644 --- a/simulations/validation/config_udc_json4.py +++ b/simulations/validation/config_udc_json4.py @@ -3,7 +3,7 @@ from datetime import timedelta from cadCAD.configuration import append_configs from cadCAD.configuration.utils import ep_time_step, config_sim from cadCAD.configuration.utils.policyAggregation import dict_op, dict_elemwise_sum -from cadCAD.configuration.utils.udo import udcBroker, udoPipe, UDO +from cadCAD.configuration.utils.userDefinedObject import udcBroker, udoPipe, UDO # ToDo: Create member for past value diff --git a/simulations/validation/incr_external_dataset.py b/simulations/validation/incr_external_dataset.py new file mode 100644 index 0000000..d503201 --- /dev/null +++ b/simulations/validation/incr_external_dataset.py @@ -0,0 +1,69 @@ +from datetime import timedelta + +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import ep_time_step, config_sim +# from cadCAD.configuration.utils.policyAggregation import dict_op, dict_elemwise_sum +from cadCAD.configuration.utils.userDefinedObject import udcBroker, udoPipe, UDO +import pandas as pd +from cadCAD.utils import SilentDF + +df = SilentDF(pd.read_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/output.csv')) + +state_dict = { + 'increment': 0, + 'external_data': {"ds1": None, "ds2": None, "ds3": None}, + 'policies': {} +} + +def query(s, df): + return df[ + (df['run'] == s['run']) & (df['substep'] == s['substep']) & (df['timestep'] == s['timestep']) + ].drop(columns=['run', 'substep', "timestep"]) + +def p1(_g, substep, sL, s): + result_dict = query(s, df).to_dict() + del result_dict["ds3"] + return {k: list(v.values()).pop() for k, v in result_dict.items()} + +def p2(_g, step, sL, s): + result_dict = query(s, df).to_dict() + del result_dict["ds1"], result_dict["ds2"] + return {k: list(v.values()).pop() for k, v in result_dict.items()} + +# ToDo: SilentDF(df) wont work +#integrate_ext_dataset +def integrate_ext_dataset(_g, step, sL, s, _input): + result_dict = query(s, df).to_dict() + return 'external_data', {k: list(v.values()).pop() for k, v in result_dict.items()} + +def increment(y, incr_by): + return lambda _g, step, sL, s, _input: (y, s[y] + incr_by) +increment = increment('increment', 1) + +def view_policies(_g, step, sL, s, _input): + return 'policies', _input + + +policies = {"p1": p1, "p2": p2} +states = {'increment': increment, 'external_data': integrate_ext_dataset, 'policies': view_policies} +PSUB = {'policies': policies, 'states': states} + +# needs M1&2 need behaviors +partial_state_update_blocks = { + 'PSUB1': PSUB, + 'PSUB2': PSUB, + 'PSUB3': PSUB +} + +sim_config = config_sim({ + "N": 2, + "T": range(4) +}) + +append_configs( + sim_config, + state_dict, + {}, {}, {}, + partial_state_update_blocks, + policy_ops=[lambda a, b: {**a, **b}] +) diff --git a/simulations/validation/udo.py b/simulations/validation/udo.py new file mode 100644 index 0000000..1f06107 --- /dev/null +++ b/simulations/validation/udo.py @@ -0,0 +1,159 @@ +from datetime import timedelta +from cadCAD.utils import SilentDF #, val_switch +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import ep_time_step, config_sim +from cadCAD.configuration.utils.userDefinedObject import udoPipe, UDO +import pandas as pd + +from fn.func import curried + +DF = SilentDF(pd.read_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/output.csv')) + +class udoExample(object): + def __init__(self, x, dataset=None): + self.x = x + self.mem_id = str(hex(id(self))) + self.ds = dataset # for setting ds initially or querying + self.perception = {} + + def anon(self, f): + return f(self) + + def updateX(self): + self.x += 1 + return self + + def perceive(self, s): + self.perception = self.ds[ + (self.ds['run'] == s['run']) & (self.ds['substep'] == s['substep']) & (self.ds['timestep'] == s['timestep']) + ].drop(columns=['run', 'substep']).to_dict() + return self + + def read(self, ds_uri): + self.ds = SilentDF(pd.read_csv(ds_uri)) + return self + + def write(self, ds_uri): + pd.to_csv(ds_uri) + + # ToDo: Generic update function + + pass + +# can be accessed after an update within the same substep and timestep + +state_udo = UDO(udo=udoExample(0, DF), masked_members=['obj', 'perception']) +policy_udoA = UDO(udo=udoExample(0, DF), masked_members=['obj', 'perception']) +policy_udoB = UDO(udo=udoExample(0, DF), masked_members=['obj', 'perception']) + +# ToDo: DataFrame Column order +state_dict = { + 'increment': 0, + 'state_udo': state_udo, 'state_udo_tracker_a': 0, 'state_udo_tracker_b': 0, + 'state_udo_perception_tracker': {"ds1": None, "ds2": None, "ds3": None, "timestep": None}, + 'udo_policies': {'udo_A': policy_udoA, 'udo_B': policy_udoB}, + 'udo_policy_tracker_a': (None, None), 'udo_policy_tracker_b': (None, None), + 'timestamp': '2019-01-01 00:00:00', +} + +@curried +def perceive(s, self): + self.perception = self.ds[ + (self.ds['run'] == s['run']) & (self.ds['substep'] == s['substep']) & (self.ds['timestep'] == s['timestep']) + ].drop(columns=['run', 'substep']).to_dict() + return self + +def view_udo_policy(_g, step, sL, s, _input): + y = 'udo_policies' + x = _input + return (y, x) + +def update_timestamp(y, timedelta, format): + return lambda _g, step, sL, s, _input: ( + y, + ep_time_step(s, dt_str=s[y], fromat_str=format, _timedelta=timedelta) + ) +time_model = update_timestamp('timestamp', timedelta(minutes=1), '%Y-%m-%d %H:%M:%S') + + +def state_udo_update(_g, step, sL, s, _input): + y = 'state_udo' + # s['hydra_state'].updateX().anon(perceive(s)) + s['state_udo'].updateX().perceive(s) + x = udoPipe(s['state_udo']) + return (y, x) + +def increment(y, incr_by): + return lambda _g, step, sL, s, _input: (y, s[y] + incr_by) + +def track(destination, source): + return lambda _g, step, sL, s, _input: (destination, s[source].x) + +def track_udo_policy(destination, source): + def val_switch(v): + if isinstance(v, pd.DataFrame) is True or isinstance(v, SilentDF) is True: + return SilentDF(v) + else: + return v.x + return lambda _g, step, sL, s, _input: (destination, tuple(val_switch(v) for _, v in s[source].items())) + +def track_state_udo_perception(destination, source): + def id(past_perception): + if len(past_perception) == 0: + return state_dict['state_udo_perception_tracker'] + else: + return past_perception + return lambda _g, step, sL, s, _input: (destination, id(s[source].perception)) + +def udo_policyA(_g, step, sL, s): + s['udo_policies']['udo_A'].updateX() + return {'udo_A': udoPipe(s['udo_policies']['udo_A'])} + +def udo_policyB(_g, step, sL, s): + s['udo_policies']['udo_B'].updateX() + return {'udo_B': udoPipe(s['udo_policies']['udo_B'])} + +policies = {"p1": udo_policyA, "p2": udo_policyB} + +states_with_ts = { + 'increment': increment('increment', 1), + 'state_udo_tracker_a': track('state_udo_tracker_a', 'state_udo'), + 'state_udo': state_udo_update, + 'state_udo_perception_tracker': track_state_udo_perception('state_udo_perception_tracker', 'state_udo'), + 'state_udo_tracker_b': track('state_udo_tracker_b', 'state_udo'), + 'udo_policy_tracker_a': track_udo_policy('udo_policy_tracker_a', 'udo_policies'), + 'udo_policies': view_udo_policy, + 'udo_policy_tracker_b': track_udo_policy('udo_policy_tracker_b', 'udo_policies'), + 'timestamp': time_model, +} +del states_with_ts['timestamp'] +states_without_ts = states_with_ts + +# needs M1&2 need behaviors +partial_state_update_blocks = { + 'PSUB1': { + 'policies': policies, + 'states': states_with_ts + }, + 'PSUB2': { + 'policies': policies, + 'states': states_without_ts + }, + 'PSUB3': { + 'policies': policies, + 'states': states_without_ts + } +} + +sim_config = config_sim({ + "N": 2, + "T": range(4) +}) + +append_configs( + sim_config, + state_dict, + {}, {}, {}, + partial_state_update_blocks, + policy_ops=[lambda a, b: {**a, **b}] +) From 3c91040401569f98d80bb880183cd25b2b07cc58 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 2 May 2019 14:10:58 -0400 Subject: [PATCH 09/19] udo refactor --- cadCAD/engine/simulation.py | 8 +- cadCAD/utils/sys_config.py | 6 + simulations/external_ds_run.py | 29 +++ simulations/externalds_run.py | 2 +- simulations/single_config_run.py | 5 +- simulations/sweep_udc_hack_run.py | 24 -- simulations/validation/config4.py | 2 - simulations/validation/config_az.py | 89 ------- simulations/validation/config_az_a.py | 113 --------- simulations/validation/config_az_b.py | 86 ------- simulations/validation/config_udc.py | 175 ------------- simulations/validation/config_udc3.py | 210 --------------- simulations/validation/config_udc_json.py | 150 ----------- simulations/validation/config_udc_json2.py | 239 ------------------ simulations/validation/config_udc_json4.py | 146 ----------- ...xternal_dataset.py => external_dataset.py} | 23 +- .../validation/historical_state_access.py | 63 +++++ ...ig1_test_pipe.py => policy_aggregation.py} | 66 ++--- simulations/validation/udo.py | 6 +- .../{externalds.py => write_simulation.py} | 0 20 files changed, 143 insertions(+), 1299 deletions(-) create mode 100644 simulations/external_ds_run.py delete mode 100644 simulations/sweep_udc_hack_run.py delete mode 100644 simulations/validation/config_az.py delete mode 100644 simulations/validation/config_az_a.py delete mode 100644 simulations/validation/config_az_b.py delete mode 100644 simulations/validation/config_udc.py delete mode 100644 simulations/validation/config_udc3.py delete mode 100644 simulations/validation/config_udc_json.py delete mode 100644 simulations/validation/config_udc_json2.py delete mode 100644 simulations/validation/config_udc_json4.py rename simulations/validation/{incr_external_dataset.py => external_dataset.py} (78%) create mode 100644 simulations/validation/historical_state_access.py rename simulations/validation/{config1_test_pipe.py => policy_aggregation.py} (56%) rename simulations/validation/{externalds.py => write_simulation.py} (100%) diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 0eb8605..f9914d5 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -88,6 +88,7 @@ class Executor: return state_dict + # ToDo: Redifined as a function that applies the tensor field to a set og last conditions # mech_step def partial_state_update( self, @@ -106,7 +107,7 @@ class Executor: last_in_obj: Dict[str, Any] = sL[-1] # last_in_obj: Dict[str, Any] = sH[-1] # print(last_in_obj) - # print(sH[-1]) + print(sH[-1]) _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sH, last_in_obj, policy_funcs)) @@ -168,7 +169,7 @@ class Executor: # ToDo: Causes Substep repeats in sL, use for yield sub_step += 1 - for [s_conf, p_conf] in configs: + for [s_conf, p_conf] in configs: # tensor field states_list: List[Dict[str, Any]] = self.partial_state_update( var_dict, sub_step, states_list, simulation_list, s_conf, p_conf, env_processes, time_step, run ) @@ -193,6 +194,7 @@ class Executor: ) -> List[List[Dict[str, Any]]]: time_seq: List[int] = [x + 1 for x in time_seq] + # ToDo: simulation_list should be a Tensor that is generated throughout the Executor simulation_list: List[List[Dict[str, Any]]] = [states_list] # print(simulation_list[-1]) @@ -211,6 +213,8 @@ class Executor: return simulation_list + # ToDo: Below can be recieved from a tensor field + # configs: List[Tuple[List[Callable], List[Callable]]] def simulation( self, var_dict: Dict[str, List[Any]], diff --git a/cadCAD/utils/sys_config.py b/cadCAD/utils/sys_config.py index 90d3250..cf45e88 100644 --- a/cadCAD/utils/sys_config.py +++ b/cadCAD/utils/sys_config.py @@ -19,6 +19,12 @@ def update_timestamp(y, timedelta, format): ep_time_step(s, dt_str=s[y], fromat_str=format, _timedelta=timedelta) ) +def add(y, x): + return lambda _g, step, sH, s, _input: (y, s[y] + x) + +def s(y, x): + return lambda _g, step, sH, s, _input: (y, x) + # def repr(_g, step, sL, s, _input): # y = 'z' diff --git a/simulations/external_ds_run.py b/simulations/external_ds_run.py new file mode 100644 index 0000000..0c1f16b --- /dev/null +++ b/simulations/external_ds_run.py @@ -0,0 +1,29 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +# from simulations.validation import config1_test_pipe +# from simulations.validation import config1 +# from simulations.validation import externalds +from simulations.validation import external_dataset +from cadCAD import configs + +exec_mode = ExecutionMode() + +print("Simulation Execution: Single Configuration") +print() +first_config = configs # only contains config1 +single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) +run = Executor(exec_context=single_proc_ctx, configs=first_config) + +raw_result, tensor_field = run.main() +result = pd.DataFrame(raw_result) +result = pd.concat([result, result['external_data'].apply(pd.Series)], axis=1)[ + ['run', 'substep', 'timestep', 'increment', 'external_data', 'ds1', 'ds2', 'ds3', 'policies'] +] +print() +print("Tensor Field: config1") +print(tabulate(tensor_field, headers='keys', tablefmt='psql')) +print("Output:") +print(tabulate(result, headers='keys', tablefmt='psql')) +print() diff --git a/simulations/externalds_run.py b/simulations/externalds_run.py index 569d432..9edcf8c 100644 --- a/simulations/externalds_run.py +++ b/simulations/externalds_run.py @@ -4,7 +4,7 @@ from tabulate import tabulate from cadCAD.engine import ExecutionMode, ExecutionContext, Executor # from simulations.validation import config1_test_pipe # from simulations.validation import config1 -from simulations.validation import externalds +from simulations.validation import write_simulation from cadCAD import configs exec_mode = ExecutionMode() diff --git a/simulations/single_config_run.py b/simulations/single_config_run.py index 081d4e1..9bd4737 100644 --- a/simulations/single_config_run.py +++ b/simulations/single_config_run.py @@ -2,10 +2,11 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -# from simulations.validation import config1_test_pipe +# from simulations.validation import policy_aggregation +from simulations.validation import historical_state_access # from simulations.validation import config1 # from simulations.validation import externalds -from simulations.validation import incr_external_dataset +# from simulations.validation import external_dataset from cadCAD import configs exec_mode = ExecutionMode() diff --git a/simulations/sweep_udc_hack_run.py b/simulations/sweep_udc_hack_run.py deleted file mode 100644 index dd33c16..0000000 --- a/simulations/sweep_udc_hack_run.py +++ /dev/null @@ -1,24 +0,0 @@ -import pandas as pd -from tabulate import tabulate -# The following imports NEED to be in the exact order -from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import config_udc_json2 -from cadCAD import configs - -exec_mode = ExecutionMode() - -print("Simulation Execution: Concurrent Execution") -multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) -run = Executor(exec_context=multi_proc_ctx, configs=configs) - -i = 0 -config_names = ['sweep_config_A', 'sweep_config_B'] -for raw_result, tensor_field in run.main(): - result = pd.DataFrame(raw_result) - print() - print("Tensor Field: " + config_names[i]) - print(tabulate(tensor_field, headers='keys', tablefmt='psql')) - print("Output:") - print(tabulate(result, headers='keys', tablefmt='psql')) - print() - i += 1 diff --git a/simulations/validation/config4.py b/simulations/validation/config4.py index 164df26..8e00129 100644 --- a/simulations/validation/config4.py +++ b/simulations/validation/config4.py @@ -92,8 +92,6 @@ def env_a(x): return 5 def env_b(x): return 10 -# def what_ever(x): -# return x + 1 # Genesis States diff --git a/simulations/validation/config_az.py b/simulations/validation/config_az.py deleted file mode 100644 index 746df2d..0000000 --- a/simulations/validation/config_az.py +++ /dev/null @@ -1,89 +0,0 @@ -from datetime import timedelta -from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import ep_time_step, config_sim - - -class MyClass: - def __init__(self): - self.x = 0 - print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") - - def update(self): - # res = deepcopy(self) - self.x += 1 - # print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") - return self #res - - def __str__(self): - return f"PRINT MyClass: @ {hex(id(self))}: value {self.x}" - -# genesis state -state_dict = { - 'classX': MyClass(), - 'a': 0, - 'b': 0, - 'timestamp': '2019-01-01 00:00:00' -} - - -timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. -ts_format = '%Y-%m-%d %H:%M:%S' -def time_model(_g, step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) - return (y, x) - - -def updateClassX(_g, step, sL, s, _input): - y = 'classX' - x = s['classX'] - return (y, x) - - -def updateA(_g, step, sL, s, _input): - y = 'a' - x = s['a'] + 1 - return (y, x) - - -def updateB(_g, step, sL, s, _input): - s['classX'].update() - y = 'b' - x = s['classX'].x - return (y, x) - - -partial_state_update_blocks = { - 'PSUB1': { - 'behaviors': { - }, - 'states': { - 'timestamp': time_model, - } - }, - 'PSUB2': { - 'behaviors': { - }, - 'states': { - 'classX': updateClassX, - 'a': updateA, - 'b': updateB - } - }, - 'PSUB3': { - 'behaviors': { - }, - 'states': { - 'classX': updateClassX, - 'a': updateA, - 'b': updateB - } - } -} - -sim_config = config_sim({ - "N": 2, - "T": range(4) -}) - -append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) diff --git a/simulations/validation/config_az_a.py b/simulations/validation/config_az_a.py deleted file mode 100644 index 153d883..0000000 --- a/simulations/validation/config_az_a.py +++ /dev/null @@ -1,113 +0,0 @@ -from datetime import timedelta -from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import ep_time_step, config_sim - - -class MyClass: - def __init__(self): - self.x = 0 - print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") - - def update(self): - self.x += 1 - print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") - return self - - def __str__(self): - return f"PRINT MyClass: @ {hex(id(self))}: value {self.x}" - -# a is Correct, and classX's value is Incorrect -# Expected: a == classX's value -# b should be tracking classX's value and a: -# b should be the same value as the previous classX value and the previous a value - -udc = MyClass() -# z = MyClass() -# pointer(z) -# separate thread/process for UCD with async calls to this thread/process - -# genesis state -state_dict = { - 'classX': udc, - 'c_udc': udc, - 'a': 0, - 'b': 0, - 'c': "", - 'd': None, - 'timestamp': '2019-01-01 00:00:00' -} - -timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. -ts_format = '%Y-%m-%d %H:%M:%S' -def time_model(_g, step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) - return (y, x) - -def updateClassX(_g, step, sL, s, _input): - y = 'classX' - x = s['classX'].update() - return (y, x) - -def updateA(_g, step, sL, s, _input): - y = 'a' - x = s['a'] + 1 - return (y, x) - -def updateB(_g, step, sL, s, _input): - y = 'b' - x = s['classX'].x - return (y, x) - -def updateC(_g, step, sL, s, _input): - y = 'c' - x = f"PRINT MyClass: @ {hex(id(s['classX']))}: value {s['classX'].x}" - return (y, x) - -def updateD(_g, step, sL, s, _input): - y = 'd' - x = s['classX'] - return (y, x) - - -partial_state_update_blocks = { - 'PSUB1': { - 'behaviors': { - }, - 'states': { - 'timestamp': time_model, - 'b': updateB, - 'c': updateC, - # 'd': updateD - } - }, - 'PSUB2': { - 'behaviors': { - }, - 'states': { - 'classX': updateClassX, - 'a': updateA, - 'b': updateB, - 'c': updateC, - # 'd': updateD - } - }, - 'PSUB3': { - 'behaviors': { - }, - 'states': { - 'classX': updateClassX, - 'a': updateA, - 'b': updateB, - 'c': updateC, - # 'd': updateD - } - } -} - -sim_config = config_sim({ - "N": 2, - "T": range(4) -}) - -append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) diff --git a/simulations/validation/config_az_b.py b/simulations/validation/config_az_b.py deleted file mode 100644 index 462b40b..0000000 --- a/simulations/validation/config_az_b.py +++ /dev/null @@ -1,86 +0,0 @@ -from copy import deepcopy -from datetime import timedelta -from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import ep_time_step, config_sim - -class MyClass: - def __init__(self): - self.x = 0 - print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") - - def update(self): - res = deepcopy(self) - res.x += 1 - print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") - return res - - def __str__(self): - return f"PRINT MyClass: @ {hex(id(self))}: value {self.x}" - - -# genesis state -state_dict = { - 'classX': MyClass(), - 'a': 0, - 'b': 0, - 'timestamp': '2019-01-01 00:00:00' -} - -timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. -ts_format = '%Y-%m-%d %H:%M:%S' -def time_model(_g, step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) - return (y, x) - -def updateClassX(_g, step, sL, s, _input): - y = 'classX' - x = s['classX'].update() - return (y, x) - -def updateA(_g, step, sL, s, _input): - y = 'a' - x = s['a'] + 1 - return (y, x) - -def updateB(_g, step, sL, s, _input): - y = 'b' - # x = s['classX'].x - x = s['a'] - return (y, x) - -partial_state_update_blocks = { - 'PSUB1': { - 'behaviors': { - }, - 'states': { - 'timestamp': time_model, - 'b': updateB - } - }, - 'PSUB2': { - 'behaviors': { - }, - 'states': { - 'classX': updateClassX, - 'a': updateA, - 'b': updateB - } - }, - 'PSUB3': { - 'behaviors': { - }, - 'states': { - 'classX': updateClassX, - 'a': updateA, - 'b': updateB - } - } -} - -sim_config = config_sim({ - "N": 2, - "T": range(4) -}) - -append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) diff --git a/simulations/validation/config_udc.py b/simulations/validation/config_udc.py deleted file mode 100644 index 5c98b69..0000000 --- a/simulations/validation/config_udc.py +++ /dev/null @@ -1,175 +0,0 @@ -from datetime import timedelta -from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import ep_time_step, config_sim -from copy import deepcopy, copy - - -# ToDo: Create member for past value -class MyClass: - def __init__(self, past_attr): - # self.past = self - self.past_attr = past_attr - self.class_id = None - self.x = 0 - - print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") - - - def update(self): - # self.past = copy(self) - self.x += 1 - print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") - return self.x #self #old_self #self.x - - - def getMemID(self): - return str(hex(id(self))) - - # can be accessed after an update within the same substep and timestep - # ToDo: id sensitive to lineage, rerepresent - def __str__(self): - # return str(self.x) - return f"{hex(id(self))} - {self.x}" - - -# a is Correct, and classX's value is Incorrect -# Expected: a == classX's value -# b should be tracking classX's value and a: -# b should be the same value as the previous classX value and the previous a value -# https://pymotw.com/2/multiprocessing/communication.html -udc = MyClass(0) - -# z = MyClass() -# pointer(z) -# separate thread/process for UCD with async calls to this thread/process - -# genesis state - -# udc_json = {'udc': udc, 'udc-1': udc} -state_dict = { - 'classX': udc, - # 'classX_MemID': udc.getMemID(), - 'pastX': udc, - 'otherX': udc, - # 'pastX_MemID': udc.getMemID(), - 'a': 0, - 'b': udc.x, - 'c': udc.x, - 'c2': udc.x, - 'z': udc.x, - 'timestamp': '2019-01-01 00:00:00' -} - -timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. -ts_format = '%Y-%m-%d %H:%M:%S' -def time_model(_g, step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) - return (y, x) - -def trackClassX(_g, step, sL, s, _input): - y = 'classX' - x = s['classX'] - return (y, x) - -# def trackClassX_str(_g, step, sL, s, _input): -# y = 'classX_MemID' -# x = s['classX'].getMemID() -# return (y, x) - -def updatePastX(_g, step, sL, s, _input): - y = 'pastX' - x = s['pastX'] - return (y, x) - -def updateOtherX(_g, step, sL, s, _input): - y = 'otherX' - x = s['otherX'] - return (y, x) - -# def updatePastX_str(_g, step, sL, s, _input): -# y = 'pastX_MemID' -# x = s['pastX'].getMemID() -# return (y, x) - -def updateA(_g, step, sL, s, _input): - y = 'a' - x = s['a'] + 1 - return (y, x) - -def updateB(_g, step, sL, s, _input): - y = 'b' - x = s['classX'].x - return (y, x) - -def updateC(_g, step, sL, s, _input): - y = 'c' - x = s['classX'].update() - return (y, x) - -def updateZ(_g, step, sL, s, _input): - y = 'z' - x = s['classX'].x - return (y, x) - -def updateC2(_g, step, sL, s, _input): - y = 'c2' - x = s['pastX'].x - return (y, x) - -partial_state_update_blocks = { - 'PSUB1': { - 'behaviors': { - }, - 'states': { - 'a': updateA, - 'b': updateB, - 'c': updateC, - 'c2': updateC2, - 'classX': trackClassX, - 'otherX': updateOtherX, - 'timestamp': time_model, - 'pastX': updatePastX, - 'z': updateZ - } - }, - 'PSUB2': { - 'behaviors': { - }, - 'states': { - 'a': updateA, - 'b': updateB, - 'c': updateC, - 'c2': updateC2, - 'classX': trackClassX, - 'otherX': updateOtherX, - # 'classX_MemID': trackClassX_str, - 'pastX': updatePastX, - # 'pastX_MemID': updatePastX_str, - 'z': updateZ - } - }, - 'PSUB3': { - 'behaviors': { - }, - 'states': { - 'a': updateA, - 'b': updateB, - 'c': updateC, - 'c2': updateC2, - 'classX': trackClassX, - 'otherX': updateOtherX, - # 'classX_MemID': trackClassX_str, - 'pastX': updatePastX, - # 'pastX_MemID': updatePastX_str, - 'z': updateZ - } - } -} - -sim_config = config_sim({ - "N": 2, - "T": range(4) -}) - -append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) diff --git a/simulations/validation/config_udc3.py b/simulations/validation/config_udc3.py deleted file mode 100644 index c4fc321..0000000 --- a/simulations/validation/config_udc3.py +++ /dev/null @@ -1,210 +0,0 @@ -from datetime import timedelta -from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import ep_time_step, config_sim - - -# ToDo: Create member for past value -class MyClassA: - def __init__(self): - self.class_id = None - self.x = 0 - - print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") - - - def update(self): - # self.past = copy(self) - self.x += 1 - print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") - return self.x #self #old_self #self.x - - - def getMemID(self): - return str(hex(id(self))) - - # can be accessed after an update within the same substep and timestep - # ToDo: id sensitive to lineage, rerepresent - def __str__(self): - # return str(self.x) - return f"{hex(id(self))} - {self.x}" - - -class MyClassB: - def __init__(self): - self.class_id = None - self.x = 5 - - print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") - - - def update(self): - # self.past = copy(self) - self.x += 1 - print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") - return self.x #self #old_self #self.x - - - def getMemID(self): - return str(hex(id(self))) - - # can be accessed after an update within the same substep and timestep - # ToDo: id sensitive to lineage, rerepresent - def __str__(self): - # return str(self.x) - return f"{hex(id(self))} - {self.x}" - -# a is Correct, and classX's value is Incorrect -# Expected: a == classX's value -# b should be tracking classX's value and a: -# b should be the same value as the previous classX value and the previous a value -# https://pymotw.com/2/multiprocessing/communication.html -udcA = MyClassA() -udcB = MyClassB() - -# z = MyClass() -# pointer(z) -# separate thread/process for UCD with async calls to this thread/process - -# genesis state - -# udc_json = {'udc': udc, 'udc-1': udc} -state_dict = { - 'ca': 0, - 'cb': udcA.x, - 'cblassX': udcA, - 'cc': udcA.x, - 'cz': udcA.x, - 'da': 5, - 'db': udcB.x, - 'dblassX': udcB, - 'dc': udcB.x, - 'dz': udcB.x, - 'timestamp': '2019-01-01 00:00:00' -} - -timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. -ts_format = '%Y-%m-%d %H:%M:%S' -def time_model(_g, step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) - return (y, x) - -def CBlassX(_g, step, sL, s, _input): - y = 'cblassX' - # x = s['cblassX'] - x = _g['cblassX'] - return (y, x) - -def DBlassX(_g, step, sL, s, _input): - y = 'dblassX' - # x = s['dblassX'] - x = _g['dblassX'] - return (y, x) - - -def CA(_g, step, sL, s, _input): - y = 'ca' - x = s['ca'] + 1 - return (y, x) - -def DA(_g, step, sL, s, _input): - y = 'da' - x = s['da'] + 1 - return (y, x) - -def CB(_g, step, sL, s, _input): - y = 'cb' - x = _g['cblassX'].x - # x = s['cblassX'].x - return (y, x) - -def DB(_g, step, sL, s, _input): - y = 'db' - x = _g['dblassX'].x - # x = s['dblassX'].x - return (y, x) - -def CC(_g, step, sL, s, _input): - y = 'cc' - # x = s['cblassX'].update() - x = _g['cblassX'].update() - return (y, x) - -def DC(_g, step, sL, s, _input): - # s['dblassX'] = _g['dblassX'].update() - - y = 'dc' - # x = s['dblassX'].update() - x = _g['dblassX'].update() - return (y, x) - -def CZ(_g, step, sL, s, _input): - y = 'cz' - x = _g['cblassX'].x - # x = s['cblassX'].x - return (y, x) - -def DZ(_g, step, sL, s, _input): - y = 'dz' - x = _g['dblassX'].x - # x = s['dblassX'].x - return (y, x) - -partial_state_update_blocks = { - 'PSUB1': { - 'behaviors': { - }, - 'states': { - 'ca': CA, - 'cb': CB, - 'cblassX': CBlassX, - 'cc': CC, - 'cz': CZ, - 'da': DA, - 'db': DB, - 'dblassX': DBlassX, - 'dc': DC, - 'dz': DZ, - 'timestamp': time_model, - } - }, - 'PSUB2': { - 'behaviors': { - }, - 'states': { - 'ca': CA, - 'cb': CB, - 'cblassX': CBlassX, - 'cc': CC, - 'cz': CZ, - 'da': DA, - 'db': DB, - 'dblassX': DBlassX, - 'dc': DC, - 'dz': DZ, - } - }, - 'PSUB3': { - 'behaviors': { - }, - 'states': { - 'ca': CA, - 'cb': CB, - 'cblassX': CBlassX, - 'cc': CC, - 'cz': CZ, - 'da': DA, - 'db': DB, - 'dblassX': DBlassX, - 'dc': DC, - 'dz': DZ, - } - } -} - -sim_config = config_sim({ - "N": 2, - "T": range(4) -}) - -append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) diff --git a/simulations/validation/config_udc_json.py b/simulations/validation/config_udc_json.py deleted file mode 100644 index 8aa8d85..0000000 --- a/simulations/validation/config_udc_json.py +++ /dev/null @@ -1,150 +0,0 @@ -from datetime import timedelta -from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import ep_time_step, config_sim -import inspect - - -# ToDo: Create member for past value -class MyClass: - def __init__(self, past_attr): - self.past_attr = past_attr - self.x = 0 - - print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") - - def update(self): - self.x += 1 - print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") - return self.x - - def getMemID(self): - return str(hex(id(self))) - - # can be accessed after an update within the same substep and timestep - def __str__(self): - # return str(self.x) - return f"{hex(id(self))} - {self.x}" - - -# a is Correct, and classX's value is Incorrect -# Expected: a == classX's value -# b should be tracking classX's value and a: -# b should be the same value as the previous classX value and the previous a value - -udc = MyClass('pastX') - -# z = MyClass() -# pointer(z) -# separate thread/process for UCD with async calls to this thread/process - -# genesis state -# seperate process -# staging - -udc_json = {'current': udc, 'past': udc} -state_dict = { - 'classX': udc_json, - 'classX_str': udc_json['current'], - # 'pastX': udc, - 'pastX_str': f"{hex(id(udc_json['past']))} - {udc_json['past'].x}", - 'a': 0, - 'b': udc_json['current'].x, - 'c': udc_json['current'].x, - 'timestamp': '2019-01-01 00:00:00' -} - -timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. -ts_format = '%Y-%m-%d %H:%M:%S' -def time_model(_g, step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) - return (y, x) - -def trackClassX(_g, step, sL, s, _input): - y = 'classX' - x = s['classX'] - return (y, x) - -def trackClassX_str(_g, step, sL, s, _input): - y = 'classX_str' - # x = s['classX']['current'] - x = s['classX_str'] - return (y, x) - -def updatePastX(_g, step, sL, s, _input): - y = 'pastX' - x = s['pastX'] - return (y, x) - -def updatePastX_str(_g, step, sL, s, _input): - y = 'pastX_str' - x = s['classX']['past'] - # x = f"{hex(id(s['classX']['past']))} - {s['classX']['past'].x}" - # x = s['pastX_str'] - return (y, x) - -def updateA(_g, step, sL, s, _input): - y = 'a' - x = s['a'] + 1 - return (y, x) - -def updateB(_g, step, sL, s, _input): - y = 'b' - x = s['classX']['current'].x - return (y, x) - -def updateC(_g, step, sL, s, _input): - y = 'c' - x = s['classX']['current'].update() - return (y, x) - - - -partial_state_update_blocks = { - 'PSUB1': { - 'behaviors': { - }, - 'states': { - 'a': updateA, - 'b': updateB, - 'c': updateC, - 'timestamp': time_model, - 'classX_str': trackClassX_str, - # 'pastX': updatePastX, - 'pastX_str': updatePastX_str - } - }, - 'PSUB2': { - 'behaviors': { - }, - 'states': { - 'a': updateA, - 'b': updateB, - 'c': updateC, - 'classX': trackClassX, - 'classX_str': trackClassX_str, - # 'pastX': updatePastX, - 'pastX_str': updatePastX_str, - } - }, - 'PSUB3': { - 'behaviors': { - }, - 'states': { - 'a': updateA, - 'b': updateB, - 'c': updateC, - 'classX': trackClassX, - 'classX_str': trackClassX_str, - # 'pastX': updatePastX, - 'pastX_str': updatePastX_str, - } - } -} - -sim_config = config_sim({ - "N": 2, - "T": range(4) -}) - -append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) diff --git a/simulations/validation/config_udc_json2.py b/simulations/validation/config_udc_json2.py deleted file mode 100644 index 4f9221e..0000000 --- a/simulations/validation/config_udc_json2.py +++ /dev/null @@ -1,239 +0,0 @@ -from copy import deepcopy, copy -from datetime import timedelta -from cadCAD.utils import UDC_Wrapper, objectview -from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import ep_time_step, config_sim -from typing import Dict, List, Any - -from collections import namedtuple - - -# ToDo: Create member for past value -class MyClassA(object): - def __init__(self, x, class_id=None): - self.class_id = class_id - self.x = x - print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") - - def update(self): - # self.past = copy(self) - self.x += 1 - print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") - return self #.x #self #old_self #self.x - - def getMemID(self): - return str(hex(id(self))) - - # can be accessed after an update within the same substep and timestep - # ToDo: id sensitive to lineage, rerepresent - def __str__(self): - # return str(self.x) - return f"{self.__class__.__name__} - {hex(id(self))} - {self.__dict__}" - - -class MyClassB: - def __init__(self, x): - self.class_id = None - self.x = x - - print(f"Instance of MyClass (mem_id {hex(id(self))}) created with value {self.x}") - - - def update(self): - # self.past = copy(self) - self.x += 1 - print(f"Instance of MyClass (mem_id {hex(id(self))}) has been updated, has now value {self.x}") - return self.x #self #old_self #self.x - - - def getMemID(self): - return str(hex(id(self))) - - # can be accessed after an update within the same substep and timestep - # ToDo: id sensitive to lineage, rerepresent - def __str__(self): - # return str(self.x) - return f"{hex(id(self))} - {self.x}" - -# a is Correct, and classX's value is Incorrect -# Expected: a == classX's value -# b should be tracking classX's value and a: -# b should be the same value as the previous classX value and the previous a value -# https://pymotw.com/2/multiprocessing/communication.html -# ccc = MyClassA -# udc = ccc(0) -# print(MyClassA(**udc.__dict__).__dict__) - -g: Dict[str, List[MyClassA]] = {'udc': [MyClassA]} - -# udcB = MyClassB() - -# z = MyClass() -# pointer(z) -# separate thread/process for UCD with async calls to this thread/process - -# genesis state -udc = MyClassA(0) -# namedtuple("Hydra", self.hybrid_members.keys())(*self.hybrid_members.values()) -udc_json = {'current': udc, 'past': udc} -hydra = UDC_Wrapper(udc, udc, current_functions=['update']) -hydra_members = hydra.get_hybrid_members() -# hydra_obj = namedtuple("Hydra", hydra_members.keys())(*hydra_members.values()) -hydra_view = objectview(hydra_members) - - -state_dict = { - 'a': 0, - 'b': 0, - 'i': 0, - 'j': 0, - # 'k': 0, - # "hydra": hydra, - "hydra_members": hydra_members, - # "hydra_obj": hydra_obj, - # 'hydra_view': hydra_view, - 'timestamp': '2019-01-01 00:00:00' -} - -timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. -ts_format = '%Y-%m-%d %H:%M:%S' -def time_model(_g, step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) - return (y, x) - -def Hydra(_g, step, sL, s, _input): - y = 'hydra' - x = s['hydra'] - return (y, x) - -def HydraMembers(_g, step, sL, s, _input): - y = 'hydra_members' - x = s['hydra_members'] #.get_hybrid_members() - return (y, x) - -def HydraObj(_g, step, sL, s, _input): - y = 'hydra_obj' - # x = s['hydra_obj'] - # v = list(map(lambda x: copy(x), list(s['hydra_members'].values()))) - - # hydra_members = s['hydra_members'] - # def generate_var_deepcopy(hydra_members): - # for k, v in hydra_members.items(): - # if k == 'x': - # yield k, deepcopy(v) - # else: - # yield k, v - # - # hydra_nt = namedtuple("Hydra", s['hydra_members'].keys())(*s['hydra_members'].values()) - # new_hydra = dict(generate_var_deepcopy(hydra_nt)) - - # new_hydra_members = dict(generate_var_deepcopy(hydra_members)) - hm = copy(s['hydra_members']) - x = namedtuple("Hydra", hm.keys())(*hm.values()) - # x = namedtuple("Hydra", new_hydra.keys())(*new_hydra.values()) - - # print(x.x) - return (y, x) - -def HydraView(_g, step, sL, s, _input): - y = 'hydra_view' - # x = objectview(s['hydra_members']) - x = s['hydra_view'].update() - return (y, x) - -def A(_g, step, sL, s, _input): - y = 'a' - x = s['a'] + 1 - return (y, x) - -def B(_g, step, sL, s, _input): - y = 'b' - x = s['hydra_members']['x'] - # x = s['hydra_view'].x - # x = s['hydra_obj'].x - return (y, x) - -def I(_g, step, sL, s, _input): - y = 'i' - x = s['hydra_members']['update']() - - # Either works - # x = s['hydra_obj'].update() - # x = s['hydra_view'].x - return (y, x) - -def J(_g, step, sL, s, _input): - y = 'j' - x = s['hydra_members']['x'] - # x = s['hydra_members'].x - # x = s['hydra_obj'].x - # x = s['hydra_view'].x - return (y, x) - - -def K(_g, step, sL, s, _input): - y = 'k' - x = s['hydra_obj'].x - # x = s['hydra_view'].x - return (y, x) - - -partial_state_update_blocks = { - 'PSUB1': { - 'behaviors': { - }, - 'states': { - # 'ca': CA, - 'a': A, - 'b': B, - # 'hydra': Hydra, - 'hydra_members': HydraMembers, - # 'hydra_obj': HydraObj, - # 'hydra_view': HydraView, - 'i': I, - 'j': J, - # 'k': K, - 'timestamp': time_model, - } - }, - 'PSUB2': { - 'behaviors': { - }, - 'states': { - # 'ca': CA, - 'a': A, - 'b': B, - # 'hydra': Hydra, - 'hydra_members': HydraMembers, - # 'hydra_obj': HydraObj, - # 'hydra_view': HydraView, - 'i': I, - 'j': J, - # 'k': K, - } - }, - 'PSUB3': { - 'behaviors': { - }, - 'states': { - 'a': A, - 'b': B, - # 'hydra': Hydra, - 'hydra_members': HydraMembers, - # 'hydra_obj': HydraObj, - # 'hydra_view': HydraView, - 'i': I, - 'j': J, - # 'k': K, - } - } -} - -sim_config = config_sim({ - "N": 2, - "T": range(4), - "M": g -}) - -append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks) diff --git a/simulations/validation/config_udc_json4.py b/simulations/validation/config_udc_json4.py deleted file mode 100644 index 29dd353..0000000 --- a/simulations/validation/config_udc_json4.py +++ /dev/null @@ -1,146 +0,0 @@ -from datetime import timedelta - -from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import ep_time_step, config_sim -from cadCAD.configuration.utils.policyAggregation import dict_op, dict_elemwise_sum -from cadCAD.configuration.utils.userDefinedObject import udcBroker, udoPipe, UDO - - -# ToDo: Create member for past value -class MyClass(object): - def __init__(self, x): - self.x = x - - def update(self): - self.x += 1 - return self - - def getMemID(self): - return str(hex(id(self))) - - pass - - -# can be accessed after an update within the same substep and timestep - -hydra_state_view = UDO(MyClass(0)) -udc_view_B = UDO(MyClass(0)) -udc_view_C = UDO(MyClass(0)) - -# g: Dict[str, List[int]] = {'MyClassB'} - -state_dict = { - 'a': 0, 'b': 0, 'j': 0, - 'k': (0, 0), 'q': (0, 0), - 'hydra_state': hydra_state_view, - 'policies': {'hydra_B': udc_view_B, 'hydra_C': udc_view_C}, - 'timestamp': '2019-01-01 00:00:00' -} - -def p1(_g, step, sL, s): - s['policies']['hydra_B'].update() - return {'hydra_B': udoPipe(s['policies']['hydra_B'])} - -def p2(_g, step, sL, s): - s['policies']['hydra_C'].update() - return {'hydra_C': udoPipe(s['policies']['hydra_C'])} - -def policies(_g, step, sL, s, _input): - y = 'policies' - x = _input - return (y, x) - -timestep_duration = timedelta(minutes=1) # In this example, a timestep has a duration of 1 minute. -ts_format = '%Y-%m-%d %H:%M:%S' -def time_model(_g, step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=timestep_duration) - return (y, x) - - -def HydraMembers(_g, step, sL, s, _input): - y = 'hydra_state' - s['hydra_state'].update() - x = udoPipe(s['hydra_state']) - return (y, x) - -def repr(_g, step, sL, s, _input): - y = 'z' - x = s['hydra_members'].__repr__() - return (y, x) - -def incriment(y, incr_val): - return lambda _g, step, sL, s, _input: (y, s[y] + incr_val) - -def A(_g, step, sL, s, _input): - y = 'a' - x = s['a'] + 1 - return (y, x) - -def hydra_state_tracker(y): - return lambda _g, step, sL, s, _input: (y, s['hydra_state'].x) - - -def hydra_policy_tracker(y): - return lambda _g, step, sL, s, _input: (y, tuple(v.x for k, v in s['policies'].items())) - - -# needs M1&2 need behaviors -partial_state_update_blocks = { - 'PSUB1': { - 'policies': { - "b1": p1, - "b2": p2 - }, - 'states': { - 'a': A, - 'b': hydra_state_tracker('b'), - 'j': hydra_state_tracker('j'), - 'k': hydra_policy_tracker('k'), - 'q': hydra_policy_tracker('q'), - 'hydra_state': HydraMembers, - 'timestamp': time_model, - 'policies': policies - } - }, - 'PSUB2': { - 'policies': { - "b1": p1, - "b2": p2 - }, - 'states': { - 'a': A, - 'b': hydra_state_tracker('b'), - 'j': hydra_state_tracker('j'), - 'k': hydra_policy_tracker('k'), - 'q': hydra_policy_tracker('q'), - 'hydra_state': HydraMembers, - 'policies': policies - } - }, - 'PSUB3': { - 'policies': { - "b1": p1, - "b2": p2 - }, - 'states': { - 'a': A, - 'b': hydra_state_tracker('b'), - 'j': hydra_state_tracker('j'), - 'k': hydra_policy_tracker('k'), - 'q': hydra_policy_tracker('q'), - 'hydra_state': HydraMembers, - 'policies': policies - } - } -} - -sim_config = config_sim({ - "N": 2, - "T": range(4) -}) - -append = lambda a, b: [a, b] -update_dict = lambda a, b: a.update(b) -take_first = lambda a, b: [a, b] -append_configs(sim_config, state_dict, {}, {}, {}, partial_state_update_blocks)#, policy_ops=[foldr(dict_op(take_first))]) diff --git a/simulations/validation/incr_external_dataset.py b/simulations/validation/external_dataset.py similarity index 78% rename from simulations/validation/incr_external_dataset.py rename to simulations/validation/external_dataset.py index d503201..cc32338 100644 --- a/simulations/validation/incr_external_dataset.py +++ b/simulations/validation/external_dataset.py @@ -1,18 +1,15 @@ -from datetime import timedelta - from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import ep_time_step, config_sim -# from cadCAD.configuration.utils.policyAggregation import dict_op, dict_elemwise_sum -from cadCAD.configuration.utils.userDefinedObject import udcBroker, udoPipe, UDO +from cadCAD.configuration.utils import config_sim import pandas as pd from cadCAD.utils import SilentDF -df = SilentDF(pd.read_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/output.csv')) +df = SilentDF(pd.read_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/external_data/output.csv')) +external_data = {'ds1': None, 'ds2': None, 'ds3': None} state_dict = { 'increment': 0, - 'external_data': {"ds1": None, "ds2": None, "ds3": None}, - 'policies': {} + 'external_data': external_data, + 'policies': external_data, } def query(s, df): @@ -61,9 +58,11 @@ sim_config = config_sim({ }) append_configs( - sim_config, - state_dict, - {}, {}, {}, - partial_state_update_blocks, + sim_configs=sim_config, + initial_state=state_dict, + # seeds=seeds, + # raw_exogenous_states=raw_exogenous_states, + # env_processes=env_processes, + partial_state_update_blocks=partial_state_update_blocks, policy_ops=[lambda a, b: {**a, **b}] ) diff --git a/simulations/validation/historical_state_access.py b/simulations/validation/historical_state_access.py new file mode 100644 index 0000000..36bc5de --- /dev/null +++ b/simulations/validation/historical_state_access.py @@ -0,0 +1,63 @@ +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import config_sim + +# last_partial_state_update_block +def last_update_block(_g, step, sH, s, _input): + return 'sh', sH[-1] + + +# Policies per Mechanism +def p(_g, step, sH, s): + return {'last_update_block': sH[-1]} + +def add(y, x): + return lambda _g, step, sH, s, _input: (y, s[y] + x) + +def policies(_g, step, sH, s, _input): + y = 'policies' + x = _input + return (y, x) + +policies = {"p1": p, "p2": p} + +genesis_states = { + 's': 0, + 'sh': {}, # {[], {}} + # 'policies': {}, +} + +variables = { + 's': add('s', 1), + 'sh': last_update_block, + # "policies": policies +} + + +PSUB = { + "policies": policies, + "variables": variables +} + +partial_state_update_block = { + "PSUB1": PSUB, + "PSUB2": PSUB, + "PSUB3": PSUB +} + +sim_config = config_sim( + { + "N": 1, + "T": range(3), + } +) + + +append_configs( + sim_configs=sim_config, + initial_state=genesis_states, + seeds={}, + raw_exogenous_states={}, + env_processes={}, + partial_state_update_blocks=partial_state_update_block, + policy_ops=[lambda a, b: a + b] +) diff --git a/simulations/validation/config1_test_pipe.py b/simulations/validation/policy_aggregation.py similarity index 56% rename from simulations/validation/config1_test_pipe.py rename to simulations/validation/policy_aggregation.py index 5d9e3dd..c7028de 100644 --- a/simulations/validation/config1_test_pipe.py +++ b/simulations/validation/policy_aggregation.py @@ -1,14 +1,8 @@ -from decimal import Decimal -from functools import reduce - import numpy as np -from datetime import timedelta - -from cadCAD.configuration.utils.policyAggregation import get_base_value - from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step, config_sim +from cadCAD.configuration.utils import config_sim +# ToDo: Use seeds = { 'z': np.random.RandomState(1), 'a': np.random.RandomState(2), @@ -23,8 +17,6 @@ def p1m1(_g, step, sL, s): def p2m1(_g, step, sL, s): return {'param2': 2} -# [] - def p1m2(_g, step, sL, s): return {'param1': 2, 'param2': 2} def p2m2(_g, step, sL, s): @@ -35,18 +27,10 @@ def p1m3(_g, step, sL, s): def p2m3(_g, step, sL, s): return {'param1': 1, 'param2': 2, 'param3': 3} -def test_pipeline(_g, step, sL, s): - return {'test': 2, 'param2': 2} - # Internal States per Mechanism -def s(y): - return lambda _g, step, sH, s, _input: (y, s[y] + 1) - -def sH(_g, step, sH, s, _input): - y = 'sh' - x = sH - return (y, x) +def add(y, x): + return lambda _g, step, sH, s, _input: (y, s[y] + x) def policies(_g, step, sH, s, _input): y = 'policies' @@ -58,49 +42,39 @@ genesis_states = { 'policies': {}, 's1': 0, 's2': 0, - # 'sh': [] } raw_exogenous_states = {} env_processes = {} +variables = { + 's1': add('s1', 1), + 's2': add('s2', 1), + "policies": policies +} + partial_state_update_block = { "m1": { "policies": { - "b1": p1m1, - "b2": p2m1 + "p1": p1m1, + "p2": p2m1 }, - "variables": { - 's1': s('s1'), - 's2': s('s2'), - # 'sh': sH, - "policies": policies - } + "variables": variables }, "m2": { "policies": { - "b1": p1m2, - "b2": p2m2 + "p1": p1m2, + "p2": p2m2 }, - "variables": { - 's1': s('s1'), - 's2': s('s2'), - # 'sh': sH, - "policies": policies - } + "variables": variables }, "m3": { "policies": { - "b1": p1m3, - "b2": p2m3 + "p1": p1m3, + "p2": p2m3 }, - "variables": { - 's1': s('s1'), - 's2': s('s2'), - # 'sh': sH, - "policies": policies - } + "variables": variables } } @@ -120,7 +94,7 @@ append_configs( raw_exogenous_states=raw_exogenous_states, env_processes=env_processes, partial_state_update_blocks=partial_state_update_block, - # policy_ops=[lambda a, b: a + b, lambda y: y + 100, lambda y: y + 300] + policy_ops=[lambda a, b: a + b, lambda y: y + 10, lambda y: y + 30] ) diff --git a/simulations/validation/udo.py b/simulations/validation/udo.py index 1f06107..4adb1a5 100644 --- a/simulations/validation/udo.py +++ b/simulations/validation/udo.py @@ -7,7 +7,7 @@ import pandas as pd from fn.func import curried -DF = SilentDF(pd.read_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/output.csv')) +DF = SilentDF(pd.read_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/external_data/output.csv')) class udoExample(object): def __init__(self, x, dataset=None): @@ -153,7 +153,9 @@ sim_config = config_sim({ append_configs( sim_config, state_dict, - {}, {}, {}, + # seeds=seeds, + # raw_exogenous_states=raw_exogenous_states, + # env_processes=env_processes, partial_state_update_blocks, policy_ops=[lambda a, b: {**a, **b}] ) diff --git a/simulations/validation/externalds.py b/simulations/validation/write_simulation.py similarity index 100% rename from simulations/validation/externalds.py rename to simulations/validation/write_simulation.py From 71264c1c8f38b4c6f0e816a9300d80e365aaf132 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 9 May 2019 13:30:47 -0400 Subject: [PATCH 10/19] udo dirty publish to 'side branch' --- cadCAD/configuration/utils/__init__.py | 52 ++++- cadCAD/engine/simulation.py | 9 +- cadCAD/utils/sys_config.py | 104 +++++++++- simulations/historical_state_access_run.py | 30 +++ simulations/param_sweep_test.py | 24 +++ simulations/regression_tests/sweep_config.py | 182 ++++++++++++++++ simulations/regression_tests/udo.py | 194 ++++++++++++++++++ simulations/single_config_run.py | 3 +- simulations/udo_test.py | 45 ++++ simulations/validation/config1.py | 38 ++-- simulations/validation/config2.py | 6 +- simulations/validation/config4.py | 4 +- .../validation/historical_state_access.py | 10 +- simulations/validation/new_sweep_config.py | 183 +++++++++++++++++ simulations/validation/policy_aggregation.py | 10 +- simulations/validation/sweep_config.py | 36 +--- simulations/validation/udo.py | 97 ++++----- 17 files changed, 906 insertions(+), 121 deletions(-) create mode 100644 simulations/historical_state_access_run.py create mode 100644 simulations/param_sweep_test.py create mode 100644 simulations/regression_tests/sweep_config.py create mode 100644 simulations/regression_tests/udo.py create mode 100644 simulations/udo_test.py create mode 100644 simulations/validation/new_sweep_config.py diff --git a/cadCAD/configuration/utils/__init__.py b/cadCAD/configuration/utils/__init__.py index ee121ad..1e69c66 100644 --- a/cadCAD/configuration/utils/__init__.py +++ b/cadCAD/configuration/utils/__init__.py @@ -1,7 +1,10 @@ from datetime import datetime, timedelta from decimal import Decimal from copy import deepcopy +from functools import reduce + from fn.func import curried +from funcy import curry import pandas as pd # Temporary @@ -39,7 +42,7 @@ def bound_norm_random(rng, low, high): @curried -def proc_trigger(trigger_time, update_f, time): +def env_proc_trigger(trigger_time, update_f, time): if time == trigger_time: return update_f else: @@ -48,14 +51,17 @@ def proc_trigger(trigger_time, update_f, time): tstep_delta = timedelta(days=0, minutes=0, seconds=30) def time_step(dt_str, dt_format='%Y-%m-%d %H:%M:%S', _timedelta = tstep_delta): + # print(dt_str) dt = datetime.strptime(dt_str, dt_format) t = dt + _timedelta return t.strftime(dt_format) +# ToDo: Inject in first elem of last PSUB from Historical state ep_t_delta = timedelta(days=0, minutes=0, seconds=1) -def ep_time_step(s, dt_str, fromat_str='%Y-%m-%d %H:%M:%S', _timedelta = ep_t_delta): - if s['substep'] == 0: +def ep_time_step(s_condition, dt_str, fromat_str='%Y-%m-%d %H:%M:%S', _timedelta = ep_t_delta): + # print(dt_str) + if s_condition: return time_step(dt_str, fromat_str, _timedelta) else: return dt_str @@ -124,6 +130,28 @@ def exo_update_per_ts(ep): return {es: ep_decorator(f, es) for es, f in ep.items()} +def trigger_condition(s, conditions, cond_opp): + condition_bools = [s[field] in precondition_values for field, precondition_values in conditions.items()] + return reduce(cond_opp, condition_bools) + +def apply_state_condition(conditions, cond_opp, y, f, _g, step, sL, s, _input): + if trigger_condition(s, conditions, cond_opp): + return f(_g, step, sL, s, _input) + else: + return y, s[y] + +def proc_trigger(y, f, conditions, cond_op): + return lambda _g, step, sL, s, _input: apply_state_condition(conditions, cond_op, y, f, _g, step, sL, s, _input) + + +def timestep_trigger(end_substep, y, f): + conditions = {'substep': [0, end_substep]} + cond_opp = lambda a, b: a and b + return proc_trigger(y, f, conditions, cond_opp) + +# trigger = curry(_trigger) +# print(timestep_trigger) + # param sweep enabling middleware def config_sim(d): @@ -140,4 +168,20 @@ def config_sim(d): ] else: d["M"] = [{}] - return d \ No newline at end of file + return d + + +def psub(policies, state_updates): + return { + 'policies': policies, + 'states': state_updates + } + +def genereate_psubs(policy_grid, states_grid, policies, state_updates): + PSUBS = [] + for policy_ids, state_list in zip(policy_grid, states_grid): + filtered_policies = {k: v for (k, v) in policies.items() if k in policy_ids} + filtered_state_updates = {k: v for (k, v) in state_updates.items() if k in state_list} + PSUBS.append(psub(filtered_policies, filtered_state_updates)) + + return PSUBS \ No newline at end of file diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index f9914d5..c80b8da 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -107,7 +107,7 @@ class Executor: last_in_obj: Dict[str, Any] = sL[-1] # last_in_obj: Dict[str, Any] = sH[-1] # print(last_in_obj) - print(sH[-1]) + # print(sH[-1]) _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sH, last_in_obj, policy_funcs)) @@ -125,6 +125,7 @@ class Executor: return destination last_in_copy: Dict[str, Any] = transfer_missing_fields(last_in_obj, dict(generate_record(state_funcs))) + # ToDo: Remove last_in_copy: Dict[str, Any] = self.apply_env_proc(env_processes, last_in_copy, last_in_copy['timestep']) # ToDo: make 'substep' & 'timestep' reserve fields last_in_copy['substep'], last_in_copy['timestep'], last_in_copy['run'] = sub_step, time_step, run @@ -152,7 +153,8 @@ class Executor: # states_list_copy: List[Dict[str, Any]] = deepcopy(states_list) # states_list_copy: List[Dict[str, Any]] = states_list # ToDo: flatten first - states_list_copy: List[Dict[str, Any]] = simulation_list[-1] + # states_list_copy: List[Dict[str, Any]] = simulation_list[-1] + states_list_copy: List[Dict[str, Any]] = deepcopy(simulation_list[-1]) # print(states_list_copy) # ToDo: Causes Substep repeats in sL: @@ -169,7 +171,9 @@ class Executor: # ToDo: Causes Substep repeats in sL, use for yield sub_step += 1 + for [s_conf, p_conf] in configs: # tensor field + states_list: List[Dict[str, Any]] = self.partial_state_update( var_dict, sub_step, states_list, simulation_list, s_conf, p_conf, env_processes, time_step, run ) @@ -177,6 +181,7 @@ class Executor: # print(simulation_list) # print(flatten(simulation_list)) sub_step += 1 + # print(sub_step) time_step += 1 diff --git a/cadCAD/utils/sys_config.py b/cadCAD/utils/sys_config.py index cf45e88..b6e3a92 100644 --- a/cadCAD/utils/sys_config.py +++ b/cadCAD/utils/sys_config.py @@ -1,5 +1,7 @@ -from cadCAD.configuration.utils import ep_time_step - +from cadCAD.configuration.utils import ep_time_step, time_step +from funcy import curry +# from fn import _ +from functools import reduce def increment(y, incr_by): return lambda _g, step, sL, s, _input: (y, s[y] + incr_by) @@ -19,12 +21,106 @@ def update_timestamp(y, timedelta, format): ep_time_step(s, dt_str=s[y], fromat_str=format, _timedelta=timedelta) ) -def add(y, x): - return lambda _g, step, sH, s, _input: (y, s[y] + x) + +def apply(f, y: str, incr_by: int): + return lambda _g, step, sL, s, _input: (y, curry(f)(s[y])(incr_by)) + +def add(y: str, incr_by: int): + return apply(lambda a, b: a + b, y, incr_by) + +def increment_state_by_int(y: str, incr_by: int): + return lambda _g, step, sL, s, _input: (y, s[y] + incr_by) def s(y, x): return lambda _g, step, sH, s, _input: (y, x) +def time_model(y, substeps, time_delta, ts_format='%Y-%m-%d %H:%M:%S'): + def apply_incriment_condition(s): + if s['substep'] == 0 or s['substep'] == substeps: + return y, time_step(dt_str=s[y], dt_format=ts_format, _timedelta=time_delta) + else: + return y, s[y] + return lambda _g, step, sL, s, _input: apply_incriment_condition(s) + + +# ToDo: Impliment Matrix reduction +# +# [ +# {'conditions': [123], 'opp': lambda a, b: a and b}, +# {'conditions': [123], 'opp': lambda a, b: a and b} +# ] + +# def trigger_condition2(s, conditions, cond_opp): +# # print(conditions) +# condition_bools = [s[field] in precondition_values for field, precondition_values in conditions.items()] +# return reduce(cond_opp, condition_bools) +# +# def trigger_multi_conditions(s, multi_conditions, multi_cond_opp): +# # print([(d['conditions'], d['reduction_opp']) for d in multi_conditions]) +# condition_bools = [ +# trigger_condition2(s, conditions, opp) for conditions, opp in [ +# (d['conditions'], d['reduction_opp']) for d in multi_conditions +# ] +# ] +# return reduce(multi_cond_opp, condition_bools) +# +# def apply_state_condition2(multi_conditions, multi_cond_opp, y, f, _g, step, sL, s, _input): +# if trigger_multi_conditions(s, multi_conditions, multi_cond_opp): +# return f(_g, step, sL, s, _input) +# else: +# return y, s[y] +# +# def proc_trigger2(y, f, multi_conditions, multi_cond_opp): +# return lambda _g, step, sL, s, _input: apply_state_condition2(multi_conditions, multi_cond_opp, y, f, _g, step, sL, s, _input) +# +# def timestep_trigger2(end_substep, y, f): +# multi_conditions = [ +# { +# 'condition': { +# 'substep': [0, end_substep] +# }, +# 'reduction_opp': lambda a, b: a and b +# } +# ] +# multi_cond_opp = lambda a, b: a and b +# return proc_trigger2(y, f, multi_conditions, multi_cond_opp) + + + +def env_trigger(trigger_field, trigger_val, input, funct_list): + y, x = input + if trigger_field == trigger_val: + i = 0 + for g in funct_list: + x = g(x) + return y, x + + + + +# def p1m1(_g, step, sL, s): +# return {'param1': 1} +# +# def apply_policy_condition(policies, policy_id, f, conditions, _g, step, sL, s): +# if trigger_condition(s, conditions): +# policies[policy_id] = f(_g, step, sL, s) +# return policies +# else: +# return policies +# +# def proc_trigger2(policies, conditions, policy_id, f): +# return lambda _g, step, sL, s: apply_policy_condition(policies, policy_id, f, conditions,_g, step, sL, s) + +# policies_updates = {"p1": udo_policyA, "p2": udo_policyB} + + +# @curried +# def proc_trigger(trigger_time, update_f, time): +# if time == trigger_time: +# return update_f +# else: +# return lambda x: x + # def repr(_g, step, sL, s, _input): # y = 'z' diff --git a/simulations/historical_state_access_run.py b/simulations/historical_state_access_run.py new file mode 100644 index 0000000..d2a5970 --- /dev/null +++ b/simulations/historical_state_access_run.py @@ -0,0 +1,30 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.validation import historical_state_access +from cadCAD import configs + +exec_mode = ExecutionMode() + +print("Simulation Execution: Single Configuration") +print() +first_config = configs # only contains config1 +single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) +run = Executor(exec_context=single_proc_ctx, configs=first_config) + +raw_result, tensor_field = run.main() +result = pd.DataFrame(raw_result) +def delSH(d): + print(d) + if 'sh' in d.keys(): + del d['sh'] + return d +result['sh'] = result['sh'].apply(lambda sh: list(map(lambda d: delSH(d), sh))) + +print() +print("Tensor Field: config1") +print(tabulate(tensor_field, headers='keys', tablefmt='psql')) +print("Output:") +print(tabulate(result, headers='keys', tablefmt='psql')) +print() diff --git a/simulations/param_sweep_test.py b/simulations/param_sweep_test.py new file mode 100644 index 0000000..c43a903 --- /dev/null +++ b/simulations/param_sweep_test.py @@ -0,0 +1,24 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.validation import new_sweep_config +from cadCAD import configs + +exec_mode = ExecutionMode() + +print("Simulation Execution: Concurrent Execution") +multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) +run = Executor(exec_context=multi_proc_ctx, configs=configs) + +i = 0 +config_names = ['sweep_config_A', 'sweep_config_B'] +for raw_result, tensor_field in run.main(): + result = pd.DataFrame(raw_result) + print() + print("Tensor Field: " + config_names[i]) + print(tabulate(tensor_field, headers='keys', tablefmt='psql')) + print("Output:") + print(tabulate(result, headers='keys', tablefmt='psql')) + print() + i += 1 diff --git a/simulations/regression_tests/sweep_config.py b/simulations/regression_tests/sweep_config.py new file mode 100644 index 0000000..a2b9fe0 --- /dev/null +++ b/simulations/regression_tests/sweep_config.py @@ -0,0 +1,182 @@ +from decimal import Decimal +import numpy as np +from datetime import timedelta +import pprint + +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import proc_trigger, ep_time_step, config_sim + +from typing import Dict, List + +pp = pprint.PrettyPrinter(indent=4) + +seeds = { + 'z': np.random.RandomState(1), + 'a': np.random.RandomState(2), + 'b': np.random.RandomState(3), + 'c': np.random.RandomState(3) +} + +# Optional +g: Dict[str, List[int]] = { + 'alpha': [1], + 'beta': [2, 5], + 'gamma': [3, 4], + 'omega': [7] +} + +# Policies per Mechanism +def p1m1(_g, step, sL, s): + return {'param1': 1} + +def p2m1(_g, step, sL, s): + return {'param2': 4} + +def p1m2(_g, step, sL, s): + return {'param1': 'a', 'param2': _g['beta']} + +def p2m2(_g, step, sL, s): + return {'param1': 'b', 'param2': 0} + +def p1m3(_g, step, sL, s): + return {'param1': np.array([10, 100])} + +def p2m3(_g, step, sL, s): + return {'param1': np.array([20, 200])} + + +# Internal States per Mechanism +def s1m1(_g, step, sL, s, _input): + return 's1', 0 + +def s2m1(_g, step, sL, s, _input): + return 's2', _g['beta'] + +def s1m2(_g, step, sL, s, _input): + return 's1', _input['param2'] + +def s2m2(_g, step, sL, s, _input): + return 's2', _input['param2'] + +def s1m3(_g, step, sL, s, _input): + return 's1', 0 + +def s2m3(_g, step, sL, s, _input): + return 's2', 0 + + +# Exogenous States +proc_one_coef_A = 0.7 +proc_one_coef_B = 1.3 + + +def es3p1(_g, step, sL, s, _input): + return 's3', _g['gamma'] +# @curried +def es4p2(_g, step, sL, s, _input): + return 's4', _g['gamma'] + +ts_format = '%Y-%m-%d %H:%M:%S' +t_delta = timedelta(days=0, minutes=0, seconds=1) +def es5p2(_g, step, sL, s, _input): + y = 'timestep' + x = ep_time_step(s, dt_str=s['timestep'], fromat_str=ts_format, _timedelta=t_delta) + return (y, x) + + +# Environment States +# @curried +# def env_a(param, x): +# return x + param +def env_a(x): + return x +def env_b(x): + return 10 + + +# Genesis States +genesis_states = { + 's1': Decimal(0.0), + 's2': Decimal(0.0), + 's3': Decimal(1.0), + 's4': Decimal(1.0), +# 'timestep': '2018-10-01 15:16:24' +} + + +# remove `exo_update_per_ts` to update every ts +raw_exogenous_states = { + "s3": es3p1, + "s4": es4p2, +# "timestep": es5p2 +} + + +# ToDo: make env proc trigger field agnostic +# ToDo: input json into function renaming __name__ +triggered_env_b = proc_trigger(1, env_b) +env_processes = { + "s3": env_a, #sweep(beta, env_a), + "s4": triggered_env_b #rename('parameterized', triggered_env_b) #sweep(beta, triggered_env_b) +} +# parameterized_env_processes = parameterize_states(env_processes) +# +# pp.pprint(parameterized_env_processes) +# exit() + +# ToDo: The number of values entered in sweep should be the # of config objs created, +# not dependent on the # of times the sweep is applied +# sweep exo_state func and point to exo-state in every other funtion +# param sweep on genesis states + +partial_state_update_block = { + "m1": { + "policies": { + "b1": p1m1, + "b2": p2m1 + }, + "variables": { + "s1": s1m1, + "s2": s2m1 + } + }, + "m2": { + "policies": { + "b1": p1m2, + "b2": p2m2, + }, + "variables": { + "s1": s1m2, + "s2": s2m2 + } + }, + "m3": { + "policies": { + "b1": p1m3, + "b2": p2m3 + }, + "variables": { + "s1": s1m3, + "s2": s2m3 + } + } +} + +# config_sim Necessary +sim_config = config_sim( + { + "N": 2, + "T": range(5), + "M": g # Optional + } +) + +# New Convention +append_configs( + sim_configs=sim_config, + initial_state=genesis_states, + seeds=seeds, + raw_exogenous_states=raw_exogenous_states, + env_processes=env_processes, + partial_state_update_blocks=partial_state_update_block +) \ No newline at end of file diff --git a/simulations/regression_tests/udo.py b/simulations/regression_tests/udo.py new file mode 100644 index 0000000..65b6b41 --- /dev/null +++ b/simulations/regression_tests/udo.py @@ -0,0 +1,194 @@ +from copy import deepcopy +from datetime import timedelta +from functools import reduce + +from cadCAD.utils import SilentDF #, val_switch +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import time_step, config_sim, proc_trigger, timestep_trigger, genereate_psubs +from cadCAD.configuration.utils.userDefinedObject import udoPipe, UDO +import pandas as pd + +from fn.func import curried + +import pprint as pp + +from cadCAD.utils.sys_config import add + +DF = SilentDF(pd.read_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/external_data/output.csv')) + + +class udoExample(object): + def __init__(self, x, dataset=None): + self.x = x + self.mem_id = str(hex(id(self))) + self.ds = dataset # for setting ds initially or querying + self.perception = {} + + def anon(self, f): + return f(self) + + def updateX(self): + self.x += 1 + return self + + def perceive(self, s): + self.perception = self.ds[ + (self.ds['run'] == s['run']) & (self.ds['substep'] == s['substep']) & (self.ds['timestep'] == s['timestep']) + ].drop(columns=['run', 'substep']).to_dict() + return self + + def read(self, ds_uri): + self.ds = SilentDF(pd.read_csv(ds_uri)) + return self + + def write(self, ds_uri): + pd.to_csv(ds_uri) + + # ToDo: Generic update function + + pass + + +state_udo = UDO(udo=udoExample(0, DF), masked_members=['obj', 'perception']) +policy_udoA = UDO(udo=udoExample(0, DF), masked_members=['obj', 'perception']) +policy_udoB = UDO(udo=udoExample(0, DF), masked_members=['obj', 'perception']) + + +sim_config = config_sim({ + "N": 2, + "T": range(4) +}) + +# ToDo: DataFrame Column order +state_dict = { + 'increment': 0, + 'state_udo': state_udo, 'state_udo_tracker': 0, + 'state_udo_perception_tracker': {"ds1": None, "ds2": None, "ds3": None, "timestep": None}, + 'udo_policies': {'udo_A': policy_udoA, 'udo_B': policy_udoB}, + 'udo_policy_tracker': (0, 0), + 'timestamp': '2019-01-01 00:00:00' +} + +policies, state_updates = {}, {} +# +# def assign_udo_policy(udo): +# def policy(_g, step, sL, s): +# s['udo_policies'][udo].updateX() +# return {udo: udoPipe(s['udo_policies'][udo])} +# return policy +# policies_updates = {p: assign_udo_policy(udo) for p, udo in zip(['p1', 'p2'], ['udo_A', 'udo_B'])} + +def udo_policyA(_g, step, sL, s): + s['udo_policies']['udo_A'].updateX() + return {'udo_A': udoPipe(s['udo_policies']['udo_A'])} +policies['a'] = udo_policyA + +def udo_policyB(_g, step, sL, s): + s['udo_policies']['udo_B'].updateX() + return {'udo_B': udoPipe(s['udo_policies']['udo_B'])} +policies['b'] = udo_policyB + + +# policies = {"p1": udo_policyA, "p2": udo_policyB} +# policies = {"A": udo_policyA, "B": udo_policyB} + +# def increment_state_by_int(y: str, incr_by: int): +# return lambda _g, step, sL, s, _input: (y, s[y] + incr_by) +state_updates['increment'] = add('increment', 1) + +@curried +def perceive(s, self): + self.perception = self.ds[ + (self.ds['run'] == s['run']) & (self.ds['substep'] == s['substep']) & (self.ds['timestep'] == s['timestep']) + ].drop(columns=['run', 'substep']).to_dict() + return self + + +def state_udo_update(_g, step, sL, s, _input): + y = 'state_udo' + # s['hydra_state'].updateX().anon(perceive(s)) + s['state_udo'].updateX().perceive(s) + x = udoPipe(s['state_udo']) + return y, x +state_updates['state_udo'] = state_udo_update + + +def track(destination, source): + return lambda _g, step, sL, s, _input: (destination, s[source].x) +state_updates['state_udo_tracker'] = track('state_udo_tracker', 'state_udo') + + +def track_state_udo_perception(destination, source): + def id(past_perception): + if len(past_perception) == 0: + return state_dict['state_udo_perception_tracker'] + else: + return past_perception + return lambda _g, step, sL, s, _input: (destination, id(s[source].perception)) +state_updates['state_udo_perception_tracker'] = track_state_udo_perception('state_udo_perception_tracker', 'state_udo') + + +def view_udo_policy(_g, step, sL, s, _input): + return 'udo_policies', _input +state_updates['udo_policies'] = view_udo_policy + + +def track_udo_policy(destination, source): + def val_switch(v): + if isinstance(v, pd.DataFrame) is True or isinstance(v, SilentDF) is True: + return SilentDF(v) + else: + return v.x + return lambda _g, step, sL, s, _input: (destination, tuple(val_switch(v) for _, v in s[source].items())) +state_updates['udo_policy_tracker'] = track_udo_policy('udo_policy_tracker', 'udo_policies') + +def update_timestamp(_g, step, sL, s, _input): + y = 'timestamp' + return y, time_step(dt_str=s[y], dt_format='%Y-%m-%d %H:%M:%S', _timedelta=timedelta(days=0, minutes=0, seconds=1)) + + +system_substeps = 3 +# state_updates['timestamp'] = update_timestamp +state_updates['timestamp'] = timestep_trigger(end_substep=system_substeps, y='timestamp', f=update_timestamp) +# state_updates['timestamp'] = proc_trigger(y='timestamp', f=update_timestamp, conditions={'substep': [0, substeps]}, cond_op=lambda a, b: a and b) + +print() +print("State Updates:") +pp.pprint(state_updates) +print() +print("Policies:") +pp.pprint(policies) +print() + +filter_out = lambda remove_list, state_list: list(filter(lambda state: state not in remove_list, state_list)) + +states = list(state_updates.keys()) +# states_noTS = filter_out(['timestamp'], states) +# states_grid = [states,states_noTS,states_noTS] + +states_grid = [states] * system_substeps #[states,states,states] +policy_grid = [['a', 'b'], ['a', 'b'], ['a', 'b']] + + +PSUBS = genereate_psubs(policy_grid, states_grid, policies, state_updates) +pp.pprint(PSUBS) +# ToDo: Bug without specifying parameters +append_configs( + sim_configs=sim_config, + initial_state=state_dict, + seeds={}, + raw_exogenous_states={}, + env_processes={}, + partial_state_update_blocks=PSUBS, + # policy_ops=[lambda a, b: {**a, **b}] +) + +# pp.pprint(partial_state_update_blocks) + +# PSUB = { +# 'policies': policies, +# 'states': state_updates +# } +# partial_state_update_blocks = [PSUB] * substeps + + diff --git a/simulations/single_config_run.py b/simulations/single_config_run.py index 9bd4737..f2fb874 100644 --- a/simulations/single_config_run.py +++ b/simulations/single_config_run.py @@ -3,8 +3,7 @@ from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor # from simulations.validation import policy_aggregation -from simulations.validation import historical_state_access -# from simulations.validation import config1 +from simulations.validation import config1 # from simulations.validation import externalds # from simulations.validation import external_dataset from cadCAD import configs diff --git a/simulations/udo_test.py b/simulations/udo_test.py new file mode 100644 index 0000000..b31713b --- /dev/null +++ b/simulations/udo_test.py @@ -0,0 +1,45 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.regression_tests import udo +from cadCAD import configs + + +exec_mode = ExecutionMode() + +print("Simulation Execution: Single Configuration") +print() + + +first_config = configs # only contains config1 +single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) +run = Executor(exec_context=single_proc_ctx, configs=first_config) +# cols = configs[0].initial_state.keys() +cols = [ + 'increment', + 'state_udo_tracker', 'state_udo', 'state_udo_perception_tracker', + 'udo_policies', 'udo_policy_tracker', + 'timestamp' +] +raw_result, tensor_field = run.main() +result = pd.DataFrame(raw_result)[['run', 'substep', 'timestep'] + cols] +# result = pd.concat([result.drop(['c'], axis=1), result['c'].apply(pd.Series)], axis=1) + +# print(list(result['c'])) + +# print(tabulate(result['c'].apply(pd.Series), headers='keys', tablefmt='psql')) + +print() +print("Tensor Field: config1") +print(tabulate(tensor_field, headers='keys', tablefmt='psql')) +print("Output:") +print(tabulate(result, headers='keys', tablefmt='psql')) +print() +print(result.info(verbose=True)) + +# def f(df, col): +# for k in df[col].iloc[0].keys(): +# df[k] = None +# for index, row in df.iterrows(): +# # df.apply(lambda row:, axis=1) \ No newline at end of file diff --git a/simulations/validation/config1.py b/simulations/validation/config1.py index c822296..13602a6 100644 --- a/simulations/validation/config1.py +++ b/simulations/validation/config1.py @@ -7,7 +7,9 @@ from datetime import timedelta from cadCAD.configuration.utils.policyAggregation import get_base_value from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step, config_sim +from cadCAD.configuration.utils import env_proc_trigger, bound_norm_random, ep_time_step, config_sim + +from cadCAD.configuration.utils import timestep_trigger seeds = { 'z': np.random.RandomState(1), @@ -23,8 +25,6 @@ def p1m1(_g, step, sL, s): def p2m1(_g, step, sL, s): return {'param1': 1, 'param2': 4} -# [] - def p1m2(_g, step, sL, s): return {'param1': 'a', 'param2': 2} def p2m2(_g, step, sL, s): @@ -113,20 +113,20 @@ genesis_states = { } -raw_exogenous_states = { - "s3": es3p1, - "s4": es4p2, -# "timestep": es5p2 -} +# raw_exogenous_states = { +# "s3": es3p1, +# "s4": es4p2, +# # "timestep": es5p2 +# } env_processes = { "s3": env_a, - "s4": proc_trigger(1, env_b) + "s4": env_proc_trigger(1, env_b) } -partial_state_update_block = { +partial_state_update_blocks = { "m1": { "policies": { "b1": p1m1, @@ -134,7 +134,9 @@ partial_state_update_block = { }, "variables": { "s1": s1m1, - "s2": s2m1 + "s2": s2m1, + "s3": es3p1, + "s4": es4p2, } }, "m2": { @@ -144,7 +146,9 @@ partial_state_update_block = { }, "variables": { "s1": s1m2, - "s2": s2m2 + "s2": s2m2, + # "s3": timestep_trigger(3, 's3', es3p1), + # "s4": timestep_trigger(3, 's4', es4p2), } }, "m3": { @@ -154,7 +158,9 @@ partial_state_update_block = { }, "variables": { "s1": s1m3, - "s2": s2m3 + "s2": s2m3, + # "s3": timestep_trigger(3, 's3', es3p1), + # "s4": timestep_trigger(3, 's4', es4p2), } } } @@ -171,8 +177,8 @@ append_configs( sim_configs=sim_config, initial_state=genesis_states, seeds=seeds, - raw_exogenous_states=raw_exogenous_states, - env_processes=env_processes, - partial_state_update_blocks=partial_state_update_block, + raw_exogenous_states={}, #raw_exogenous_states, + env_processes={}, #env_processes, + partial_state_update_blocks=partial_state_update_blocks, policy_ops=[lambda a, b: a + b] ) \ No newline at end of file diff --git a/simulations/validation/config2.py b/simulations/validation/config2.py index a75b22a..c93efa3 100644 --- a/simulations/validation/config2.py +++ b/simulations/validation/config2.py @@ -3,7 +3,7 @@ import numpy as np from datetime import timedelta from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step, config_sim +from cadCAD.configuration.utils import env_proc_trigger, bound_norm_random, ep_time_step, config_sim seeds = { 'z': np.random.RandomState(1), @@ -108,8 +108,8 @@ raw_exogenous_states = { env_processes = { - "s3": proc_trigger(1, env_a), - "s4": proc_trigger(1, env_b) + "s3": env_proc_trigger(1, env_a), + "s4": env_proc_trigger(1, env_b) } diff --git a/simulations/validation/config4.py b/simulations/validation/config4.py index 8e00129..f59839a 100644 --- a/simulations/validation/config4.py +++ b/simulations/validation/config4.py @@ -3,7 +3,7 @@ import numpy as np from datetime import timedelta from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import proc_trigger, bound_norm_random, ep_time_step, config_sim +from cadCAD.configuration.utils import env_proc_trigger, bound_norm_random, ep_time_step, config_sim seeds = { @@ -113,7 +113,7 @@ raw_exogenous_states = { env_processes = { "s3": env_a, - "s4": proc_trigger('2018-10-01 15:16:25', env_b) + "s4": env_proc_trigger('2018-10-01 15:16:25', env_b) } diff --git a/simulations/validation/historical_state_access.py b/simulations/validation/historical_state_access.py index 36bc5de..05e293d 100644 --- a/simulations/validation/historical_state_access.py +++ b/simulations/validation/historical_state_access.py @@ -2,18 +2,18 @@ from cadCAD.configuration import append_configs from cadCAD.configuration.utils import config_sim # last_partial_state_update_block -def last_update_block(_g, step, sH, s, _input): +def last_update_block(_g, substep, sH, s, _input): return 'sh', sH[-1] # Policies per Mechanism -def p(_g, step, sH, s): +def p(_g, substep, sH, s): return {'last_update_block': sH[-1]} def add(y, x): - return lambda _g, step, sH, s, _input: (y, s[y] + x) + return lambda _g, substep, sH, s, _input: (y, s[y] + x) -def policies(_g, step, sH, s, _input): +def policies(_g, substep, sH, s, _input): y = 'policies' x = _input return (y, x) @@ -22,7 +22,7 @@ policies = {"p1": p, "p2": p} genesis_states = { 's': 0, - 'sh': {}, # {[], {}} + 'sh': [{}], # {[], {}} # 'policies': {}, } diff --git a/simulations/validation/new_sweep_config.py b/simulations/validation/new_sweep_config.py new file mode 100644 index 0000000..bd2847b --- /dev/null +++ b/simulations/validation/new_sweep_config.py @@ -0,0 +1,183 @@ +from decimal import Decimal +import numpy as np +from datetime import timedelta +import pprint + +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import env_proc_trigger, ep_time_step, config_sim + +from typing import Dict, List + +# from cadCAD.utils.sys_config import exo, exo_check + +pp = pprint.PrettyPrinter(indent=4) + +seeds = { + 'z': np.random.RandomState(1), + 'a': np.random.RandomState(2), + 'b': np.random.RandomState(3), + 'c': np.random.RandomState(3) +} + +# Optional +g: Dict[str, List[int]] = { + 'alpha': [1], + 'beta': [2, 5], + 'gamma': [3, 4], + 'omega': [7] +} + +# Policies per Mechanism +def p1m1(_g, step, sL, s): + return {'param1': 1} + +def p2m1(_g, step, sL, s): + return {'param2': 4} + +def p1m2(_g, step, sL, s): + return {'param1': 'a', 'param2': _g['beta']} + +def p2m2(_g, step, sL, s): + return {'param1': 'b', 'param2': 0} + +def p1m3(_g, step, sL, s): + return {'param1': np.array([10, 100])} + +def p2m3(_g, step, sL, s): + return {'param1': np.array([20, 200])} + +# Internal States per Mechanism +def s1m1(_g, step, sL, s, _input): + return 's1', 0 + +def s2m1(_g, step, sL, s, _input): + return 's2', _g['beta'] + +def s1m2(_g, step, sL, s, _input): + return 's1', _input['param2'] + +def s2m2(_g, step, sL, s, _input): + return 's2', _input['param2'] + +def s1m3(_g, step, sL, s, _input): + return 's1', 0 + +def s2m3(_g, step, sL, s, _input): + return 's2', 0 + + +# Exogenous States +proc_one_coef_A = 0.7 +proc_one_coef_B = 1.3 + + +def es3p1(_g, step, sL, s, _input): + return 's3', _g['gamma'] +# @curried +def es4p2(_g, step, sL, s, _input): + return 's4', _g['gamma'] + +ts_format = '%Y-%m-%d %H:%M:%S' +t_delta = timedelta(days=0, minutes=0, seconds=1) +def es5p2(_g, step, sL, s, _input): + y = 'timestep' + x = ep_time_step(s, dt_str=s['timestep'], fromat_str=ts_format, _timedelta=t_delta) + return (y, x) + + +# Environment States +# @curried +# def env_a(param, x): +# return x + param +def env_a(x): + return x +def env_b(x): + return 10 + + +# Genesis States +genesis_states = { + 's1': Decimal(0.0), + 's2': Decimal(0.0), + 's3': Decimal(1.0), + 's4': Decimal(1.0), +# 'timestep': '2018-10-01 15:16:24' +} + + +# remove `exo_update_per_ts` to update every ts +raw_exogenous_states = { + "s3": es3p1, + "s4": es4p2, +# "timestep": es5p2 +} + + +# ToDo: make env proc trigger field agnostic +# ToDo: input json into function renaming __name__ +triggered_env_b = env_proc_trigger(1, env_b) +env_processes = { + "s3": env_a, #sweep(beta, env_a), + "s4": triggered_env_b #rename('parameterized', triggered_env_b) #sweep(beta, triggered_env_b) +} +# parameterized_env_processes = parameterize_states(env_processes) +# +# pp.pprint(parameterized_env_processes) +# exit() + +# ToDo: The number of values entered in sweep should be the # of config objs created, +# not dependent on the # of times the sweep is applied +# sweep exo_state func and point to exo-state in every other funtion +# param sweep on genesis states + +partial_state_update_block = { + "m1": { + "policies": { + "b1": p1m1, + "b2": p2m1 + }, + "variables": { + "s1": s1m1, + "s2": s2m1 + } + }, + "m2": { + "policies": { + "b1": p1m2, + "b2": p2m2, + }, + "variables": { + "s1": s1m2, + "s2": s2m2 + } + }, + "m3": { + "policies": { + "b1": p1m3, + "b2": p2m3 + }, + "variables": { + "s1": s1m3, + "s2": s2m3 + } + } +} + +# config_sim Necessary +sim_config = config_sim( + { + "N": 2, + "T": range(5), + "M": g # Optional + } +) + +# New Convention +append_configs( + sim_configs=sim_config, + initial_state=genesis_states, + seeds=seeds, + raw_exogenous_states={}, #raw_exogenous_states, + env_processes={}, #env_processes, + partial_state_update_blocks=partial_state_update_block +) \ No newline at end of file diff --git a/simulations/validation/policy_aggregation.py b/simulations/validation/policy_aggregation.py index c7028de..7a65216 100644 --- a/simulations/validation/policy_aggregation.py +++ b/simulations/validation/policy_aggregation.py @@ -95,12 +95,4 @@ append_configs( env_processes=env_processes, partial_state_update_blocks=partial_state_update_block, policy_ops=[lambda a, b: a + b, lambda y: y + 10, lambda y: y + 30] -) - - -# def p1m3(_g, step, sL, s): -# return {'param1': 1, 'param2': 2, 'param3': 3} -# def p2m3(_g, step, sL, s): -# return {'param1': 1, 'param2': 2, 'param3': 3} -# -# xx = {'param1': [1,1], 'param2': [2,2], 'param3': [3,3]} \ No newline at end of file +) \ No newline at end of file diff --git a/simulations/validation/sweep_config.py b/simulations/validation/sweep_config.py index 40071b6..92de2ab 100644 --- a/simulations/validation/sweep_config.py +++ b/simulations/validation/sweep_config.py @@ -4,7 +4,7 @@ from datetime import timedelta import pprint from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import proc_trigger, ep_time_step, config_sim +from cadCAD.configuration.utils import env_proc_trigger, ep_time_step, config_sim from typing import Dict, List @@ -46,34 +46,22 @@ def p2m3(_g, step, sL, s): # Internal States per Mechanism def s1m1(_g, step, sL, s, _input): - y = 's1' - x = 0 - return (y, x) + return 's1', 0 def s2m1(_g, step, sL, s, _input): - y = 's2' - x = _g['beta'] - return (y, x) + return 's2', _g['beta'] def s1m2(_g, step, sL, s, _input): - y = 's1' - x = _input['param2'] - return (y, x) + return 's1', _input['param2'] def s2m2(_g, step, sL, s, _input): - y = 's2' - x = _input['param2'] - return (y, x) + return 's2', _input['param2'] def s1m3(_g, step, sL, s, _input): - y = 's1' - x = 0 - return (y, x) + return 's1', 0 def s2m3(_g, step, sL, s, _input): - y = 's2' - x = 0 - return (y, x) + return 's2', 0 # Exogenous States @@ -82,14 +70,10 @@ proc_one_coef_B = 1.3 def es3p1(_g, step, sL, s, _input): - y = 's3' - x = _g['gamma'] - return (y, x) + return 's3', _g['gamma'] # @curried def es4p2(_g, step, sL, s, _input): - y = 's4' - x = _g['gamma'] - return (y, x) + return 's4', _g['gamma'] ts_format = '%Y-%m-%d %H:%M:%S' t_delta = timedelta(days=0, minutes=0, seconds=1) @@ -129,7 +113,7 @@ raw_exogenous_states = { # ToDo: make env proc trigger field agnostic # ToDo: input json into function renaming __name__ -triggered_env_b = proc_trigger(1, env_b) +triggered_env_b = env_proc_trigger(1, env_b) env_processes = { "s3": env_a, #sweep(beta, env_a), "s4": triggered_env_b #rename('parameterized', triggered_env_b) #sweep(beta, triggered_env_b) diff --git a/simulations/validation/udo.py b/simulations/validation/udo.py index 4adb1a5..dc644e5 100644 --- a/simulations/validation/udo.py +++ b/simulations/validation/udo.py @@ -1,7 +1,8 @@ +from copy import deepcopy from datetime import timedelta from cadCAD.utils import SilentDF #, val_switch from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import ep_time_step, config_sim +from cadCAD.configuration.utils import time_step, ep_time_step, config_sim from cadCAD.configuration.utils.userDefinedObject import udoPipe, UDO import pandas as pd @@ -46,14 +47,25 @@ state_udo = UDO(udo=udoExample(0, DF), masked_members=['obj', 'perception']) policy_udoA = UDO(udo=udoExample(0, DF), masked_members=['obj', 'perception']) policy_udoB = UDO(udo=udoExample(0, DF), masked_members=['obj', 'perception']) +def udo_policyA(_g, step, sL, s): + s['udo_policies']['udo_A'].updateX() + return {'udo_A': udoPipe(s['udo_policies']['udo_A'])} + +def udo_policyB(_g, step, sL, s): + s['udo_policies']['udo_B'].updateX() + return {'udo_B': udoPipe(s['udo_policies']['udo_B'])} + + +policies = {"p1": udo_policyA, "p2": udo_policyB} + # ToDo: DataFrame Column order state_dict = { 'increment': 0, 'state_udo': state_udo, 'state_udo_tracker_a': 0, 'state_udo_tracker_b': 0, 'state_udo_perception_tracker': {"ds1": None, "ds2": None, "ds3": None, "timestep": None}, 'udo_policies': {'udo_A': policy_udoA, 'udo_B': policy_udoB}, - 'udo_policy_tracker_a': (None, None), 'udo_policy_tracker_b': (None, None), - 'timestamp': '2019-01-01 00:00:00', + 'udo_policy_tracker_a': (0, 0), 'udo_policy_tracker_b': (0, 0), + 'timestamp': '2019-01-01 00:00:00' } @curried @@ -64,24 +76,14 @@ def perceive(s, self): return self def view_udo_policy(_g, step, sL, s, _input): - y = 'udo_policies' - x = _input - return (y, x) - -def update_timestamp(y, timedelta, format): - return lambda _g, step, sL, s, _input: ( - y, - ep_time_step(s, dt_str=s[y], fromat_str=format, _timedelta=timedelta) - ) -time_model = update_timestamp('timestamp', timedelta(minutes=1), '%Y-%m-%d %H:%M:%S') - + return 'udo_policies', _input def state_udo_update(_g, step, sL, s, _input): y = 'state_udo' # s['hydra_state'].updateX().anon(perceive(s)) s['state_udo'].updateX().perceive(s) x = udoPipe(s['state_udo']) - return (y, x) + return y, x def increment(y, incr_by): return lambda _g, step, sL, s, _input: (y, s[y] + incr_by) @@ -105,17 +107,17 @@ def track_state_udo_perception(destination, source): return past_perception return lambda _g, step, sL, s, _input: (destination, id(s[source].perception)) -def udo_policyA(_g, step, sL, s): - s['udo_policies']['udo_A'].updateX() - return {'udo_A': udoPipe(s['udo_policies']['udo_A'])} -def udo_policyB(_g, step, sL, s): - s['udo_policies']['udo_B'].updateX() - return {'udo_B': udoPipe(s['udo_policies']['udo_B'])} +def time_model(y, substeps, time_delta, ts_format='%Y-%m-%d %H:%M:%S'): + def apply_incriment_condition(s): + if s['substep'] == 0 or s['substep'] == substeps: + return y, time_step(dt_str=s[y], dt_format=ts_format, _timedelta=time_delta) + else: + return y, s[y] + return lambda _g, step, sL, s, _input: apply_incriment_condition(s) -policies = {"p1": udo_policyA, "p2": udo_policyB} -states_with_ts = { +states = { 'increment': increment('increment', 1), 'state_udo_tracker_a': track('state_udo_tracker_a', 'state_udo'), 'state_udo': state_udo_update, @@ -123,39 +125,38 @@ states_with_ts = { 'state_udo_tracker_b': track('state_udo_tracker_b', 'state_udo'), 'udo_policy_tracker_a': track_udo_policy('udo_policy_tracker_a', 'udo_policies'), 'udo_policies': view_udo_policy, - 'udo_policy_tracker_b': track_udo_policy('udo_policy_tracker_b', 'udo_policies'), - 'timestamp': time_model, + 'udo_policy_tracker_b': track_udo_policy('udo_policy_tracker_b', 'udo_policies') +} + +substeps=3 +update_timestamp = time_model( + 'timestamp', + substeps=3, + time_delta=timedelta(days=0, minutes=0, seconds=1), + ts_format='%Y-%m-%d %H:%M:%S' +) +states['timestamp'] = update_timestamp + +PSUB = { + 'policies': policies, + 'states': states } -del states_with_ts['timestamp'] -states_without_ts = states_with_ts # needs M1&2 need behaviors -partial_state_update_blocks = { - 'PSUB1': { - 'policies': policies, - 'states': states_with_ts - }, - 'PSUB2': { - 'policies': policies, - 'states': states_without_ts - }, - 'PSUB3': { - 'policies': policies, - 'states': states_without_ts - } -} +partial_state_update_blocks = [PSUB] * substeps sim_config = config_sim({ "N": 2, "T": range(4) }) +# ToDo: Bug without specifying parameters append_configs( - sim_config, - state_dict, - # seeds=seeds, - # raw_exogenous_states=raw_exogenous_states, - # env_processes=env_processes, - partial_state_update_blocks, - policy_ops=[lambda a, b: {**a, **b}] + sim_configs=sim_config, + initial_state=state_dict, + seeds={}, + raw_exogenous_states={}, + env_processes={}, + partial_state_update_blocks=partial_state_update_blocks, + # policy_ops=[lambda a, b: {**a, **b}] ) From 01c594572428e89a245c247f096868fbb0f195e0 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 9 May 2019 16:16:07 -0400 Subject: [PATCH 11/19] deepcopy gate --- cadCAD/engine/simulation.py | 26 +- dist/cadCAD-0.2-py3-none-any.whl | Bin 10811 -> 14927 bytes simulations/regression_tests/udo.py | 3 +- simulations/validation/marbles.py | 166 ++ simulations/validation/marbles2.py | 205 +++ simulations/validation/marbles2_run.py | 25 + .../robot-marbles-agents-advanced-udo.ipynb | 1617 +++++++++++++++++ simulations/validation/udo.py | 1 + 8 files changed, 2040 insertions(+), 3 deletions(-) create mode 100644 simulations/validation/marbles.py create mode 100644 simulations/validation/marbles2.py create mode 100644 simulations/validation/marbles2_run.py create mode 100644 simulations/validation/robot-marbles-agents-advanced-udo.ipynb diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index c80b8da..77c3bf5 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -103,8 +103,20 @@ class Executor: run: int ) -> List[Dict[str, Any]]: - # last_in_obj: Dict[str, Any] = deepcopy(sL[-1]) - last_in_obj: Dict[str, Any] = sL[-1] + # def dp_psu(d): + # for k, v in deepcopy(d).items(): + # yield k, deepcopy(v) + # + # def dp_psub(l): + # for d in l: + # yield dict(dp_psu(d)) + + # last_in_obj: Dict[str, Any] = dict(dp_psu(sL[-1])) + + last_in_obj: Dict[str, Any] = deepcopy(sL[-1]) + # print(last_in_obj) + # last_in_obj: Dict[str, Any] = sL[-1] + # last_in_obj: Dict[str, Any] = sH[-1] # print(last_in_obj) # print(sH[-1]) @@ -155,6 +167,16 @@ class Executor: # ToDo: flatten first # states_list_copy: List[Dict[str, Any]] = simulation_list[-1] states_list_copy: List[Dict[str, Any]] = deepcopy(simulation_list[-1]) + + # def dp_psu(d): + # for k, v in deepcopy(d).items(): + # yield k, deepcopy(v) + # + # def dp_psub(l): + # for d in l: + # yield dict(dp_psu(d)) + + # states_list_copy: List[Dict[str, Any]] = list(dp_psub(simulation_list[-1])) # print(states_list_copy) # ToDo: Causes Substep repeats in sL: diff --git a/dist/cadCAD-0.2-py3-none-any.whl b/dist/cadCAD-0.2-py3-none-any.whl index eb56bb3e2e006e18ecc452735178216f7508f3ac..ccb0606b0f0cdd7bec143ee2f6c9aa93fdd7a47a 100644 GIT binary patch delta 12758 zcmZvC1yG#J)-CSt?(QC3LvVL@cXuYZ>)`J05ZobHaCf%=!CfEczW=;)@4J6jO?A~b ztGm9Q*|mDFy;q0olq;g53^)V^2nYxa$aS7t0-`i3UoKEShLa8d01^ab5E}%91lWs+ zlkgo8pcS{qh2pfW%qW#tSh{1A;l9U1Qksl* zW|#}odLLypeTQI+K$&tC?@ocxCN_LQkKX$>J^2mGuRm)qaE|eLjH0Rsk+4*$c%(9) z_NDp2c^4%Kfih-DEet=9GEj(+6~Wv>qdD*#Ko?-cYY^h|DsJ~Rv~F;JZg^_WYK04v zPx`^~%kB84k3#NsB{0CB+BG{Gq({En@ADL~jm$kyGONcSS5tK$yTaA054pq-q-yQdX`U=J##6AQqs$+))xnK;K_ z%2jYIBvhQ$;(FXof*J&D{4-=Usi`3$nuSs#R9fIVa#9=I!o}aOb2aPL=(BXcqwvtD zdw1u!4A)XtdUF395Ud$EEbP;%jzCnr?KvI5v`@hY#X?rww3hRQ)x(|y3uHh5_bT<5 z>fLj;xTfR!1xtW!T*VuPzNZ5Qz(!c@56&ca=hCi?o}W;9Rv>fb;T`SIt}K(yzV{H# z%(+vr$ULRVN>H6LhhBkK!+${_yY(IZcHe=~<-@Zy2KP+(=pynW!tn%t*qq3YHi7N& zL}AzY3>uYn?4^hV=^5;Pd5ihZYso4)wZ1o!Q**=As(_kSxR66NS!Y@a$iQ4_GKsfe zz&qixY0o#Xsr5C7gf8AJI9aBzp^GngBuWW;VW@8b6Kr);tQoT9*%YuVnbei9m`9q{ z5ylN8DhhRjjF&wv*I<`3Ur<}>+aJfz5^B&Nmes>nc%b>MZMvI!J0q)1h~$`N#z#&O zDv2Dw1029GL95OJUmfHGobS%a2q$lrg|IT$OWmZ7o~ma};3YGuSc9&XN~A?h?Zc2; z`;tekSj2`2x*8qNJslH8yvgO7KP_Y{Hcsp_~iLjocj;5p088mVB%&)9nXVX;zO)v> zv=-?+iM}IuYnBX8WqQDWWwJR2?Xt_8pSBNetM_M}P|!Y|a%UN~`BH1|x3Pb17?_u% zO5k5N+2#WcsXV0UoW)q^m-TpPPEW~*!k_HduR%@)uhwRRL}cTHqZQEq3WeFKY9JII zY^>vPIjhQ21IU=hzpZ{-FFB684=8x&yK!YfFc0MKlj2~Xt{i`yDU7bAT$Wb!sm@Z1p>^cq5)*%*f^|lU3LH< zXUJGW==2*wUq~1Rnz6*883v}^iJ=BiEEAktk0BG~&!C>1w8_cNnX4I)J-nKUq zimh$>(^0=I29C7bB*UI2ZGY&hhE7OF(ZFdnRt)=Gq#&s`Z3wcU6b$?bN?I#nvD5iY zR0XkxWqg=F0fy?WSr1T?h7(jgHyQCi#3r=io`ZOI_vP}D>xNmk7>i=S0X&?(h|h2p zuiIj>H`rn6kkfZ<7pUa5Y&^s0(&c-@W^m4q)RJAKu_datacxKVuH}o}hBqEyjwgqV z%4p%{D_@G4q}jUK-xGS)Ri&{Oj3Vkd?38Zv7zbIrtuk3G1_SQ>G82%UC74F3jhZWF zAgb7Wt{v&|*ox5;GUk>|*wUk(^`&eV_7ochB-lg)ISE74~nWPhs(9R}Y zD#X>ae+KYgsOnH4!G4g~q6Un(MRPZLcAeX(uM4pJ(4EP) zrF36ydCUO`AtSe1h{p7O0!6}+Sg*57Y#ibo4_sJ0rbFiB7_gezLNT|NeK(7!uGES@-_M)%Fzt!R4{pnCoqVyPm> z@qV19?2;LMW7}=IUchW-Z3fz6&xUKynR4Ws0z2-1ySc)z>1YUMW*f15`3YtV ze+Fu@xXh?Vy~I8EET`9EZ>0jPKMZrAZFR z-1~~BazB6Y-PS6u9tyC>dX3IUNah@BxN7rj=G@xKugkd<4Laf)L88l0j~Y*Y3O0;v ze})=PDT%i)bb(>gH_mA}UC;lq(-o?X!byi&au#JZKA?1LJz|_UU1^TTQP_hwBu638 zK^mm~v?68L9jaovot@$+QRE&Hu`3S1KE{$WcUsJd zCM5rLWkq02$o1SA$uk@(6{eLZFRFG({!TmP)@w1q{wy#Y-m)z9rT#5W)ItJKvdweq zpm6USULz!|FsbxbMF>Sz@#oJn3cT#3MF%Q{k`iD zoPMa7`UW^Z%S+~TdgsJvb*+m7ro2Xo*|SpqaF#Bi%ln?k1)4aeaiy`w?KGL1!ls6X zTi#(z&s2XulQX8(3VMe)FE%C2d}aIM=I(i8`B)WnVbfG9WBw+-J5A!@6R1CBy~HP? z)m(FlSdo&Yu^T{Zq_<=6t*MGuA=HD~fam7Y7d_9ZMz%Y1W~LE$I>i4OaB_%AgzrCS z$y0$9Cn5f@kXy^vpL(fPJlxBS^k|0W3ErDIb3dm9-|a-XJ+e!wSR2~joWTu>1pRAt zZ|-E8;e4(-h#~*2P?Og?j0fixdM-?&DH>uH^n@3wcW5eiQji{Fwc3$ck4B&G>BYB@ z;$oPphr1ScvsOsL1!HpBA~tzH0Kx(9jedz%MCl#Dr5QV>^pq)x52@kdfhalxdRQZ? z1Eda*pO9U^S0!tg-)j`%47w2Nei8Z2)sWoVRNPw>nj6$9p0V`f-)`HCOdWaj-D+fV zFc1(hC=d|VPx(azw1cMsjO1&2*fDn@29e6RihfOjP;xp!dbt4FnmO^7Rx^bPK0XpX z9WSmS6}f>c9#tXdq3Eq*i|5xtVugZr88KE{S!u}SF8F~u`2{U{TL$xI1#1vE@lu0v z7SSTuaFfE&&Op{_2V8cTgi*2FQf!|y{XFB|>*N;_+zP@TggH|LD9f-ZI}w>dG(qi} zh?x(Y41punZHSJFKvZFlD%LQEg7}q@zgki3jjd#0 zVYE)Y);pgYLrJoxbQS0h`jfP~B{uI;Ht38>v2i)v$1WFzBL4^<_k67Ss!I}8Wu1PD z<^R$4U7qmiDx)0-u%?*I{G!^(N(M?JJ&e#Cg5u!0ttq%1Mbx(pf>;1Lq*$}LW|d(4 z!!yIj=(33bGbCHi2YlP5ak4YRm7-_JICn!U8S$H(sX!`8^qBm%Gv_%!-i#64+0b_i zSKdl=$>N#~4(Obk(SZ@R(Ss)?;)~>^TKH&jf(vKnBW{dGf6l0dV$h#{ z?3bflBoZ&?vh06+NHq15ugMLNePtC7F8m--xWfJ?P2&uRl85MKR%QJR98VY!5a!Ro zAqI|9qXRb7)*{zfFnpIZ2rk7;@nruD3+LD6)-!yoFBTt6iDq!tC?2&2~YpSI_wpAo^%iz zp9t@Hz}3@`a^z5T!u^J80JEJ4W=gMSlTOFt9ST^?hV14cs^tMV}`Ybn9q0#sZAna?#< zci3ZIXv>UFlIJ4n2m9Ti9(pG+|K%fesEe+^d7&iX?^&$!>gY_qT3uRwACu+c@>X>7mBEPWgW3zjfw z?_=A0-Qk@EZD)r>2H3y)QQ^Qvf-0^ST(X>INSlk*Fnk&C`<9gId8S1tB$oDPTPK{k z-++e*#H{y1*f7Iz{whrSF2OdO+t@s<#{qzi;wmnzfI@^FQzvgG0QfS9wc0yDC=h)a z_VqX_;SMI&Gj2O*T!Ng^qczSjtOL*>k8K@)V-+FzYgQ;O0EY~@6*jC7ZKRU>Rxj48I3UI4cH&b88 zSV%`799)$YE);TBUZQYF=GaE_a&47+m$Ag2OWYZ%evunUVyKuHu-Mk_sF$@z>V*J9 zq;D)pQD~(j%wGg=8who+a?iq>K`!#pkgbF|n31QhQ%VXGnL#J+t^RuG81eHrGS=BF z9+%goPR$L!x?(D=5v^5O1>pNZ4f<%H6@LT2MW-;JfqcB>(aG|G8SM0JJB`-ZFdQq` zsJyv&QZO-bT1sM!yrb)j#psiBCNnf0kHCUq?U9a4g@gfwGMrRJ5&}1he=_dUbQ$+m zzY=4ySye5ebuz{NMr<(g5SPT>^jBiBKy)#>SvY~98oXOWz4y`%SOC}R)QZDtBr;Qe zWUS^vzIR6=;>$+QGVxMGL$_Eyi?OwJ1JMsQU#IRCmR9_ZDP>iXEdu_g(xNk+jxn!M z8$@T1aH`s4PwFo>AzVt|OjRs9&m3(@O9kYy`pLQJSc@CcjYIowaznZt)~XI85Bq}& z$z-j6^jo=i4jOI zvG3xM??n7g%%4_jGXHyFsgG0+c>>A_{iIZr=#K`?CQq11W+owsV9u)eK_9soME7Q@l_(=eM zYtKL7O6`^)PZDgSo&^B{qKgUwf(rtKr@;sO(U5ak=R)Z^)j&D#L#u*u>Oq44EMUq_ zi|ye}6kw~N;6$vVeA1Rwo6W~F60H&$ZS@XOq&^-=Ly9juLAO^{J}<4jq|jXwF{;3w zsP!!reeq?zk`;82-AyXAd)!T)J~wtWh;AGDyrep#Tz^4c%iW zB7nsM71nEoP4`%sJ5NZde|0MY7Zw3lNLe0EJm9Byu@bl$5Y)N)TP!mAmA=d192K@2?j$m*U#(C)ev|UHxq; zR+=y0UFq>D+IO(Qp7bmO)G*NN%v;h7_6PS7^X@WuoVFroJSzwLCi&U`+B-n=Ay!H# zHqB`2a9dY;Jnnt4D>F}5!<}P|@}3zS?N?eF|FSyKaJB;kG&T0ng<7&L%V)LjoQ!!J z#%lu8IfaC%s7E3y4u*+ZTNDVo$<0dyY$`n}T z%^*S@JY%8ETv_q4;3lFcuM6PHo@!E9BJ0Si0+h5#VE~lg>}XmaDH4g7Sui z-+MNDBmXK~@@Cg-5h>a^Y8algOEhP_SmiXXXkh>e50BDA8^$7&>4;5T`KmH|P1anp4=sJMKt31f9VDTT2OQ;>hp<0QqM8SZ zv2@zr)o~$thD;o5x;FLeS+Ov&^M}!0%BBRc-!e-IK&n zZp5t@h`6lP*V6%=_9%;w7the6A&peUsm9M!dbN zy5WIfk(>EzD41{xZ>jqXv}{J6#V+Q^*2RTqyE9xTtE~cl71*+Q^;J?=1y0bcG2N)w zOZ0OLCf4iT3ObRB@RNSoo^|^U|FGCpX1V-N9zAT?&rGh30+^0L3Ruv#i~3}+_wF81 z$LU6Ni%~yLU9k)dFgC{PEiA=wUj5jS81<|1>aA#=^rjZY zwpwHzy4ng`s{jEKfHKqLZ@M(Q6~wU_Wka4Q57x!F9g!XUG&RH0@a1FA8*2|X>wl!e zmdL47v+uOy^>GDv;yT()*xe`u@O zjp96Cp=yv{SapkDRyqYm2D%1gi<9Ms6A#z{(>wAVq}&*u0RacISvu9dOnJCr?faf0 zS`QfR)O*ImEBFW97+uf$MDzx@g4oAcTgk{prJ^bqG&Vk9+vX0w1+8Rtq}?dVC@Woc zXmfmdTC{-zhase78!XPABV0Vs6dYnbi8bdvI392(Qe{Oy>|;7KK8U2X=i}Mi#qZ1pQJ{`uea)4i6zjLxgABWVW58?n zdOsPvj+m`tz_#0{aDb0oS$0mbtTY=Ua{Lh>l&;J>A;3v{Nd89=9$E_NAin8frlnm{r>; zJEhB%Cu}iZ3XEC?+*c&cf?q@ta(#RmaIkjGe0$Bvn5k?O?wQ$LGYipVeKR2gASM&y zh?aVz0hF}Cv4t{pfN%O?J(3f`+r$ss3)#1mU6+$ZV;W-g>edHe2AGif=^qw>XVZh) z+xPx0OktoNZps5c&==z!e-b9`B6Ojoc2Y1}9vR>52MO^X=5@)3vILTec-tN5N-~o$ z{35GC4dxBr3s*vEzmBJ>sQuy3N};ynTg_}P37}*&11c}e>X^%M3x5AI9$U*4ZkJiP zF;0LzL}D03tC&Ggy*HI`&uO}JYbOVUt6PlAg9ui5M?-44erLG&WV7Q(4;4FP)5g04+IQ(NX%|N0ynq{mDFMKCKD(C4$Q~ z1H$z;yWu@7De0-#y$;$2`Nr+V3xVGtr&S(#jX1tRg)QdKsI6q(A-X;UdzQfFjDTXi zz%{%pB1_22dmkF-crvxEg$B0zh!;=|55V;{)n!c^;+oVYF2T#*179KA+Ds4M0w6Xu zXQbV)O6&Z)@sM2h1y>|SNhUtgpp52ap2EEMPH&};KNv~T1}0UEXa73$OCy#p+<+pm zB4;rIeZp!vkppX74F-D0Es4a@7)ln)`=)I)$?Hgux72JHPc{nNDBAT!G!RKfQ)m`W z`y7v5zFiq6vW%IJP8`oJ_Cjgt7a-%jE+VK#3`8I`oM05QMnh1Gulg~7&?cBLrIN%W z_<{VY-4tmmv44J)R;bhpg_rm$AJBVLLAoJus?UEm#*g&Aajz_#5+3+VuWTg0bNCo- z_bXc7MSL+PQ%BUDWJb?dt<=i=DwnX2l8PZ^jQzElLn}HqTPpC(%fY>+|(g z>}fg<(^~jK->KH@g%PcZXs0IyH90~t6!}_03BIB9M7p8j$_jUhXU1rPev;7=kHUD# zc^!e=wu?8di1*Ne(ATs4DS&wk=j+M*w=`?Jpj4M`1B_Rgwe&xVN!sfnC*i33W6W~h6wr?Z6fQ`pYsREbST*mrPDAEE z1$t!oqoeRA*Olz~&~2}$mVSL0xDECk!8}h0_`7>WXHPLErt}m+d}G@po{@2- zWWQ1>$|+fe^b(4q9R!>|qn4ng`ANGLHmG(tk&KV?aq{QCwL(iX85kcccwyoAmOMoi z!k7_;os-#ZmIs=@2p+)>8i1PnHM9yQ1onG#2@%jg{}hYjL)5TvPeRh42(XIWIU-H; z&@nv@F(L5#T^P=+{&1Xe&zz@O#QD(jR6Fh8Yxe=EW{ng!8U@&f4(ET0X}>)UTfJkX zwSVy|jJQZdsmlRZpMt39K~kQAECX&sY?=$QWyeYPm&~OipmLtuz_&c+AX&Y3L?5FU z_RDT)Af{~ ztS3&g_>{B#9{Pn&`k^78So3pfve#kmNMZEZLFRj%m13@m_#_pXoJDqC7d-3I_xId? z5|6*?k6f#1jg6onAY`8s3hUDwVq#<}Cenz6+XqUF-wXH$tCgK955Ger=<#V4VgHm` zI3U0+8azO?hRk15tNT==?!Hg80c)3{i=j?Dffp*WSvY|ESOf(^yI?W6Em2tcbOYk) zf)I%~(S500?<=)5`^`kqjdEc-qE%=!n;tbMYG$`eD2+ZcrUIS}$BTZ`sc;)svR1rM z2v@AOoAUKocuh<{r^7OdTufGvo0v_j^EjA1J3yf8L^wg}^^{zN@pv}E{mb#l6xxLIve2jrWv?>OA^y z4$317c^h@NNQ;=aEb-7riu&oQ3oo72MUj(E+b8mFs5i&)&~Ee4IZ0`%)j!aVdjyOd zYyhnj38M<1cF6B894Z`NEoZC;RwL*B7`qBVg0-oEQ)lyT!-7UIfZ7%CLfK_{lchdm z>~XFAl>FFS^gXI|#xtIQb0IK($$)Jjb4Y}B_*vwe_;44gOQ?sMD4JA(G%M`#3s7dPkL*eq$JtyhC~{7GH9t9@~#6r8z+P)r^$^! z!)x`MF%3H@w#Q^}x4C3xx-KgA%SeDi{Z)4u#z;{!En=7BdHd~sMQHJa#9sy=o8!@S zuOPxpxRS^Ou8bq#TeaIhx4-H-Kt+4~w%(DRX#-U(VjyHkYJpTsOvS(OaJv0vM6Z^^ zB@v{;2c5M8)Sc;7uIK2sr%p?%ZodOGRgt|JwhoxSchR`CI>iB{`l z-Z}ebFv@RRO8rX-&$~1W%Te9_E|f+Cg_<3QeWWyF7LX_%bN!&cr~+SS)hOI71zRu2 z!IgjMVhB<-#?Q0C7UppxjMWE2hR-=OB!}olJa)oqc9a5lH@t|N>@DOnM}|dJ^bF|g zyT3FqBacZR%0$S8=_WSUiCqF>)sX8*7VyF_*mvb6jqZip{?v3(J}s+PEqVH=8{vqQ z!C8|gH4Tng4x7QU#TP{7+5GUX$h31|R*Sw7jt$z+dhO#mkMKTr(qrFP28CFnxl4j4 zA1i&akLPUL%k!MG^Rl~3^FccQPDsErkdn3n9@B5p(#vJ38D%Jz=}HV}3O<#F6@JU4 zB~PQsjYgca(4859UQt8N;D@8FKE%8KroTcqGGftdGT7o^$6eXjD6Z-i^zCi$EFJN9 zz%!(TJ6*H;uH61u=E0h6LU1IkjfJd z%>;A5r8x|Z8G}lZKDh;eWy zyG8P2i4lxtg6d3i?RWDh7o(dCu;17taLU~Y%i6-x@eSnC%b`Yg#E|OyZ&%x2gJ0zm z+6UCQI}yV?#Ca!o(?jv8?qpOM!Zmh~9R_x+FiW#MAIPZnjbA(f@o{#@BXi{iX(01D zmITDfCm3Bg%={sP+lu=aXT)PZVDOyszBGKj_*|DS`OLGeOSxm{ElWb|Dg-y>x)D9f zQ==(`#Cb;tGLK>2_Cv3RoC>BBQB9rWVIML6m#I%d*A{20{v|w^+=e`_gY4kja=dDO z%L8>GgfAQ)9npq>(SNIkd03ar2mFNeq4=coSY!|oyua!EE3=!cm8}c2iCl3}41G-YB(>5?Gtj8-*wL^O(~NRwLZmK(la0WHbL7D!sS1Wa za8!h&i0(vyQPMv;BDUxQ-Jf;TYYvf(v~0Q5_skWlVU(v9{qS{ zJOeQ=9Dv5fd-SY@y%9c2Pv>gjShjmeU-X-uD1G_8u%TPnp_8x{WmH;Hbs7BA&|or) zpvjrOA51WbEM;dTAr_7L2Geb@JIs(S3f}`3txbGKWan|aHNvm7XxKqg-6?cWFPp*m zCQ*IOZ=|6ZAy^=Rz=C z3*GG@@-)~3M4aA@*@(KPxu;O)fpi4f0npN0lh>TVEaC#kV00oVr>foFvXVxN3@R5e(~|HFORs+2&*+gGhm70 zVx#Xo{w-8A<~VtW-~9R6Sj9iW*L)hvwp{9$2S%96jV#xhYv{ z9N-*5c$pOb7z@4LrZj*8iruJRd7N!4j-w}n(QJE@9MZXD`7!MJp{V7eFQbcMlut>{ zakR`wujx_0OG)M4X2~h$dfl+y0vVKHmB&qflXNI?HU>`pLhRp8%KS;ed&jtQ)0ZY- z(+r!M$y&P+lX9$ABsDK{Rcucq9XhZ#cJ4Z{D^&hlKh(@~qC5exK;cqiK|5r=8F3$< zxlJUNJz2Tug)Z@Sdm@i7y}@_sY(!q^VH4lvm=_EaTQS2U{~&10Jm{-+}k8#=dr3FM?C3%HDFvR&ow0fca__bR*(U zfUnlm;eyB$@jDG*;o7>MF^P~rR6u4E7s(pgKDg8It9}~)xPr>9GlD__Ahy6ocCg=H z$03!^685w-kN&HYLq7Z35u{q$+O_Ly1?*&=w|Rt$nqE)M((4sG^mdlM1%jbo@g%yg z;Up|6@7AmO4VSpAGB%I#*!i27@>(%zk6`M z#wSbe?Y>I*ie7(%rx07&-B2PY;?J;rd2MhbRsZrZODyyJ5?<>Dq)L-Yfn#MMS{1s6 z*VZZy>D6{mZ?q%xwweZjd6sL0%PZFXnm_u`Zua5!ogbW8rOa}$RWO@wUj?;CnA7%XVP`IJ82|KH=|sHZGq&IHpRVn@Xh$4my-98h`ZA?)*dF$r=gtE z$}*fVRJD^Xo7HEdHG`u29hJ3l#$igJdZ)yYMjgb!_P7wBvZhLto?LL;u-o-MP5lt| zl4*rbMbS%ORKrz)0mj^+eX8MN6kWWQ@=blX8%L3V{{6?)v_*Ex0gOdCxKvbwsazu6VA2ttLFau&}ALq~o*y`%W3A}{2wNqYHEh_>@=F+=*LgI(k# zQG1OQWa%^9iJ@Zi6AfjZ`a~o?eO$kpb=U4(jUcxuyQR5bOY=D2xDe#=SFmCfVqP?k znk$lv>E-K_qWxs+zc}#s0&L5z{07Z*ljBgY3}mzdykg-%o4JF8T^YLt-*Ie`NwvMd zSU|lJpB)kT@81hod^rl|e{SwZc0#B-VI@gH^7zxN+HTZ_Z%J!A!&-0UF9E8x>z}}J z)8AE8M$OUhb>mm+6ItRpR;oRiV4l!b6w3Fc`9n62?3igPs$wf847Q&wwJ?0?>uxnI z)@9TY;ACaq?GzZ??4q@$9)b?(ruOT6Xq8wLi{VR9_qq0tH@5b!b-!ZZ_rx9?AO~Yr z18b%JNVac(0W*QTbCBQKwa>QPuZ8J}BV#~&T<{TZ2y4x*8&)XCaXB-To7;jDXXNue zvZ4$q7zX(N^V!Q!d|Y@CeqO}{ARs_6DpsWbG~EM5sDw%W$9DN|uFFsS|BRvlN2pH^ zCI`uXxkdg)e9lDy*NDJAM8Z$=2o{ir8iC}$90LCVHbwaa6MY&6h)MpdC|L(*6vrADxG3IAij+*2@%iN!=@DJwm{XOx3*%*XC2O5Nb9rXW~rGI0K z0^Z1gnKTq6|E)*=hWzzw>{EQIQ!2~w*0Bcm6$! z0{$=o9ci(F{InP(e*qg$=i_ugzy0g;R|@v`C<^Fc{RGndizYK^@xc_>{h5;6!fNW>?l)^YxY_&-x!p{M`= delta 8561 zcmZu$1yCK!wmta4-TmP1?(Xgo+}(rc1PShk;1Jy14iF?bgy0?=f_oAi!souK_wT#^ z{jQqond-fJrmJ_aS$pj%pU=AJnksPccmMzZ2ym#hDCIpmSjBypAFJoYpkZ02WS+Ch08X5h->^l}0F^_Em!VFPq;gDi-=A9y zr7@7)L(0CUB_&9RC-;7T*W-us7>Qnp&RcDi1pjsA1?nLO^QM}nxC!mHU1>T5y%kOe zC7Brc%kt7JTz2H=hds|Uc3YjRZj8nBkdg8Pj8l9A@Eadh1a9?4gOc4QWWHssou1MX z>vmfdzlZFdrjKD6_2xH0LpN{V@yZ3PSkfCl_wx;O*4Jkj)#%myczY}mZN|w*GFP%q z{sLo$z2FIIIFC=7)a%Y%%v)z2SC(p#H-C2Z>UfO$XxjJ{OEgUnXpIp_bdVi`Qga!| z+3@bM11xEG){>Do6R+jEY4|JNgifW5ubF4j?h=q5v^Z)e0;kTgC$WKSL=owW%8*`b z=FUp~6FAbv*oNu@2K!Sx)kisevko&<=PGC=)MUhyMfB?Px0{M^QO$Z8CYltfphd_#EstJePNa%nQf~n9EhD{h`-ib zWc(QhX^B4c`N$|JTy=6$We-R0{}D^_4Gj$s7kys3CX0rUsUfcF;rOt2)yg5&QdekY znT>cPDsB6@sJ?-AfGDDWcZwOp$ob&o<+6gpoXEkPb8y&zDq^wWGTq7_QVw~cPa3qk zuwZ%kV#7NeBR3(geI^{u<5HTS^GNGO4%#VQ7LEX@4Q*C}QL;e!jtj{~R#?vV3*+x0 zv})kbE9S?%rn24jla*CxMz7qu&GS2|V5l^v#DT$JqF5-Ne$9zq_@|gmX4>+VvEVob zmI1^mSxbm{g~KyOm=#;sGYq&JJ$HuZ5$xYKau$Y;VUMPkuS!57^d+zwqRAaFcVpT$ zV%}s4*)Ca#un(ITQLejyO5ejo<_xrD@`sYeF$KFA%Zj=1srMU&X(5LcL!mPt64iO( zdoL&Zoqc{ORsvaew|Hv#Ne$QBGA0!N{z z+{@c?#j0jr#6O9;QR?>crs`@?X?ufbWW~>ib98B_N&>(rNTIwecR8-wlyO|CYLcp{fdV7L3O$jujI4M9mOT!12YD$L$|!2q?P*Hu%pk({AOq|qh&ef z{%x!UPgRvd=eR>GI*ErrQ+$5pfI)oYZ=-7f&!hX-uYtb0bNSG%Uv9Bv~cJ||UiYd9@YQ^#g87o zMD-2|mfqYeep4v1$0M_jiy83t+#q~hf->)lB?pdggP}6$R@Era#%;hV|I>?=YiBu) zH}aEZ6zZM`C!ofenA}}0D4P`<@-|L|aM8$MABnX+LwtXtHS!wBaMm8MTx70sy9P@x z@CoYC(A6EgWBxbfV!vW-{y9*I_7!WTumAx1SFELgR3f5*4L03Z`LW*JnoAXMsusqk zyL_RD?^vELI2^c{y|(2<3ngHRik3yD-CHKV>AgrSvH6;w*CK^u^kdzxSA1ip)Xk|s z3txXRY_!w0g?vLN?4_fEHRq$cCSH@B9>?vW`Lo$f%Lt5QG{NSxdSz3Sj$E&ikFiRh z{dTKuERW0<7`oTIBJxvjOL$JI!{TX|GRbJBLi=2JqQ*sc(a0z(K2ARz$?b@zKv(*f zA5CZWt2@X&t1VTpqY+DIqm?L|m|CQ!&(mnREA1n`@Q3I$TkWhh3KK5x9=@cB)ZkPk zFNGf9s-rEi-8t#Uo)ROsZM>q4lViND2nhe4XoQOe%xs4yTccVmp`>-i0lRKE34HR< zN)^q(Qq0$(shSOd{~l1C1G`n9C6nx65ZEmw^Eld-0o701(?O=1hp%ws?$(0Hb(ScI*AR7d(4rsRaw&Db>U= zHL;B*z7}m27{Vr2s?5`h+k)d{sFGSyu+bj~V?=Wi)0nO7FHTF!iE^4CcZ|UPlN%Bn z2d9j_`W+kZ=6$E(cR|5G)iG*AG%x&WXf8G**f6o2imq%`T3QzM)1QdmGyvi1Bzuej zM#XBGj;KLGop*Gs0N;XBMru`n(imh-3|+o~q~T*BV>RQ@#4T0&({FF(wbi_!#|3-M zboB!PSx~Z4@ckB6hE@ddp$f?}tt|8w3nuc-GXQpn=ZS-(Ln zFmC%{=f0Di)zMx6LZB^_o!OfS+Y4-%3MXjGb<*Md8Ea!YjLBZ1xl^MsuL9#PhB5(w z=+*qNC+ zi4R8um^^tPpL?H1uq9tckQ{7)Bv;_Tzhj$;ms>Uo&%=`l@T1-*c#m{^ILqspatA)x zJnQN=n*koN36hp#*UWWKxn~%P8$*8t&UAwkKN{m{bOeMJ65Az}?^ZAkt(k=q3mPj#zld-W z-mT5{bdJa2f9tE<>PJAH3nUN=Q0Ri|IahaHi+Q?X#E$G_w9KtoOKRRK275NU;mUVo zs$QnR2&6tFaPdwyo!E##bJTFbbrK?i3w6s%1>p(`$UH(gib{hziWm5q;sd*(F%C3% zUEU-S9kjEC%}eYW{pz>c$7$}O^4qB@8u%TBoO!L+4!+HAJ2vigV#2$-LLIml+oU}M z#|3!{sl;cZLC0uZE_PhM!Uyk5em?=be!~Q$-jxa@`!Jq;`Qq$)j?!EQ=61@4vkzQi z`Ynvy69?Ft)<;j;C2QF^bgNhcg`bj=*ij-aSRKm8nVe2c<`7KwHLj>YtB{?YIDCtp zU_#mhhl;*`j;rI`SKHqhy~6FCQyQgY==yB&Lt;evxSwPuzVm$lQ}-^q+ii z=Q$6KZj+FFdB*%3P>EjwRj8au4Fn4SJiLM`*S`Rj1%nZMS!n2I%F&G;POai2Et?Ln z<++dkcmZ*-^%RC?u}6yE-BH{hPOegv_|j-z)L<51nSLjfD{O!xh=hF}lsBH6>B*MJ z8G+q-fX}<0MhOr_try(+tq$*`q(cPtkjK(4LbYgx-M%fSP_rOX{4yXDyW%|@5EAe% z0G=Znd$|x?l4w)1Bl;ESfIc`9`7OUZUV*HzuJ8vHEPt#GBXmhdwsrZ25mO89_KkXn z*AoF^p$5>#BE2Q&{*=*TN{=9uz}NY{?)DWjm0NK-vo@r}%I_Yru5p+f-ykRnJb7M9 z&oM{f{`p!gB!n+vFbb+RW-uM7-!z!+pfP_}!afY<*WEjzUT}$31jNpAg`ZIkl2Qnz zV`5lIa`H>YIlSje%YnW%E;X#p-!~%?tyhqU0I$JR^7&(_w1-6OzPQwk%T3ajf^EINalDy7A& z%$(WLSPmB=0tpU0+;ZI2g`BIa1l8$86w9=xRvYSK&;rnDc>_k zo`}@u?6F1r%N-_Cy4-psir8Tx(9 z4K)>(vyY0eLGMnHL%?#FsFCQqZow-CgsZk}~88n-<{-C1bD_0~Tkp$;QSR z-9l9*Ojv|iV#7RP?9W#dg*VJ9vW4zzvSBY3VP=)iL>I*5ojo?nbOqBNSDYBfiME|| z*jqQ#d;b{JmVkq_--(t6j(n12`^`{n19t6p%sLh8piR@ow}#0L9*c?h0mUYg7Z;E+ ze#SBDHVheC3dA0BbKa&>FB)9O;Cda79@ooHs>^}W9L zsAv5)^ETb~}twFOes6MTX)Lm6=bCjHJSS6YrEcw05Trj@H3 z)|u#WS+@tvK9%S>&m08oHEY9ag|EG^I_Emcoh0?HW#sMktD+nig{ z_cQ1G+R9Uic9L%h)OdY*B4k zTfbrTCGc&?R76t`x0&t{WCqh)7sqQOd>?oSN6z(?fT<&!M1OX^vySKiC?w-r!NL;( zU@(M;gNsN|10Sl(DmIIH=9PE5BtcU&xNJO{Kuu$p?FC*_zHF=Sbsy(4xB6r$3{=2# z=_q~hU`rb=R}b7Y$Cl4Kve^SH3s+{dh;EjhL>(+HQ_|MiSiH8G?Kli_82;JC5Xnv-eY^a8Zza4us@@{B14P%NG z1wR%Wj=3Gtzpw{6sNx3XEXXnH;Li?LSH_1~37trO0C6SpeI@`UVbn)YagF>;%(Xh2 zX$@^$GwC->Q_0vuZf~Y>@5mfMp!C981@Jar1jlZbB2=YIo#32Iq0Y9$!O>17Vv=u;-U8a zA@<#bq2GHj&o(u?egD(V%&j0Q5Pk>+Zrv{BV>eUfqUvJ1-v}ph2}aKIK2g%a0774s zE~oNl=VZAq!?&vvuR|39aqnXse8w-dN63v)plStrJ$u9e9dVr*uJB(GN2yh6j8fJg z-wzlY)LCLP)Zv}~HsWAu_ciZ|UJ znWe`(w+U!V72kA$iue#>U!$y??tC^TzV!Om=00t<{?aISi-rg#aSQL&F+yD|f*uZQ z8iW$Q-6bLEa>tLBDo{iQ+g6i-e~M)^k1N<< z4-r$k*H;X99S_+8T>W68+p$9={SN%zZ(%)2K$_BqJ3IyRU4=<x zFnieLGSFSxWpm}eA&pz|E8*Eabf(+7Z2J8l)J5a<%s+6tjB&vO04taP0O{+m$lB6I zMoN~$*3BMLK!Odf)mL#}75qzs!y4_IUF1uxhM|mYE4RCCSkAF%h?f;|anMrHUb)-6 zK%@77N(_FjR<4QlZXf#%@Kbi&tDo=Pm! z({ot`RlaDB|56no!-!wokhUK=Zl@wlC+AyO#WUx(x$sT?Ek+}yi506h_^a+|p3oZy zTf%rrqM7|j(MyFJ*0f<)=4Lm~B}wB2n~?EZgd}3yBT#=9saZsv+3`N#(V z(N~5-@=`VtZ{@LVN&c4OR)Ve#Jb2OG;SU5A3p+kLfz3N9&QDCsGYK;y(6MseJ=7j6 z;sV~MjCfYPq-CLB$1^LRoYuKq5g@#gznmL9YwCQ6p+-_~d>S4Ng$Ry} z{tLkj{fV@8qhJz`fz|5AWHW*q$)j*dn&(O*S8H&;Spqn%UrN--R&{*##D+h^Zi2u>$!`yl6#G|; zkUVu6Yx397-OkfoSh4Ot2WkA-NX(7R;)|}@UxreRByB+h-$p)nOyL0iigt0#sBz;V zW>@oci7DfnWKEZ3+O9O}7l2w!Tu={sl+@n*)~*U`k^{AV1O(fTNN`w!7KH9bPy_a6 z(94EM=Pjxw#*3*aSLerq>x;PUtgRrqxMlf64n zJ0r3Y%AaYFLP^NS_iXH{Lz(KqFiXoVa+^n76NLnS_=inx^d;}MKw_l7I~nuGs9kB^P_IYqnUsqMZcu{n|WBhj%%dqG-)T0*Wa`F?ojni!d_`#cTBKQzW>91LN3DlVgN z(m&5aUDFFVos68}viI8~Wwvh|Ruw~lD!!ACtCVg?&7>i7Y?eoecs=8>cwWWQ9r&2K z*Io>&j_(ibD3HL=FOly07Sl}cO`1en%?JKSibdyOH0i<*+QLiPG{9uR5PcC%Rh-=9HBQL zcJ@bSw9#a08a+L77&2^U5n2#Y>|Vt(-9TQ4qHOa4gd#ASfAAcpQtb(;TVyp|-Q5lD zW{t)-=jp5FMYH#!B$N7bLdCg7z6$2Iwp!qWJSq8&hnK6wcb1#VO4*YmaZZbMKD-3! zP%|BSVr_RYO`8)LORjc=;mNhPjBSQOy%pKGUG4$HX`D8jC?BVZabRE=IjI(lhS^jp zXHjr>YhKE!ywq0hI#Y~ie{Go0`zX-(?}}QFKBNPKC5y#?A(x3k8z2_$9!ojQwfS}! zL1YNJVVQgFpaf-n?YZdZrC|%3O5<|owCmUqIMF`v4&JpCaz|h*ne;n)?U(CdyNe&Y zfu$?F0fIa5UTw}2r@a-=cZp&23>;z;6E3F+2sA15*xLyLbX-;``NB<^T192iA=!pD z)r)Op2m$>Y26^E*sj}ltKPE|i&3CNEjuq?7KDOwjf8i#dDhqL9iiLqX9FnSXbkF)&$P(Q~*n0p*UfPofzws}B+nf=^!l08vE z!$9ti{zETS=zoef0@d2h5A5TxaIZIzQLdDK^>4W4(O;j{iHr;jt6$ZeEJ!^!EqFY& zh!YQS>sb`_1^|;b#44Fwy@jqNQ0ajkh6NHBD*sfMf5cQhespFO{PM{gr`V>>L4tY^ z-Vdv1CdJWvFmAS9Gcq!h$R>H<-Xo|+48Wgz7i<~%^uQ^I@lxTBbg6b;Vdv&w6tv(gk0+<^*@dL>JH|`M(}R?< z_%)OQ-d$_(FCq^vU2ad(pM`r%aUl&})D35k&zCH@1bntZh{T0?G{uoa2UpFqyS71j zOEsw9PG`)F;vcARqjq%@2g%+AhbEk$xu47r++E?)i_aEAM})tuQ8mbJc+i)P`Z|35bt`5j82*G#Vx1@# zdQ0?mp)mJm9HAJa-3~tAp4?-He&{6q=6U~Rb_cd2w%?7;h`pAfnhKm@nzMO%*bwTB z2;{lq5VYL9gTh1C;UV77-jx@4YAZ~ioCv5V&h(q_9hux!zP1tdC6K6(FODq<0Dm>d zCl@HKpLf$^mDMjy&->nPwu-%2m=b0sCF&WCk*=E7YLIE3nL$aI3H(ytlB%70d)V0g z;agt7OFv3ZcVF**j1Vi8d~>mv4WCciV&SJl7{U{|;smA?4t2*{WGu15d;GD?{le5a zbMQyrI+L;&MpFd_77y-!tLu;-A5{z+Q^0HS{w&61?jk-Vl_fA7X$NJAz>3TsFS5fWmd`U_~9Pv(=}yt@5$k^IA* z9t4RHV*D2>)xY|5f*{|7IREAIzu@%xU4P|k|86ZXa5NAyVLA*900uz(b?|`huc7|| DSIu#c diff --git a/simulations/regression_tests/udo.py b/simulations/regression_tests/udo.py index 65b6b41..f974b3e 100644 --- a/simulations/regression_tests/udo.py +++ b/simulations/regression_tests/udo.py @@ -166,7 +166,8 @@ states = list(state_updates.keys()) # states_noTS = filter_out(['timestamp'], states) # states_grid = [states,states_noTS,states_noTS] -states_grid = [states] * system_substeps #[states,states,states] +# states_grid = [states] * system_substeps # +states_grid = [states,states,states] policy_grid = [['a', 'b'], ['a', 'b'], ['a', 'b']] diff --git a/simulations/validation/marbles.py b/simulations/validation/marbles.py new file mode 100644 index 0000000..8a0f2c3 --- /dev/null +++ b/simulations/validation/marbles.py @@ -0,0 +1,166 @@ +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from cadCAD.configuration import Configuration +from cadCAD.configuration.utils.userDefinedObject import udoPipe, UDO +import networkx as nx +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +import pprint as pp + +T = 50 #iterations in our simulation +n = 3 #number of boxes in our network +m = 2 #for barabasi graph type number of edges is (n-2)*m + +G = nx.barabasi_albert_graph(n, m) +k = len(G.edges) + + +# class udoExample(object): +# def __init__(self, G): +# self.G = G +# self.mem_id = str(hex(id(self))) + + +g = UDO(udo=G) +print() +# print(g.edges) +# print(G.edges) +# pp.pprint(f"{type(g)}: {g}") +# next +balls = np.zeros(n,) + +for node in g.nodes: + rv = np.random.randint(1,25) + g.nodes[node]['initial_balls'] = rv + balls[node] = rv + +# pp.pprint(balls) + +# next +scale=100 +nx.draw_kamada_kawai(G, node_size=balls*scale,labels=nx.get_node_attributes(G,'initial_balls')) + +# next + +initial_conditions = {'balls':balls, 'network':G} +print(initial_conditions) + +# next + +def update_balls(params, step, sL, s, _input): + delta_balls = _input['delta'] + new_balls = s['balls'] + for e in G.edges: + move_ball = delta_balls[e] + src = e[0] + dst = e[1] + if (new_balls[src] >= move_ball) and (new_balls[dst] >= -move_ball): + new_balls[src] = new_balls[src] - move_ball + new_balls[dst] = new_balls[dst] + move_ball + + key = 'balls' + value = new_balls + + return (key, value) + + +def update_network(params, step, sL, s, _input): + new_nodes = _input['nodes'] + new_edges = _input['edges'] + new_balls = _input['quantity'] + + graph = s['network'] + + for node in new_nodes: + graph.add_node(node) + graph.nodes[node]['initial_balls'] = new_balls[node] + graph.nodes[node]['strat'] = _input['node_strats'][node] + + for edge in new_edges: + graph.add_edge(edge[0], edge[1]) + graph.edges[edge]['strat'] = _input['edge_strats'][edge] + + key = 'network' + value = graph + return (key, value) + + +def update_network_balls(params, step, sL, s, _input): + new_nodes = _input['nodes'] + new_balls = _input['quantity'] + balls = np.zeros(len(s['balls']) + len(new_nodes)) + + for node in s['network'].nodes: + balls[node] = s['balls'][node] + + for node in new_nodes: + balls[node] = new_balls[node] + + key = 'balls' + value = balls + + return (key, value) + +# next + + +def greedy_robot(src_balls, dst_balls): + # robot wishes to accumlate balls at its source + # takes half of its neighbors balls + if src_balls < dst_balls: + delta = -np.floor(dst_balls / 2) + else: + delta = 0 + + return delta + + +def fair_robot(src_balls, dst_balls): + # robot follows the simple balancing rule + delta = np.sign(src_balls - dst_balls) + + return delta + + +def giving_robot(src_balls, dst_balls): + # robot wishes to gice away balls one at a time + if src_balls > 0: + delta = 1 + else: + delta = 0 + + return delta + +# next + +robot_strategies = [greedy_robot,fair_robot, giving_robot] + +for node in G.nodes: + nstrats = len(robot_strategies) + rv = np.random.randint(0,nstrats) + G.nodes[node]['strat'] = robot_strategies[rv] + +for e in G.edges: + owner_node = e[0] + G.edges[e]['strat'] = G.nodes[owner_node]['strat'] + +# next + +def robotic_network(params, step, sL, s): + graph = s['network'] + + delta_balls = {} + for e in graph.edges: + src = e[0] + src_balls = s['balls'][src] + dst = e[1] + dst_balls = s['balls'][dst] + + # transfer balls according to specific robot strat + strat = graph.edges[e]['strat'] + delta_balls[e] = strat(src_balls, dst_balls) + + return_dict = {'nodes': [], 'edges': {}, 'quantity': {}, 'node_strats': {}, 'edge_strats': {}, 'delta': delta_balls} + + return (return_dict) \ No newline at end of file diff --git a/simulations/validation/marbles2.py b/simulations/validation/marbles2.py new file mode 100644 index 0000000..91c3f93 --- /dev/null +++ b/simulations/validation/marbles2.py @@ -0,0 +1,205 @@ +from cadCAD.configuration import append_configs +import networkx as nx +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +T = 50 #iterations in our simulation +n = 3 #number of boxes in our network +m = 2 #for barabasi graph type number of edges is (n-2)*m + +G = nx.barabasi_albert_graph(n, m) +k = len(G.edges) + +balls = np.zeros(n,) + +for node in G.nodes: + rv = np.random.randint(1,25) + G.nodes[node]['initial_balls'] = rv + balls[node] = rv + +scale=100 +nx.draw_kamada_kawai(G, node_size=balls*scale,labels=nx.get_node_attributes(G,'initial_balls')) + +initial_conditions = {'balls': balls, 'network': G} + + +def update_balls(params, step, sL, s, _input): + delta_balls = _input['delta'] + new_balls = s['balls'] + for e in G.edges: + move_ball = delta_balls[e] + src = e[0] + dst = e[1] + if (new_balls[src] >= move_ball) and (new_balls[dst] >= -move_ball): + new_balls[src] = new_balls[src] - move_ball + new_balls[dst] = new_balls[dst] + move_ball + + key = 'balls' + value = new_balls + + return (key, value) + + +def update_network(params, step, sL, s, _input): + new_nodes = _input['nodes'] + new_edges = _input['edges'] + new_balls = _input['quantity'] + + graph = s['network'] + + for node in new_nodes: + graph.add_node(node) + graph.nodes[node]['initial_balls'] = new_balls[node] + graph.nodes[node]['strat'] = _input['node_strats'][node] + + for edge in new_edges: + graph.add_edge(edge[0], edge[1]) + graph.edges[edge]['strat'] = _input['edge_strats'][edge] + + key = 'network' + value = graph + return (key, value) + + +def update_network_balls(params, step, sL, s, _input): + new_nodes = _input['nodes'] + new_balls = _input['quantity'] + balls = np.zeros(len(s['balls']) + len(new_nodes)) + + for node in s['network'].nodes: + balls[node] = s['balls'][node] + + for node in new_nodes: + balls[node] = new_balls[node] + + key = 'balls' + value = balls + + return (key, value) + + +def greedy_robot(src_balls, dst_balls): + # robot wishes to accumlate balls at its source + # takes half of its neighbors balls + if src_balls < dst_balls: + delta = -np.floor(dst_balls / 2) + else: + delta = 0 + + return delta + + +def fair_robot(src_balls, dst_balls): + # robot follows the simple balancing rule + delta = np.sign(src_balls - dst_balls) + + return delta + + +def giving_robot(src_balls, dst_balls): + # robot wishes to gice away balls one at a time + if src_balls > 0: + delta = 1 + else: + delta = 0 + + return delta + +robot_strategies = [greedy_robot,fair_robot, giving_robot] + +for node in G.nodes: + nstrats = len(robot_strategies) + rv = np.random.randint(0,nstrats) + G.nodes[node]['strat'] = robot_strategies[rv] + +for e in G.edges: + owner_node = e[0] + G.edges[e]['strat'] = G.nodes[owner_node]['strat'] + + +def robotic_network(params, step, sL, s): + graph = s['network'] + + delta_balls = {} + for e in graph.edges: + src = e[0] + src_balls = s['balls'][src] + dst = e[1] + dst_balls = s['balls'][dst] + + # transfer balls according to specific robot strat + strat = graph.edges[e]['strat'] + delta_balls[e] = strat(src_balls, dst_balls) + + return_dict = {'nodes': [], 'edges': {}, 'quantity': {}, 'node_strats': {}, 'edge_strats': {}, 'delta': delta_balls} + + return (return_dict) + + +def agent_arrival(params, step, sL, s): + node = len(s['network'].nodes) + edge_list = s['network'].edges + + # choose a m random edges without replacement + # new = np.random.choose(edgelist,m) + new = [0, 1] # tester + + nodes = [node] + edges = [(node, new_node) for new_node in new] + + initial_balls = {node: np.random.randint(1, 25)} + + rv = np.random.randint(0, nstrats) + node_strat = {node: robot_strategies[rv]} + + edge_strats = {e: robot_strategies[rv] for e in edges} + + return_dict = {'nodes': nodes, + 'edges': edges, + 'quantity': initial_balls, + 'node_strats': node_strat, + 'edge_strats': edge_strats, + 'delta': np.zeros(node + 1) + } + return (return_dict) + + +partial_state_update_blocks = [ + { + 'policies': { + # The following policy functions will be evaluated and their returns will be passed to the state update functions + 'p1': robotic_network + }, + 'variables': { # The following state variables will be updated simultaneously + 'balls': update_balls + + } + }, + { + 'policies': { + # The following policy functions will be evaluated and their returns will be passed to the state update functions + 'p1': agent_arrival + }, + 'variables': { # The following state variables will be updated simultaneously + 'network': update_network, + 'balls': update_network_balls + } + } +] + +simulation_parameters = { + 'T': range(T), + 'N': 1, + 'M': {} +} +append_configs( + sim_configs=simulation_parameters, #dict containing state update functions + initial_state=initial_conditions, #dict containing variable names and initial values + partial_state_update_blocks= partial_state_update_blocks #, #dict containing state update functions + # policy_ops=[lambda a, b: {**a, **b}] +) +# config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values +# partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions +# sim_config=simulation_parameters #dict containing simulation parameters +# ) diff --git a/simulations/validation/marbles2_run.py b/simulations/validation/marbles2_run.py new file mode 100644 index 0000000..b275b76 --- /dev/null +++ b/simulations/validation/marbles2_run.py @@ -0,0 +1,25 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.validation import marbles2 +from cadCAD import configs + +exec_mode = ExecutionMode() + +print("Simulation Execution: Single Configuration") +print() +first_config = configs # only contains config1 +single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) +run = Executor(exec_context=single_proc_ctx, configs=first_config) + +raw_result, tensor_field = run.main() +result = pd.DataFrame(raw_result) +print() +print("Tensor Field: config1") +print(tabulate(tensor_field, headers='keys', tablefmt='psql')) +print("Output:") +print(tabulate(result, headers='keys', tablefmt='psql')) +print() + +print(result[['network', 'substep']]) \ No newline at end of file diff --git a/simulations/validation/robot-marbles-agents-advanced-udo.ipynb b/simulations/validation/robot-marbles-agents-advanced-udo.ipynb new file mode 100644 index 0000000..aa848fd --- /dev/null +++ b/simulations/validation/robot-marbles-agents-advanced-udo.ipynb @@ -0,0 +1,1617 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# cadCAD Tutorials: The Robot and the Marbles, Networks Addition\n", + "In [Part 2](https://github.com/BlockScience/SimCAD-Tutorials/blob/master/demos/robot-marbles-part-2/robot-marbles-part-2.ipynb) we introduced the 'language' in which a system must be described in order for it to be interpretable by cadCAD and some of the basic concepts of the library:\n", + "* State Variables\n", + "* Timestep\n", + "* Policies\n", + "* State Update Functions\n", + "* Partial State Update Blocks\n", + "* Simulation Configuration Parameters\n", + "\n", + "In the previous example, we observed how two robotic arms acting in parallel could result in counterintuitive system level behavior despite the simplicity of the individual robotic arm policies. \n", + "In this notebook we'll introduce the concept of networks. This done by extending from two boxes of marbles to *n* boxes which are the nodes in our network. Furthermore, there are are going to be arms between some of the boxes but not others forming a network where the arms are the edges.\n", + "\n", + "__The robot and the marbles__ \n", + "* Picture a set of n boxes (`balls`) with an integer number of marbles in each; a network of robotic arms is capable of taking a marble from their one of their boxes and dropping it into the other one.\n", + "* Each robotic arm in the network only controls 2 boxes and they act by moving a marble from one box to the other.\n", + "* Each robotic arm is programmed to take one marble at a time from the box containing the largest number of marbles and drop it in the other box. It repeats that process until the boxes contain an equal number of marbles.\n", + "* For the purposes of our analysis of this system, suppose we are only interested in monitoring the number of marbles in only their two boxes." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from cadCAD.engine import ExecutionMode, ExecutionContext, Executor\n", + "from cadCAD.configuration import Configuration\n", + "from cadCAD.configuration.utils.userDefinedObject import udoPipe, UDO\n", + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "%matplotlib inline\n", + "\n", + "T = 50 #iterations in our simulation\n", + "n=3 #number of boxes in our network\n", + "m= 2 #for barabasi graph type number of edges is (n-2)*m\n", + "\n", + "G = nx.barabasi_albert_graph(n, m)\n", + "k = len(G.edges)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "balls = np.zeros(n,)\n", + "\n", + "for node in G.nodes:\n", + " rv = np.random.randint(1,25)\n", + " G.nodes[node]['initial_balls'] = rv\n", + " balls[node] = rv" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAcUAAAE1CAYAAACWU/udAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3XlcVXX+P/DXOefeyyKbuAAuCEoqcAFlE3cpremr2TJWptZUI8oFNUvbm+93WiZtcilTriZT09hM5UxaaVOZSy4FgQvCBXXKNc0VBNnvdn5/RP6mmZILnHvPvZfX8/Hojwg+59Uf9/Hi/eGczxFkWZZBREREENUOQERE5C5YikRERC1YikRERC1YikRERC1YikRERC1YikRERC1YikRERC1YikRERC1YikRERC1YikRERC1YikRERC1YikRERC1YikRERC1YikRERC1YikRERC1YikRERC00agew22VUnLuCstM12HuyCvtPVePClSZY7DIgA1pJQGgXHZL6hiA9KhT63sFI6B0MrcQ+JyIiZQmyLMtqXPhyvRnv7f0Of9pzHA1mK+wy0GixXfNnfLUiNKIIUQDuzeiHezOiEB7s66LERETk7VxeilX1Zjy3uQKfmM5CEIAmi71d6/hoRMgARg7ohhduS0DvED9lgxIRUafj0lL8tPwcHv3HQTRZbLDYlLmsJArQSSKemRiLaemREARBkXWJiKjzcUkp1jVb8fB7Jdjz7aVWt0jby08rIS4iCKtnpKBHoI9TrkFERN7N6aV4ud6Mu14vwMmqBpit7dsqdZRGFNCtiw7vG0agT1d/p16LiIi8j1NLsabRgttWfYnT1Q2KbZe2RhIEhPhrsXnuKEQE8++MRETkOKc912C12TE9vxBnXFiIAGCTZVQ3WvBr41eoa7a67LpEROT5nFaKK3d8i6MX62B2YSH+yGaXUVlvxv99ZHL5tYmIyHM5pRSPnKvF6l1H0djOxy2U0Gy14+Oyc9jz7SXVMhARkWdRvBStNjty/rYPzSoW4o+aLDbMe/cAt1GJiMghipfi1sMXcLamCaock/MzGs02vFd8Su0YRETkARQvxdU7j6LB7JxnEduj0WLD67uPQaXT7IiIyIMoWopHL9bh0NkrSi6piLomK746Wql2DCIicnOKluLbhSdhs7vfRFZvtiF/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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "scale=100\n", + "nx.draw_kamada_kawai(G, node_size=balls*scale,labels=nx.get_node_attributes(G,'initial_balls'))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "initial_conditions = {'balls':balls, 'network':G}" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "#input the deltas along the edges and update the boxes\n", + "#mechanism: edge by node dimensional operator\n", + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n", + "# We make the state update functions less \"intelligent\",\n", + "# ie. they simply add the number of marbles specified in _input \n", + "# (which, per the policy function definition, may be negative)\n", + "\n", + "\n", + "def update_balls(params, step, sL, s, _input):\n", + " \n", + " delta_balls = _input['delta']\n", + " new_balls = s['balls']\n", + " for e in G.edges:\n", + " move_ball = delta_balls[e]\n", + " src = e[0]\n", + " dst = e[1]\n", + " if (new_balls[src] >= move_ball) and (new_balls[dst] >= -move_ball):\n", + " new_balls[src] = new_balls[src]-move_ball\n", + " new_balls[dst] = new_balls[dst]+move_ball\n", + " \n", + " \n", + " key = 'balls'\n", + " value = new_balls\n", + " \n", + " return (key, value)\n", + "\n", + "def update_network(params, step, sL, s, _input):\n", + " \n", + " new_nodes = _input['nodes']\n", + " new_edges = _input['edges']\n", + " new_balls = _input['quantity']\n", + " \n", + " graph = s['network']\n", + " \n", + " for node in new_nodes:\n", + " graph.add_node(node)\n", + " graph.nodes[node]['initial_balls'] = new_balls[node]\n", + " graph.nodes[node]['strat'] = _input['node_strats'][node]\n", + " \n", + " for edge in new_edges:\n", + " graph.add_edge(edge[0], edge[1])\n", + " graph.edges[edge]['strat'] = _input['edge_strats'][edge]\n", + " \n", + " \n", + " key = 'network'\n", + " value = graph\n", + " return (key, value)\n", + "\n", + "def update_network_balls(params, step, sL, s, _input):\n", + " \n", + " new_nodes = _input['nodes']\n", + " new_balls = _input['quantity']\n", + " balls = np.zeros(len(s['balls'])+len(new_nodes))\n", + " \n", + " for node in s['network'].nodes:\n", + " balls[node] = s['balls'][node]\n", + " \n", + " for node in new_nodes:\n", + " balls[node] = new_balls[node]\n", + " \n", + " key = 'balls'\n", + " value = balls\n", + " \n", + " return (key, value)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# this time lets make three kinds of robots\n", + "\n", + "def greedy_robot(src_balls, dst_balls):\n", + " \n", + " #robot wishes to accumlate balls at its source\n", + " #takes half of its neighbors balls\n", + " if src_balls < dst_balls:\n", + " delta = -np.floor(dst_balls/2)\n", + " else:\n", + " delta = 0\n", + " \n", + " return delta\n", + "\n", + "def fair_robot(src_balls, dst_balls):\n", + " \n", + " #robot follows the simple balancing rule\n", + " delta = np.sign(src_balls-dst_balls)\n", + " \n", + " return delta\n", + "\n", + "\n", + "def giving_robot(src_balls, dst_balls):\n", + " \n", + " #robot wishes to gice away balls one at a time\n", + " if src_balls > 0:\n", + " delta = 1\n", + " else:\n", + " delta = 0\n", + " \n", + " return delta" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "#in the previous version the robots were assigned to the edges\n", + "#moving towards an agent based model formulation we assign the stratgies\n", + "#instead to the nodes\n", + "robot_strategies = [greedy_robot,fair_robot, giving_robot]\n", + "\n", + "for node in G.nodes:\n", + " nstrats = len(robot_strategies)\n", + " rv = np.random.randint(0,nstrats)\n", + " G.nodes[node]['strat'] = robot_strategies[rv]\n", + "\n", + "for e in G.edges:\n", + " owner_node = e[0]\n", + " G.edges[e]['strat'] = G.nodes[owner_node]['strat']" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "#Policy: node by edge dimensional operator\n", + "#input the states of the boxes output the deltas along the edges\n", + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n", + "# We specify the robotic networks logic in a Policy Function\n", + "# unlike previous examples our policy controls a vector valued action, defined over the edges of our network\n", + "def robotic_network(params, step, sL, s):\n", + " \n", + " graph = s['network']\n", + " \n", + " \n", + " delta_balls = {}\n", + " for e in graph.edges:\n", + " src = e[0]\n", + " src_balls = s['balls'][src]\n", + " dst = e[1]\n", + " dst_balls = s['balls'][dst]\n", + " \n", + " #transfer balls according to specific robot strat\n", + " strat = graph.edges[e]['strat']\n", + " delta_balls[e] = strat(src_balls,dst_balls)\n", + " \n", + " return_dict = {'nodes': [],'edges': {}, 'quantity': {}, 'node_strats':{},'edge_strats':{},'delta': delta_balls}\n", + "\n", + " return(return_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def agent_arrival(params, step, sL, s):\n", + " \n", + " node= len(s['network'].nodes)\n", + " edge_list = s['network'].edges\n", + " \n", + " #choose a m random edges without replacement\n", + " #new = np.random.choose(edgelist,m) \n", + " new = [0, 1]#tester\n", + " \n", + " nodes = [node]\n", + " edges = [(node,new_node) for new_node in new]\n", + " \n", + " initial_balls = {node:np.random.randint(1,25) }\n", + " \n", + " rv = np.random.randint(0,nstrats)\n", + " node_strat = {node: robot_strategies[rv]}\n", + " \n", + " edge_strats = {e: robot_strategies[rv] for e in edges}\n", + "\n", + " return_dict = {'nodes': nodes,\n", + " 'edges': edges, \n", + " 'quantity':initial_balls, \n", + " 'node_strats':node_strat,\n", + " 'edge_strats':edge_strats,\n", + " 'delta': np.zeros(node+1)\n", + " } \n", + " return(return_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n", + "# In the Partial State Update Blocks, the user specifies if state update functions will be run in series or in parallel\n", + "partial_state_update_blocks = [\n", + " { \n", + " 'policies': { # The following policy functions will be evaluated and their returns will be passed to the state update functions\n", + " 'p1': robotic_network\n", + " },\n", + " 'variables': { # The following state variables will be updated simultaneously\n", + " 'balls': update_balls\n", + " \n", + " }\n", + " },\n", + " {\n", + " 'policies': { # The following policy functions will be evaluated and their returns will be passed to the state update functions\n", + " 'p1': agent_arrival\n", + " },\n", + " 'variables': { # The following state variables will be updated simultaneously\n", + " 'network': update_network,\n", + " 'balls': update_network_balls\n", + " }\n", + " }\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n", + "# Settings of general simulation parameters, unrelated to the system itself\n", + "# `T` is a range with the number of discrete units of time the simulation will run for;\n", + "# `N` is the number of times the simulation will be run (Monte Carlo runs)\n", + "# In this example, we'll run the simulation once (N=1) and its duration will be of 10 timesteps\n", + "# We'll cover the `M` key in a future article. For now, let's leave it empty\n", + "simulation_parameters = {\n", + " 'T': range(T),\n", + " 'N': 1,\n", + " 'M': {}\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n", + "# The configurations above are then packaged into a `Configuration` object\n", + "config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values\n", + " partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n", + " sim_config=simulation_parameters #dict containing simulation parameters\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "single_proc: []\n" + ] + } + ], + "source": [ + "exec_mode = ExecutionMode()\n", + "exec_context = ExecutionContext(exec_mode.single_proc)\n", + "executor = Executor(exec_context, [config]) # Pass the configuration object inside an array\n", + "raw_result, tensor = executor.main() # The `main()` method returns a tuple; its first elements contains the raw results" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.DataFrame(raw_result)\n", + "balls_list = [b for b in df.balls]" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ballsnetworkrunsubsteptimestep
0[13.0, 16.0, 24.0](0, 1, 2)100
1[25.0, 15.0, 13.0](0, 1, 2, 3)111
2[25.0, 15.0, 13.0, 10.0](0, 1, 2, 3)121
3[25.0, 14.0, 14.0, 10.0](0, 1, 2, 3, 4)112
4[25.0, 14.0, 14.0, 10.0, 9.0](0, 1, 2, 3, 4)122
5[25.0, 13.0, 15.0, 10.0, 9.0](0, 1, 2, 3, 4, 5)113
6[25.0, 13.0, 15.0, 10.0, 9.0, 24.0](0, 1, 2, 3, 4, 5)123
7[25.0, 12.0, 16.0, 10.0, 9.0, 24.0](0, 1, 2, 3, 4, 5, 6)114
8[25.0, 12.0, 16.0, 10.0, 9.0, 24.0, 14.0](0, 1, 2, 3, 4, 5, 6)124
9[25.0, 11.0, 17.0, 10.0, 9.0, 24.0, 14.0](0, 1, 2, 3, 4, 5, 6, 7)115
10[25.0, 11.0, 17.0, 10.0, 9.0, 24.0, 14.0, 5.0](0, 1, 2, 3, 4, 5, 6, 7)125
11[25.0, 10.0, 18.0, 10.0, 9.0, 24.0, 14.0, 5.0](0, 1, 2, 3, 4, 5, 6, 7, 8)116
12[25.0, 10.0, 18.0, 10.0, 9.0, 24.0, 14.0, 5.0,...(0, 1, 2, 3, 4, 5, 6, 7, 8)126
13[25.0, 9.0, 19.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)117
14[25.0, 9.0, 19.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)127
15[25.0, 8.0, 20.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)118
16[25.0, 8.0, 20.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)128
17[25.0, 7.0, 21.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)119
18[25.0, 7.0, 21.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)129
19[25.0, 6.0, 22.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)1110
20[25.0, 6.0, 22.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)1210
21[25.0, 5.0, 23.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)1111
22[25.0, 5.0, 23.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)1211
23[25.0, 4.0, 24.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1112
24[25.0, 4.0, 24.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1212
25[25.0, 3.0, 25.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1113
26[25.0, 3.0, 25.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1213
27[25.0, 2.0, 26.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1114
28[25.0, 2.0, 26.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1214
29[38.0, 1.0, 14.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1115
..................
71[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1136
72[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1236
73[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1137
74[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1237
75[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1138
76[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1238
77[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1139
78[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1239
79[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1140
80[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1240
81[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1141
82[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1241
83[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1142
84[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1242
85[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1143
86[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1243
87[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1144
88[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1244
89[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1145
90[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1245
91[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1146
92[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1246
93[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1147
94[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1247
95[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1148
96[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1248
97[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1149
98[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1249
99[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1150
100[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1250
\n", + "

101 rows × 5 columns

\n", + "
" + ], + "text/plain": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ballsnetworkrunsubsteptimestep
0[13.0, 16.0, 24.0](0, 1, 2)100
1[25.0, 15.0, 13.0](0, 1, 2, 3)111
2[25.0, 15.0, 13.0, 10.0](0, 1, 2, 3)121
3[25.0, 14.0, 14.0, 10.0](0, 1, 2, 3, 4)112
4[25.0, 14.0, 14.0, 10.0, 9.0](0, 1, 2, 3, 4)122
5[25.0, 13.0, 15.0, 10.0, 9.0](0, 1, 2, 3, 4, 5)113
6[25.0, 13.0, 15.0, 10.0, 9.0, 24.0](0, 1, 2, 3, 4, 5)123
7[25.0, 12.0, 16.0, 10.0, 9.0, 24.0](0, 1, 2, 3, 4, 5, 6)114
8[25.0, 12.0, 16.0, 10.0, 9.0, 24.0, 14.0](0, 1, 2, 3, 4, 5, 6)124
9[25.0, 11.0, 17.0, 10.0, 9.0, 24.0, 14.0](0, 1, 2, 3, 4, 5, 6, 7)115
10[25.0, 11.0, 17.0, 10.0, 9.0, 24.0, 14.0, 5.0](0, 1, 2, 3, 4, 5, 6, 7)125
11[25.0, 10.0, 18.0, 10.0, 9.0, 24.0, 14.0, 5.0](0, 1, 2, 3, 4, 5, 6, 7, 8)116
12[25.0, 10.0, 18.0, 10.0, 9.0, 24.0, 14.0, 5.0,...(0, 1, 2, 3, 4, 5, 6, 7, 8)126
13[25.0, 9.0, 19.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)117
14[25.0, 9.0, 19.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)127
15[25.0, 8.0, 20.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)118
16[25.0, 8.0, 20.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)128
17[25.0, 7.0, 21.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)119
18[25.0, 7.0, 21.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)129
19[25.0, 6.0, 22.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)1110
20[25.0, 6.0, 22.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)1210
21[25.0, 5.0, 23.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)1111
22[25.0, 5.0, 23.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)1211
23[25.0, 4.0, 24.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1112
24[25.0, 4.0, 24.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1212
25[25.0, 3.0, 25.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1113
26[25.0, 3.0, 25.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1213
27[25.0, 2.0, 26.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1114
28[25.0, 2.0, 26.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1214
29[38.0, 1.0, 14.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1115
..................
71[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1136
72[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1236
73[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1137
74[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1237
75[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1138
76[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1238
77[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1139
78[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1239
79[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1140
80[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1240
81[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1141
82[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1241
83[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1142
84[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1242
85[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1143
86[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1243
87[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1144
88[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1244
89[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1145
90[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1245
91[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1146
92[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1246
93[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1147
94[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1247
95[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1148
96[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1248
97[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1149
98[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1249
99[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1150
100[38.0, 0.0, 15.0, 10.0, 9.0, 24.0, 14.0, 5.0, ...(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...1250
\n", + "

101 rows × 5 columns

\n", + "
" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "setting an array element with a sequence.", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtimestep\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalues\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mballs_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mxlabel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'iteration'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mylabel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'number of balls'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtitle\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'balls in each box'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlegend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'Box #'\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mnode\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mncol\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/pyplot.py\u001b[0m in \u001b[0;36mplot\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 3356\u001b[0m mplDeprecation)\n\u001b[1;32m 3357\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 3358\u001b[0;31m \u001b[0mret\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0max\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3359\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3360\u001b[0m \u001b[0max\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_hold\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwashold\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/__init__.py\u001b[0m in \u001b[0;36minner\u001b[0;34m(ax, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1853\u001b[0m \u001b[0;34m\"the Matplotlib list!)\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlabel_namer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1854\u001b[0m RuntimeWarning, stacklevel=2)\n\u001b[0;32m-> 1855\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0max\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1856\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1857\u001b[0m inner.__doc__ = _add_data_doc(inner.__doc__,\n", + "\u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/axes/_axes.py\u001b[0m in \u001b[0;36mplot\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1526\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1527\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mline\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_lines\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1528\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_line\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1529\u001b[0m \u001b[0mlines\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1530\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36madd_line\u001b[0;34m(self, line)\u001b[0m\n\u001b[1;32m 1930\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_clip_path\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpatch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1931\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1932\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_line_limits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1933\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_label\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1934\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_label\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'_line%d'\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlines\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m_update_line_limits\u001b[0;34m(self, line)\u001b[0m\n\u001b[1;32m 1952\u001b[0m \u001b[0mFigures\u001b[0m \u001b[0mout\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mdata\u001b[0m \u001b[0mlimit\u001b[0m \u001b[0mof\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mgiven\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mupdating\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdataLim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1953\u001b[0m \"\"\"\n\u001b[0;32m-> 1954\u001b[0;31m \u001b[0mpath\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_path\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1955\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvertices\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msize\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1956\u001b[0m \u001b[0;32mreturn\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/lines.py\u001b[0m in \u001b[0;36mget_path\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 949\u001b[0m \"\"\"\n\u001b[1;32m 950\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_invalidy\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_invalidx\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 951\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrecache\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 952\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_path\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 953\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/lines.py\u001b[0m in \u001b[0;36mrecache\u001b[0;34m(self, always)\u001b[0m\n\u001b[1;32m 655\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0malways\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_invalidy\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 656\u001b[0m \u001b[0myconv\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconvert_yunits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_yorig\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 657\u001b[0;31m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_to_unmasked_float_array\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0myconv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mravel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 658\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 659\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_y\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/matplotlib/cbook/__init__.py\u001b[0m in \u001b[0;36m_to_unmasked_float_array\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 2048\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfloat\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfilled\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnan\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2049\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2050\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfloat\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2051\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2052\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/numpy/core/numeric.py\u001b[0m in \u001b[0;36masarray\u001b[0;34m(a, dtype, order)\u001b[0m\n\u001b[1;32m 490\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 491\u001b[0m \"\"\"\n\u001b[0;32m--> 492\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcopy\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0morder\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0morder\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 493\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 494\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: setting an array element with a sequence." + ], + "output_type": "error" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(df.timestep.values, balls_list)\n", + "plt.xlabel('iteration')\n", + "plt.ylabel('number of balls')\n", + "plt.title('balls in each box')\n", + "plt.legend(['Box #'+str(node) for node in range(n)], ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "end_state_balls = np.array([b for b in balls_list[-1]])\n", + "avg_balls = np.array([np.mean(b) for b in balls_list])\n", + "\n", + "for node in G.nodes:\n", + " G.nodes[node]['final_balls'] = end_state_balls[node]\n", + " G.nodes[node]['avg_balls'] = avg_balls[node]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cmap = plt.cm.jet\n", + "Nc = cmap.N\n", + "Ns = len(robot_strategies)\n", + "d = int(Nc/Ns)\n", + "\n", + "k = len(G.edges)\n", + "strat_color = []\n", + "for e in G.edges:\n", + " \n", + " for i in range(Ns):\n", + " if G.edges[e]['strat']==robot_strategies[i]:\n", + " color = cmap(i*d)\n", + " G.edges[e]['color'] = color\n", + " strat_color = strat_color+[color]\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "nx.draw_kamada_kawai(G, node_size=end_state_balls*scale, labels=nx.get_node_attributes(G,'final_balls'), edge_color=strat_color)\n", + "print(end_state_balls)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rolling_avg_balls = np.zeros((T+1, n))\n", + "for t in range(T+1):\n", + " for node in G.nodes:\n", + " for tau in range(t):\n", + " rolling_avg_balls[t,node] = (tau)/(tau+1)*rolling_avg_balls[t, node]+ 1/(tau+1)*balls_list[tau][node]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(range(len(rolling_avg_balls)),rolling_avg_balls)\n", + "plt.xlabel('iteration')\n", + "plt.ylabel('number of balls')\n", + "plt.title('time average balls in each box')\n", + "plt.legend(['Box #'+str(node) for node in range(n)], ncol = 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for node in G.nodes:\n", + " G.nodes[node]['avg_balls'] = int(10*(rolling_avg_balls[node][-1]))/10\n", + "\n", + "nx.draw_kamada_kawai(G, node_size=avg_balls*scale, labels=nx.get_node_attributes(G,'avg_balls'))\n", + "print(end_state_balls)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cmap = plt.cm.jet\n", + "Nc = cmap.N\n", + "Nt = len(simulation_parameters['T'])\n", + "dN = int(Nc/Nt)\n", + "cmaplist = [cmap(i*dN) for i in range(Nt)]\n", + "\n", + "for t in simulation_parameters['T']:\n", + " state = np.array([b for b in balls_list[t]])\n", + " nx.draw_kamada_kawai(G, node_size=state*scale, alpha = .4/(t+1), node_color = cmaplist[t])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/simulations/validation/udo.py b/simulations/validation/udo.py index dc644e5..e3c2e66 100644 --- a/simulations/validation/udo.py +++ b/simulations/validation/udo.py @@ -144,6 +144,7 @@ PSUB = { # needs M1&2 need behaviors partial_state_update_blocks = [PSUB] * substeps +# pp.pprint(partial_state_update_blocks) sim_config = config_sim({ "N": 2, From 1c89d28ab542f2f982816198b7843784356db7b0 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Sat, 11 May 2019 12:28:31 -0400 Subject: [PATCH 12/19] add new ver --- cadCAD/engine/simulation.py | 1 - setup.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 77c3bf5..36d4067 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -114,7 +114,6 @@ class Executor: # last_in_obj: Dict[str, Any] = dict(dp_psu(sL[-1])) last_in_obj: Dict[str, Any] = deepcopy(sL[-1]) - # print(last_in_obj) # last_in_obj: Dict[str, Any] = sL[-1] # last_in_obj: Dict[str, Any] = sH[-1] diff --git a/setup.py b/setup.py index a01f39c..1aaf50c 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ long_description = "cadCAD is a differential games based simulation software pac monte carlo analysis and other common numerical methods is provided." setup(name='cadCAD', - version='0.2', + version='0.2.2', description="cadCAD: a differential games based simulation software package for research, validation, and \ Computer Aided Design of economic systems", long_description=long_description, From 2de989db0ac300032906eea496d5299918bf48c2 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 16 May 2019 12:51:56 -0400 Subject: [PATCH 13/19] fefactored env_process --- .gitignore | 2 +- cadCAD/configuration/__init__.py | 2 +- cadCAD/configuration/utils/__init__.py | 51 +++-- .../configuration/utils/userDefinedObject.py | 2 +- cadCAD/engine/simulation.py | 51 ++++- cadCAD/utils/sys_config.py | 28 ++- ...-any.whl => cadCAD-0.2.2-py3-none-any.whl} | Bin 14927 -> 15013 bytes requirements.txt | 3 +- simulations/historical_state_access_run.py | 1 - simulations/param_sweep_test.py | 3 +- simulations/regression_tests/sweep_config.py | 151 ++++++------- simulations/regression_tests/udo.py | 107 +++++----- .../udo_inter_substep_update.py} | 7 +- .../{validation => tutorials}/marbles.py | 0 .../{validation => tutorials}/marbles2.py | 198 ++++++++++-------- .../{validation => tutorials}/marbles2_run.py | 2 +- .../robot-marbles-agents-advanced.ipynb} | 0 simulations/udo_run.py | 3 +- simulations/udo_test.py | 1 - .../validation/historical_state_access.py | 29 ++- 20 files changed, 344 insertions(+), 297 deletions(-) rename dist/{cadCAD-0.2-py3-none-any.whl => cadCAD-0.2.2-py3-none-any.whl} (64%) rename simulations/{validation/udo.py => regression_tests/udo_inter_substep_update.py} (98%) rename simulations/{validation => tutorials}/marbles.py (100%) rename simulations/{validation => tutorials}/marbles2.py (54%) rename simulations/{validation => tutorials}/marbles2_run.py (89%) rename simulations/{validation/robot-marbles-agents-advanced-udo.ipynb => tutorials/robot-marbles-agents-advanced.ipynb} (100%) diff --git a/.gitignore b/.gitignore index 5194f30..adec4a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.idea +jupyter notebook.idea .ipynb_checkpoints .DS_Store .idea diff --git a/cadCAD/configuration/__init__.py b/cadCAD/configuration/__init__.py index 0bae909..8720047 100644 --- a/cadCAD/configuration/__init__.py +++ b/cadCAD/configuration/__init__.py @@ -28,7 +28,7 @@ class Configuration(object): sanitize_config(self) - +# ToDo: Remove Seeds def append_configs(sim_configs={}, initial_state={}, seeds={}, raw_exogenous_states={}, env_processes={}, partial_state_update_blocks={}, policy_ops=[lambda a, b: a + b], _exo_update_per_ts: bool = True) -> None: if _exo_update_per_ts is True: diff --git a/cadCAD/configuration/utils/__init__.py b/cadCAD/configuration/utils/__init__.py index 1e69c66..9345fa3 100644 --- a/cadCAD/configuration/utils/__init__.py +++ b/cadCAD/configuration/utils/__init__.py @@ -134,20 +134,43 @@ def trigger_condition(s, conditions, cond_opp): condition_bools = [s[field] in precondition_values for field, precondition_values in conditions.items()] return reduce(cond_opp, condition_bools) -def apply_state_condition(conditions, cond_opp, y, f, _g, step, sL, s, _input): - if trigger_condition(s, conditions, cond_opp): +def apply_state_condition(pre_conditions, cond_opp, y, f, _g, step, sL, s, _input): + if trigger_condition(s, pre_conditions, cond_opp): return f(_g, step, sL, s, _input) else: return y, s[y] -def proc_trigger(y, f, conditions, cond_op): - return lambda _g, step, sL, s, _input: apply_state_condition(conditions, cond_op, y, f, _g, step, sL, s, _input) +def var_trigger(y, f, pre_conditions, cond_op): + return lambda _g, step, sL, s, _input: apply_state_condition(pre_conditions, cond_op, y, f, _g, step, sL, s, _input) -def timestep_trigger(end_substep, y, f): - conditions = {'substep': [0, end_substep]} - cond_opp = lambda a, b: a and b - return proc_trigger(y, f, conditions, cond_opp) +def var_substep_trigger(substeps): + def trigger(end_substep, y, f): + pre_conditions = {'substep': substeps} + cond_opp = lambda a, b: a and b + return var_trigger(y, f, pre_conditions, cond_opp) + return lambda y, f: curry(trigger)(substeps)(y)(f) + + +def env_trigger(end_substep): + def trigger(end_substep, trigger_field, trigger_vals, funct_list): + def env_update(state_dict, target_value): + state_dict_copy = deepcopy(state_dict) + # Use supstep to simulate current sysMetrics + if state_dict_copy['substep'] == end_substep: + state_dict_copy['timestep'] = state_dict_copy['timestep'] + 1 + + if state_dict_copy[trigger_field] in trigger_vals: + for g in funct_list: + target_value = g(target_value) + + del state_dict_copy + return target_value + + return env_update + + return lambda trigger_field, trigger_vals, funct_list: \ + curry(trigger)(end_substep)(trigger_field)(trigger_vals)(funct_list) # trigger = curry(_trigger) # print(timestep_trigger) @@ -157,19 +180,15 @@ def timestep_trigger(end_substep, y, f): def config_sim(d): def process_variables(d): return flatten_tabulated_dict(tabulate_dict(d)) + if "M" in d: - return [ - { - "N": d["N"], - "T": d["T"], - "M": M - } - for M in process_variables(d["M"]) - ] + return [{"N": d["N"], "T": d["T"], "M": M} for M in process_variables(d["M"])] else: d["M"] = [{}] return d +def psub_list(psu_block, psu_steps): + return [psu_block[psu] for psu in psu_steps] def psub(policies, state_updates): return { diff --git a/cadCAD/configuration/utils/userDefinedObject.py b/cadCAD/configuration/utils/userDefinedObject.py index c013952..c1f532c 100644 --- a/cadCAD/configuration/utils/userDefinedObject.py +++ b/cadCAD/configuration/utils/userDefinedObject.py @@ -27,7 +27,7 @@ class udcView(object): } members['methods'] = [k for k, v in self.__dict__.items() if str(type(v)) == ""] members.update(variables) - return f"{members}" + return f"{members}" #[1:-1] class udcBroker(object): diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 36d4067..a6e4750 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -3,6 +3,7 @@ from typing import Any, Callable, Dict, List, Tuple from pathos.pools import ThreadPool as TPool from copy import deepcopy from functools import reduce +from funcy import compose from cadCAD.engine.utils import engine_exception from cadCAD.utils import flatten @@ -72,19 +73,48 @@ class Executor: # return {k: reduce(f1, val_list) for k, val_list in new_dict.items()} # return foldr(call, col_results)(ops) + # def apply_env_proc( + # self, + # env_processes: Dict[str, Callable], + # state_dict: Dict[str, Any], + # time_step: int + # ) -> Dict[str, Any]: + # for state in state_dict.keys(): + # if state in list(env_processes.keys()): + # env_state: Callable = env_processes[state] + # if (env_state.__name__ == '_curried') or (env_state.__name__ == 'proc_trigger'): + # state_dict[state] = env_state(sub_step)(state_dict[state]) + # else: + # state_dict[state] = env_state(state_dict[state]) + # + # return state_dict + def apply_env_proc( self, env_processes: Dict[str, Callable], state_dict: Dict[str, Any], - sub_step: int ) -> Dict[str, Any]: - for state in state_dict.keys(): - if state in list(env_processes.keys()): - env_state: Callable = env_processes[state] - if (env_state.__name__ == '_curried') or (env_state.__name__ == 'proc_trigger'): - state_dict[state] = env_state(sub_step)(state_dict[state]) - else: - state_dict[state] = env_state(state_dict[state]) + + def env_composition(target_field, state_dict, target_value): + function_type = type(lambda x: x) + env_update = env_processes[target_field] + if isinstance(env_update, list): + target_value = compose(*env_update[::-1])(target_value) + elif isinstance(env_update, function_type): + target_value = env_update(state_dict, target_value) + else: + target_value = env_update + + return target_value + + filtered_state_dict = {k: v for k, v in state_dict.items() if k in env_processes.keys()} + env_proc_dict = { + target_field: env_composition(target_field, state_dict, target_value) + for target_field, target_value in filtered_state_dict.items() + } + + for k, v in env_proc_dict.items(): + state_dict[k] = v return state_dict @@ -137,7 +167,10 @@ class Executor: last_in_copy: Dict[str, Any] = transfer_missing_fields(last_in_obj, dict(generate_record(state_funcs))) # ToDo: Remove - last_in_copy: Dict[str, Any] = self.apply_env_proc(env_processes, last_in_copy, last_in_copy['timestep']) + # last_in_copy: Dict[str, Any] = self.apply_env_proc(env_processes, last_in_copy, last_in_copy['timestep']) + last_in_copy: Dict[str, Any] = self.apply_env_proc(env_processes, last_in_copy) + + # ToDo: make 'substep' & 'timestep' reserve fields last_in_copy['substep'], last_in_copy['timestep'], last_in_copy['run'] = sub_step, time_step, run diff --git a/cadCAD/utils/sys_config.py b/cadCAD/utils/sys_config.py index b6e3a92..738161c 100644 --- a/cadCAD/utils/sys_config.py +++ b/cadCAD/utils/sys_config.py @@ -1,5 +1,10 @@ +from copy import deepcopy + +from fn.func import curried + from cadCAD.configuration.utils import ep_time_step, time_step from funcy import curry +import pprint as pp # from fn import _ from functools import reduce @@ -25,7 +30,7 @@ def update_timestamp(y, timedelta, format): def apply(f, y: str, incr_by: int): return lambda _g, step, sL, s, _input: (y, curry(f)(s[y])(incr_by)) -def add(y: str, incr_by: int): +def add(y: str, incr_by): return apply(lambda a, b: a + b, y, incr_by) def increment_state_by_int(y: str, incr_by: int): @@ -85,15 +90,22 @@ def time_model(y, substeps, time_delta, ts_format='%Y-%m-%d %H:%M:%S'): # multi_cond_opp = lambda a, b: a and b # return proc_trigger2(y, f, multi_conditions, multi_cond_opp) +# +# @curried -def env_trigger(trigger_field, trigger_val, input, funct_list): - y, x = input - if trigger_field == trigger_val: - i = 0 - for g in funct_list: - x = g(x) - return y, x + +# print(env_trigger(3).__module__) +# pp.pprint(dir(env_trigger)) + + + +# @curried +# def env_proc_trigger(trigger_time, update_f, time): +# if time == trigger_time: +# return update_f +# else: +# return lambda x: x diff --git a/dist/cadCAD-0.2-py3-none-any.whl b/dist/cadCAD-0.2.2-py3-none-any.whl similarity index 64% rename from dist/cadCAD-0.2-py3-none-any.whl rename to dist/cadCAD-0.2.2-py3-none-any.whl index ccb0606b0f0cdd7bec143ee2f6c9aa93fdd7a47a..c460fad49fc47abda0b3754f25fd6d29afbc7f83 100644 GIT binary patch delta 3964 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 delta 3934 zcmZvfcQ71k)W%nfda-(0WpzonTV>EIZ_TJj0syq|pi@}D z$SDi2gn1e2>-G*c-?0W-m%%V;D`*ZKzO~Lh%5ysys8D3DYNOHmUi-Ys;gVunjjZnC zoUY}pC{O*%Paj=v3bN}b#;%{9`f)C>r*@CsPg;ccQ?v=qHNE%pd$x#U4}_g zirBnlsdp1NvmX5nU24|zX=}l}twBa;AuAm>HLV0zu^dS`m3_g~C+=m;@8aJn3DoxM zkXH?zFU*~I% z_WE8DvX;9OE0KOE7b1q33!CyFFX5$ zmb~Sv1GiJZpq^=~qnS`Ff(fFOzeR(&7Q`M056H-$^GHEE(rR}*n1aZ*?p2mHdOv?> ze#5R~F_|LS@-a{3>jS4zhC}yea^&}!U`w&27*J}GR)*~THxIpa6RqMq8zt^W|}dYqe7!yKiH}ooo|@VP$E-Z`?aZ z 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 0: + return 1 + else: + return 0 + +robot_strategies = [greedy_robot,fair_robot, giving_robot] + +for node in G.nodes: + nstrats = len(robot_strategies) + rv = np.random.randint(0,nstrats) + G.nodes[node]['strat'] = robot_strategies[rv] + +for e in G.edges: + owner_node = e[0] + G.edges[e]['strat'] = G.nodes[owner_node]['strat'] + +default_policy = {'nodes': [], 'edges': {}, 'quantity': {}, 'node_strats': {}, 'edge_strats': {}, 'delta': {}} +class robot(object): + def __init__(self, graph, balls, internal_policy=default_policy): + self.mem_id = str(hex(id(self))) + self.internal_policy = internal_policy + self.graph = graph + self.balls = balls + + + def robotic_network(self, graph, balls): # move balls + self.graph, self.balls = graph, balls + delta_balls = {} + for e in self.graph.edges: + src = e[0] + src_balls = self.balls[src] + dst = e[1] + dst_balls = self.balls[dst] + + # transfer balls according to specific robot strat + strat = self.graph.edges[e]['strat'] + delta_balls[e] = strat(src_balls, dst_balls) + + self.internal_policy = {'nodes': [], 'edges': {}, 'quantity': {}, 'node_strats': {}, 'edge_strats': {}, 'delta': delta_balls} + return self + + def agent_arrival(self, graph, balls): # add node + self.graph, self.balls = graph, balls + node = len(self.graph.nodes) + edge_list = self.graph.edges + + # choose a m random edges without replacement + # new = np.random.choose(edgelist,m) + new = [0, 1] # tester + + nodes = [node] + edges = [(node, new_node) for new_node in new] + + initial_balls = {node: np.random.randint(1, 25)} + + rv = np.random.randint(0, nstrats) + node_strat = {node: robot_strategies[rv]} + + edge_strats = {e: robot_strategies[rv] for e in edges} + + self.internal_policy = {'nodes': nodes, + 'edges': edges, + 'quantity': initial_balls, + 'node_strats': node_strat, + 'edge_strats': edge_strats, + 'delta': np.zeros(node + 1) + } + + return self + +robot_udo = UDO(udo=robot(G, balls), masked_members=['obj']) +initial_conditions = {'balls': balls, 'network': G, 'robot': robot_udo} def update_balls(params, step, sL, s, _input): - delta_balls = _input['delta'] + delta_balls = _input['robot'].internal_policy['delta'] new_balls = s['balls'] for e in G.edges: move_ball = delta_balls[e] @@ -42,20 +130,20 @@ def update_balls(params, step, sL, s, _input): def update_network(params, step, sL, s, _input): - new_nodes = _input['nodes'] - new_edges = _input['edges'] - new_balls = _input['quantity'] + new_nodes = _input['robot'].internal_policy['nodes'] + new_edges = _input['robot'].internal_policy['edges'] + new_balls = _input['robot'].internal_policy['quantity'] graph = s['network'] for node in new_nodes: graph.add_node(node) graph.nodes[node]['initial_balls'] = new_balls[node] - graph.nodes[node]['strat'] = _input['node_strats'][node] + graph.nodes[node]['strat'] = _input['robot'].internal_policy['node_strats'][node] for edge in new_edges: graph.add_edge(edge[0], edge[1]) - graph.edges[edge]['strat'] = _input['edge_strats'][edge] + graph.edges[edge]['strat'] = _input['robot'].internal_policy['edge_strats'][edge] key = 'network' value = graph @@ -63,8 +151,8 @@ def update_network(params, step, sL, s, _input): def update_network_balls(params, step, sL, s, _input): - new_nodes = _input['nodes'] - new_balls = _input['quantity'] + new_nodes = _input['robot'].internal_policy['nodes'] + new_balls = _input['robot'].internal_policy['quantity'] balls = np.zeros(len(s['balls']) + len(new_nodes)) for node in s['network'].nodes: @@ -79,91 +167,17 @@ def update_network_balls(params, step, sL, s, _input): return (key, value) -def greedy_robot(src_balls, dst_balls): - # robot wishes to accumlate balls at its source - # takes half of its neighbors balls - if src_balls < dst_balls: - delta = -np.floor(dst_balls / 2) - else: - delta = 0 - - return delta - - -def fair_robot(src_balls, dst_balls): - # robot follows the simple balancing rule - delta = np.sign(src_balls - dst_balls) - - return delta - - -def giving_robot(src_balls, dst_balls): - # robot wishes to gice away balls one at a time - if src_balls > 0: - delta = 1 - else: - delta = 0 - - return delta - -robot_strategies = [greedy_robot,fair_robot, giving_robot] - -for node in G.nodes: - nstrats = len(robot_strategies) - rv = np.random.randint(0,nstrats) - G.nodes[node]['strat'] = robot_strategies[rv] - -for e in G.edges: - owner_node = e[0] - G.edges[e]['strat'] = G.nodes[owner_node]['strat'] - - def robotic_network(params, step, sL, s): - graph = s['network'] - - delta_balls = {} - for e in graph.edges: - src = e[0] - src_balls = s['balls'][src] - dst = e[1] - dst_balls = s['balls'][dst] - - # transfer balls according to specific robot strat - strat = graph.edges[e]['strat'] - delta_balls[e] = strat(src_balls, dst_balls) - - return_dict = {'nodes': [], 'edges': {}, 'quantity': {}, 'node_strats': {}, 'edge_strats': {}, 'delta': delta_balls} - - return (return_dict) + s['robot'].robotic_network(s['network'], s['balls']) + return {'robot': udoPipe(s['robot'])} def agent_arrival(params, step, sL, s): - node = len(s['network'].nodes) - edge_list = s['network'].edges - - # choose a m random edges without replacement - # new = np.random.choose(edgelist,m) - new = [0, 1] # tester - - nodes = [node] - edges = [(node, new_node) for new_node in new] - - initial_balls = {node: np.random.randint(1, 25)} - - rv = np.random.randint(0, nstrats) - node_strat = {node: robot_strategies[rv]} - - edge_strats = {e: robot_strategies[rv] for e in edges} - - return_dict = {'nodes': nodes, - 'edges': edges, - 'quantity': initial_balls, - 'node_strats': node_strat, - 'edge_strats': edge_strats, - 'delta': np.zeros(node + 1) - } - return (return_dict) + s['robot'].agent_arrival(s['network'], s['balls']) + return {'robot': udoPipe(s['robot'])} +def get_robot(params, step, sL, s, _input): + return 'robot', _input['robot'] partial_state_update_blocks = [ { @@ -172,7 +186,8 @@ partial_state_update_blocks = [ 'p1': robotic_network }, 'variables': { # The following state variables will be updated simultaneously - 'balls': update_balls + 'balls': update_balls, + 'robot': get_robot } }, @@ -183,7 +198,8 @@ partial_state_update_blocks = [ }, 'variables': { # The following state variables will be updated simultaneously 'network': update_network, - 'balls': update_network_balls + 'balls': update_network_balls, + 'robot': get_robot } } ] diff --git a/simulations/validation/marbles2_run.py b/simulations/tutorials/marbles2_run.py similarity index 89% rename from simulations/validation/marbles2_run.py rename to simulations/tutorials/marbles2_run.py index b275b76..b004696 100644 --- a/simulations/validation/marbles2_run.py +++ b/simulations/tutorials/marbles2_run.py @@ -2,7 +2,6 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import marbles2 from cadCAD import configs exec_mode = ExecutionMode() @@ -20,6 +19,7 @@ print("Tensor Field: config1") print(tabulate(tensor_field, headers='keys', tablefmt='psql')) print("Output:") print(tabulate(result, headers='keys', tablefmt='psql')) +print(result[['network']]) print() print(result[['network', 'substep']]) \ No newline at end of file diff --git a/simulations/validation/robot-marbles-agents-advanced-udo.ipynb b/simulations/tutorials/robot-marbles-agents-advanced.ipynb similarity index 100% rename from simulations/validation/robot-marbles-agents-advanced-udo.ipynb rename to simulations/tutorials/robot-marbles-agents-advanced.ipynb diff --git a/simulations/udo_run.py b/simulations/udo_run.py index bbb07ba..319969d 100644 --- a/simulations/udo_run.py +++ b/simulations/udo_run.py @@ -2,10 +2,9 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import udo +from simulations.regression_tests import udo_inter_substep_update from cadCAD import configs - exec_mode = ExecutionMode() print("Simulation Execution: Single Configuration") diff --git a/simulations/udo_test.py b/simulations/udo_test.py index b31713b..6bb9333 100644 --- a/simulations/udo_test.py +++ b/simulations/udo_test.py @@ -5,7 +5,6 @@ from cadCAD.engine import ExecutionMode, ExecutionContext, Executor from simulations.regression_tests import udo from cadCAD import configs - exec_mode = ExecutionMode() print("Simulation Execution: Single Configuration") diff --git a/simulations/validation/historical_state_access.py b/simulations/validation/historical_state_access.py index 05e293d..d4a86ed 100644 --- a/simulations/validation/historical_state_access.py +++ b/simulations/validation/historical_state_access.py @@ -1,25 +1,25 @@ from cadCAD.configuration import append_configs from cadCAD.configuration.utils import config_sim +# Policies per Mechanism +# def p(_g, substep, sH, s): +# return {'last_update_block': sH[-1]} + +# def policies(_g, substep, sH, s, _input): +# y = 'policies' +# x = _input +# return (y, x) + +# policies = {"p1": p, "p2": p} + + # last_partial_state_update_block def last_update_block(_g, substep, sH, s, _input): return 'sh', sH[-1] - -# Policies per Mechanism -def p(_g, substep, sH, s): - return {'last_update_block': sH[-1]} - def add(y, x): return lambda _g, substep, sH, s, _input: (y, s[y] + x) -def policies(_g, substep, sH, s, _input): - y = 'policies' - x = _input - return (y, x) - -policies = {"p1": p, "p2": p} - genesis_states = { 's': 0, 'sh': [{}], # {[], {}} @@ -34,7 +34,7 @@ variables = { PSUB = { - "policies": policies, + "policies": {}, #policies, "variables": variables } @@ -58,6 +58,5 @@ append_configs( seeds={}, raw_exogenous_states={}, env_processes={}, - partial_state_update_blocks=partial_state_update_block, - policy_ops=[lambda a, b: a + b] + partial_state_update_blocks=partial_state_update_block ) From 7f28bae21a793b441d4af4f18dc5bed0f1cad0d8 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 16 May 2019 16:33:53 -0400 Subject: [PATCH 14/19] changed proc_trigger to var_trigger in sweep test --- simulations/regression_tests/sweep_config.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/simulations/regression_tests/sweep_config.py b/simulations/regression_tests/sweep_config.py index 21d2c49..3db1750 100644 --- a/simulations/regression_tests/sweep_config.py +++ b/simulations/regression_tests/sweep_config.py @@ -5,7 +5,7 @@ from funcy import compose import pprint from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import proc_trigger, env_trigger, var_substep_trigger, config_sim, env_proc_trigger, \ +from cadCAD.configuration.utils import var_trigger, env_trigger, var_substep_trigger, config_sim, env_proc_trigger, \ time_step, psub_list from typing import Dict, List @@ -94,9 +94,9 @@ def update_timestamp(_g, step, sL, s, _input): for m in ['m1','m2','m3']: # psu_block[m]["variables"]['timestamp'] = update_timestamp psu_block[m]["variables"]['timestamp'] = var_timestep_trigger(y='timestamp', f=update_timestamp) - # psu_block[m]["variables"]['timestamp'] = proc_trigger( - # y='timestamp', f=update_timestamp, pre_conditions={'substep': [0, system_substeps]}, cond_op=lambda a, b: a and b - # ) + psu_block[m]["variables"]['timestamp'] = var_trigger( + y='timestamp', f=update_timestamp, pre_conditions={'substep': [0, system_substeps]}, cond_op=lambda a, b: a and b + ) proc_one_coef_A = 0.7 def es3p1(_g, step, sL, s, _input): @@ -128,7 +128,7 @@ genesis_states = { # Environment Process # ToDo: Validate - make env proc trigger field agnostic env_process["s3"] = [lambda x: x + 1, lambda x: x + 1] -env_process["s4"] = env_timestep_trigger(trigger_field='timestep', trigger_vals=[5], funct_list=[lambda x: 1, lambda x: x + 2]) +env_process["s4"] = env_timestep_trigger(trigger_field='field', trigger_vals=[5], funct_list=[lambda x: 1, lambda x: x + 2]) # config_sim Necessary @@ -156,4 +156,3 @@ print("Policie State Update Block:") pp.pprint(partial_state_update_blocks) print() print() - From f224df3ed4bcbc619fe59686a65e96737b1176c8 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Fri, 31 May 2019 11:25:54 -0400 Subject: [PATCH 15/19] param sweep patch --- cadCAD/configuration/__init__.py | 1 + cadCAD/configuration/utils/__init__.py | 39 +- cadCAD/engine/__init__.py | 20 +- cadCAD/engine/simulation.py | 56 +- dist/cadCAD-0.2.2-py3-none-any.whl | Bin 15013 -> 0 bytes dist/cadCAD-0.2.3-py3-none-any.whl | Bin 0 -> 15601 bytes setup.py | 2 +- .../{single_config_run.py => config1_run.py} | 6 +- simulations/config2_run.py | 25 + simulations/external_ds_run.py | 5 +- ...externalds_run.py => external_ds_write.py} | 2 +- simulations/multi_config_run.py | 2 +- simulations/param_sweep_run.py | 2 +- simulations/param_sweep_test.py | 6 +- simulations/policy_agg_run.py | 23 + .../external_dataset.py | 16 +- .../historical_state_access.py | 85 + .../policy_aggregation.py | 33 +- simulations/regression_tests/sweep_config.py | 42 +- simulations/udo_run.py | 2 +- simulations/udo_test.py | 2 +- simulations/validation/config1.py | 73 +- simulations/validation/config2.py | 56 +- .../validation/conviction_cadCAD.ipynb | 1743 +++++++++++++++++ simulations/validation/conviction_helpers.py | 150 ++ .../validation/conviction_system_logic.py | 548 ++++++ .../validation/historical_state_access.py | 62 - .../historical_state_access_run.py | 11 +- 28 files changed, 2755 insertions(+), 257 deletions(-) delete mode 100644 dist/cadCAD-0.2.2-py3-none-any.whl create mode 100644 dist/cadCAD-0.2.3-py3-none-any.whl rename simulations/{single_config_run.py => config1_run.py} (78%) create mode 100644 simulations/config2_run.py rename simulations/{externalds_run.py => external_ds_write.py} (95%) create mode 100644 simulations/policy_agg_run.py rename simulations/{validation => regression_tests}/external_dataset.py (97%) create mode 100644 simulations/regression_tests/historical_state_access.py rename simulations/{validation => regression_tests}/policy_aggregation.py (69%) create mode 100644 simulations/validation/conviction_cadCAD.ipynb create mode 100644 simulations/validation/conviction_helpers.py create mode 100644 simulations/validation/conviction_system_logic.py delete mode 100644 simulations/validation/historical_state_access.py rename simulations/{ => validation}/historical_state_access_run.py (73%) diff --git a/cadCAD/configuration/__init__.py b/cadCAD/configuration/__init__.py index 8720047..120b9ad 100644 --- a/cadCAD/configuration/__init__.py +++ b/cadCAD/configuration/__init__.py @@ -17,6 +17,7 @@ class Configuration(object): def __init__(self, sim_config={}, initial_state={}, seeds={}, env_processes={}, exogenous_states={}, partial_state_update_blocks={}, policy_ops=[lambda a, b: a + b], **kwargs) -> None: + # print(exogenous_states) self.sim_config = sim_config self.initial_state = initial_state self.seeds = seeds diff --git a/cadCAD/configuration/utils/__init__.py b/cadCAD/configuration/utils/__init__.py index 9345fa3..a4ab297 100644 --- a/cadCAD/configuration/utils/__init__.py +++ b/cadCAD/configuration/utils/__init__.py @@ -2,6 +2,7 @@ from datetime import datetime, timedelta from decimal import Decimal from copy import deepcopy from functools import reduce +from pprint import pprint from fn.func import curried from funcy import curry @@ -38,13 +39,14 @@ def bound_norm_random(rng, low, high): res = rng.normal((high+low)/2, (high-low)/6) if res < low or res > high: res = bound_norm_random(rng, low, high) - return Decimal(res) + # return Decimal(res) + return float(res) @curried -def env_proc_trigger(trigger_time, update_f, time): - if time == trigger_time: - return update_f +def env_proc_trigger(timestep, f, time): + if time == timestep: + return f else: return lambda x: x @@ -130,8 +132,8 @@ def exo_update_per_ts(ep): return {es: ep_decorator(f, es) for es, f in ep.items()} -def trigger_condition(s, conditions, cond_opp): - condition_bools = [s[field] in precondition_values for field, precondition_values in conditions.items()] +def trigger_condition(s, pre_conditions, cond_opp): + condition_bools = [s[field] in precondition_values for field, precondition_values in pre_conditions.items()] return reduce(cond_opp, condition_bools) def apply_state_condition(pre_conditions, cond_opp, y, f, _g, step, sL, s, _input): @@ -149,12 +151,13 @@ def var_substep_trigger(substeps): pre_conditions = {'substep': substeps} cond_opp = lambda a, b: a and b return var_trigger(y, f, pre_conditions, cond_opp) + return lambda y, f: curry(trigger)(substeps)(y)(f) def env_trigger(end_substep): def trigger(end_substep, trigger_field, trigger_vals, funct_list): - def env_update(state_dict, target_value): + def env_update(state_dict, sweep_dict, target_value): state_dict_copy = deepcopy(state_dict) # Use supstep to simulate current sysMetrics if state_dict_copy['substep'] == end_substep: @@ -162,7 +165,7 @@ def env_trigger(end_substep): if state_dict_copy[trigger_field] in trigger_vals: for g in funct_list: - target_value = g(target_value) + target_value = g(sweep_dict, target_value) del state_dict_copy return target_value @@ -182,6 +185,7 @@ def config_sim(d): return flatten_tabulated_dict(tabulate_dict(d)) if "M" in d: + # print([{"N": d["N"], "T": d["T"], "M": M} for M in process_variables(d["M"])]) return [{"N": d["N"], "T": d["T"], "M": M} for M in process_variables(d["M"])] else: d["M"] = [{}] @@ -203,4 +207,21 @@ def genereate_psubs(policy_grid, states_grid, policies, state_updates): filtered_state_updates = {k: v for (k, v) in state_updates.items() if k in state_list} PSUBS.append(psub(filtered_policies, filtered_state_updates)) - return PSUBS \ No newline at end of file + return PSUBS + +def access_block(sH, y, psu_block_offset, exculsion_list=[]): + exculsion_list += [y] + def filter_history(key_list, sH): + filter = lambda key_list: \ + lambda d: {k: v for k, v in d.items() if k not in key_list} + return list(map(filter(key_list), sH)) + + if psu_block_offset < -1: + if len(sH) >= abs(psu_block_offset): + return filter_history(exculsion_list, sH[psu_block_offset]) + else: + return [] + elif psu_block_offset < 0: + return filter_history(exculsion_list, sH[psu_block_offset]) + else: + return [] \ No newline at end of file diff --git a/cadCAD/engine/__init__.py b/cadCAD/engine/__init__.py index aeed31a..75c1e51 100644 --- a/cadCAD/engine/__init__.py +++ b/cadCAD/engine/__init__.py @@ -27,9 +27,11 @@ def single_proc_exec( Ts: List[range], Ns: List[int] ): - + # print(env_processes_list) + # print(configs_structs) l = [simulation_execs, states_lists, configs_structs, env_processes_list, Ts, Ns] simulation_exec, states_list, config, env_processes, T, N = list(map(lambda x: x.pop(), l)) + # print(config.env_processes) result = simulation_exec(var_dict_list, states_list, config, env_processes, T, N) return flatten(result) @@ -66,7 +68,7 @@ class Executor: self.exec_method = exec_context.method self.exec_context = exec_context.name self.configs = configs - self.main = self.execute + # self.main = self.execute def execute(self) -> Tuple[List[Dict[str, Any]], DataFrame]: config_proc = Processor() @@ -76,6 +78,7 @@ class Executor: var_dict_list, states_lists, Ts, Ns, eps, configs_structs, env_processes_list, partial_state_updates, simulation_execs = \ [], [], [], [], [], [], [], [], [] config_idx = 0 + print(self.configs) for x in self.configs: Ts.append(x.sim_config['T']) @@ -84,6 +87,7 @@ class Executor: states_lists.append([x.initial_state]) eps.append(list(x.exogenous_states.values())) configs_structs.append(config_proc.generate_config(x.initial_state, x.partial_state_updates, eps[config_idx])) + # print(env_processes_list) env_processes_list.append(x.env_processes) partial_state_updates.append(x.partial_state_updates) simulation_execs.append(SimExecutor(x.policy_ops).simulation) @@ -98,12 +102,12 @@ class Executor: result = self.exec_method(simulation_execs, var_dict_list, states_lists, configs_structs, env_processes_list, Ts, Ns) final_result = result, tensor_field elif self.exec_context == ExecutionMode.multi_proc: - if len(self.configs) > 1: - simulations = self.exec_method(simulation_execs, var_dict_list, states_lists, configs_structs, env_processes_list, Ts, Ns) - results = [] - for result, partial_state_updates, ep in list(zip(simulations, partial_state_updates, eps)): - results.append((flatten(result), create_tensor_field(partial_state_updates, ep))) + # if len(self.configs) > 1: + simulations = self.exec_method(simulation_execs, var_dict_list, states_lists, configs_structs, env_processes_list, Ts, Ns) + results = [] + for result, partial_state_updates, ep in list(zip(simulations, partial_state_updates, eps)): + results.append((flatten(result), create_tensor_field(partial_state_updates, ep))) - final_result = results + final_result = results return final_result diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index a6e4750..d278633 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -28,7 +28,7 @@ class Executor: # get_behavior_input # sL: State Window def get_policy_input( self, - var_dict: Dict[str, List[Any]], + sweep_dict: Dict[str, List[Any]], sub_step: int, sL: List[Dict[str, Any]], s: Dict[str, Any], @@ -39,8 +39,8 @@ class Executor: ops = self.policy_ops - def get_col_results(var_dict, sub_step, sL, s, funcs): - return list(map(lambda f: f(var_dict, sub_step, sL, s), funcs)) + def get_col_results(sweep_dict, sub_step, sL, s, funcs): + return list(map(lambda f: f(sweep_dict, sub_step, sL, s), funcs)) def compose(init_reduction_funct, funct_list, val_list): result, i = None, 0 @@ -53,7 +53,7 @@ class Executor: result = g(result) return result - col_results = get_col_results(var_dict, sub_step, sL, s, funcs) + col_results = get_col_results(sweep_dict, sub_step, sL, s, funcs) key_set = list(set(list(reduce(lambda a, b: a + b, list(map(lambda x: list(x.keys()), col_results)))))) new_dict = {k: [] for k in key_set} for d in col_results: @@ -73,24 +73,9 @@ class Executor: # return {k: reduce(f1, val_list) for k, val_list in new_dict.items()} # return foldr(call, col_results)(ops) - # def apply_env_proc( - # self, - # env_processes: Dict[str, Callable], - # state_dict: Dict[str, Any], - # time_step: int - # ) -> Dict[str, Any]: - # for state in state_dict.keys(): - # if state in list(env_processes.keys()): - # env_state: Callable = env_processes[state] - # if (env_state.__name__ == '_curried') or (env_state.__name__ == 'proc_trigger'): - # state_dict[state] = env_state(sub_step)(state_dict[state]) - # else: - # state_dict[state] = env_state(state_dict[state]) - # - # return state_dict - def apply_env_proc( self, + sweep_dict, env_processes: Dict[str, Callable], state_dict: Dict[str, Any], ) -> Dict[str, Any]: @@ -99,9 +84,10 @@ class Executor: function_type = type(lambda x: x) env_update = env_processes[target_field] if isinstance(env_update, list): - target_value = compose(*env_update[::-1])(target_value) + for f in env_update: + target_value = f(sweep_dict, target_value) elif isinstance(env_update, function_type): - target_value = env_update(state_dict, target_value) + target_value = env_update(state_dict, sweep_dict, target_value) else: target_value = env_update @@ -122,7 +108,7 @@ class Executor: # mech_step def partial_state_update( self, - var_dict: Dict[str, List[Any]], + sweep_dict: Dict[str, List[Any]], sub_step: int, sL: Any, sH: Any, @@ -150,13 +136,13 @@ class Executor: # print(last_in_obj) # print(sH[-1]) - _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sH, last_in_obj, policy_funcs)) + _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(sweep_dict, sub_step, sH, last_in_obj, policy_funcs)) # ToDo: add env_proc generator to `last_in_copy` iterator as wrapper function # ToDo: Can be multithreaded ?? def generate_record(state_funcs): for f in state_funcs: - yield self.state_update_exception(f(var_dict, sub_step, sH, last_in_obj, _input)) + yield self.state_update_exception(f(sweep_dict, sub_step, sH, last_in_obj, _input)) def transfer_missing_fields(source, destination): for k in source: @@ -168,7 +154,9 @@ class Executor: last_in_copy: Dict[str, Any] = transfer_missing_fields(last_in_obj, dict(generate_record(state_funcs))) # ToDo: Remove # last_in_copy: Dict[str, Any] = self.apply_env_proc(env_processes, last_in_copy, last_in_copy['timestep']) - last_in_copy: Dict[str, Any] = self.apply_env_proc(env_processes, last_in_copy) + # print(env_processes) + # print() + last_in_copy: Dict[str, Any] = self.apply_env_proc(sweep_dict, env_processes, last_in_copy) # ToDo: make 'substep' & 'timestep' reserve fields @@ -185,7 +173,7 @@ class Executor: # mech_pipeline - state_update_block def state_update_pipeline( self, - var_dict: Dict[str, List[Any]], + sweep_dict: Dict[str, List[Any]], simulation_list, #states_list: List[Dict[str, Any]], configs: List[Tuple[List[Callable], List[Callable]]], env_processes: Dict[str, Callable], @@ -229,7 +217,7 @@ class Executor: for [s_conf, p_conf] in configs: # tensor field states_list: List[Dict[str, Any]] = self.partial_state_update( - var_dict, sub_step, states_list, simulation_list, s_conf, p_conf, env_processes, time_step, run + sweep_dict, sub_step, states_list, simulation_list, s_conf, p_conf, env_processes, time_step, run ) # print(sub_step) # print(simulation_list) @@ -244,7 +232,7 @@ class Executor: # state_update_pipeline def run_pipeline( self, - var_dict: Dict[str, List[Any]], + sweep_dict: Dict[str, List[Any]], states_list: List[Dict[str, Any]], configs: List[Tuple[List[Callable], List[Callable]]], env_processes: Dict[str, Callable], @@ -262,7 +250,7 @@ class Executor: # print(simulation_list) for time_step in time_seq: pipe_run: List[Dict[str, Any]] = self.state_update_pipeline( - var_dict, simulation_list, configs, env_processes, time_step, run + sweep_dict, simulation_list, configs, env_processes, time_step, run ) _, *pipe_run = pipe_run @@ -276,7 +264,7 @@ class Executor: # configs: List[Tuple[List[Callable], List[Callable]]] def simulation( self, - var_dict: Dict[str, List[Any]], + sweep_dict: Dict[str, List[Any]], states_list: List[Dict[str, Any]], configs: List[Tuple[List[Callable], List[Callable]]], env_processes: Dict[str, Callable], @@ -284,7 +272,7 @@ class Executor: runs: int ) -> List[List[Dict[str, Any]]]: - def execute_run(var_dict, states_list, configs, env_processes, time_seq, run) -> List[Dict[str, Any]]: + def execute_run(sweep_dict, states_list, configs, env_processes, time_seq, run) -> List[Dict[str, Any]]: run += 1 def generate_init_sys_metrics(genesis_states_list): @@ -294,14 +282,14 @@ class Executor: states_list_copy: List[Dict[str, Any]] = list(generate_init_sys_metrics(deepcopy(states_list))) - first_timestep_per_run: List[Dict[str, Any]] = self.run_pipeline(var_dict, states_list_copy, configs, env_processes, time_seq, run) + first_timestep_per_run: List[Dict[str, Any]] = self.run_pipeline(sweep_dict, states_list_copy, configs, env_processes, time_seq, run) del states_list_copy return first_timestep_per_run pipe_run: List[List[Dict[str, Any]]] = flatten( TPool().map( - lambda run: execute_run(var_dict, states_list, configs, env_processes, time_seq, run), + lambda run: execute_run(sweep_dict, states_list, configs, env_processes, time_seq, run), list(range(runs)) ) ) diff --git a/dist/cadCAD-0.2.2-py3-none-any.whl b/dist/cadCAD-0.2.2-py3-none-any.whl deleted file mode 100644 index c460fad49fc47abda0b3754f25fd6d29afbc7f83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15013 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 diff --git a/dist/cadCAD-0.2.3-py3-none-any.whl b/dist/cadCAD-0.2.3-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..19d55e7fc5efae02e7b3c981fa143f9ce6f710e1 GIT binary patch literal 15601 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 literal 0 HcmV?d00001 diff --git a/setup.py b/setup.py index 1aaf50c..8e98475 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ long_description = "cadCAD is a differential games based simulation software pac monte carlo analysis and other common numerical methods is provided." setup(name='cadCAD', - version='0.2.2', + version='0.2.3', description="cadCAD: a differential games based simulation software package for research, validation, and \ Computer Aided Design of economic systems", long_description=long_description, diff --git a/simulations/single_config_run.py b/simulations/config1_run.py similarity index 78% rename from simulations/single_config_run.py rename to simulations/config1_run.py index f2fb874..6e6e9c4 100644 --- a/simulations/single_config_run.py +++ b/simulations/config1_run.py @@ -2,10 +2,8 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -# from simulations.validation import policy_aggregation from simulations.validation import config1 -# from simulations.validation import externalds -# from simulations.validation import external_dataset + from cadCAD import configs exec_mode = ExecutionMode() @@ -16,7 +14,7 @@ first_config = configs # only contains config1 single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) run = Executor(exec_context=single_proc_ctx, configs=first_config) -raw_result, tensor_field = run.main() +raw_result, tensor_field = run.execute() result = pd.DataFrame(raw_result) print() print("Tensor Field: config1") diff --git a/simulations/config2_run.py b/simulations/config2_run.py new file mode 100644 index 0000000..890f356 --- /dev/null +++ b/simulations/config2_run.py @@ -0,0 +1,25 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.validation import config2 + +from cadCAD import configs + +exec_mode = ExecutionMode() + +print("Simulation Execution: Single Configuration") +print() +first_config = configs # only contains config2 +# print(configs[0].env_processes) +single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) +run = Executor(exec_context=single_proc_ctx, configs=first_config) + +raw_result, tensor_field = run.execute() +result = pd.DataFrame(raw_result) +print() +print("Tensor Field: config1") +print(tabulate(tensor_field, headers='keys', tablefmt='psql')) +print("Output:") +print(tabulate(result, headers='keys', tablefmt='psql')) +print() diff --git a/simulations/external_ds_run.py b/simulations/external_ds_run.py index 0c1f16b..7c4eec4 100644 --- a/simulations/external_ds_run.py +++ b/simulations/external_ds_run.py @@ -5,7 +5,6 @@ from cadCAD.engine import ExecutionMode, ExecutionContext, Executor # from simulations.validation import config1_test_pipe # from simulations.validation import config1 # from simulations.validation import externalds -from simulations.validation import external_dataset from cadCAD import configs exec_mode = ExecutionMode() @@ -16,10 +15,10 @@ first_config = configs # only contains config1 single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) run = Executor(exec_context=single_proc_ctx, configs=first_config) -raw_result, tensor_field = run.main() +raw_result, tensor_field = run.execute() result = pd.DataFrame(raw_result) result = pd.concat([result, result['external_data'].apply(pd.Series)], axis=1)[ - ['run', 'substep', 'timestep', 'increment', 'external_data', 'ds1', 'ds2', 'ds3', 'policies'] + ['run', 'substep', 'timestep', 'increment', 'external_data', 'policies', 'ds1', 'ds2', 'ds3', ] ] print() print("Tensor Field: config1") diff --git a/simulations/externalds_run.py b/simulations/external_ds_write.py similarity index 95% rename from simulations/externalds_run.py rename to simulations/external_ds_write.py index 9edcf8c..23fe62f 100644 --- a/simulations/externalds_run.py +++ b/simulations/external_ds_write.py @@ -17,7 +17,7 @@ run = Executor(exec_context=single_proc_ctx, configs=first_config) raw_result, _ = run.main() result = pd.DataFrame(raw_result) -result.to_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/output.csv', index=False) +result.to_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/external_data/output.csv', index=False) print("Output:") print(tabulate(result, headers='keys', tablefmt='psql')) diff --git a/simulations/multi_config_run.py b/simulations/multi_config_run.py index 28cee65..045ef84 100644 --- a/simulations/multi_config_run.py +++ b/simulations/multi_config_run.py @@ -13,7 +13,7 @@ run = Executor(exec_context=multi_proc_ctx, configs=configs) i = 0 config_names = ['config1', 'config2'] -for raw_result, tensor_field in run.main(): +for raw_result, tensor_field in run.execute(): result = pd.DataFrame(raw_result) print() print("Tensor Field: " + config_names[i]) diff --git a/simulations/param_sweep_run.py b/simulations/param_sweep_run.py index 328e774..ace8091 100644 --- a/simulations/param_sweep_run.py +++ b/simulations/param_sweep_run.py @@ -13,7 +13,7 @@ run = Executor(exec_context=multi_proc_ctx, configs=configs) i = 0 config_names = ['sweep_config_A', 'sweep_config_B'] -for raw_result, tensor_field in run.main(): +for raw_result, tensor_field in run.execute(): result = pd.DataFrame(raw_result) print() print("Tensor Field: " + config_names[i]) diff --git a/simulations/param_sweep_test.py b/simulations/param_sweep_test.py index 2b057c3..d114536 100644 --- a/simulations/param_sweep_test.py +++ b/simulations/param_sweep_test.py @@ -1,3 +1,5 @@ +from pprint import pprint + import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order @@ -6,6 +8,8 @@ from cadCAD.engine import ExecutionMode, ExecutionContext, Executor from simulations.regression_tests import sweep_config from cadCAD import configs +# pprint(configs) + exec_mode = ExecutionMode() print("Simulation Execution: Concurrent Execution") @@ -14,7 +18,7 @@ run = Executor(exec_context=multi_proc_ctx, configs=configs) i = 0 config_names = ['sweep_config_A', 'sweep_config_B'] -for raw_result, tensor_field in run.main(): +for raw_result, tensor_field in run.execute(): result = pd.DataFrame(raw_result) print() print("Tensor Field: " + config_names[i]) diff --git a/simulations/policy_agg_run.py b/simulations/policy_agg_run.py new file mode 100644 index 0000000..ec6d564 --- /dev/null +++ b/simulations/policy_agg_run.py @@ -0,0 +1,23 @@ +import pandas as pd +from tabulate import tabulate +# The following imports NEED to be in the exact order +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.regression_tests import policy_aggregation +from cadCAD import configs + +exec_mode = ExecutionMode() + +print("Simulation Execution: Single Configuration") +print() +first_config = configs # only contains config1 +single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) +run = Executor(exec_context=single_proc_ctx, configs=first_config) + +raw_result, tensor_field = run.execute() +result = pd.DataFrame(raw_result) +print() +print("Tensor Field: config1") +print(tabulate(tensor_field, headers='keys', tablefmt='psql')) +print("Output:") +print(tabulate(result, headers='keys', tablefmt='psql')) +print() diff --git a/simulations/validation/external_dataset.py b/simulations/regression_tests/external_dataset.py similarity index 97% rename from simulations/validation/external_dataset.py rename to simulations/regression_tests/external_dataset.py index cc32338..32fd6e6 100644 --- a/simulations/validation/external_dataset.py +++ b/simulations/regression_tests/external_dataset.py @@ -5,12 +5,6 @@ from cadCAD.utils import SilentDF df = SilentDF(pd.read_csv('/Users/jjodesty/Projects/DiffyQ-SimCAD/simulations/external_data/output.csv')) -external_data = {'ds1': None, 'ds2': None, 'ds3': None} -state_dict = { - 'increment': 0, - 'external_data': external_data, - 'policies': external_data, -} def query(s, df): return df[ @@ -22,7 +16,7 @@ def p1(_g, substep, sL, s): del result_dict["ds3"] return {k: list(v.values()).pop() for k, v in result_dict.items()} -def p2(_g, step, sL, s): +def p2(_g, substep, sL, s): result_dict = query(s, df).to_dict() del result_dict["ds1"], result_dict["ds2"] return {k: list(v.values()).pop() for k, v in result_dict.items()} @@ -41,6 +35,14 @@ def view_policies(_g, step, sL, s, _input): return 'policies', _input +external_data = {'ds1': None, 'ds2': None, 'ds3': None} +state_dict = { + 'increment': 0, + 'external_data': external_data, + 'policies': external_data +} + + policies = {"p1": p1, "p2": p2} states = {'increment': increment, 'external_data': integrate_ext_dataset, 'policies': view_policies} PSUB = {'policies': policies, 'states': states} diff --git a/simulations/regression_tests/historical_state_access.py b/simulations/regression_tests/historical_state_access.py new file mode 100644 index 0000000..67dce76 --- /dev/null +++ b/simulations/regression_tests/historical_state_access.py @@ -0,0 +1,85 @@ +from cadCAD.configuration import append_configs +from cadCAD.configuration.utils import config_sim, access_block + +policies, variables = {}, {} +exclusion_list = ['nonexsistant', 'last_x', '2nd_to_last_x', '3rd_to_last_x', '4th_to_last_x'] + +# Policies per Mechanism + +# WARNING: DO NOT delete elements from sH + +def last_update(_g, substep, sH, s): + return {"last_x": access_block(sH, "last_x", -1, exclusion_list)} +policies["last_x"] = last_update + +def second2last_update(_g, substep, sH, s): + return {"2nd_to_last_x": access_block(sH, "2nd_to_last_x", -2, exclusion_list)} +policies["2nd_to_last_x"] = second2last_update + + +# Internal States per Mechanism + +# WARNING: DO NOT delete elements from sH +def add(y, x): + return lambda _g, substep, sH, s, _input: (y, s[y] + x) +variables['x'] = add('x', 1) + +# last_partial_state_update_block +def nonexsistant(_g, substep, sH, s, _input): + return 'nonexsistant', access_block(sH, "nonexsistant", 0, exclusion_list) +variables['nonexsistant'] = nonexsistant + +# last_partial_state_update_block +def last_x(_g, substep, sH, s, _input): + return 'last_x', _input["last_x"] +variables['last_x'] = last_x + +# 2nd to last partial state update block +def second_to_last_x(_g, substep, sH, s, _input): + return '2nd_to_last_x', _input["2nd_to_last_x"] +variables['2nd_to_last_x'] = second_to_last_x + +# 3rd to last partial state update block +def third_to_last_x(_g, substep, sH, s, _input): + return '3rd_to_last_x', access_block(sH, "3rd_to_last_x", -3, exclusion_list) +variables['3rd_to_last_x'] = third_to_last_x + +# 4th to last partial state update block +def fourth_to_last_x(_g, substep, sH, s, _input): + return '4th_to_last_x', access_block(sH, "4th_to_last_x", -4, exclusion_list) +variables['4th_to_last_x'] = fourth_to_last_x + + +genesis_states = { + 'x': 0, + 'nonexsistant': [], + 'last_x': [], + '2nd_to_last_x': [], + '3rd_to_last_x': [], + '4th_to_last_x': [] +} + +PSUB = { + "policies": policies, + "variables": variables +} + +partial_state_update_block = { + "PSUB1": PSUB, + "PSUB2": PSUB, + "PSUB3": PSUB +} + +sim_config = config_sim( + { + "N": 1, + "T": range(3), + } +) + + +append_configs( + sim_configs=sim_config, + initial_state=genesis_states, + partial_state_update_blocks=partial_state_update_block +) diff --git a/simulations/validation/policy_aggregation.py b/simulations/regression_tests/policy_aggregation.py similarity index 69% rename from simulations/validation/policy_aggregation.py rename to simulations/regression_tests/policy_aggregation.py index 7a65216..35d4745 100644 --- a/simulations/validation/policy_aggregation.py +++ b/simulations/regression_tests/policy_aggregation.py @@ -2,7 +2,7 @@ import numpy as np from cadCAD.configuration import append_configs from cadCAD.configuration.utils import config_sim -# ToDo: Use + seeds = { 'z': np.random.RandomState(1), 'a': np.random.RandomState(2), @@ -13,19 +13,19 @@ seeds = { # Policies per Mechanism def p1m1(_g, step, sL, s): - return {'param1': 1} + return {'policy1': 1} def p2m1(_g, step, sL, s): - return {'param2': 2} + return {'policy2': 2} def p1m2(_g, step, sL, s): - return {'param1': 2, 'param2': 2} + return {'policy1': 2, 'policy2': 2} def p2m2(_g, step, sL, s): - return {'param1': 2, 'param2': 2} + return {'policy1': 2, 'policy2': 2} def p1m3(_g, step, sL, s): - return {'param1': 1, 'param2': 2, 'param3': 3} + return {'policy1': 1, 'policy2': 2, 'policy3': 3} def p2m3(_g, step, sL, s): - return {'param1': 1, 'param2': 2, 'param3': 3} + return {'policy1': 1, 'policy2': 2, 'policy3': 3} # Internal States per Mechanism @@ -37,22 +37,19 @@ def policies(_g, step, sH, s, _input): x = _input return (y, x) + # Genesis States genesis_states = { 'policies': {}, - 's1': 0, - 's2': 0, + 's1': 0 } -raw_exogenous_states = {} - -env_processes = {} - variables = { 's1': add('s1', 1), - 's2': add('s2', 1), "policies": policies } +# test_varablies = deepcopy(variables) +# test_varablies['test'] = test partial_state_update_block = { "m1": { @@ -87,12 +84,14 @@ sim_config = config_sim( ) + +# Aggregation == Reduce Map / Reduce Map Aggregation +# ToDo: subsequent functions should accept the entire datastructure +# using env functions (include in reg test using / for env proc) append_configs( sim_configs=sim_config, initial_state=genesis_states, seeds=seeds, - raw_exogenous_states=raw_exogenous_states, - env_processes=env_processes, partial_state_update_blocks=partial_state_update_block, - policy_ops=[lambda a, b: a + b, lambda y: y + 10, lambda y: y + 30] + policy_ops=[lambda a, b: a + b, lambda y: y * 2] # Default: lambda a, b: a + b ToDO: reduction function requires high lvl explanation ) \ No newline at end of file diff --git a/simulations/regression_tests/sweep_config.py b/simulations/regression_tests/sweep_config.py index 3db1750..c72e489 100644 --- a/simulations/regression_tests/sweep_config.py +++ b/simulations/regression_tests/sweep_config.py @@ -1,7 +1,6 @@ from decimal import Decimal import numpy as np from datetime import timedelta -from funcy import compose import pprint from cadCAD.configuration import append_configs @@ -22,6 +21,8 @@ seeds = { # Optional g: Dict[str, List[int]] = { 'alpha': [1], + # 'beta': [2], + # 'gamma': [3], 'beta': [2, 5], 'gamma': [3, 4], 'omega': [7] @@ -29,7 +30,7 @@ g: Dict[str, List[int]] = { psu_steps = ['m1', 'm2', 'm3'] system_substeps = len(psu_steps) -var_timestep_trigger = var_substep_trigger(system_substeps) +var_timestep_trigger = var_substep_trigger([0, system_substeps]) env_timestep_trigger = env_trigger(system_substeps) env_process = {} psu_block = {k: {"policies": {}, "variables": {}} for k in psu_steps} @@ -67,6 +68,7 @@ def s1m1(_g, step, sL, s, _input): psu_block['m1']["variables"]['s1'] = s1m1 def s2m1(_g, step, sL, s, _input): + print(_g) return 's2', _g['beta'] psu_block['m1']["variables"]['s2'] = s2m1 @@ -94,22 +96,22 @@ def update_timestamp(_g, step, sL, s, _input): for m in ['m1','m2','m3']: # psu_block[m]["variables"]['timestamp'] = update_timestamp psu_block[m]["variables"]['timestamp'] = var_timestep_trigger(y='timestamp', f=update_timestamp) - psu_block[m]["variables"]['timestamp'] = var_trigger( - y='timestamp', f=update_timestamp, pre_conditions={'substep': [0, system_substeps]}, cond_op=lambda a, b: a and b - ) + # psu_block[m]["variables"]['timestamp'] = var_trigger( + # y='timestamp', f=update_timestamp, pre_conditions={'substep': [0, system_substeps]}, cond_op=lambda a, b: a and b + # ) -proc_one_coef_A = 0.7 -def es3p1(_g, step, sL, s, _input): - return 's3', s['s3'] +proc_one_coef = 0.7 +def es3(_g, step, sL, s, _input): + return 's3', s['s3'] + proc_one_coef # use `timestep_trigger` to update every ts for m in ['m1','m2','m3']: - psu_block[m]["variables"]['s3'] = var_timestep_trigger(y='s3', f=es3p1) + psu_block[m]["variables"]['s3'] = var_timestep_trigger(y='s3', f=es3) -proc_one_coef_B = 1.3 -def es4p2(_g, step, sL, s, _input): - return 's4', s['s4'] #+ 4 #g['gamma'] + proc_one_coef_B + +def es4(_g, step, sL, s, _input): + return 's4', s['s4'] + _g['gamma'] for m in ['m1','m2','m3']: - psu_block[m]["variables"]['s4'] = var_timestep_trigger(y='s4', f=es4p2) + psu_block[m]["variables"]['s4'] = var_timestep_trigger(y='s4', f=es4) # ToDo: The number of values entered in sweep should be the # of config objs created, @@ -119,16 +121,18 @@ for m in ['m1','m2','m3']: # Genesis States genesis_states = { - 's1': Decimal(0.0), - 's2': Decimal(0.0), - 's3': Decimal(1.0), - 's4': Decimal(1.0), + 's1': 0.0, + 's2': 0.0, + 's3': 1.0, + 's4': 1.0, 'timestamp': '2018-10-01 15:16:24' } + + # Environment Process # ToDo: Validate - make env proc trigger field agnostic -env_process["s3"] = [lambda x: x + 1, lambda x: x + 1] -env_process["s4"] = env_timestep_trigger(trigger_field='field', trigger_vals=[5], funct_list=[lambda x: 1, lambda x: x + 2]) +env_process["s3"] = [lambda _g, x: _g['beta'], lambda _g, x: x + 1] +env_process["s4"] = env_timestep_trigger(trigger_field='timestep', trigger_vals=[5], funct_list=[lambda _g, x: _g['beta']]) # config_sim Necessary diff --git a/simulations/udo_run.py b/simulations/udo_run.py index 319969d..4b77a28 100644 --- a/simulations/udo_run.py +++ b/simulations/udo_run.py @@ -21,7 +21,7 @@ cols = [ 'udo_policy_tracker_a', 'udo_policies', 'udo_policy_tracker_b', 'timestamp' ] -raw_result, tensor_field = run.main() +raw_result, tensor_field = run.execute() result = pd.DataFrame(raw_result)[['run', 'substep', 'timestep'] + cols] # result = pd.concat([result.drop(['c'], axis=1), result['c'].apply(pd.Series)], axis=1) diff --git a/simulations/udo_test.py b/simulations/udo_test.py index 6bb9333..f4cf6d9 100644 --- a/simulations/udo_test.py +++ b/simulations/udo_test.py @@ -21,7 +21,7 @@ cols = [ 'udo_policies', 'udo_policy_tracker', 'timestamp' ] -raw_result, tensor_field = run.main() +raw_result, tensor_field = run.execute() result = pd.DataFrame(raw_result)[['run', 'substep', 'timestep'] + cols] # result = pd.concat([result.drop(['c'], axis=1), result['c'].apply(pd.Series)], axis=1) diff --git a/simulations/validation/config1.py b/simulations/validation/config1.py index 13602a6..14ec2e1 100644 --- a/simulations/validation/config1.py +++ b/simulations/validation/config1.py @@ -7,9 +7,12 @@ from datetime import timedelta from cadCAD.configuration.utils.policyAggregation import get_base_value from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import env_proc_trigger, bound_norm_random, ep_time_step, config_sim +from cadCAD.configuration.utils import env_proc_trigger, bound_norm_random, ep_time_step, config_sim, time_step, \ + env_trigger + +# from cadCAD.configuration.utils import timestep_trigger +from simulations.regression_tests.sweep_config import var_timestep_trigger -from cadCAD.configuration.utils import timestep_trigger seeds = { 'z': np.random.RandomState(1), @@ -70,59 +73,40 @@ def policies(_g, step, sL, s, _input): return (y, x) - - # Exogenous States proc_one_coef_A = 0.7 proc_one_coef_B = 1.3 -def es3p1(_g, step, sL, s, _input): +def es3(_g, step, sL, s, _input): y = 's3' x = s['s3'] * bound_norm_random(seeds['a'], proc_one_coef_A, proc_one_coef_B) return (y, x) -def es4p2(_g, step, sL, s, _input): +def es4(_g, step, sL, s, _input): y = 's4' x = s['s4'] * bound_norm_random(seeds['b'], proc_one_coef_A, proc_one_coef_B) return (y, x) -ts_format = '%Y-%m-%d %H:%M:%S' -t_delta = timedelta(days=0, minutes=0, seconds=1) -def es5p2(_g, step, sL, s, _input): - y = 'timestep' - x = ep_time_step(s, dt_str=s['timestep'], fromat_str=ts_format, _timedelta=t_delta) - return (y, x) - - -# Environment States -def env_a(x): - return 5 -def env_b(x): - return 10 -# def what_ever(x): -# return x + 1 +def update_timestamp(_g, step, sL, s, _input): + y = 'timestamp' + return y, time_step(dt_str=s[y], dt_format='%Y-%m-%d %H:%M:%S', _timedelta=timedelta(days=0, minutes=0, seconds=1)) # Genesis States genesis_states = { - 's1': Decimal(0.0), - 's2': Decimal(0.0), - 's3': Decimal(1.0), - 's4': Decimal(1.0) -# 'timestep': '2018-10-01 15:16:24' + 's1': 0.0, + 's2': 0.0, + 's3': 1.0, + 's4': 1.0, + 'timestamp': '2018-10-01 15:16:24' } -# raw_exogenous_states = { -# "s3": es3p1, -# "s4": es4p2, -# # "timestep": es5p2 -# } - - +# Environment Process +# ToDo: Depreciation Waring for env_proc_trigger convention env_processes = { - "s3": env_a, - "s4": env_proc_trigger(1, env_b) + "s3": [lambda _g, x: 5], + "s4": env_trigger(3)(trigger_field='timestep', trigger_vals=[1], funct_list=[lambda _g, x: 10]) } @@ -135,8 +119,9 @@ partial_state_update_blocks = { "variables": { "s1": s1m1, "s2": s2m1, - "s3": es3p1, - "s4": es4p2, + "s3": es3, + "s4": es4, + "timestamp": update_timestamp } }, "m2": { @@ -147,8 +132,8 @@ partial_state_update_blocks = { "variables": { "s1": s1m2, "s2": s2m2, - # "s3": timestep_trigger(3, 's3', es3p1), - # "s4": timestep_trigger(3, 's4', es4p2), + # "s3": es3p1, + # "s4": es4p2, } }, "m3": { @@ -159,8 +144,8 @@ partial_state_update_blocks = { "variables": { "s1": s1m3, "s2": s2m3, - # "s3": timestep_trigger(3, 's3', es3p1), - # "s4": timestep_trigger(3, 's4', es4p2), + # "s3": es3p1, + # "s4": es4p2, } } } @@ -176,9 +161,9 @@ sim_config = config_sim( append_configs( sim_configs=sim_config, initial_state=genesis_states, - seeds=seeds, - raw_exogenous_states={}, #raw_exogenous_states, - env_processes={}, #env_processes, + # seeds=seeds, + # raw_exogenous_states=raw_exogenous_states, + env_processes=env_processes, partial_state_update_blocks=partial_state_update_blocks, policy_ops=[lambda a, b: a + b] ) \ No newline at end of file diff --git a/simulations/validation/config2.py b/simulations/validation/config2.py index c93efa3..5c36b46 100644 --- a/simulations/validation/config2.py +++ b/simulations/validation/config2.py @@ -1,15 +1,15 @@ -from decimal import Decimal import numpy as np from datetime import timedelta from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import env_proc_trigger, bound_norm_random, ep_time_step, config_sim +from cadCAD.configuration.utils import env_proc_trigger, bound_norm_random, ep_time_step, config_sim, env_trigger, \ + time_step seeds = { 'z': np.random.RandomState(1), 'a': np.random.RandomState(2), 'b': np.random.RandomState(3), - 'c': np.random.RandomState(3) + 'c': np.random.RandomState(4) } @@ -63,56 +63,38 @@ def s2m3(_g, step, sL, s, _input): proc_one_coef_A = 0.7 proc_one_coef_B = 1.3 -def es3p1(_g, step, sL, s, _input): +def es3(_g, step, sL, s, _input): y = 's3' x = s['s3'] * bound_norm_random(seeds['a'], proc_one_coef_A, proc_one_coef_B) return (y, x) -def es4p2(_g, step, sL, s, _input): +def es4(_g, step, sL, s, _input): y = 's4' x = s['s4'] * bound_norm_random(seeds['b'], proc_one_coef_A, proc_one_coef_B) return (y, x) -ts_format = '%Y-%m-%d %H:%M:%S' -t_delta = timedelta(days=0, minutes=0, seconds=1) -def es5p2(_g, step, sL, s, _input): - y = 'timestep' - x = ep_time_step(s, dt_str=s['timestep'], fromat_str=ts_format, _timedelta=t_delta) - return (y, x) - - -# Environment States -def env_a(x): - return 10 -def env_b(x): - return 10 -# def what_ever(x): -# return x + 1 +def update_timestamp(_g, step, sL, s, _input): + y = 'timestamp' + return y, time_step(dt_str=s[y], dt_format='%Y-%m-%d %H:%M:%S', _timedelta=timedelta(days=0, minutes=0, seconds=1)) # Genesis States genesis_states = { - 's1': Decimal(0.0), - 's2': Decimal(0.0), - 's3': Decimal(1.0), - 's4': Decimal(1.0), -# 'timestep': '2018-10-01 15:16:24' -} - - -raw_exogenous_states = { - "s3": es3p1, - "s4": es4p2, -# "timestep": es5p2 + 's1': 0, + 's2': 0, + 's3': 1, + 's4': 1, + 'timestamp': '2018-10-01 15:16:24' } +# Environment Process +# ToDo: Depreciation Waring for env_proc_trigger convention env_processes = { - "s3": env_proc_trigger(1, env_a), - "s4": env_proc_trigger(1, env_b) + "s3": [lambda _g, x: 5], + "s4": env_trigger(3)(trigger_field='timestep', trigger_vals=[2], funct_list=[lambda _g, x: 10]) } - partial_state_update_block = { "m1": { "policies": { @@ -122,6 +104,9 @@ partial_state_update_block = { "states": { "s1": s1m1, # "s2": s2m1 + "s3": es3, + "s4": es4, + "timestep": update_timestamp } }, "m2": { @@ -159,7 +144,6 @@ append_configs( sim_configs=sim_config, initial_state=genesis_states, seeds=seeds, - raw_exogenous_states=raw_exogenous_states, env_processes=env_processes, partial_state_update_blocks=partial_state_update_block ) \ No newline at end of file diff --git a/simulations/validation/conviction_cadCAD.ipynb b/simulations/validation/conviction_cadCAD.ipynb new file mode 100644 index 0000000..c7b219f --- /dev/null +++ b/simulations/validation/conviction_cadCAD.ipynb @@ -0,0 +1,1743 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from cadCAD.engine import ExecutionMode, ExecutionContext, Executor\n", + "from cadCAD.configuration import Configuration\n", + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import scipy.stats as sts\n", + "import pandas as pd\n", + "import seaborn as sns\n", + "import matplotlib.colors as colors\n", + "import matplotlib.cm as cmx\n", + "\n", + "%matplotlib inline\n", + "\n", + "#import conviction files\n", + "from conviction_helpers import *\n", + "from conviction_system_logic import *" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook uses the differential games framework developed by BlockScience. It is currently in private beta, and building towards a full open source release.\n", + "\n", + "**Description:**\n", + "\n", + "cadCAD is a Python library that assists in the processes of designing, testing and validating complex systems through simulation. At its core, cadCAD is a differential games engine that supports parameter sweeping and Monte Carlo analyses and can be easily integrated with other scientific computing Python modules and data science workflows.\n", + "\n", + "To learn more about cadCAD, follow our [tutorial series](https://github.com/BlockScience/cadCAD-Tutorials/tree/master/01%20Tutorials)\n", + "\n", + "**Installing cadCAD:**\n", + "\n", + "cadCAD is in private beta. Tokens are issued to participants. Replace `` in the installation URL below\n", + "```bash\n", + "pip3 install cadCAD --extra-index-url https://@repo.fury.io/blockscience/\n", + "```\n", + "\n", + "If you'd like to participate in the beta program, contact cadcad [at] block [dot] science.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "#THIS policy is one of the main paramters of this system!\n", + "\n", + "#maximum share of funds a proposal can take\n", + "default_beta = .2 #later we should set this to be param so we can sweep it\n", + "# tuning param for the trigger function\n", + "default_rho = default_beta**2\n", + "\n", + "def trigger_threshold(requested, funds, supply, beta=default_beta , rho=default_rho):\n", + " \n", + " share = requested/funds\n", + " if share < beta:\n", + " return rho*supply/(beta-share)**2\n", + " else: \n", + " return np.inf" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/Zargham/Documents/GitHub/conviction/conviction_helpers.py:96: RuntimeWarning: divide by zero encountered in log10\n", + " demo_data_Z0[sof_ind,ts_ind] = np.log10(trigger)\n", + "/Users/Zargham/Documents/GitHub/conviction/conviction_helpers.py:98: RuntimeWarning: invalid value encountered in double_scalars\n", + " demo_data_Z2[sof_ind,ts_ind] = trigger/tc #share of maximum possible conviction\n", + "/Users/Zargham/Documents/GitHub/conviction/conviction_helpers.py:99: RuntimeWarning: invalid value encountered in double_scalars\n", + " demo_data_Z3[sof_ind,ts_ind] = np.log10(trigger/tc)\n" + ] + } + ], + "source": [ + "dict1 = trigger_sweep('token_supply',trigger_threshold)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.15940000000000001, 1008990000.0, 'fixed alpha = 0.5')" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "trigger_plotter(dict1['share_of_funds'],\n", + " dict1['log10_trigger'], \n", + " 'log10_trigger',\n", + " dict1['total_supply'],\n", + " 'Token Supply')\n", + "axis = plt.axis()\n", + "plt.text(.2*axis[0]+.8*axis[1],axis[-1]*1.01, 'fixed alpha = 0.5')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "dict2 = trigger_sweep('alpha',trigger_threshold)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['log10_trigger', 'trigger', 'share_of_max_conv', 'log10_share_of_max_conv', 'alpha', 'share_of_funds'])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict2.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "trigger_plotter(dict2['share_of_funds'],\n", + " dict2['log10_share_of_max_conv'], \n", + " 'Log10 Share of Conviction Required to Pass',\n", + " dict2['alpha'],\n", + " 'alpha')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "n= 25 #initial participants\n", + "m= 3 #initial proposals\n", + "\n", + "initial_sentiment = .5\n", + "\n", + "network, initial_funds, initial_supply, total_requested = initialize_network(n,m,total_funds_given_total_supply,trigger_threshold)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note from Kris, consider: substitutibility of proposals st when a substitute passes, affinity for the others goes away; this will make the process more realistic because proposals will end up never passing.\n", + "\n", + "implementation notes:\n", + "- create substitutability matrix (proposal x proposal)\n", + "- update accounting when thing pass: change affinities and should affect sentiments\n", + "- define a new 'type' of proposals for tracking 'dead' ones (no longer candidates = zero tokens in support)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "proposals = get_nodes_by_type(network, 'proposal')\n", + "participants = get_nodes_by_type(network, 'participant')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'type': 'proposal',\n", + " 'conviction': 0,\n", + " 'status': 'candidate',\n", + " 'age': 0,\n", + " 'funds_requested': 63248.8961832528,\n", + " 'trigger': inf}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#sample proposal\n", + "network.nodes[proposals[0]]" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'type': 'participant',\n", + " 'holdings': 753.5821088000997,\n", + " 'sentiment': 0.20260793282468248}" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#sample participant\n", + "network.nodes[participants[0]]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'affinity': 0.13023927236764288, 'tokens': 0, 'conviction': 0}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#sample relationship participant to proposal\n", + "network.edges[(participants[0],proposals[0])]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Explore the data created in initial conditions!" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'Histogram of Participants Token Holdings')" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.hist([ network.nodes[i]['holdings'] for i in participants])\n", + "plt.title('Histogram of Participants Token Holdings')" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'Histogram of Proposals Funds Requested')" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.hist([ network.nodes[i]['funds_requested'] for i in proposals])\n", + "plt.title('Histogram of Proposals Funds Requested')" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'Histogram of Affinities between Participants and Proposals')" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.hist([ network.edges[e]['affinity'] for e in network.edges])\n", + "plt.title('Histogram of Affinities between Participants and Proposals')" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'Histogram of Affinities between Participants and Proposals weighted by holdings')" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.hist([ network.edges[e]['affinity'] for e in network.edges], weights = [network.nodes[e[0]]['holdings']for e in network.edges],alpha = 1)\n", + "plt.title('Histogram of Affinities between Participants and Proposals weighted by holdings')" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "affinities = np.empty((n,m))\n", + "for i_ind in range(n):\n", + " for j_ind in range(m):\n", + " i = participants[i_ind]\n", + " j = proposals[j_ind]\n", + " affinities[i_ind][j_ind] = network.edges[(i,j)]['affinity']" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 106.33200000000001, 'participant_id')" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "dims = (20, 5)\n", + "fig, ax = plt.subplots(figsize=dims)\n", + "\n", + "sns.heatmap(affinities.T,\n", + " xticklabels=participants,\n", + " yticklabels=proposals,\n", + " square=True,\n", + " cbar=True,\n", + " ax=ax)\n", + "\n", + "plt.title('affinities between participants and proposals')\n", + "plt.ylabel('proposal_id')\n", + "plt.xlabel('participant_id')" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.5]\n" + ] + } + ], + "source": [ + "#power of 1 token forever\n", + "conviction_capactity = [2]\n", + "alpha = [1-1/cc for cc in conviction_capactity]\n", + "print(alpha)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "params= {\n", + " 'sensitivity': [.75],\n", + " 'tmin': [7], #unit days; minimum periods passed before a proposal can pass\n", + " 'sentiment_decay': [.001], #termed mu in the state update function\n", + " 'alpha': alpha,\n", + " 'base_completion_rate': [10],\n", + " 'trigger_func': [trigger_threshold]\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n", + "# Settings of general simulation parameters, unrelated to the system itself\n", + "# `T` is a range with the number of discrete units of time the simulation will run for;\n", + "# `N` is the number of times the simulation will be run (Monte Carlo runs)\n", + "time_periods_per_run = 250\n", + "monte_carlo_runs = 1\n", + "\n", + "simulation_parameters = {\n", + " 'T': range(time_periods_per_run),\n", + " 'N': monte_carlo_runs,\n", + " 'M': params\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "initial_conditions = {'network':network,\n", + " 'supply': initial_supply,\n", + " 'funds':initial_funds,\n", + " 'sentiment': initial_sentiment}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#functions for partial state update block 1\n", + "\n", + "def gen_new_participant(network, new_participant_holdings):\n", + " \n", + " i = len([node for node in network.nodes])\n", + " \n", + " network.add_node(i)\n", + " network.nodes[i]['type']=\"participant\"\n", + " \n", + " s_rv = np.random.rand() \n", + " network.nodes[i]['sentiment'] = s_rv\n", + " network.nodes[i]['holdings']=new_participant_holdings\n", + " \n", + " for j in get_nodes_by_type(network, 'proposal'):\n", + " network.add_edge(i, j)\n", + " \n", + " rv = np.random.rand()\n", + " a_rv = 1-4*(1-rv)*rv #polarized distribution\n", + " network.edges[(i, j)]['affinity'] = a_rv\n", + " network.edges[(i,j)]['tokens'] = a_rv*network.nodes[i]['holdings']\n", + " network.edges[(i, j)]['conviction'] = 0\n", + " \n", + " return network\n", + " \n", + "\n", + "def gen_new_proposal(network, funds, supply):\n", + " j = len([node for node in network.nodes])\n", + " network.add_node(j)\n", + " network.nodes[j]['type']=\"proposal\"\n", + " \n", + " network.nodes[j]['conviction']=0\n", + " network.nodes[j]['status']='candidate'\n", + " network.nodes[j]['age']=0\n", + " \n", + " rescale = 10000*funds/initial_funds\n", + " r_rv = gamma.rvs(3,loc=0.001, scale=rescale)\n", + " network.node[j]['funds_requested'] = r_rv\n", + " \n", + " network.nodes[j]['trigger']= trigger_threshold(r_rv, funds, supply)\n", + " \n", + " participants = get_nodes_by_type(network, 'participant')\n", + " proposing_participant = np.random.choice(participants)\n", + " \n", + " for i in participants:\n", + " network.add_edge(i, j)\n", + " if i==proposing_participant:\n", + " network.edges[(i, j)]['affinity']=1\n", + " else:\n", + " rv = np.random.rand()\n", + " a_rv = 1-4*(1-rv)*rv #polarized distribution\n", + " network.edges[(i, j)]['affinity'] = a_rv\n", + " \n", + " network.edges[(i, j)]['conviction'] = 0\n", + " network.edges[(i,j)]['tokens'] = 0\n", + " return network\n", + " \n", + " \n", + "\n", + "def driving_process(params, step, sL, s):\n", + " \n", + " #placeholder plumbing for random processes\n", + " arrival_rate = 10/s['sentiment']\n", + " rv1 = np.random.rand()\n", + " new_participant = bool(rv1<1/arrival_rate)\n", + " if new_participant:\n", + " h_rv = expon.rvs(loc=0.0, scale=1000)\n", + " new_participant_holdings = h_rv\n", + " else:\n", + " new_participant_holdings = 0\n", + " \n", + " network = s['network']\n", + " affinities = [network.edges[e]['affinity'] for e in network.edges ]\n", + " median_affinity = np.median(affinities)\n", + " \n", + " proposals = get_nodes_by_type(network, 'proposal')\n", + " fund_requests = [network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate' ]\n", + " \n", + " funds = s['funds']\n", + " total_funds_requested = np.sum(fund_requests)\n", + " \n", + " proposal_rate = 10/median_affinity * total_funds_requested/funds\n", + " rv2 = np.random.rand()\n", + " new_proposal = bool(rv2<1/proposal_rate)\n", + " \n", + " sentiment = s['sentiment']\n", + " funds = s['funds']\n", + " scale_factor = 1+4000*sentiment**2\n", + " \n", + " #this shouldn't happen but expon is throwing domain errors\n", + " if scale_factor > 1: \n", + " funds_arrival = expon.rvs(loc = 0, scale = scale_factor )\n", + " else:\n", + " funds_arrival = 0\n", + " \n", + " return({'new_participant':new_participant,\n", + " 'new_participant_holdings':new_participant_holdings,\n", + " 'new_proposal':new_proposal, \n", + " 'funds_arrival':funds_arrival})\n", + "\n", + "def update_network(params, step, sL, s, _input):\n", + " \n", + " network = s['network']\n", + " funds = s['funds']\n", + " supply = s['supply']\n", + " #placeholder plumbing for new proposals and new participants\n", + " new_participant = _input['new_participant'] #T/F\n", + " new_proposal = _input['new_proposal'] #T/F\n", + " # IF THEN logic to create new nodes // left out for now since always FALSE\n", + " if new_participant:\n", + " new_participant_holdings = _input['new_participant_holdings']\n", + " network = gen_new_participant(network, new_participant_holdings)\n", + " \n", + " if new_proposal:\n", + " network= gen_new_proposal(network,funds,supply )\n", + " \n", + " #update age of the existing proposals\n", + " proposals = get_nodes_by_type(network, 'proposal')\n", + " \n", + " for j in proposals:\n", + " network.nodes[j]['age'] = network.nodes[j]['age']+1\n", + " if network.nodes[j]['status'] == 'candidate':\n", + " requested = network.nodes[j]['funds_requested']\n", + " network.nodes[j]['trigger'] = trigger_threshold(requested, funds, supply)\n", + " else:\n", + " network.nodes[j]['trigger'] = np.nan\n", + " \n", + " key = 'network'\n", + " value = network\n", + " \n", + " return (key, value)\n", + "\n", + "def increment_funds(params, step, sL, s, _input):\n", + " \n", + " funds = s['funds']\n", + " funds_arrival = _input['funds_arrival']\n", + "\n", + " #increment funds\n", + " funds = funds + funds_arrival\n", + " \n", + " key = 'funds'\n", + " value = funds\n", + " \n", + " return (key, value)\n", + "\n", + "def increment_supply(params, step, sL, s, _input):\n", + " \n", + " supply = s['supply']\n", + " supply_arrival = _input['new_participant_holdings']\n", + "\n", + " #increment funds\n", + " supply = supply + supply_arrival\n", + " \n", + " key = 'supply'\n", + " value = supply\n", + " \n", + " return (key, value)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#partial state update block 2\n", + "def check_progress(params, step, sL, s):\n", + " \n", + " network = s['network']\n", + " proposals = get_nodes_by_type(network, 'proposal')\n", + " \n", + " completed = []\n", + " for j in proposals:\n", + " if network.nodes[j]['status'] == 'active':\n", + " grant_size = network.nodes[j]['funds_requested']\n", + " likelihood = 1.0/(min_completion_rate+np.log(grant_size))\n", + " if np.random.rand() < likelihood:\n", + " completed.append(j)\n", + " \n", + " return({'completed':completed})\n", + "\n", + "def complete_proposal(params, step, sL, s, _input):\n", + " \n", + " network = s['network']\n", + " participants = get_nodes_by_type(network, 'participant')\n", + " \n", + " completed = _input['completed']\n", + " for j in completed:\n", + " network.nodes[j]['status']='completed'\n", + " for i in participants:\n", + " force = network.edges[(i,j)]['affinity']\n", + " sentiment = network.node[i]['sentiment']\n", + " network.node[i]['sentiment'] = get_sentimental(sentiment, force, decay=False)\n", + " \n", + " key = 'network'\n", + " value = network\n", + " \n", + " return (key, value)\n", + "\n", + "def update_sentiment_on_completion(params, step, sL, s, _input):\n", + " \n", + " network = s['network']\n", + " proposals = get_nodes_by_type(network, 'proposal')\n", + " completed = _input['completed']\n", + " \n", + " grants_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='active'])\n", + " \n", + " grants_completed = np.sum([network.nodes[j]['funds_requested'] for j in completed])\n", + " \n", + " sentiment = s['sentiment']\n", + " \n", + " force = grants_completed/grants_outstanding\n", + " if (force >=0) and (force <=1):\n", + " sentiment = get_sentimental(sentiment, force, True)\n", + " else:\n", + " sentiment = get_sentimental(sentiment, 0, True)\n", + " \n", + " \n", + " key = 'sentiment'\n", + " value = sentiment\n", + " \n", + " return (key, value)\n", + "\n", + "def get_sentimental(sentiment, force, decay=True):\n", + " sentiment = sentiment*(1-int(decay)*mu) + force\n", + " \n", + " if sentiment > 1:\n", + " sentiment = 1\n", + " \n", + " return sentiment" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#partial state update block 3\n", + "def trigger_function(params, step, sL, s):\n", + " \n", + " network = s['network']\n", + " funds = s['funds']\n", + " supply = s['supply']\n", + " proposals = get_nodes_by_type(network, 'proposal')\n", + " \n", + " accepted = []\n", + " triggers = {}\n", + " for j in proposals:\n", + " if network.nodes[j]['status'] == 'candidate':\n", + " requested = network.nodes[j]['funds_requested']\n", + " age = network.nodes[j]['age']\n", + " threshold = trigger_threshold(requested, funds, supply)\n", + " if age > tmin:\n", + " conviction = network.nodes[j]['conviction']\n", + " if conviction >threshold:\n", + " accepted.append(j)\n", + " else:\n", + " threshold = np.nan\n", + " \n", + " triggers[j] = threshold\n", + " \n", + " \n", + " \n", + " return({'accepted':accepted, 'triggers':triggers})\n", + "\n", + "def decrement_funds(params, step, sL, s, _input):\n", + " \n", + " funds = s['funds']\n", + " network = s['network']\n", + " accepted = _input['accepted']\n", + "\n", + " #decrement funds\n", + " for j in accepted:\n", + " funds = funds - network.nodes[j]['funds_requested']\n", + " \n", + " key = 'funds'\n", + " value = funds\n", + " \n", + " return (key, value)\n", + "\n", + "def update_proposals(params, step, sL, s, _input):\n", + " \n", + " network = s['network']\n", + " accepted = _input['accepted']\n", + " triggers = _input['triggers']\n", + " participants = get_nodes_by_type(network, 'participant')\n", + " proposals = get_nodes_by_type(network, 'proposals')\n", + " \n", + " for j in proposals:\n", + " network.nodes[j]['trigger'] = triggers[j]\n", + " \n", + " #bookkeeping conviction and participant sentiment\n", + " for j in accepted:\n", + " network.nodes[j]['status']='active'\n", + " network.nodes[j]['conviction']=np.nan\n", + " #change status to active\n", + " for i in participants:\n", + " \n", + " edge = (i,j)\n", + " #reset tokens assigned to other candidates\n", + " network.edges[(i,j)]['tokens']=0\n", + " network.edges[(i,j)]['conviction'] = np.nan\n", + " \n", + " #update participants sentiments (positive or negative) \n", + " affinities = [network.edges[(i,p)]['affinity'] for p in proposals if not(p in accepted)]\n", + " if len(affinities)>1:\n", + " max_affinity = np.max(affinities)\n", + " force = network.edges[(i,j)]['affinity']-sensitivity*max_affinity\n", + " else:\n", + " force = 0\n", + " \n", + " #based on what their affinities to the accepted proposals\n", + " network.nodes[i]['sentiment'] = get_sentimental(network.nodes[i]['sentiment'], force, False)\n", + " \n", + " \n", + " key = 'network'\n", + " value = network\n", + " \n", + " return (key, value)\n", + "\n", + "def update_sentiment_on_release(params, step, sL, s, _input):\n", + " \n", + " network = s['network']\n", + " proposals = get_nodes_by_type(network, 'proposal')\n", + " accepted = _input['accepted']\n", + " \n", + " proposals_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate'])\n", + " \n", + " proposals_accepted = np.sum([network.nodes[j]['funds_requested'] for j in accepted])\n", + " \n", + " sentiment = s['sentiment']\n", + " force = proposals_accepted/proposals_outstanding\n", + " if (force >=0) and (force <=1):\n", + " sentiment = get_sentimental(sentiment, force, False)\n", + " else:\n", + " sentiment = get_sentimental(sentiment, 0, False)\n", + " \n", + " key = 'sentiment'\n", + " value = sentiment\n", + " \n", + " return (key, value)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "def participants_decisions(params, step, sL, s):\n", + " \n", + " network = s['network']\n", + " participants = get_nodes_by_type(network, 'participant')\n", + " proposals = get_nodes_by_type(network, 'proposal')\n", + " candidates = [j for j in proposals if network.nodes[j]['status']=='candidate']\n", + " \n", + " gain = .01\n", + " delta_holdings={}\n", + " proposals_supported ={}\n", + " for i in participants:\n", + " force = network.nodes[i]['sentiment']-sensitivity\n", + " delta_holdings[i] = network.nodes[i]['holdings']*gain*force\n", + " \n", + " support = []\n", + " for j in candidates:\n", + " affinity = network.edges[(i, j)]['affinity']\n", + " cutoff = sensitivity*np.max([network.edges[(i,p)]['affinity'] for p in candidates])\n", + " if cutoff <.5:\n", + " cutoff = .5\n", + " \n", + " if affinity > cutoff:\n", + " support.append(j)\n", + " \n", + " proposals_supported[i] = support\n", + " \n", + " return({'delta_holdings':delta_holdings, 'proposals_supported':proposals_supported})\n", + "\n", + "def update_tokens(params, step, sL, s, _input):\n", + " \n", + " network = s['network']\n", + " delta_holdings = _input['delta_holdings']\n", + " proposals = get_nodes_by_type(network, 'proposal')\n", + " proposals_supported = _input['proposals_supported']\n", + " participants = get_nodes_by_type(network, 'participant')\n", + " \n", + " for i in participants:\n", + " network.nodes[i]['holdings'] = network.nodes[i]['holdings']+delta_holdings[i]\n", + " supported = proposals_supported[i]\n", + " total_affinity = np.sum([ network.edges[(i, j)]['affinity'] for j in supported])\n", + " for j in proposals:\n", + " if j in supported:\n", + " normalized_affinity = network.edges[(i, j)]['affinity']/total_affinity\n", + " network.edges[(i, j)]['tokens'] = normalized_affinity*network.nodes[i]['holdings']\n", + " else:\n", + " network.edges[(i, j)]['tokens'] = 0\n", + " \n", + " prior_conviction = network.edges[(i, j)]['conviction']\n", + " current_tokens = network.edges[(i, j)]['tokens']\n", + " network.edges[(i, j)]['conviction'] =current_tokens+alpha*prior_conviction\n", + " \n", + " for j in proposals:\n", + " network.nodes[j]['conviction'] = np.sum([ network.edges[(i, j)]['conviction'] for i in participants])\n", + " \n", + " key = 'network'\n", + " value = network\n", + " \n", + " return (key, value)\n", + "\n", + "def update_supply(params, step, sL, s, _input):\n", + " \n", + " supply = s['supply']\n", + " delta_holdings = _input['delta_holdings']\n", + " delta_supply = np.sum([v for v in delta_holdings.values()])\n", + " \n", + " supply = supply + delta_supply\n", + " \n", + " key = 'supply'\n", + " value = supply\n", + " \n", + " return (key, value)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n", + "# The Partial State Update Blocks\n", + "partial_state_update_blocks = [\n", + " { \n", + " 'policies': { \n", + " #new proposals or new participants\n", + " 'random': driving_process\n", + " },\n", + " 'variables': {\n", + " 'network': update_network,\n", + " 'funds':increment_funds,\n", + " 'supply':increment_supply\n", + " }\n", + " },\n", + " {\n", + " 'policies': {\n", + " 'completion': check_progress #see if any of the funded proposals completes\n", + " },\n", + " 'variables': { # The following state variables will be updated simultaneously\n", + " 'sentiment': update_sentiment_on_completion, #note completing decays sentiment, completing bumps it\n", + " 'network': complete_proposal #book-keeping\n", + " }\n", + " },\n", + " {\n", + " 'policies': {\n", + " 'release': trigger_function #check each proposal to see if it passes\n", + " },\n", + " 'variables': { # The following state variables will be updated simultaneously\n", + " 'funds': decrement_funds, #funds expended\n", + " 'sentiment': update_sentiment_on_release, #releasing funds can bump sentiment\n", + " 'network': update_proposals #reset convictions, and participants sentiments\n", + " #update based on affinities\n", + " }\n", + " },\n", + " { \n", + " 'policies': { \n", + " 'participants_act': participants_decisions, #high sentiment, high affinity =>buy\n", + " #low sentiment, low affinities => burn\n", + " #assign tokens to top affinities\n", + " },\n", + " 'variables': {\n", + " 'supply': update_supply,\n", + " 'network': update_tokens #update everyones holdings \n", + " #and their conviction for each proposal\n", + " }\n", + " }\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n", + "# The configurations above are then packaged into a `Configuration` object\n", + "config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values\n", + " partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n", + " sim_config=simulation_parameters #dict containing simulation parameters\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "single_proc: []\n", + "[{'sensitivity': [0.75], 'tmin': [7], 'sentiment_decay': [0.001], 'alpha': [0.5], 'base_completion_rate': [10], 'trigger_func': []}]\n", + "\n" + ] + }, + { + "ename": "TypeError", + "evalue": "list indices must be integers or slices, not str", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mexec_context\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mExecutionContext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexec_mode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msingle_proc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mexecutor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mExecutor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexec_context\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# Pass the configuration object inside an array\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mraw_result\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtensor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mexecutor\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# The `main()` method returns a tuple; its first elements contains the raw results\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/cadCAD/engine/__init__.py\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 96\u001b[0m \u001b[0;31m# ToDO: Deprication Handler - \"sanitize\" in appropriate place\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 97\u001b[0m \u001b[0mtensor_field\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcreate_tensor_field\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpartial_state_updates\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0meps\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 98\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexec_method\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msimulation_execs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvar_dict_list\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstates_lists\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfigs_structs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv_processes_list\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mNs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 99\u001b[0m \u001b[0mfinal_result\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtensor_field\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 100\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexec_context\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mExecutionMode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmulti_proc\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/cadCAD/engine/__init__.py\u001b[0m in \u001b[0;36msingle_proc_exec\u001b[0;34m(simulation_execs, var_dict_list, states_lists, configs_structs, env_processes_list, Ts, Ns)\u001b[0m\n\u001b[1;32m 31\u001b[0m \u001b[0ml\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0msimulation_execs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstates_lists\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfigs_structs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv_processes_list\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mNs\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 32\u001b[0m \u001b[0msimulation_exec\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstates_list\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfig\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv_processes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mT\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mN\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mlambda\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ml\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 33\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msimulation_exec\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvar_dict_list\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstates_list\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfig\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv_processes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mT\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mN\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 34\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mflatten\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 35\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/cadCAD/engine/simulation.py\u001b[0m in \u001b[0;36msimulation\u001b[0;34m(self, var_dict, states_list, configs, env_processes, time_seq, runs)\u001b[0m\n\u001b[1;32m 270\u001b[0m TPool().map(\n\u001b[1;32m 271\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mrun\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mexecute_run\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvar_dict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstates_list\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfigs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv_processes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtime_seq\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrun\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 272\u001b[0;31m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mruns\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 273\u001b[0m )\n\u001b[1;32m 274\u001b[0m )\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pathos/threading.py\u001b[0m in \u001b[0;36mmap\u001b[0;34m(self, f, *args, **kwds)\u001b[0m\n\u001b[1;32m 132\u001b[0m \u001b[0mAbstractWorkerPool\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_AbstractWorkerPool__map\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwds\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 133\u001b[0m \u001b[0m_pool\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_serve\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 134\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_pool\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstar\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# chunksize\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 135\u001b[0m \u001b[0mmap\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__doc__\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mAbstractWorkerPool\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmap\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__doc__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 136\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mimap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwds\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/multiprocess/pool.py\u001b[0m in \u001b[0;36mmap\u001b[0;34m(self, func, iterable, chunksize)\u001b[0m\n\u001b[1;32m 264\u001b[0m \u001b[0;32min\u001b[0m \u001b[0ma\u001b[0m \u001b[0mlist\u001b[0m \u001b[0mthat\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0mreturned\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 265\u001b[0m '''\n\u001b[0;32m--> 266\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_map_async\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0miterable\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmapstar\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchunksize\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 267\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 268\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mstarmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0miterable\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchunksize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/multiprocess/pool.py\u001b[0m in \u001b[0;36mget\u001b[0;34m(self, timeout)\u001b[0m\n\u001b[1;32m 642\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_value\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 643\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 644\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_value\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 645\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 646\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_set\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/multiprocess/pool.py\u001b[0m in \u001b[0;36mworker\u001b[0;34m(inqueue, outqueue, initializer, initargs, maxtasks, wrap_exception)\u001b[0m\n\u001b[1;32m 117\u001b[0m \u001b[0mjob\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwds\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtask\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 118\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 119\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwds\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 120\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 121\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mwrap_exception\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mfunc\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0m_helper_reraises_exception\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/multiprocess/pool.py\u001b[0m in \u001b[0;36mmapstar\u001b[0;34m(args)\u001b[0m\n\u001b[1;32m 42\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 43\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmapstar\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 44\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 45\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 46\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mstarmapstar\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pathos/helpers/mp_helper.py\u001b[0m in \u001b[0;36m\u001b[0;34m(args)\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mstarargs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0;34m\"\"\"decorator to convert a many-arg function to a single-arg function\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0mfunc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 16\u001b[0m \u001b[0;31m#func.__module__ = f.__module__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;31m#func.__name__ = f.__name__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/cadCAD/engine/simulation.py\u001b[0m in \u001b[0;36m\u001b[0;34m(run)\u001b[0m\n\u001b[1;32m 269\u001b[0m pipe_run: List[List[Dict[str, Any]]] = flatten(\n\u001b[1;32m 270\u001b[0m TPool().map(\n\u001b[0;32m--> 271\u001b[0;31m \u001b[0;32mlambda\u001b[0m \u001b[0mrun\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mexecute_run\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvar_dict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstates_list\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfigs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv_processes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtime_seq\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrun\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 272\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mruns\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 273\u001b[0m )\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/cadCAD/engine/simulation.py\u001b[0m in \u001b[0;36mexecute_run\u001b[0;34m(var_dict, states_list, configs, env_processes, time_seq, run)\u001b[0m\n\u001b[1;32m 262\u001b[0m \u001b[0mstates_list_copy\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mList\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mDict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mAny\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgenerate_init_sys_metrics\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdeepcopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstates_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 263\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 264\u001b[0;31m \u001b[0mfirst_timestep_per_run\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mList\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mDict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mAny\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_pipeline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvar_dict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstates_list_copy\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfigs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv_processes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtime_seq\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrun\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 265\u001b[0m \u001b[0;32mdel\u001b[0m \u001b[0mstates_list_copy\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 266\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/cadCAD/engine/simulation.py\u001b[0m in \u001b[0;36mrun_pipeline\u001b[0;34m(self, var_dict, states_list, configs, env_processes, time_seq, run)\u001b[0m\n\u001b[1;32m 230\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mtime_step\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtime_seq\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 231\u001b[0m pipe_run: List[Dict[str, Any]] = self.state_update_pipeline(\n\u001b[0;32m--> 232\u001b[0;31m \u001b[0mvar_dict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msimulation_list\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfigs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv_processes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtime_step\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrun\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 233\u001b[0m )\n\u001b[1;32m 234\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/cadCAD/engine/simulation.py\u001b[0m in \u001b[0;36mstate_update_pipeline\u001b[0;34m(self, var_dict, simulation_list, configs, env_processes, time_step, run)\u001b[0m\n\u001b[1;32m 197\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 198\u001b[0m states_list: List[Dict[str, Any]] = self.partial_state_update(\n\u001b[0;32m--> 199\u001b[0;31m \u001b[0mvar_dict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msub_step\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstates_list\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msimulation_list\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ms_conf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mp_conf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv_processes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtime_step\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrun\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 200\u001b[0m )\n\u001b[1;32m 201\u001b[0m \u001b[0;31m# print(sub_step)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/cadCAD/engine/simulation.py\u001b[0m in \u001b[0;36mpartial_state_update\u001b[0;34m(self, var_dict, sub_step, sL, sH, state_funcs, policy_funcs, env_processes, time_step, run)\u001b[0m\n\u001b[1;32m 136\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdestination\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 137\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 138\u001b[0;31m \u001b[0mlast_in_copy\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mDict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mAny\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtransfer_missing_fields\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlast_in_obj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgenerate_record\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstate_funcs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 139\u001b[0m \u001b[0;31m# ToDo: Remove\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 140\u001b[0m \u001b[0mlast_in_copy\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mDict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mAny\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mapply_env_proc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0menv_processes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlast_in_copy\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlast_in_copy\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'timestep'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/cadCAD/engine/simulation.py\u001b[0m in \u001b[0;36mgenerate_record\u001b[0;34m(state_funcs)\u001b[0m\n\u001b[1;32m 127\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mgenerate_record\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstate_funcs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 128\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mf\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mstate_funcs\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 129\u001b[0;31m \u001b[0;32myield\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate_update_exception\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvar_dict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msub_step\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msH\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlast_in_obj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_input\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 130\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtransfer_missing_fields\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msource\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdestination\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/GitHub/conviction/conviction_system_logic.py\u001b[0m in \u001b[0;36mupdate_network\u001b[0;34m(params, step, sL, s, _input)\u001b[0m\n\u001b[1;32m 119\u001b[0m \u001b[0mfunds\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'funds'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 120\u001b[0m \u001b[0msupply\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'supply'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 121\u001b[0;31m \u001b[0mtrigger_func\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'trigger_func'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 122\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 123\u001b[0m \u001b[0mnew_participant\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_input\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'new_participant'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;31m#T/F\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: list indices must be integers or slices, not str" + ] + } + ], + "source": [ + "exec_mode = ExecutionMode()\n", + "exec_context = ExecutionContext(exec_mode.single_proc)\n", + "executor = Executor(exec_context, [config]) # Pass the configuration object inside an array\n", + "raw_result, tensor = executor.main() # The `main()` method returns a tuple; its first elements contains the raw results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.DataFrame(raw_result)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df.tail(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df.supply.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df.sentiment.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df.plot(x='timestep', y='funds')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def pad(vec, length,fill=True):\n", + " \n", + " if fill:\n", + " padded = np.zeros(length,)\n", + " else:\n", + " padded = np.empty(length,)\n", + " padded[:] = np.nan\n", + " \n", + " for i in range(len(vec)):\n", + " padded[i]= vec[i]\n", + " \n", + " return padded\n", + "\n", + "def make2D(key, data, fill=False):\n", + " maxL = data[key].apply(len).max()\n", + " newkey = 'padded_'+key\n", + " data[newkey] = data[key].apply(lambda x: pad(x,maxL,fill))\n", + " reshaped = np.array([a for a in data[newkey].values])\n", + " \n", + " return reshaped" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df['conviction'] = df.network.apply(lambda g: np.array([g.nodes[j]['conviction'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='candidate']))\n", + "df['candidate_count'] = df.network.apply(lambda g: len([j for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='candidate']))\n", + "df['candidate_funds'] = df.network.apply(lambda g: np.sum([g.nodes[j]['funds_requested'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='candidate']))\n", + "df['candidate_funds_requested'] = df.network.apply(lambda g: np.array([g.nodes[j]['funds_requested'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='candidate']))\n", + "df['active_count'] = df.network.apply(lambda g: len([j for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='active']))\n", + "df['active_funds'] = df.network.apply(lambda g: np.sum([g.nodes[j]['funds_requested'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='active']))\n", + "df['completed_count'] = df.network.apply(lambda g: len([j for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='completed']))\n", + "df['completed_funds'] = df.network.apply(lambda g: np.sum([g.nodes[j]['funds_requested'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='completed']))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df['funds_requested'] = df.network.apply(lambda g: np.array([g.nodes[j]['funds_requested'] for j in get_nodes_by_type(g, 'proposal')]))\n", + "df['share_of_funds_requested'] = df.candidate_funds_requested/df.funds\n", + "\n", + "df['share_of_funds_requested_all'] = df.funds_requested/df.funds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df['triggers'] = df.network.apply(lambda g: np.array([g.nodes[j]['trigger'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='candidate' ]))\n", + "df['conviction_share_of_trigger'] = df.conviction/df.triggers\n", + "df['age'] = df.network.apply(lambda g: np.array([g.nodes[j]['age'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='candidate' ]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df['age_all'] = df.network.apply(lambda g: np.array([g.nodes[j]['age'] for j in get_nodes_by_type(g, 'proposal') ]))\n", + "df['conviction_all'] = df.network.apply(lambda g: np.array([g.nodes[j]['conviction'] for j in get_nodes_by_type(g, 'proposal') ]))\n", + "df['triggers_all'] = df.network.apply(lambda g: np.array([g.nodes[j]['trigger'] for j in get_nodes_by_type(g, 'proposal') ]))\n", + "\n", + "df['conviction_share_of_trigger_all'] = df.conviction_all/df.triggers_all" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rdf= df[df.substep==4].copy()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "last_net= df.network.values[-1]\n", + "last_props=get_nodes_by_type(last_net, 'proposal')\n", + "M = len(last_props)\n", + "last_parts=get_nodes_by_type(last_net, 'participant')\n", + "N = len(last_parts)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "affinities = np.empty((N,M))\n", + "for i_ind in range(N):\n", + " for j_ind in range(M):\n", + " i = last_parts[i_ind]\n", + " j = last_props[j_ind]\n", + " affinities[i_ind][j_ind] = last_net.edges[(i,j)]['affinity']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dims = (100, 25)\n", + "fig, ax = plt.subplots(figsize=dims)\n", + "\n", + "sns.heatmap(affinities,\n", + " yticklabels=last_parts,\n", + " xticklabels=last_props,\n", + " square=True,\n", + " cbar=True,\n", + " ax=ax)\n", + "\n", + "plt.title('affinities between participants and proposals')\n", + "plt.xlabel('proposal_id')\n", + "plt.ylabel('participant_id')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#working on deduplicating colors\n", + "#\n", + "#last_props=get_nodes_by_type(last_net, 'proposal')\n", + "#M = len(last_props)\n", + "\n", + "#cm = plt.get_cmap('gist_rainbow')\n", + "#c= [cm(1.*j/M) for j in range(M)] " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "make2D('age_all', rdf)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(rdf.timestep,make2D('age_all', rdf))\n", + "plt.title('check age')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rdf.plot(x='timestep',y=['candidate_count','active_count','completed_count'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rdf.plot(x='timestep',y=['candidate_funds','active_funds','completed_funds'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.semilogy(rdf.timestep,make2D('conviction_all', rdf))\n", + "plt.title('conviction by proposal')\n", + "plt.xlabel('time $t$')\n", + "plt.ylabel('conviction')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.semilogy(make2D('age_all', rdf),make2D('conviction_all', rdf))\n", + "plt.title('conviction by proposal')\n", + "plt.xlabel('proposal age')\n", + "plt.ylabel('conviction')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(rdf.timestep,make2D('share_of_funds_requested_all', rdf))\n", + "plt.title('share_of_funds_requested by proposal')\n", + "plt.xlabel('time $t$')\n", + "plt.ylabel('share_of_funds_requested')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.semilogy(make2D('age_all', rdf),make2D('share_of_funds_requested_all', rdf))\n", + "plt.title('share_of_funds_requested by proposal')\n", + "plt.xlabel('proposal age')\n", + "plt.ylabel('share_of_funds_requested')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.loglog(make2D('share_of_funds_requested_all', rdf), make2D('conviction_all', rdf), '.')\n", + "plt.ylabel('conviction')\n", + "plt.xlabel('share_of_funds_requested')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.semilogy(make2D('age_all', rdf), make2D('triggers_all', rdf))\n", + "plt.ylabel('triggers')\n", + "plt.xlabel('proposal_age')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.loglog(make2D('conviction_all', rdf), make2D('triggers_all', rdf))\n", + "a = plt.axis()\n", + "plt.loglog(a[:2],a[2:], 'k',alpha=.5 )\n", + "plt.ylabel('triggers')\n", + "plt.xlabel('conviction')\n", + "plt.title('phase: Triggers & Conviction')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.semilogy(rdf.timestep,make2D('conviction_share_of_trigger_all', rdf))\n", + "plt.title('conviction_share_of_trigger')\n", + "plt.xlabel('time $t$')\n", + "plt.ylabel('conviction_share_of_trigger')\n", + "plt.hlines(1,0,T, linestyle='--')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.semilogy(make2D('age_all', rdf), make2D('conviction_share_of_trigger_all', rdf))\n", + "plt.ylabel('triggers')\n", + "plt.xlabel('proposal_age')\n", + "plt.hlines(1,0,T, linestyle='--')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pos = {}\n", + "for ind in range(N):\n", + " i = last_parts[ind] \n", + " pos[i] = np.array([0, 2*ind-N])\n", + "\n", + "for ind in range(M):\n", + " j = last_props[ind] \n", + " pos[j] = np.array([1, 2*N/M *ind-N])\n", + "\n", + "#for i in last_parts:\n", + "#for j in last_props:\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "edges = [e for e in last_net.edges]\n", + "max_tok = np.max([last_net.edges[e]['tokens'] for e in edges])\n", + "\n", + "E = len(edges)\n", + "\n", + "node_color = np.empty((M+N,4))\n", + "node_size = np.empty(M+N)\n", + "\n", + "edge_color = np.empty((E,4))\n", + "cm = plt.get_cmap('Reds')\n", + "\n", + "cNorm = colors.Normalize(vmin=0, vmax=max_tok)\n", + "scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cm)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "size_scale = 1/500\n", + "node_label = {}\n", + "\n", + "for j in last_props:\n", + " node_size[j] = last_net.nodes[j]['funds_requested']*size_scale\n", + " if last_net.nodes[j]['status']==\"candidate\":\n", + " node_color[j] = colors.to_rgba('blue')\n", + " trigger = last_net.nodes[j]['trigger']\n", + " #print(trigger)\n", + " conviction = last_net.nodes[j]['conviction']\n", + " #print(conviction)\n", + " percent_of_trigger = str(int(100*conviction/trigger))+'%'\n", + " #age = last_net.nodes[j]['age']\n", + " node_label[j] = str(percent_of_trigger)\n", + " elif last_net.nodes[j]['status']==\"active\":\n", + " node_color[j] = colors.to_rgba('orange')\n", + " node_label[j] = ''\n", + " elif last_net.nodes[j]['status']==\"completed\":\n", + " node_color[j] = colors.to_rgba('green')\n", + " node_label[j] = ''\n", + "\n", + "for i in last_parts: \n", + " node_size[i] = last_net.nodes[i]['holdings']*size_scale\n", + " node_color[i] = colors.to_rgba('red')\n", + " node_label[i] = ''\n", + "\n", + "included_edges = []\n", + "for ind in range(E):\n", + " e = edges[ind]\n", + " tokens = last_net.edges[e]['tokens']\n", + " if tokens >0:\n", + " included_edges.append(e)\n", + " #print(tokens)\n", + " edge_color[ind] = scalarMap.to_rgba(tokens)\n", + "\n", + "iE = len(included_edges)\n", + "included_edge_color = np.empty((iE,4))\n", + "for ind in range(iE):\n", + " e = included_edges[ind]\n", + " tokens = last_net.edges[e]['tokens']\n", + " included_edge_color[ind] = scalarMap.to_rgba(tokens)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "nx.draw(last_net,\n", + " pos=pos, \n", + " node_size = node_size, \n", + " node_color = node_color, \n", + " edge_color = included_edge_color, \n", + " edgelist=included_edges,\n", + " labels = node_label)\n", + "plt.title('Tokens Staked by Partipants to Proposals')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "nets = rdf.network.values" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def snap_plot(nets, size_scale = 1/500, ani = False, dims = (20,20), savefigs=False ):\n", + " \n", + " last_net= df.network.values[-1]\n", + " last_props=get_nodes_by_type(last_net, 'proposal')\n", + " M = len(last_props)\n", + " last_parts=get_nodes_by_type(last_net, 'participant')\n", + " N = len(last_parts)\n", + " pos = {}\n", + " \n", + " for ind in range(N):\n", + " i = last_parts[ind] \n", + " pos[i] = np.array([0, 2*ind-N])\n", + "\n", + " for ind in range(M):\n", + " j = last_props[ind] \n", + " pos[j] = np.array([1, 2*N/M *ind-N])\n", + " \n", + " if ani:\n", + " figs = []\n", + " fig, ax = plt.subplots(figsize=dims)\n", + " \n", + " if savefigs:\n", + " counter = 0\n", + " length = 10\n", + " import string\n", + " unique_id = ''.join([np.random.choice(list(string.ascii_letters + string.digits)) for _ in range(length)])\n", + " for net in nets:\n", + " edges = [e for e in net.edges]\n", + " max_tok = np.max([net.edges[e]['tokens'] for e in edges])\n", + "\n", + " E = len(edges)\n", + " \n", + " net_props = get_nodes_by_type(net, 'proposal')\n", + " net_parts = get_nodes_by_type(net, 'participant')\n", + " net_node_label ={}\n", + " \n", + " num_nodes = len([node for node in net.nodes])\n", + " \n", + " node_color = np.empty((num_nodes,4))\n", + " node_size = np.empty(num_nodes)\n", + "\n", + " edge_color = np.empty((E,4))\n", + " cm = plt.get_cmap('Reds')\n", + "\n", + " cNorm = colors.Normalize(vmin=0, vmax=max_tok)\n", + " scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cm)\n", + " \n", + " \n", + "\n", + " for j in net_props:\n", + " node_size[j] = net.nodes[j]['funds_requested']*size_scale\n", + " if net.nodes[j]['status']==\"candidate\":\n", + " node_color[j] = colors.to_rgba('blue')\n", + " trigger = net.nodes[j]['trigger'] \n", + " conviction = net.nodes[j]['conviction']\n", + " percent_of_trigger = \" \"+str(int(100*conviction/trigger))+'%'\n", + " net_node_label[j] = str(percent_of_trigger)\n", + " elif net.nodes[j]['status']==\"active\":\n", + " node_color[j] = colors.to_rgba('orange')\n", + " net_node_label[j] = ''\n", + " elif net.nodes[j]['status']==\"completed\":\n", + " node_color[j] = colors.to_rgba('green')\n", + " net_node_label[j] = ''\n", + "\n", + " for i in net_parts: \n", + " node_size[i] = net.nodes[i]['holdings']*size_scale\n", + " node_color[i] = colors.to_rgba('red')\n", + " net_node_label[i] = ''\n", + "\n", + " included_edges = []\n", + " for ind in range(E):\n", + " e = edges[ind]\n", + " tokens = net.edges[e]['tokens']\n", + " if tokens >0:\n", + " included_edges.append(e)\n", + " edge_color[ind] = scalarMap.to_rgba(tokens)\n", + "\n", + " iE = len(included_edges)\n", + " included_edge_color = np.empty((iE,4))\n", + " for ind in range(iE):\n", + " e = included_edges[ind]\n", + " tokens = net.edges[e]['tokens']\n", + " included_edge_color[ind] = scalarMap.to_rgba(tokens)\n", + " \n", + " nx.draw(net,\n", + " pos=pos, \n", + " node_size = node_size,\n", + " node_color = node_color, \n", + " edge_color = included_edge_color, \n", + " edgelist=included_edges,\n", + " labels = net_node_label)\n", + " plt.title('Tokens Staked by Partipants to Proposals')\n", + " if ani:\n", + " nx.draw(net,\n", + " pos=pos, \n", + " node_size = node_size,\n", + " node_color = node_color, \n", + " edge_color = included_edge_color, \n", + " edgelist=included_edges,\n", + " labels = net_node_label, ax=ax)\n", + " figs.append(fig)\n", + " \n", + " else:\n", + " nx.draw(net,\n", + " pos=pos, \n", + " node_size = node_size,\n", + " node_color = node_color, \n", + " edge_color = included_edge_color, \n", + " edgelist=included_edges,\n", + " labels = net_node_label)\n", + " plt.title('Tokens Staked by Partipants to Proposals')\n", + " if savefigs:\n", + " plt.savefig(unique_id+'_fig'+str(counter)+'.png')\n", + " counter = counter+1\n", + " plt.show()\n", + " \n", + " if ani:\n", + " False\n", + " #anim = animation.ArtistAnimation(fig, , interval=50, blit=True, repeat_delay=1000)\n", + " #plt.show()\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "snap_plot(nets, ani=False, savefigs=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#totally failing at animating by trying to save a sequence of figures.\n", + "#snap_plot(nets, ani=True)\n", + "\n", + "#saving the images to files works so there is almost the option to compile a video from the images" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/simulations/validation/conviction_helpers.py b/simulations/validation/conviction_helpers.py new file mode 100644 index 0000000..909d437 --- /dev/null +++ b/simulations/validation/conviction_helpers.py @@ -0,0 +1,150 @@ +import networkx as nx +from scipy.stats import expon, gamma +import numpy as np +import matplotlib.pyplot as plt + +#helper functions +def get_nodes_by_type(g, node_type_selection): + return [node for node in g.nodes if g.nodes[node]['type']== node_type_selection ] + +def get_edges_by_type(g, edge_type_selection): + return [edge for edge in g.edges if g.edges[edge]['type']== edge_type_selection ] + +def total_funds_given_total_supply(total_supply): + + #can put any bonding curve invariant here for initializatio! + total_funds = total_supply + + return total_funds + +#maximum share of funds a proposal can take +default_beta = .2 #later we should set this to be param so we can sweep it +# tuning param for the trigger function +default_rho = .001 + +def trigger_threshold(requested, funds, supply, beta = default_beta, rho = default_rho): + + share = requested/funds + if share < beta: + return rho*supply/(beta-share)**2 + else: + return np.inf + +def initialize_network(n,m, funds_func=total_funds_given_total_supply, trigger_func =trigger_threshold ): + network = nx.DiGraph() + for i in range(n): + network.add_node(i) + network.nodes[i]['type']="participant" + + h_rv = expon.rvs(loc=0.0, scale=1000) + network.nodes[i]['holdings'] = h_rv + + s_rv = np.random.rand() + network.nodes[i]['sentiment'] = s_rv + + participants = get_nodes_by_type(network, 'participant') + initial_supply = np.sum([ network.nodes[i]['holdings'] for i in participants]) + + initial_funds = funds_func(initial_supply) + + #generate initial proposals + for ind in range(m): + j = n+ind + network.add_node(j) + network.nodes[j]['type']="proposal" + network.nodes[j]['conviction']=0 + network.nodes[j]['status']='candidate' + network.nodes[j]['age']=0 + + r_rv = gamma.rvs(3,loc=0.001, scale=10000) + network.node[j]['funds_requested'] = r_rv + + network.nodes[j]['trigger']= trigger_threshold(r_rv, initial_funds, initial_supply) + + for i in range(n): + network.add_edge(i, j) + + rv = np.random.rand() + a_rv = 1-4*(1-rv)*rv #polarized distribution + network.edges[(i, j)]['affinity'] = a_rv + network.edges[(i,j)]['tokens'] = 0 + network.edges[(i, j)]['conviction'] = 0 + + proposals = get_nodes_by_type(network, 'proposal') + total_requested = np.sum([ network.nodes[i]['funds_requested'] for i in proposals]) + + return network, initial_funds, initial_supply, total_requested + +def trigger_sweep(field, trigger_func,xmax=.2,default_alpha=.5): + + if field == 'token_supply': + alpha = default_alpha + share_of_funds = np.arange(.001,xmax,.001) + total_supply = np.arange(0,10**9, 10**6) + demo_data_XY = np.outer(share_of_funds,total_supply) + + demo_data_Z0=np.empty(demo_data_XY.shape) + demo_data_Z1=np.empty(demo_data_XY.shape) + demo_data_Z2=np.empty(demo_data_XY.shape) + demo_data_Z3=np.empty(demo_data_XY.shape) + for sof_ind in range(len(share_of_funds)): + sof = share_of_funds[sof_ind] + for ts_ind in range(len(total_supply)): + ts = total_supply[ts_ind] + tc = ts /(1-alpha) + trigger = trigger_func(sof, 1, ts) + demo_data_Z0[sof_ind,ts_ind] = np.log10(trigger) + demo_data_Z1[sof_ind,ts_ind] = trigger + demo_data_Z2[sof_ind,ts_ind] = trigger/tc #share of maximum possible conviction + demo_data_Z3[sof_ind,ts_ind] = np.log10(trigger/tc) + return {'log10_trigger':demo_data_Z0, + 'trigger':demo_data_Z1, + 'share_of_max_conv': demo_data_Z2, + 'log10_share_of_max_conv':demo_data_Z3, + 'total_supply':total_supply, + 'share_of_funds':share_of_funds} + elif field == 'alpha': + alpha = np.arange(.5,1,.01) + share_of_funds = np.arange(.001,xmax,.001) + total_supply = 10**9 + demo_data_XY = np.outer(share_of_funds,alpha) + + demo_data_Z4=np.empty(demo_data_XY.shape) + demo_data_Z5=np.empty(demo_data_XY.shape) + demo_data_Z6=np.empty(demo_data_XY.shape) + demo_data_Z7=np.empty(demo_data_XY.shape) + for sof_ind in range(len(share_of_funds)): + sof = share_of_funds[sof_ind] + for a_ind in range(len(alpha)): + ts = total_supply + a = alpha[a_ind] + tc = ts /(1-a) + trigger = trigger_func(sof, 1, ts) + demo_data_Z4[sof_ind,a_ind] = np.log10(trigger) + demo_data_Z5[sof_ind,a_ind] = trigger + demo_data_Z6[sof_ind,a_ind] = trigger/tc #share of maximum possible conviction + demo_data_Z7[sof_ind,a_ind] = np.log10(trigger/tc) + + return {'log10_trigger':demo_data_Z4, + 'trigger':demo_data_Z5, + 'share_of_max_conv': demo_data_Z6, + 'log10_share_of_max_conv':demo_data_Z7, + 'alpha':alpha, + 'share_of_funds':share_of_funds} + + else: + return "invalid field" + +def trigger_plotter(share_of_funds,Z, color_label,y, ylabel,cmap='jet'): + dims = (10, 5) + fig, ax = plt.subplots(figsize=dims) + + cf = plt.contourf(share_of_funds, y, Z.T, 100, cmap=cmap) + cbar=plt.colorbar(cf) + plt.axis([share_of_funds[0], share_of_funds[-1], y[0], y[-1]]) + #ax.set_xscale('log') + plt.ylabel(ylabel) + plt.xlabel('Share of Funds Requested') + plt.title('Trigger Function Map') + + cbar.ax.set_ylabel(color_label) \ No newline at end of file diff --git a/simulations/validation/conviction_system_logic.py b/simulations/validation/conviction_system_logic.py new file mode 100644 index 0000000..c739b82 --- /dev/null +++ b/simulations/validation/conviction_system_logic.py @@ -0,0 +1,548 @@ +import numpy as np + +from cadCAD.configuration.utils import config_sim +from simulations.validation.conviction_helpers import * +#import networkx as nx +from scipy.stats import expon, gamma + + +#functions for partial state update block 1 + +#Driving processes: arrival of participants, proposals and funds +##----------------------------------------- +def gen_new_participant(network, new_participant_holdings): + + i = len([node for node in network.nodes]) + + network.add_node(i) + network.nodes[i]['type']="participant" + + s_rv = np.random.rand() + network.nodes[i]['sentiment'] = s_rv + network.nodes[i]['holdings']=new_participant_holdings + + for j in get_nodes_by_type(network, 'proposal'): + network.add_edge(i, j) + + rv = np.random.rand() + a_rv = 1-4*(1-rv)*rv #polarized distribution + network.edges[(i, j)]['affinity'] = a_rv + network.edges[(i,j)]['tokens'] = a_rv*network.nodes[i]['holdings'] + network.edges[(i, j)]['conviction'] = 0 + + return network + + +scale_factor = 1000 + +def gen_new_proposal(network, funds, supply, total_funds, trigger_func): + j = len([node for node in network.nodes]) + network.add_node(j) + network.nodes[j]['type']="proposal" + + network.nodes[j]['conviction']=0 + network.nodes[j]['status']='candidate' + network.nodes[j]['age']=0 + + rescale = scale_factor*funds/total_funds + r_rv = gamma.rvs(3,loc=0.001, scale=rescale) + network.node[j]['funds_requested'] = r_rv + + network.nodes[j]['trigger']= trigger_func(r_rv, funds, supply) + + participants = get_nodes_by_type(network, 'participant') + proposing_participant = np.random.choice(participants) + + for i in participants: + network.add_edge(i, j) + if i==proposing_participant: + network.edges[(i, j)]['affinity']=1 + else: + rv = np.random.rand() + a_rv = 1-4*(1-rv)*rv #polarized distribution + network.edges[(i, j)]['affinity'] = a_rv + + network.edges[(i, j)]['conviction'] = 0 + network.edges[(i,j)]['tokens'] = 0 + return network + + + +def driving_process(params, step, sL, s): + + #placeholder plumbing for random processes + arrival_rate = 10/s['sentiment'] + rv1 = np.random.rand() + new_participant = bool(rv1<1/arrival_rate) + if new_participant: + h_rv = expon.rvs(loc=0.0, scale=1000) + new_participant_holdings = h_rv + else: + new_participant_holdings = 0 + + network = s['network'] + affinities = [network.edges[e]['affinity'] for e in network.edges ] + median_affinity = np.median(affinities) + + proposals = get_nodes_by_type(network, 'proposal') + fund_requests = [network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate' ] + + funds = s['funds'] + total_funds_requested = np.sum(fund_requests) + + proposal_rate = 10/median_affinity * total_funds_requested/funds + rv2 = np.random.rand() + new_proposal = bool(rv2<1/proposal_rate) + + sentiment = s['sentiment'] + funds = s['funds'] + scale_factor = 1+4000*sentiment**2 + + #this shouldn't happen but expon is throwing domain errors + if scale_factor > 1: + funds_arrival = expon.rvs(loc = 0, scale = scale_factor ) + else: + funds_arrival = 0 + + return({'new_participant':new_participant, + 'new_participant_holdings':new_participant_holdings, + 'new_proposal':new_proposal, + 'funds_arrival':funds_arrival}) + + +#Mechanisms for updating the state based on driving processes +##--- +def update_network(params, step, sL, s, _input): + + print(params) + print(type(params)) + + network = s['network'] + funds = s['funds'] + supply = s['supply'] + trigger_func = params['trigger_func'] + + new_participant = _input['new_participant'] #T/F + new_proposal = _input['new_proposal'] #T/F + + if new_participant: + new_participant_holdings = _input['new_participant_holdings'] + network = gen_new_participant(network, new_participant_holdings) + + if new_proposal: + network= gen_new_proposal(network,funds,supply ) + + #update age of the existing proposals + proposals = get_nodes_by_type(network, 'proposal') + + for j in proposals: + network.nodes[j]['age'] = network.nodes[j]['age']+1 + if network.nodes[j]['status'] == 'candidate': + requested = network.nodes[j]['funds_requested'] + network.nodes[j]['trigger'] = trigger_func(requested, funds, supply) + else: + network.nodes[j]['trigger'] = np.nan + + key = 'network' + value = network + + return (key, value) + +def increment_funds(params, step, sL, s, _input): + + funds = s['funds'] + funds_arrival = _input['funds_arrival'] + + #increment funds + funds = funds + funds_arrival + + key = 'funds' + value = funds + + return (key, value) + +def increment_supply(params, step, sL, s, _input): + + supply = s['supply'] + supply_arrival = _input['new_participant_holdings'] + + #increment funds + supply = supply + supply_arrival + + key = 'supply' + value = supply + + return (key, value) + +#functions for partial state update block 2 + +#Driving processes: completion of previously funded proposals +##----------------------------------------- + +def check_progress(params, step, sL, s): + + network = s['network'] + proposals = get_nodes_by_type(network, 'proposal') + + completed = [] + for j in proposals: + if network.nodes[j]['status'] == 'active': + grant_size = network.nodes[j]['funds_requested'] + base_completion_rate=params['base_completion_rate'] + likelihood = 1.0/(base_completion_rate+np.log(grant_size)) + if np.random.rand() < likelihood: + completed.append(j) + + return({'completed':completed}) + + +#Mechanisms for updating the state based on check progress +##--- +def complete_proposal(params, step, sL, s, _input): + + network = s['network'] + participants = get_nodes_by_type(network, 'participant') + + completed = _input['completed'] + for j in completed: + network.nodes[j]['status']='completed' + for i in participants: + force = network.edges[(i,j)]['affinity'] + sentiment = network.node[i]['sentiment'] + network.node[i]['sentiment'] = get_sentimental(sentiment, force, decay=0) + + key = 'network' + value = network + + return (key, value) + +def update_sentiment_on_completion(params, step, sL, s, _input): + + network = s['network'] + proposals = get_nodes_by_type(network, 'proposal') + completed = _input['completed'] + + grants_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='active']) + + grants_completed = np.sum([network.nodes[j]['funds_requested'] for j in completed]) + + sentiment = s['sentiment'] + + force = grants_completed/grants_outstanding + mu = params['sentiment_decay'] + if (force >=0) and (force <=1): + sentiment = get_sentimental(sentiment, force, mu) + else: + sentiment = get_sentimental(sentiment, 0, mu) + + + key = 'sentiment' + value = sentiment + + return (key, value) + +def get_sentimental(sentiment, force, decay=0): + mu = decay + sentiment = sentiment*(1-mu) + force + + if sentiment > 1: + sentiment = 1 + + return sentiment + +#functions for partial state update block 3 + +#Decision processes: trigger function policy +##----------------------------------------- + +def trigger_function(params, step, sL, s): + + network = s['network'] + funds = s['funds'] + supply = s['supply'] + proposals = get_nodes_by_type(network, 'proposal') + tmin = params['tmin'] + + accepted = [] + triggers = {} + for j in proposals: + if network.nodes[j]['status'] == 'candidate': + requested = network.nodes[j]['funds_requested'] + age = network.nodes[j]['age'] + threshold = trigger_threshold(requested, funds, supply) + if age > tmin: + conviction = network.nodes[j]['conviction'] + if conviction >threshold: + accepted.append(j) + else: + threshold = np.nan + + triggers[j] = threshold + + + + return({'accepted':accepted, 'triggers':triggers}) + +def decrement_funds(params, step, sL, s, _input): + + funds = s['funds'] + network = s['network'] + accepted = _input['accepted'] + + #decrement funds + for j in accepted: + funds = funds - network.nodes[j]['funds_requested'] + + key = 'funds' + value = funds + + return (key, value) + +def update_proposals(params, step, sL, s, _input): + + network = s['network'] + accepted = _input['accepted'] + triggers = _input['triggers'] + participants = get_nodes_by_type(network, 'participant') + proposals = get_nodes_by_type(network, 'proposals') + sensitivity = params['sensitivity'] + + for j in proposals: + network.nodes[j]['trigger'] = triggers[j] + + #bookkeeping conviction and participant sentiment + for j in accepted: + network.nodes[j]['status']='active' + network.nodes[j]['conviction']=np.nan + #change status to active + for i in participants: + + #operating on edge = (i,j) + #reset tokens assigned to other candidates + network.edges[(i,j)]['tokens']=0 + network.edges[(i,j)]['conviction'] = np.nan + + #update participants sentiments (positive or negative) + affinities = [network.edges[(i,p)]['affinity'] for p in proposals if not(p in accepted)] + if len(affinities)>1: + max_affinity = np.max(affinities) + force = network.edges[(i,j)]['affinity']-sensitivity*max_affinity + else: + force = 0 + + #based on what their affinities to the accepted proposals + network.nodes[i]['sentiment'] = get_sentimental(network.nodes[i]['sentiment'], force, False) + + + key = 'network' + value = network + + return (key, value) + +def update_sentiment_on_release(params, step, sL, s, _input): + + network = s['network'] + proposals = get_nodes_by_type(network, 'proposal') + accepted = _input['accepted'] + + proposals_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate']) + + proposals_accepted = np.sum([network.nodes[j]['funds_requested'] for j in accepted]) + + sentiment = s['sentiment'] + force = proposals_accepted/proposals_outstanding + if (force >=0) and (force <=1): + sentiment = get_sentimental(sentiment, force, False) + else: + sentiment = get_sentimental(sentiment, 0, False) + + key = 'sentiment' + value = sentiment + + return (key, value) + +def participants_decisions(params, step, sL, s): + network = s['network'] + participants = get_nodes_by_type(network, 'participant') + proposals = get_nodes_by_type(network, 'proposal') + candidates = [j for j in proposals if network.nodes[j]['status']=='candidate'] + sensitivity = params['sensitivity'] + + gain = .01 + delta_holdings={} + proposals_supported ={} + for i in participants: + force = network.nodes[i]['sentiment']-sensitivity + delta_holdings[i] = network.nodes[i]['holdings']*gain*force + + support = [] + for j in candidates: + affinity = network.edges[(i, j)]['affinity'] + cutoff = sensitivity*np.max([network.edges[(i,p)]['affinity'] for p in candidates]) + if cutoff <.5: + cutoff = .5 + + if affinity > cutoff: + support.append(j) + + proposals_supported[i] = support + + return({'delta_holdings':delta_holdings, 'proposals_supported':proposals_supported}) + +def update_tokens(params, step, sL, s, _input): + + network = s['network'] + delta_holdings = _input['delta_holdings'] + proposals = get_nodes_by_type(network, 'proposal') + proposals_supported = _input['proposals_supported'] + participants = get_nodes_by_type(network, 'participant') + alpha = params['alpha'] + + for i in participants: + network.nodes[i]['holdings'] = network.nodes[i]['holdings']+delta_holdings[i] + supported = proposals_supported[i] + total_affinity = np.sum([ network.edges[(i, j)]['affinity'] for j in supported]) + for j in proposals: + if j in supported: + normalized_affinity = network.edges[(i, j)]['affinity']/total_affinity + network.edges[(i, j)]['tokens'] = normalized_affinity*network.nodes[i]['holdings'] + else: + network.edges[(i, j)]['tokens'] = 0 + + prior_conviction = network.edges[(i, j)]['conviction'] + current_tokens = network.edges[(i, j)]['tokens'] + network.edges[(i, j)]['conviction'] =current_tokens+alpha*prior_conviction + + for j in proposals: + network.nodes[j]['conviction'] = np.sum([ network.edges[(i, j)]['conviction'] for i in participants]) + + key = 'network' + value = network + + return (key, value) + +def update_supply(params, step, sL, s, _input): + + supply = s['supply'] + delta_holdings = _input['delta_holdings'] + delta_supply = np.sum([v for v in delta_holdings.values()]) + + supply = supply + delta_supply + + key = 'supply' + value = supply + + return (key, value) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# The Partial State Update Blocks +partial_state_update_blocks = [ + { + 'policies': { + #new proposals or new participants + 'random': driving_process + }, + 'variables': { + 'network': update_network, + 'funds':increment_funds, + 'supply':increment_supply + } + }, + { + 'policies': { + 'completion': check_progress #see if any of the funded proposals completes + }, + 'variables': { # The following state variables will be updated simultaneously + 'sentiment': update_sentiment_on_completion, #note completing decays sentiment, completing bumps it + 'network': complete_proposal #book-keeping + } + }, + { + 'policies': { + 'release': trigger_function #check each proposal to see if it passes + }, + 'variables': { # The following state variables will be updated simultaneously + 'funds': decrement_funds, #funds expended + 'sentiment': update_sentiment_on_release, #releasing funds can bump sentiment + 'network': update_proposals #reset convictions, and participants sentiments + #update based on affinities + } + }, + { + 'policies': { + 'participants_act': participants_decisions, #high sentiment, high affinity =>buy + #low sentiment, low affinities => burn + #assign tokens to top affinities + }, + 'variables': { + 'supply': update_supply, + 'network': update_tokens #update everyones holdings + #and their conviction for each proposal + } + } +] + +n= 25 #initial participants +m= 3 #initial proposals + +initial_sentiment = .5 + +network, initial_funds, initial_supply, total_requested = initialize_network(n,m,total_funds_given_total_supply,trigger_threshold) + +initial_conditions = {'network':network, + 'supply': initial_supply, + 'funds':initial_funds, + 'sentiment': initial_sentiment} + +#power of 1 token forever +# conviction_capactity = [2] +# alpha = [1-1/cc for cc in conviction_capactity] +# print(alpha) + +params={ + 'sensitivity': [.75], + 'tmin': [7], #unit days; minimum periods passed before a proposal can pass + 'sentiment_decay': [.001], #termed mu in the state update function + 'alpha': [0.5, 0.9], + 'base_completion_rate': [10], + 'trigger_func': [trigger_threshold] +} + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Settings of general simulation parameters, unrelated to the system itself +# `T` is a range with the number of discrete units of time the simulation will run for; +# `N` is the number of times the simulation will be run (Monte Carlo runs) +time_periods_per_run = 250 +monte_carlo_runs = 1 + +simulation_parameters = config_sim({ + 'T': range(time_periods_per_run), + 'N': monte_carlo_runs, + 'M': params +}) + + +from cadCAD.configuration import append_configs + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# The configurations above are then packaged into a `Configuration` object +append_configs( + initial_state=initial_conditions, #dict containing variable names and initial values + partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions + sim_configs=simulation_parameters #dict containing simulation parameters +) + +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from cadCAD import configs + +exec_mode = ExecutionMode() +multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) +run = Executor(exec_context=multi_proc_ctx, configs=configs) +raw_result, tensor = run.execute() + +# exec_mode = ExecutionMode() +# exec_context = ExecutionContext(context=exec_mode.multi_proc) +# # run = Executor(exec_context=exec_context, configs=configs) +# executor = Executor(exec_context, configs) # Pass the configuration object inside an array +# raw_result, tensor = executor.execute() # The `main()` method returns a tuple; its first elements contains the raw results \ No newline at end of file diff --git a/simulations/validation/historical_state_access.py b/simulations/validation/historical_state_access.py deleted file mode 100644 index d4a86ed..0000000 --- a/simulations/validation/historical_state_access.py +++ /dev/null @@ -1,62 +0,0 @@ -from cadCAD.configuration import append_configs -from cadCAD.configuration.utils import config_sim - -# Policies per Mechanism -# def p(_g, substep, sH, s): -# return {'last_update_block': sH[-1]} - -# def policies(_g, substep, sH, s, _input): -# y = 'policies' -# x = _input -# return (y, x) - -# policies = {"p1": p, "p2": p} - - -# last_partial_state_update_block -def last_update_block(_g, substep, sH, s, _input): - return 'sh', sH[-1] - -def add(y, x): - return lambda _g, substep, sH, s, _input: (y, s[y] + x) - -genesis_states = { - 's': 0, - 'sh': [{}], # {[], {}} - # 'policies': {}, -} - -variables = { - 's': add('s', 1), - 'sh': last_update_block, - # "policies": policies -} - - -PSUB = { - "policies": {}, #policies, - "variables": variables -} - -partial_state_update_block = { - "PSUB1": PSUB, - "PSUB2": PSUB, - "PSUB3": PSUB -} - -sim_config = config_sim( - { - "N": 1, - "T": range(3), - } -) - - -append_configs( - sim_configs=sim_config, - initial_state=genesis_states, - seeds={}, - raw_exogenous_states={}, - env_processes={}, - partial_state_update_blocks=partial_state_update_block -) diff --git a/simulations/historical_state_access_run.py b/simulations/validation/historical_state_access_run.py similarity index 73% rename from simulations/historical_state_access_run.py rename to simulations/validation/historical_state_access_run.py index 67ad0b5..23225e5 100644 --- a/simulations/historical_state_access_run.py +++ b/simulations/validation/historical_state_access_run.py @@ -2,7 +2,6 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import historical_state_access from cadCAD import configs exec_mode = ExecutionMode() @@ -13,13 +12,11 @@ first_config = configs # only contains config1 single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) run = Executor(exec_context=single_proc_ctx, configs=first_config) -raw_result, tensor_field = run.main() +raw_result, tensor_field = run.execute() result = pd.DataFrame(raw_result) -def delSH(d): - if 'sh' in d.keys(): - del d['sh'] - return d -result['sh'] = result['sh'].apply(lambda sh: list(map(lambda d: delSH(d), sh))) +cols = ['run','substep','timestep','x','nonexsistant','last_x','2nd_to_last_x','3rd_to_last_x','4th_to_last_x'] +result = result[cols] + print() print("Tensor Field: config1") From 4870f2db927fd3a70cda08e404c2205205d1949c Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Fri, 31 May 2019 16:26:58 -0400 Subject: [PATCH 16/19] pre push mad stuff --- cadCAD/configuration/__init__.py | 20 +- .../config1.py | 7 - .../config2.py | 1 - simulations/regression_tests/sweep_config.py | 1 - .../config1_test.py} | 3 +- .../config2_test.py} | 3 +- .../external_ds_test.py} | 4 +- .../historical_state_access_test.py} | 1 + .../multi_config_test.py} | 5 +- .../{ => test_executions}/param_sweep_test.py | 1 - .../policy_agg_test.py} | 0 simulations/{ => test_executions}/udo_test.py | 0 .../udo_update_test.py} | 0 .../validation/conviction_system_logic_sim.py | 555 ++++++++++++++++++ 14 files changed, 567 insertions(+), 34 deletions(-) rename simulations/{validation => regression_tests}/config1.py (93%) rename simulations/{validation => regression_tests}/config2.py (99%) rename simulations/{config1_run.py => test_executions/config1_test.py} (93%) rename simulations/{config2_run.py => test_executions/config2_test.py} (93%) rename simulations/{external_ds_run.py => test_executions/external_ds_test.py} (86%) rename simulations/{validation/historical_state_access_run.py => test_executions/historical_state_access_test.py} (93%) rename simulations/{multi_config_run.py => test_executions/multi_config_test.py} (85%) rename simulations/{ => test_executions}/param_sweep_test.py (94%) rename simulations/{policy_agg_run.py => test_executions/policy_agg_test.py} (100%) rename simulations/{ => test_executions}/udo_test.py (100%) rename simulations/{udo_run.py => test_executions/udo_update_test.py} (100%) create mode 100644 simulations/validation/conviction_system_logic_sim.py diff --git a/cadCAD/configuration/__init__.py b/cadCAD/configuration/__init__.py index 120b9ad..56aae05 100644 --- a/cadCAD/configuration/__init__.py +++ b/cadCAD/configuration/__init__.py @@ -12,7 +12,6 @@ from cadCAD.configuration.utils.depreciationHandler import sanitize_partial_stat # policy_ops=[foldr(dict_elemwise_sum())] # policy_ops=[reduce, lambda a, b: {**a, **b}] - class Configuration(object): def __init__(self, sim_config={}, initial_state={}, seeds={}, env_processes={}, exogenous_states={}, partial_state_update_blocks={}, policy_ops=[lambda a, b: a + b], @@ -37,21 +36,12 @@ def append_configs(sim_configs={}, initial_state={}, seeds={}, raw_exogenous_sta else: exogenous_states = raw_exogenous_states - if isinstance(sim_configs, list): - for sim_config in sim_configs: - config = Configuration( - sim_config=sim_config, - initial_state=initial_state, - seeds=seeds, - exogenous_states=exogenous_states, - env_processes=env_processes, - partial_state_update_blocks=partial_state_update_blocks, - policy_ops=policy_ops - ) - configs.append(config) - elif isinstance(sim_configs, dict): + if isinstance(sim_configs, dict): + sim_configs = [sim_configs] + + for sim_config in sim_configs: config = Configuration( - sim_config=sim_configs, + sim_config=sim_config, initial_state=initial_state, seeds=seeds, exogenous_states=exogenous_states, diff --git a/simulations/validation/config1.py b/simulations/regression_tests/config1.py similarity index 93% rename from simulations/validation/config1.py rename to simulations/regression_tests/config1.py index 14ec2e1..33faabf 100644 --- a/simulations/validation/config1.py +++ b/simulations/regression_tests/config1.py @@ -1,18 +1,11 @@ -from decimal import Decimal -from functools import reduce - import numpy as np from datetime import timedelta -from cadCAD.configuration.utils.policyAggregation import get_base_value from cadCAD.configuration import append_configs from cadCAD.configuration.utils import env_proc_trigger, bound_norm_random, ep_time_step, config_sim, time_step, \ env_trigger -# from cadCAD.configuration.utils import timestep_trigger -from simulations.regression_tests.sweep_config import var_timestep_trigger - seeds = { 'z': np.random.RandomState(1), diff --git a/simulations/validation/config2.py b/simulations/regression_tests/config2.py similarity index 99% rename from simulations/validation/config2.py rename to simulations/regression_tests/config2.py index 5c36b46..3962f76 100644 --- a/simulations/validation/config2.py +++ b/simulations/regression_tests/config2.py @@ -139,7 +139,6 @@ sim_config = config_sim( } ) - append_configs( sim_configs=sim_config, initial_state=genesis_states, diff --git a/simulations/regression_tests/sweep_config.py b/simulations/regression_tests/sweep_config.py index c72e489..ef7885c 100644 --- a/simulations/regression_tests/sweep_config.py +++ b/simulations/regression_tests/sweep_config.py @@ -68,7 +68,6 @@ def s1m1(_g, step, sL, s, _input): psu_block['m1']["variables"]['s1'] = s1m1 def s2m1(_g, step, sL, s, _input): - print(_g) return 's2', _g['beta'] psu_block['m1']["variables"]['s2'] = s2m1 diff --git a/simulations/config1_run.py b/simulations/test_executions/config1_test.py similarity index 93% rename from simulations/config1_run.py rename to simulations/test_executions/config1_test.py index 6e6e9c4..052c602 100644 --- a/simulations/config1_run.py +++ b/simulations/test_executions/config1_test.py @@ -2,8 +2,7 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import config1 - +from simulations.regression_tests import config1 from cadCAD import configs exec_mode = ExecutionMode() diff --git a/simulations/config2_run.py b/simulations/test_executions/config2_test.py similarity index 93% rename from simulations/config2_run.py rename to simulations/test_executions/config2_test.py index 890f356..e6c349e 100644 --- a/simulations/config2_run.py +++ b/simulations/test_executions/config2_test.py @@ -2,8 +2,7 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import config2 - +from simulations.regression_tests import config2 from cadCAD import configs exec_mode = ExecutionMode() diff --git a/simulations/external_ds_run.py b/simulations/test_executions/external_ds_test.py similarity index 86% rename from simulations/external_ds_run.py rename to simulations/test_executions/external_ds_test.py index 7c4eec4..f5470f1 100644 --- a/simulations/external_ds_run.py +++ b/simulations/test_executions/external_ds_test.py @@ -2,9 +2,7 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -# from simulations.validation import config1_test_pipe -# from simulations.validation import config1 -# from simulations.validation import externalds +from simulations.regression_tests import external_dataset from cadCAD import configs exec_mode = ExecutionMode() diff --git a/simulations/validation/historical_state_access_run.py b/simulations/test_executions/historical_state_access_test.py similarity index 93% rename from simulations/validation/historical_state_access_run.py rename to simulations/test_executions/historical_state_access_test.py index 23225e5..2b3b477 100644 --- a/simulations/validation/historical_state_access_run.py +++ b/simulations/test_executions/historical_state_access_test.py @@ -2,6 +2,7 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.regression_tests import historical_state_access from cadCAD import configs exec_mode = ExecutionMode() diff --git a/simulations/multi_config_run.py b/simulations/test_executions/multi_config_test.py similarity index 85% rename from simulations/multi_config_run.py rename to simulations/test_executions/multi_config_test.py index 045ef84..f044a50 100644 --- a/simulations/multi_config_run.py +++ b/simulations/test_executions/multi_config_test.py @@ -2,7 +2,7 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import config1, config2 +from simulations.regression_tests import config1, config2 from cadCAD import configs exec_mode = ExecutionMode() @@ -11,12 +11,13 @@ print("Simulation Execution: Concurrent Execution") multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) run = Executor(exec_context=multi_proc_ctx, configs=configs) +# print(configs) i = 0 config_names = ['config1', 'config2'] for raw_result, tensor_field in run.execute(): result = pd.DataFrame(raw_result) print() - print("Tensor Field: " + config_names[i]) + print(f"Tensor Field: {config_names[i]}") print(tabulate(tensor_field, headers='keys', tablefmt='psql')) print("Output:") print(tabulate(result, headers='keys', tablefmt='psql')) diff --git a/simulations/param_sweep_test.py b/simulations/test_executions/param_sweep_test.py similarity index 94% rename from simulations/param_sweep_test.py rename to simulations/test_executions/param_sweep_test.py index d114536..a7e736c 100644 --- a/simulations/param_sweep_test.py +++ b/simulations/test_executions/param_sweep_test.py @@ -4,7 +4,6 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -# from simulations.validation import new_sweep_config from simulations.regression_tests import sweep_config from cadCAD import configs diff --git a/simulations/policy_agg_run.py b/simulations/test_executions/policy_agg_test.py similarity index 100% rename from simulations/policy_agg_run.py rename to simulations/test_executions/policy_agg_test.py diff --git a/simulations/udo_test.py b/simulations/test_executions/udo_test.py similarity index 100% rename from simulations/udo_test.py rename to simulations/test_executions/udo_test.py diff --git a/simulations/udo_run.py b/simulations/test_executions/udo_update_test.py similarity index 100% rename from simulations/udo_run.py rename to simulations/test_executions/udo_update_test.py diff --git a/simulations/validation/conviction_system_logic_sim.py b/simulations/validation/conviction_system_logic_sim.py new file mode 100644 index 0000000..85b62a0 --- /dev/null +++ b/simulations/validation/conviction_system_logic_sim.py @@ -0,0 +1,555 @@ +from pprint import pprint + +import numpy as np +from tabulate import tabulate + +from cadCAD.configuration.utils import config_sim +from simulations.validation.conviction_helpers import * +#import networkx as nx +from scipy.stats import expon, gamma + + +#functions for partial state update block 1 + +#Driving processes: arrival of participants, proposals and funds +##----------------------------------------- + + +def gen_new_participant(network, new_participant_holdings): + + i = len([node for node in network.nodes]) + + network.add_node(i) + network.nodes[i]['type']="participant" + + s_rv = np.random.rand() + network.nodes[i]['sentiment'] = s_rv + network.nodes[i]['holdings']=new_participant_holdings + + for j in get_nodes_by_type(network, 'proposal'): + network.add_edge(i, j) + + rv = np.random.rand() + a_rv = 1-4*(1-rv)*rv #polarized distribution + network.edges[(i, j)]['affinity'] = a_rv + network.edges[(i,j)]['tokens'] = a_rv*network.nodes[i]['holdings'] + network.edges[(i, j)]['conviction'] = 0 + + return network + + +scale_factor = 1000 + +def gen_new_proposal(network, funds, supply, trigger_func): + j = len([node for node in network.nodes]) + network.add_node(j) + network.nodes[j]['type']="proposal" + + network.nodes[j]['conviction']=0 + network.nodes[j]['status']='candidate' + network.nodes[j]['age']=0 + + rescale = scale_factor*funds + r_rv = gamma.rvs(3,loc=0.001, scale=rescale) + network.node[j]['funds_requested'] = r_rv + + network.nodes[j]['trigger']= trigger_func(r_rv, funds, supply) + + participants = get_nodes_by_type(network, 'participant') + proposing_participant = np.random.choice(participants) + + for i in participants: + network.add_edge(i, j) + if i==proposing_participant: + network.edges[(i, j)]['affinity']=1 + else: + rv = np.random.rand() + a_rv = 1-4*(1-rv)*rv #polarized distribution + network.edges[(i, j)]['affinity'] = a_rv + + network.edges[(i, j)]['conviction'] = 0 + network.edges[(i,j)]['tokens'] = 0 + return network + + + +def driving_process(params, step, sL, s): + + #placeholder plumbing for random processes + arrival_rate = 10/s['sentiment'] + rv1 = np.random.rand() + new_participant = bool(rv1<1/arrival_rate) + if new_participant: + h_rv = expon.rvs(loc=0.0, scale=1000) + new_participant_holdings = h_rv + else: + new_participant_holdings = 0 + + network = s['network'] + affinities = [network.edges[e]['affinity'] for e in network.edges ] + median_affinity = np.median(affinities) + + proposals = get_nodes_by_type(network, 'proposal') + fund_requests = [network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate' ] + + funds = s['funds'] + total_funds_requested = np.sum(fund_requests) + + proposal_rate = 10/median_affinity * total_funds_requested/funds + rv2 = np.random.rand() + new_proposal = bool(rv2<1/proposal_rate) + + sentiment = s['sentiment'] + funds = s['funds'] + scale_factor = 1+4000*sentiment**2 + + #this shouldn't happen but expon is throwing domain errors + if scale_factor > 1: + funds_arrival = expon.rvs(loc = 0, scale = scale_factor ) + else: + funds_arrival = 0 + + return({'new_participant':new_participant, + 'new_participant_holdings':new_participant_holdings, + 'new_proposal':new_proposal, + 'funds_arrival':funds_arrival}) + + +#Mechanisms for updating the state based on driving processes +##--- +def update_network(params, step, sL, s, _input): + + network = s['network'] + funds = s['funds'] + supply = s['supply'] + trigger_func = params['trigger_func'] + + new_participant = _input['new_participant'] #T/F + new_proposal = _input['new_proposal'] #T/F + + if new_participant: + new_participant_holdings = _input['new_participant_holdings'] + network = gen_new_participant(network, new_participant_holdings) + + if new_proposal: + network= gen_new_proposal(network,funds,supply,trigger_func ) + + #update age of the existing proposals + proposals = get_nodes_by_type(network, 'proposal') + + for j in proposals: + network.nodes[j]['age'] = network.nodes[j]['age']+1 + if network.nodes[j]['status'] == 'candidate': + requested = network.nodes[j]['funds_requested'] + network.nodes[j]['trigger'] = trigger_func(requested, funds, supply) + else: + network.nodes[j]['trigger'] = np.nan + + key = 'network' + value = network + + return (key, value) + +def increment_funds(params, step, sL, s, _input): + + funds = s['funds'] + funds_arrival = _input['funds_arrival'] + + #increment funds + funds = funds + funds_arrival + + key = 'funds' + value = funds + + return (key, value) + +def increment_supply(params, step, sL, s, _input): + + supply = s['supply'] + supply_arrival = _input['new_participant_holdings'] + + #increment funds + supply = supply + supply_arrival + + key = 'supply' + value = supply + + return (key, value) + +#functions for partial state update block 2 + +#Driving processes: completion of previously funded proposals +##----------------------------------------- + +def check_progress(params, step, sL, s): + + network = s['network'] + proposals = get_nodes_by_type(network, 'proposal') + + completed = [] + for j in proposals: + if network.nodes[j]['status'] == 'active': + grant_size = network.nodes[j]['funds_requested'] + base_completion_rate=params['base_completion_rate'] + likelihood = 1.0/(base_completion_rate+np.log(grant_size)) + if np.random.rand() < likelihood: + completed.append(j) + + return({'completed':completed}) + + +#Mechanisms for updating the state based on check progress +##--- +def complete_proposal(params, step, sL, s, _input): + + network = s['network'] + participants = get_nodes_by_type(network, 'participant') + + completed = _input['completed'] + for j in completed: + network.nodes[j]['status']='completed' + for i in participants: + force = network.edges[(i,j)]['affinity'] + sentiment = network.node[i]['sentiment'] + network.node[i]['sentiment'] = get_sentimental(sentiment, force, decay=0) + + key = 'network' + value = network + + return (key, value) + +def update_sentiment_on_completion(params, step, sL, s, _input): + + network = s['network'] + proposals = get_nodes_by_type(network, 'proposal') + completed = _input['completed'] + + grants_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='active']) + + grants_completed = np.sum([network.nodes[j]['funds_requested'] for j in completed]) + + sentiment = s['sentiment'] + + force = grants_completed/grants_outstanding + mu = params['sentiment_decay'] + if (force >=0) and (force <=1): + sentiment = get_sentimental(sentiment, force, mu) + else: + sentiment = get_sentimental(sentiment, 0, mu) + + + key = 'sentiment' + value = sentiment + + return (key, value) + +def get_sentimental(sentiment, force, decay=0): + mu = decay + sentiment = sentiment*(1-mu) + force + + if sentiment > 1: + sentiment = 1 + + return sentiment + +#functions for partial state update block 3 + +#Decision processes: trigger function policy +##----------------------------------------- + +def trigger_function(params, step, sL, s): + + network = s['network'] + funds = s['funds'] + supply = s['supply'] + proposals = get_nodes_by_type(network, 'proposal') + tmin = params['tmin'] + + accepted = [] + triggers = {} + for j in proposals: + if network.nodes[j]['status'] == 'candidate': + requested = network.nodes[j]['funds_requested'] + age = network.nodes[j]['age'] + threshold = trigger_threshold(requested, funds, supply) + if age > tmin: + conviction = network.nodes[j]['conviction'] + if conviction >threshold: + accepted.append(j) + else: + threshold = np.nan + + triggers[j] = threshold + + + + return({'accepted':accepted, 'triggers':triggers}) + +def decrement_funds(params, step, sL, s, _input): + + funds = s['funds'] + network = s['network'] + accepted = _input['accepted'] + + #decrement funds + for j in accepted: + funds = funds - network.nodes[j]['funds_requested'] + + key = 'funds' + value = funds + + return (key, value) + +def update_proposals(params, step, sL, s, _input): + + network = s['network'] + accepted = _input['accepted'] + triggers = _input['triggers'] + participants = get_nodes_by_type(network, 'participant') + proposals = get_nodes_by_type(network, 'proposals') + sensitivity = params['sensitivity'] + + for j in proposals: + network.nodes[j]['trigger'] = triggers[j] + + #bookkeeping conviction and participant sentiment + for j in accepted: + network.nodes[j]['status']='active' + network.nodes[j]['conviction']=np.nan + #change status to active + for i in participants: + + #operating on edge = (i,j) + #reset tokens assigned to other candidates + network.edges[(i,j)]['tokens']=0 + network.edges[(i,j)]['conviction'] = np.nan + + #update participants sentiments (positive or negative) + affinities = [network.edges[(i,p)]['affinity'] for p in proposals if not(p in accepted)] + if len(affinities)>1: + max_affinity = np.max(affinities) + force = network.edges[(i,j)]['affinity']-sensitivity*max_affinity + else: + force = 0 + + #based on what their affinities to the accepted proposals + network.nodes[i]['sentiment'] = get_sentimental(network.nodes[i]['sentiment'], force, False) + + + key = 'network' + value = network + + return (key, value) + +def update_sentiment_on_release(params, step, sL, s, _input): + + network = s['network'] + proposals = get_nodes_by_type(network, 'proposal') + accepted = _input['accepted'] + + proposals_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate']) + + proposals_accepted = np.sum([network.nodes[j]['funds_requested'] for j in accepted]) + + sentiment = s['sentiment'] + force = proposals_accepted/proposals_outstanding + if (force >=0) and (force <=1): + sentiment = get_sentimental(sentiment, force, False) + else: + sentiment = get_sentimental(sentiment, 0, False) + + key = 'sentiment' + value = sentiment + + return (key, value) + +def participants_decisions(params, step, sL, s): + network = s['network'] + participants = get_nodes_by_type(network, 'participant') + proposals = get_nodes_by_type(network, 'proposal') + candidates = [j for j in proposals if network.nodes[j]['status']=='candidate'] + sensitivity = params['sensitivity'] + + gain = .01 + delta_holdings={} + proposals_supported ={} + for i in participants: + force = network.nodes[i]['sentiment']-sensitivity + delta_holdings[i] = network.nodes[i]['holdings']*gain*force + + support = [] + for j in candidates: + affinity = network.edges[(i, j)]['affinity'] + cutoff = sensitivity*np.max([network.edges[(i,p)]['affinity'] for p in candidates]) + if cutoff <.5: + cutoff = .5 + + if affinity > cutoff: + support.append(j) + + proposals_supported[i] = support + + return({'delta_holdings':delta_holdings, 'proposals_supported':proposals_supported}) + +def update_tokens(params, step, sL, s, _input): + + network = s['network'] + delta_holdings = _input['delta_holdings'] + proposals = get_nodes_by_type(network, 'proposal') + proposals_supported = _input['proposals_supported'] + participants = get_nodes_by_type(network, 'participant') + alpha = params['alpha'] + + for i in participants: + network.nodes[i]['holdings'] = network.nodes[i]['holdings']+delta_holdings[i] + supported = proposals_supported[i] + total_affinity = np.sum([ network.edges[(i, j)]['affinity'] for j in supported]) + for j in proposals: + if j in supported: + normalized_affinity = network.edges[(i, j)]['affinity']/total_affinity + network.edges[(i, j)]['tokens'] = normalized_affinity*network.nodes[i]['holdings'] + else: + network.edges[(i, j)]['tokens'] = 0 + + prior_conviction = network.edges[(i, j)]['conviction'] + current_tokens = network.edges[(i, j)]['tokens'] + network.edges[(i, j)]['conviction'] =current_tokens+alpha*prior_conviction + + for j in proposals: + network.nodes[j]['conviction'] = np.sum([ network.edges[(i, j)]['conviction'] for i in participants]) + + key = 'network' + value = network + + return (key, value) + +def update_supply(params, step, sL, s, _input): + + supply = s['supply'] + delta_holdings = _input['delta_holdings'] + delta_supply = np.sum([v for v in delta_holdings.values()]) + + supply = supply + delta_supply + + key = 'supply' + value = supply + + return (key, value) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# The Partial State Update Blocks +partial_state_update_blocks = [ + { + 'policies': { + #new proposals or new participants + 'random': driving_process + }, + 'variables': { + 'network': update_network, + 'funds':increment_funds, + 'supply':increment_supply + } + }, + { + 'policies': { + 'completion': check_progress #see if any of the funded proposals completes + }, + 'variables': { # The following state variables will be updated simultaneously + 'sentiment': update_sentiment_on_completion, #note completing decays sentiment, completing bumps it + 'network': complete_proposal #book-keeping + } + }, + { + 'policies': { + 'release': trigger_function #check each proposal to see if it passes + }, + 'variables': { # The following state variables will be updated simultaneously + 'funds': decrement_funds, #funds expended + 'sentiment': update_sentiment_on_release, #releasing funds can bump sentiment + 'network': update_proposals #reset convictions, and participants sentiments + #update based on affinities + } + }, + { + 'policies': { + 'participants_act': participants_decisions, #high sentiment, high affinity =>buy + #low sentiment, low affinities => burn + #assign tokens to top affinities + }, + 'variables': { + 'supply': update_supply, + 'network': update_tokens #update everyones holdings + #and their conviction for each proposal + } + } +] + +n= 25 #initial participants +m= 3 #initial proposals + +initial_sentiment = .5 + +network, initial_funds, initial_supply, total_requested = initialize_network(n,m,total_funds_given_total_supply,trigger_threshold) + +initial_conditions = {'network':network, + 'supply': initial_supply, + 'funds':initial_funds, + 'sentiment': initial_sentiment} + +#power of 1 token forever +# conviction_capactity = [2] +# alpha = [1-1/cc for cc in conviction_capactity] +# print(alpha) + +params={ + 'sensitivity': [.75], + 'tmin': [7], #unit days; minimum periods passed before a proposal can pass + 'sentiment_decay': [.001], #termed mu in the state update function + 'alpha': [0.5], + 'base_completion_rate': [10], + 'trigger_func': [trigger_threshold] +} + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Settings of general simulation parameters, unrelated to the system itself +# `T` is a range with the number of discrete units of time the simulation will run for; +# `N` is the number of times the simulation will be run (Monte Carlo runs) +time_periods_per_run = 250 +monte_carlo_runs = 1 + +simulation_parameters = config_sim({ + 'T': range(time_periods_per_run), + 'N': monte_carlo_runs, + 'M': params +}) + + +from cadCAD.configuration import append_configs + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# The configurations above are then packaged into a `Configuration` object +append_configs( + initial_state=initial_conditions, #dict containing variable names and initial values + partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions + sim_configs=simulation_parameters #dict containing simulation parameters +) + +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from cadCAD import configs +import pandas as pd + +exec_mode = ExecutionMode() +multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) +run = Executor(exec_context=multi_proc_ctx, configs=configs) + +i = 0 +for raw_result, tensor_field in run.execute(): + result = pd.DataFrame(raw_result) + print() + print(f"Tensor Field: {type(tensor_field)}") + print(tabulate(tensor_field, headers='keys', tablefmt='psql')) + print(f"Output: {type(result)}") + print(tabulate(result, headers='keys', tablefmt='psql')) + print() + i += 1 From 0a0d85c2578697035be570d59e98dbba04fca470 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Fri, 31 May 2019 16:57:30 -0400 Subject: [PATCH 17/19] ver. 0.2.4 --- cadCAD/configuration/utils/__init__.py | 7 -- .../configuration/utils/policyAggregation.py | 37 ----------- cadCAD/engine/__init__.py | 4 -- cadCAD/engine/simulation.py | 60 +----------------- cadCAD/utils/sys_config.py | 9 +-- dist/cadCAD-0.2.3-py3-none-any.whl | Bin 15601 -> 0 bytes dist/cadCAD-0.2.4-py3-none-any.whl | Bin 0 -> 15046 bytes setup.py | 2 +- 8 files changed, 3 insertions(+), 116 deletions(-) delete mode 100644 dist/cadCAD-0.2.3-py3-none-any.whl create mode 100644 dist/cadCAD-0.2.4-py3-none-any.whl diff --git a/cadCAD/configuration/utils/__init__.py b/cadCAD/configuration/utils/__init__.py index a4ab297..3efdcc3 100644 --- a/cadCAD/configuration/utils/__init__.py +++ b/cadCAD/configuration/utils/__init__.py @@ -1,9 +1,6 @@ from datetime import datetime, timedelta -from decimal import Decimal from copy import deepcopy from functools import reduce -from pprint import pprint - from fn.func import curried from funcy import curry import pandas as pd @@ -175,9 +172,6 @@ def env_trigger(end_substep): return lambda trigger_field, trigger_vals, funct_list: \ curry(trigger)(end_substep)(trigger_field)(trigger_vals)(funct_list) -# trigger = curry(_trigger) -# print(timestep_trigger) - # param sweep enabling middleware def config_sim(d): @@ -185,7 +179,6 @@ def config_sim(d): return flatten_tabulated_dict(tabulate_dict(d)) if "M" in d: - # print([{"N": d["N"], "T": d["T"], "M": M} for M in process_variables(d["M"])]) return [{"N": d["N"], "T": d["T"], "M": M} for M in process_variables(d["M"])] else: d["M"] = [{}] diff --git a/cadCAD/configuration/utils/policyAggregation.py b/cadCAD/configuration/utils/policyAggregation.py index c43de03..96077dc 100644 --- a/cadCAD/configuration/utils/policyAggregation.py +++ b/cadCAD/configuration/utils/policyAggregation.py @@ -1,6 +1,5 @@ from fn.op import foldr from fn.func import curried -from collections import Counter def get_base_value(x): if isinstance(x, str): @@ -39,43 +38,7 @@ def dict_op(f, d1, d2): key_set = set(list(d1.keys()) + list(d2.keys())) - return {k: f(set_base_value(d1, d2, k), set_base_value(d2, d1, k)) for k in key_set} -# -# @curried -# def dict_op(f, d1, d2): -# def set_base_value(target_dict, source_dict, key): -# if key not in target_dict: -# return get_base_value(source_dict[key]) -# else: -# return target_dict[key] -# -# key_set = set(list(d1.keys()) + list(d2.keys())) -# -# norm_d1 = {k: set_base_value(d1, d2, k) for k in key_set} -# norm_d2 = {k: set_base_value(d2, d1, k) for k in key_set} -# -# return {k: f(set_base_value(d1, d2, k), set_base_value(d2, d1, k)) for k in key_set} - - -# @curried -# def dict_op(f, d1, d2): -# # d1C = Counter(d1) -# # d2C = Counter(d2) -# def set_base_value(target_dict, source_dict, key): -# if key not in target_dict: -# return get_base_value(source_dict[key]) -# else: -# return target_dict[key] -# key_set = set(list(d1.keys()) + list(d2.keys())) -# norm_d1 = Counter({k: set_base_value(d1, d2, k) for k in key_set}) -# norm_d2 = Counter({k: set_base_value(d2, d1, k) for k in key_set}) -# # print(norm_d1) -# # print(norm_d2) -# print(norm_d1 + norm_d2) -# # print(f(norm_d1, norm_d2)) -# print() -# return f(norm_d1, norm_d2) def dict_elemwise_sum(): return dict_op(add) diff --git a/cadCAD/engine/__init__.py b/cadCAD/engine/__init__.py index 75c1e51..a8002b9 100644 --- a/cadCAD/engine/__init__.py +++ b/cadCAD/engine/__init__.py @@ -27,11 +27,8 @@ def single_proc_exec( Ts: List[range], Ns: List[int] ): - # print(env_processes_list) - # print(configs_structs) l = [simulation_execs, states_lists, configs_structs, env_processes_list, Ts, Ns] simulation_exec, states_list, config, env_processes, T, N = list(map(lambda x: x.pop(), l)) - # print(config.env_processes) result = simulation_exec(var_dict_list, states_list, config, env_processes, T, N) return flatten(result) @@ -68,7 +65,6 @@ class Executor: self.exec_method = exec_context.method self.exec_context = exec_context.name self.configs = configs - # self.main = self.execute def execute(self) -> Tuple[List[Dict[str, Any]], DataFrame]: config_proc = Processor() diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index d278633..8a5c5df 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -1,9 +1,7 @@ from typing import Any, Callable, Dict, List, Tuple - from pathos.pools import ThreadPool as TPool from copy import deepcopy from functools import reduce -from funcy import compose from cadCAD.engine.utils import engine_exception from cadCAD.utils import flatten @@ -19,13 +17,10 @@ class Executor: state_update_exception: Callable = id_exception ) -> None: - # behavior_ops self.policy_ops = policy_ops self.state_update_exception = state_update_exception self.policy_update_exception = policy_update_exception - # behavior_update_exception - # get_behavior_input # sL: State Window def get_policy_input( self, sweep_dict: Dict[str, List[Any]], @@ -35,10 +30,8 @@ class Executor: funcs: List[Callable] ) -> Dict[str, Any]: - # ops = self.policy_ops[::-1] ops = self.policy_ops - def get_col_results(sweep_dict, sub_step, sL, s, funcs): return list(map(lambda f: f(sweep_dict, sub_step, sL, s), funcs)) @@ -119,23 +112,7 @@ class Executor: run: int ) -> List[Dict[str, Any]]: - # def dp_psu(d): - # for k, v in deepcopy(d).items(): - # yield k, deepcopy(v) - # - # def dp_psub(l): - # for d in l: - # yield dict(dp_psu(d)) - - # last_in_obj: Dict[str, Any] = dict(dp_psu(sL[-1])) - last_in_obj: Dict[str, Any] = deepcopy(sL[-1]) - # last_in_obj: Dict[str, Any] = sL[-1] - - # last_in_obj: Dict[str, Any] = sH[-1] - # print(last_in_obj) - # print(sH[-1]) - _input: Dict[str, Any] = self.policy_update_exception(self.get_policy_input(sweep_dict, sub_step, sH, last_in_obj, policy_funcs)) # ToDo: add env_proc generator to `last_in_copy` iterator as wrapper function @@ -152,22 +129,13 @@ class Executor: return destination last_in_copy: Dict[str, Any] = transfer_missing_fields(last_in_obj, dict(generate_record(state_funcs))) - # ToDo: Remove - # last_in_copy: Dict[str, Any] = self.apply_env_proc(env_processes, last_in_copy, last_in_copy['timestep']) - # print(env_processes) - # print() last_in_copy: Dict[str, Any] = self.apply_env_proc(sweep_dict, env_processes, last_in_copy) - - # ToDo: make 'substep' & 'timestep' reserve fields last_in_copy['substep'], last_in_copy['timestep'], last_in_copy['run'] = sub_step, time_step, run sL.append(last_in_copy) del last_in_copy - # print(sL) - # print() - return sL # mech_pipeline - state_update_block @@ -182,23 +150,7 @@ class Executor: ) -> List[Dict[str, Any]]: sub_step = 0 - # states_list_copy: List[Dict[str, Any]] = deepcopy(states_list) - # states_list_copy: List[Dict[str, Any]] = states_list - # ToDo: flatten first - # states_list_copy: List[Dict[str, Any]] = simulation_list[-1] states_list_copy: List[Dict[str, Any]] = deepcopy(simulation_list[-1]) - - # def dp_psu(d): - # for k, v in deepcopy(d).items(): - # yield k, deepcopy(v) - # - # def dp_psub(l): - # for d in l: - # yield dict(dp_psu(d)) - - # states_list_copy: List[Dict[str, Any]] = list(dp_psub(simulation_list[-1])) - # print(states_list_copy) - # ToDo: Causes Substep repeats in sL: genesis_states: Dict[str, Any] = states_list_copy[-1] @@ -211,7 +163,7 @@ class Executor: del states_list_copy states_list: List[Dict[str, Any]] = [genesis_states] - # ToDo: Causes Substep repeats in sL, use for yield + # ToDo: Was causing Substep repeats in sL, use for yield sub_step += 1 for [s_conf, p_conf] in configs: # tensor field @@ -219,11 +171,7 @@ class Executor: states_list: List[Dict[str, Any]] = self.partial_state_update( sweep_dict, sub_step, states_list, simulation_list, s_conf, p_conf, env_processes, time_step, run ) - # print(sub_step) - # print(simulation_list) - # print(flatten(simulation_list)) sub_step += 1 - # print(sub_step) time_step += 1 @@ -244,10 +192,6 @@ class Executor: # ToDo: simulation_list should be a Tensor that is generated throughout the Executor simulation_list: List[List[Dict[str, Any]]] = [states_list] - # print(simulation_list[-1]) - # print() - # pipe_run = simulation_list[-1] - # print(simulation_list) for time_step in time_seq: pipe_run: List[Dict[str, Any]] = self.state_update_pipeline( sweep_dict, simulation_list, configs, env_processes, time_step, run @@ -255,8 +199,6 @@ class Executor: _, *pipe_run = pipe_run simulation_list.append(pipe_run) - # print(simulation_list) - # print() return simulation_list diff --git a/cadCAD/utils/sys_config.py b/cadCAD/utils/sys_config.py index 738161c..3da1efe 100644 --- a/cadCAD/utils/sys_config.py +++ b/cadCAD/utils/sys_config.py @@ -1,12 +1,6 @@ -from copy import deepcopy - -from fn.func import curried +from funcy import curry from cadCAD.configuration.utils import ep_time_step, time_step -from funcy import curry -import pprint as pp -# from fn import _ -from functools import reduce def increment(y, incr_by): return lambda _g, step, sL, s, _input: (y, s[y] + incr_by) @@ -26,7 +20,6 @@ def update_timestamp(y, timedelta, format): ep_time_step(s, dt_str=s[y], fromat_str=format, _timedelta=timedelta) ) - def apply(f, y: str, incr_by: int): return lambda _g, step, sL, s, _input: (y, curry(f)(s[y])(incr_by)) diff --git a/dist/cadCAD-0.2.3-py3-none-any.whl b/dist/cadCAD-0.2.3-py3-none-any.whl deleted file mode 100644 index 19d55e7fc5efae02e7b3c981fa143f9ce6f710e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15601 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 diff --git a/dist/cadCAD-0.2.4-py3-none-any.whl b/dist/cadCAD-0.2.4-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..052468e42d539d3e683af988c828b461a19d534a GIT binary patch literal 15046 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 I>mR@VAAOw@?*IS* literal 0 HcmV?d00001 diff --git a/setup.py b/setup.py index 8e98475..966cce7 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ long_description = "cadCAD is a differential games based simulation software pac monte carlo analysis and other common numerical methods is provided." setup(name='cadCAD', - version='0.2.3', + version='0.2.4', description="cadCAD: a differential games based simulation software package for research, validation, and \ Computer Aided Design of economic systems", long_description=long_description, From 3019715d837c6bc628272046db584e28161c5513 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Fri, 31 May 2019 17:10:28 -0400 Subject: [PATCH 18/19] ver. 0.2.4 --- dist/cadCAD-0.2.4-py3-none-any.whl | Bin 15046 -> 15046 bytes simulations/regression_tests/config1.py | 6 +----- simulations/regression_tests/config2.py | 4 +--- .../regression_tests/external_dataset.py | 3 --- .../regression_tests/policy_aggregation.py | 11 ----------- simulations/regression_tests/sweep_config.py | 4 +--- simulations/regression_tests/udo.py | 14 ++++---------- .../udo_inter_substep_update.py | 13 +++++++------ simulations/test_executions/config2_test.py | 1 - .../test_executions/param_sweep_test.py | 2 -- 10 files changed, 14 insertions(+), 44 deletions(-) diff --git a/dist/cadCAD-0.2.4-py3-none-any.whl b/dist/cadCAD-0.2.4-py3-none-any.whl index 052468e42d539d3e683af988c828b461a19d534a..d8157ed5bea223abdb88a3edd1c344edebd5efd0 100644 GIT binary patch delta 65 zcmX?BdaQIqtUi Date: Fri, 31 May 2019 17:24:36 -0400 Subject: [PATCH 19/19] ver. 0.2.4 --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index adec4a4..2990b51 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -jupyter notebook.idea +.idea +jupyter notebook .ipynb_checkpoints .DS_Store .idea