From a2453e8adf93baebf7640df05b7d51fbc22afaea Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 21 Feb 2019 15:09:19 -0500 Subject: [PATCH 01/18] rebased master --- README.md | 73 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 6a7fe07..3e5cadc 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# SimCad +# cadCAD **Warning**: **Do not** publish this package / software to **any** software repository **except** one permitted by BlockScience. **Description:** -SimCAD is a differential games based simulation software package for research, validation, and Computer \ +cadCAD is a differential games based simulation software package for research, validation, and Computer \ Aided Design of economic systems. An economic system is treated as a state based model and defined through a \ set of endogenous and exogenous state variables which are updated through mechanisms and environmental \ processes, respectively. Behavioral models, which may be deterministic or stochastic, provide the evolution of \ @@ -41,48 +41,89 @@ Intructions: Examples: `/simulations/validation/*` -**3. Import SimCAD & Run Simulation:** +**3. Import cadCAD & Run Simulations:** -Examples: `/simulations/example_run.py` or `/simulations/example_run.ipynb` +Examples: `/simulations/*.py` or `/simulations/*.ipynb` -`/simulations/example_run.py`: +Single Simulation Run: `/simulations/single_config_run.py` ```python -import pandas as pd from tabulate import tabulate - # The following imports NEED to be in the exact order -from SimCAD.engine import ExecutionMode, ExecutionContext, Executor -from validation import config1, config2 -from SimCAD import configs +from cadCAD.engine import ExecutionMode, ExecutionContext, Executor +from simulations.validation import config1 +from cadCAD import configs exec_mode = ExecutionMode() -print("Simulation Execution 1") +print("Simulation Execution: Single Configuration") print() -first_config = [configs[0]] # from config1 +first_config = configs # only contains config1 single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) run1 = Executor(exec_context=single_proc_ctx, configs=first_config) run1_raw_result, tensor_field = run1.main() result = pd.DataFrame(run1_raw_result) print() -print("Tensor Field:") +print("Tensor Field: config1") print(tabulate(tensor_field, headers='keys', tablefmt='psql')) print("Output:") print(tabulate(result, headers='keys', tablefmt='psql')) print() +``` -print("Simulation Execution 2: Pairwise Execution") -print() +Parameter Sweep Simulation Run (Concurrent): `/simulations/param_sweep_run.py` +```python +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 sweep_config +from cadCAD import configs + +exec_mode = ExecutionMode() + +print("Simulation Execution: Concurrent Execution") multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) run2 = Executor(exec_context=multi_proc_ctx, configs=configs) + +i = 0 +config_names = ['sweep_config_A', 'sweep_config_B'] for raw_result, tensor_field in run2.main(): result = pd.DataFrame(raw_result) print() - print("Tensor Field:") + 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 +``` + +Multiple Simulation Runs (Concurrent): `/simulations/multi_config run.py` +```python +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 cadCAD import configs + +exec_mode = ExecutionMode() + +print("Simulation Execution: Concurrent Execution") +multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) +run2 = Executor(exec_context=multi_proc_ctx, configs=configs) + +i = 0 +config_names = ['config1', 'config2'] +for raw_result, tensor_field in run2.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 ``` The above can be run in Jupyter. From 9c848b5cb94fa8619bd9ec50ba4a48cef7958474 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 21 Feb 2019 15:28:20 -0500 Subject: [PATCH 02/18] readme update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e5cadc..3ad319e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ A/B testing policies, monte carlo analysis and other common numerical methods is **Option A:** Package Repository Access -*Note:* Tokens are issued to trial users and BlockScience employees. Please replace with and issued token in the script below. +***IMPORTANT NOTE:*** Tokens are issued and to be used by trial users and BlockScience employees **ONLY**. Please replace with and issued token in the script below. ```bash pip3 install pandas pathos fn tabulate pip3 install cadCAD --extra-index-url https://@repo.fury.io/blockscience/ From 516b77d693cb789cb9453ad870297b2f8345638e Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 21 Feb 2019 15:29:36 -0500 Subject: [PATCH 03/18] readme update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ad319e..f976d7b 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ A/B testing policies, monte carlo analysis and other common numerical methods is **Option A:** Package Repository Access -***IMPORTANT NOTE:*** Tokens are issued and to be used by trial users and BlockScience employees **ONLY**. Please replace with and issued token in the script below. +***IMPORTANT NOTE:*** Tokens are issued to and meant to be used by trial users and BlockScience employees **ONLY**. Please replace with and issued token in the script below. ```bash pip3 install pandas pathos fn tabulate pip3 install cadCAD --extra-index-url https://@repo.fury.io/blockscience/ From d932332fcc8020fa0f01e3b50bfcc13111c91e95 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 21 Feb 2019 15:30:23 -0500 Subject: [PATCH 04/18] readme update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f976d7b..c207825 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ A/B testing policies, monte carlo analysis and other common numerical methods is **Option A:** Package Repository Access -***IMPORTANT NOTE:*** Tokens are issued to and meant to be used by trial users and BlockScience employees **ONLY**. Please replace with and issued token in the script below. +***IMPORTANT NOTE:*** Tokens are issued to and meant to be used by trial users and BlockScience employees **ONLY**. Replace with an issued token in the script below. ```bash pip3 install pandas pathos fn tabulate pip3 install cadCAD --extra-index-url https://@repo.fury.io/blockscience/ From fe1960797eebe2e6807d3c123d7bfc3d80ff3cba Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 21 Feb 2019 15:32:11 -0500 Subject: [PATCH 05/18] readme update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c207825..dbe65ab 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ A/B testing policies, monte carlo analysis and other common numerical methods is **Option A:** Package Repository Access -***IMPORTANT NOTE:*** Tokens are issued to and meant to be used by trial users and BlockScience employees **ONLY**. Replace with an issued token in the script below. +***IMPORTANT NOTE:*** Tokens are issued to and meant to be used by trial users and BlockScience employees **ONLY**. Replace \ with an issued token in the script below. ```bash pip3 install pandas pathos fn tabulate pip3 install cadCAD --extra-index-url https://@repo.fury.io/blockscience/ From 2fb0dcf754b9d0a5b1050672c68265e47775f356 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 21 Feb 2019 15:34:27 -0500 Subject: [PATCH 06/18] readme update --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dbe65ab..a067880 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Examples: Examples: `/simulations/*.py` or `/simulations/*.ipynb` -Single Simulation Run: `/simulations/single_config_run.py` +Single Simulation: `/simulations/single_config_run.py` ```python from tabulate import tabulate # The following imports NEED to be in the exact order @@ -70,7 +70,7 @@ print(tabulate(result, headers='keys', tablefmt='psql')) print() ``` -Parameter Sweep Simulation Run (Concurrent): `/simulations/param_sweep_run.py` +Parameter Sweep Simulation (Concurrent): `/simulations/param_sweep_run.py` ```python import pandas as pd from tabulate import tabulate @@ -98,7 +98,7 @@ for raw_result, tensor_field in run2.main(): i += 1 ``` -Multiple Simulation Runs (Concurrent): `/simulations/multi_config run.py` +Multiple Simulations (Concurrent): `/simulations/multi_config run.py` ```python import pandas as pd from tabulate import tabulate From 9d9e33b766a802cc518427749875f0562e2ad6f7 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Tue, 26 Feb 2019 11:47:41 -0500 Subject: [PATCH 07/18] type annotations: simulation.py --- README.md | 2 +- cadCAD/engine/__init__.py | 4 +- cadCAD/engine/simulation.py | 136 +++++++++++++----- cadCAD/engine/utils.py | 2 +- cadCAD/utils/__init__.py | 10 +- monkeytype.sqlite3 | Bin 0 -> 761856 bytes ...ulti_config run.py => multi_config_run.py} | 0 simulations/validation/config1.py | 2 + simulations/validation/sweep_config.py | 4 +- 9 files changed, 117 insertions(+), 43 deletions(-) create mode 100644 monkeytype.sqlite3 rename simulations/{multi_config run.py => multi_config_run.py} (100%) diff --git a/README.md b/README.md index a067880..0719875 100644 --- a/README.md +++ b/README.md @@ -129,4 +129,4 @@ for raw_result, tensor_field in run2.main(): The above can be run in Jupyter. ```bash jupyter notebook -``` \ No newline at end of file +``` diff --git a/cadCAD/engine/__init__.py b/cadCAD/engine/__init__.py index b0727e8..6ba51e9 100644 --- a/cadCAD/engine/__init__.py +++ b/cadCAD/engine/__init__.py @@ -22,8 +22,8 @@ class ExecutionContext: result = simulation(var_dict, states_list, config, env_processes, T, N) return flatten(result) - def parallelize_simulations(fs, var_dict_list, states_list, configs, env_processes, Ts, Ns): - l = list(zip(fs, var_dict_list, states_list, configs, env_processes, Ts, Ns)) + def parallelize_simulations(simulations, var_dict_list, states_list, configs, env_processes, Ts, Ns): + l = list(zip(simulations, var_dict_list, states_list, configs, env_processes, Ts, Ns)) with Pool(len(configs)) as p: results = p.map(lambda t: t[0](t[1], t[2], t[3], t[4], t[5], t[6]), l) return results diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 3881cde..97af73d 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -2,19 +2,38 @@ from copy import deepcopy from fn.op import foldr, call from cadCAD.engine.utils import engine_exception +from typing import Any, Callable, Dict, List, Tuple -id_exception = engine_exception(KeyError, KeyError, None) +id_exception: Callable = engine_exception(KeyError, KeyError, None) + +import pprint as pp class Executor: - def __init__(self, policy_ops, policy_update_exception=id_exception, state_update_exception=id_exception): - self.policy_ops = policy_ops # behavior_ops - self.state_update_exception = state_update_exception - self.policy_update_exception = policy_update_exception # behavior_update_exception + def __init__( + 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 + 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, + var_dict: Dict[str, List[Any]], + sub_step: int, + sL: List[Dict[str, Any]], + s: Dict[str, Any], + funcs: List[Callable] + ) -> Dict[str, Any]: - # get_behavior_input - def get_policy_input(self, var_dict, sub_step, sL, s, funcs): ops = self.policy_ops[::-1] def get_col_results(var_dict, sub_step, sL, s, funcs): @@ -22,23 +41,39 @@ class Executor: return foldr(call, get_col_results(var_dict, sub_step, sL, s, funcs))(ops) - def apply_env_proc(self, env_processes, state_dict, sub_step): + def apply_env_proc( + self, + env_processes: Dict[str, Callable], + state_dict: Dict[str, Any], + sub_step: int + ) -> None: for state in state_dict.keys(): if state in list(env_processes.keys()): - env_state = env_processes[state] + 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]) + state_dict[state]: Any = env_state(sub_step)(state_dict[state]) else: - state_dict[state] = env_state(state_dict[state]) + state_dict[state]: Any = env_state(state_dict[state]) # mech_step - def partial_state_update(self, var_dict, sub_step, sL, state_funcs, policy_funcs, env_processes, time_step, run): - last_in_obj = sL[-1] + def partial_state_update( + self, + var_dict: Dict[str, List[Any]], + sub_step: int, + sL: Any, + state_funcs: List[Callable], + policy_funcs: List[Callable], + env_processes: Dict[str, Callable], + time_step: int, + run: int + ) -> List[Dict[str, Any]]: - _input = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sL, last_in_obj, policy_funcs)) + 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 - last_in_copy = dict( + 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 ] @@ -46,58 +81,91 @@ class Executor: for k in last_in_obj: if k not in last_in_copy: - last_in_copy[k] = last_in_obj[k] + last_in_copy[k]: Any = last_in_obj[k] del last_in_obj self.apply_env_proc(env_processes, last_in_copy, last_in_copy['timestep']) 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 return sL - # mech_pipeline - def state_update_pipeline(self, var_dict, states_list, configs, env_processes, time_step, run): + def state_update_pipeline( + self, + var_dict: Dict[str, List[Any]], + states_list: List[Dict[str, Any]], + configs: List[Tuple[List[Callable], List[Callable]]], + env_processes: Dict[str, Callable], + time_step: int, + run: int + ) -> List[Dict[str, Any]]: + sub_step = 0 - states_list_copy = deepcopy(states_list) - genesis_states = states_list_copy[-1] + states_list_copy: List[Dict[str, Any]] = deepcopy(states_list) + genesis_states: Dict[str, Any] = states_list_copy[-1] genesis_states['substep'], genesis_states['timestep'] = sub_step, time_step - states_list = [genesis_states] + states_list: List[Dict[str, Any]] = [genesis_states] sub_step += 1 for config in configs: s_conf, p_conf = config[0], config[1] - states_list = self.partial_state_update(var_dict, sub_step, states_list, s_conf, p_conf, env_processes, time_step, run) + 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 + ) + sub_step += 1 time_step += 1 return states_list - def run_pipeline(self, var_dict, states_list, configs, env_processes, time_seq, run): - time_seq = [x + 1 for x in time_seq] - simulation_list = [states_list] + def run_pipeline( + self, + var_dict: Dict[str, List[Any]], + states_list: List[Dict[str, Any]], + configs: List[Tuple[List[Callable], List[Callable]]], + env_processes: Dict[str, Callable], + time_seq: range, + run: int + ) -> List[List[Dict[str, Any]]]: + + time_seq: List[int] = [x + 1 for x in time_seq] + simulation_list: List[List[Dict[str, Any]]] = [states_list] for time_step in time_seq: - pipe_run = self.state_update_pipeline(var_dict, simulation_list[-1], configs, env_processes, time_step, run) + pipe_run: List[Dict[str, Any]] = self.state_update_pipeline( + var_dict, simulation_list[-1], configs, env_processes, time_step, run + ) _, *pipe_run = pipe_run simulation_list.append(pipe_run) return simulation_list # ToDo: Muiltithreaded Runs - def simulation(self, var_dict, states_list, configs, env_processes, time_seq, runs): - pipe_run = [] + def simulation( + self, + var_dict: Dict[str, List[Any]], + states_list: List[Dict[str, Any]], + configs: List[Tuple[List[Callable], List[Callable]]], + env_processes: Dict[str, Callable], + time_seq: range, + runs: int + ) -> List[List[Dict[str, Any]]]: + + pipe_run: List[List[Dict[str, Any]]] = [] for run in range(runs): run += 1 - states_list_copy = deepcopy(states_list) + 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) - genesis = head.pop() - genesis['substep'], genesis['timestep'], genesis['run'] = 0, 0, run - first_timestep_per_run = [genesis] + tail.pop(0) - pipe_run += [first_timestep_per_run] + tail del states_list_copy - return pipe_run \ No newline at end of file + 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) + pipe_run += [first_timestep_per_run] + tail + + return pipe_run diff --git a/cadCAD/engine/utils.py b/cadCAD/engine/utils.py index 08428cc..c0f3a76 100644 --- a/cadCAD/engine/utils.py +++ b/cadCAD/engine/utils.py @@ -39,4 +39,4 @@ def engine_exception(ErrorType, error_message, exception_function, try_function) def fit_param(param, x): return x + param -# fit_param = lambda param: lambda x: x + param \ No newline at end of file +# fit_param = lambda param: lambda x: x + param diff --git a/cadCAD/utils/__init__.py b/cadCAD/utils/__init__.py index dac15c1..59fc9c8 100644 --- a/cadCAD/utils/__init__.py +++ b/cadCAD/utils/__init__.py @@ -1,6 +1,8 @@ from collections import defaultdict from itertools import product import warnings +from typing import Dict, List + def pipe(x): return x @@ -41,11 +43,11 @@ def dict_filter(dictionary, condition): return dict([(k, v) for k, v in dictionary.items() if condition(v)]) -def get_max_dict_val_len(g): +def get_max_dict_val_len(g: Dict[str, List[int]]) -> int: return len(max(g.values(), key=len)) -def tabulate_dict(d): +def tabulate_dict(d: Dict[str, List[int]]) -> Dict[str, List[int]]: max_len = get_max_dict_val_len(d) _d = {} for k, vl in d.items(): @@ -57,7 +59,7 @@ def tabulate_dict(d): return _d -def flatten_tabulated_dict(d): +def flatten_tabulated_dict(d: Dict[str, List[int]]) -> List[Dict[str, int]]: max_len = get_max_dict_val_len(d) dl = [{} for i in range(max_len)] @@ -133,4 +135,4 @@ def curry_pot(f, *argv): # def decorator(f): # f.__name__ = newname # return f -# return decorator \ No newline at end of file +# return decorator diff --git a/monkeytype.sqlite3 b/monkeytype.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..c8ea7409a5b1f1bff8411e094c63cd9c9f178813 GIT binary patch literal 761856 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 literal 0 HcmV?d00001 diff --git a/simulations/multi_config run.py b/simulations/multi_config_run.py similarity index 100% rename from simulations/multi_config run.py rename to simulations/multi_config_run.py diff --git a/simulations/validation/config1.py b/simulations/validation/config1.py index 532f6e7..2c26f91 100644 --- a/simulations/validation/config1.py +++ b/simulations/validation/config1.py @@ -21,6 +21,8 @@ def p1m1(_g, step, sL, s): def p2m1(_g, step, sL, s): return {'param2': 4} +# [] + def p1m2(_g, step, sL, s): return {'param1': 'a', 'param2': 2} def p2m2(_g, step, sL, s): diff --git a/simulations/validation/sweep_config.py b/simulations/validation/sweep_config.py index 09ccd6b..2350d95 100644 --- a/simulations/validation/sweep_config.py +++ b/simulations/validation/sweep_config.py @@ -7,6 +7,8 @@ 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 typing import Dict, List + pp = pprint.PrettyPrinter(indent=4) seeds = { @@ -17,7 +19,7 @@ seeds = { } -g = { +g: Dict[str, List[int]] = { 'alpha': [1], 'beta': [2, 5], 'gamma': [3, 4], From cb6acce3d9e713e41f9a877620cb92382981e5c8 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Tue, 26 Feb 2019 11:49:00 -0500 Subject: [PATCH 08/18] type annotations: simulation.py --- .gitignore | 2 ++ monkeytype.sqlite3 | Bin 761856 -> 0 bytes 2 files changed, 2 insertions(+) delete mode 100644 monkeytype.sqlite3 diff --git a/.gitignore b/.gitignore index f668855..5194f30 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ cadCAD.egg-info build cadCAD.egg-info SimCAD.egg-info + +monkeytype.sqlite3 \ No newline at end of file diff --git a/monkeytype.sqlite3 b/monkeytype.sqlite3 deleted file mode 100644 index c8ea7409a5b1f1bff8411e094c63cd9c9f178813..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 761856 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 From 9e9f7be17e3e2cb536cd457aee83b19ffce23099 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Wed, 27 Feb 2019 11:36:10 -0500 Subject: [PATCH 09/18] wrapping up type stuff --- .../utils/depreciationHandler.py | 4 +- .../configuration/utils/policyAggregation.py | 2 +- cadCAD/engine/__init__.py | 79 ++++++++++++++----- cadCAD/engine/simulation.py | 8 +- simulations/multi_config_run.py | 6 +- simulations/param_sweep_run.py | 6 +- simulations/single_config_run.py | 9 ++- 7 files changed, 78 insertions(+), 36 deletions(-) diff --git a/cadCAD/configuration/utils/depreciationHandler.py b/cadCAD/configuration/utils/depreciationHandler.py index 8997771..330823b 100644 --- a/cadCAD/configuration/utils/depreciationHandler.py +++ b/cadCAD/configuration/utils/depreciationHandler.py @@ -30,10 +30,10 @@ def sanitize_partial_state_updates(partial_state_updates): # Also for backwards compatibility, we accept partial state update blocks both as list or dict # No need for a deprecation warning as it's already raised by cadCAD.utils.key_filter - if (type(new_partial_state_updates)==list): + if isinstance(new_partial_state_updates, list): for v in new_partial_state_updates: rename_keys(v) - elif (type(new_partial_state_updates)==dict): + elif isinstance(new_partial_state_updates, dict): for k, v in new_partial_state_updates.items(): rename_keys(v) diff --git a/cadCAD/configuration/utils/policyAggregation.py b/cadCAD/configuration/utils/policyAggregation.py index eac845d..68535a3 100644 --- a/cadCAD/configuration/utils/policyAggregation.py +++ b/cadCAD/configuration/utils/policyAggregation.py @@ -14,7 +14,7 @@ def get_base_value(x): def policy_to_dict(v): - return dict(list(zip(map(lambda n: 'b' + str(n + 1), list(range(len(v)))), v))) + return dict(list(zip(map(lambda n: 'p' + str(n + 1), list(range(len(v)))), v))) add = lambda a, b: a + b diff --git a/cadCAD/engine/__init__.py b/cadCAD/engine/__init__.py index 6ba51e9..ee36088 100644 --- a/cadCAD/engine/__init__.py +++ b/cadCAD/engine/__init__.py @@ -1,33 +1,72 @@ +from typing import Callable, Dict, List, Any, Tuple from pathos.multiprocessing import ProcessingPool as Pool +from pandas.core.frame import DataFrame from cadCAD.utils import flatten -from cadCAD.configuration import Processor +from cadCAD.configuration import Configuration, Processor from cadCAD.configuration.utils import TensorFieldReport from cadCAD.engine.simulation import Executor as SimExecutor +VarDictType = Dict[str, List[Any]] +StatesListsType = List[Dict[str, Any]] +ConfigsType = List[Tuple[List[Callable], List[Callable]]] +EnvProcessesType = Dict[str, Callable] + +SimulationType = Callable[ + [ + SimExecutor, + VarDictType, + StatesListsType, + ConfigsType, + EnvProcessesType, + range, + int + ], + List[List[Dict[str, Any]]] +] + class ExecutionMode: single_proc = 'single_proc' multi_proc = 'multi_proc' +def single_proc_exec( + simulation_execs: List[SimulationType], + var_dict_list: List[VarDictType], + states_lists: List[StatesListsType], + configs_structs: List[ConfigsType], + env_processes_list: List[EnvProcessesType], + Ts: List[range], + Ns: List[int] + ): + + 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)) + result = simulation_exec(var_dict_list, states_list, config, env_processes, T, N) + return flatten(result) + + +def parallelize_simulations( + simulation_execs: List[SimulationType], + var_dict_list: List[VarDictType], + states_lists: List[StatesListsType], + configs_structs: List[ConfigsType], + env_processes_list: List[EnvProcessesType], + Ts: List[range], + Ns: List[int] + ): + l = list(zip(simulation_execs, var_dict_list, states_lists, configs_structs, env_processes_list, Ts, Ns)) + with Pool(len(configs_structs)) as p: + results = p.map(lambda t: t[0](t[1], t[2], t[3], t[4], t[5], t[6]), l) + return results + + class ExecutionContext: - def __init__(self, context=ExecutionMode.multi_proc): + def __init__(self, context: str = ExecutionMode.multi_proc) -> None: self.name = context self.method = None - def single_proc_exec(simulation_execs, var_dict, states_lists, configs_structs, env_processes_list, Ts, Ns): - l = [simulation_execs, states_lists, configs_structs, env_processes_list, Ts, Ns] - simulation, states_list, config, env_processes, T, N = list(map(lambda x: x.pop(), l)) - result = simulation(var_dict, states_list, config, env_processes, T, N) - return flatten(result) - - def parallelize_simulations(simulations, var_dict_list, states_list, configs, env_processes, Ts, Ns): - l = list(zip(simulations, var_dict_list, states_list, configs, env_processes, Ts, Ns)) - with Pool(len(configs)) as p: - results = p.map(lambda t: t[0](t[1], t[2], t[3], t[4], t[5], t[6]), l) - return results - if context == 'single_proc': self.method = single_proc_exec elif context == 'multi_proc': @@ -35,14 +74,14 @@ class ExecutionContext: class Executor: - def __init__(self, exec_context, configs): + def __init__(self, exec_context: ExecutionContext, configs: List[Configuration]) -> None: self.SimExecutor = SimExecutor self.exec_method = exec_context.method self.exec_context = exec_context.name self.configs = configs self.main = self.execute - def execute(self): + def execute(self) -> Tuple[List[Dict[str, Any]], DataFrame]: config_proc = Processor() create_tensor_field = TensorFieldReport(config_proc).create_tensor_field @@ -64,11 +103,13 @@ class Executor: config_idx += 1 + final_result = None + if self.exec_context == ExecutionMode.single_proc: # ToDO: Deprication Handler - "sanitize" in appropriate place tensor_field = create_tensor_field(partial_state_updates.pop(), eps.pop()) result = self.exec_method(simulation_execs, var_dict_list, states_lists, configs_structs, env_processes_list, Ts, Ns) - return result, tensor_field + 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) @@ -76,4 +117,6 @@ class Executor: 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))) - return results \ No newline at end of file + final_result = results + + return final_result diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 97af73d..ed0b466 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -6,8 +6,6 @@ from typing import Any, Callable, Dict, List, Tuple id_exception: Callable = engine_exception(KeyError, KeyError, None) -import pprint as pp - class Executor: @@ -51,9 +49,9 @@ class Executor: 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]: Any = env_state(sub_step)(state_dict[state]) + state_dict[state] = env_state(sub_step)(state_dict[state]) else: - state_dict[state]: Any = env_state(state_dict[state]) + state_dict[state] = env_state(state_dict[state]) # mech_step def partial_state_update( @@ -81,7 +79,7 @@ class Executor: for k in last_in_obj: if k not in last_in_copy: - last_in_copy[k]: Any = last_in_obj[k] + last_in_copy[k] = last_in_obj[k] del last_in_obj diff --git a/simulations/multi_config_run.py b/simulations/multi_config_run.py index c81e1f0..28cee65 100644 --- a/simulations/multi_config_run.py +++ b/simulations/multi_config_run.py @@ -9,11 +9,11 @@ exec_mode = ExecutionMode() print("Simulation Execution: Concurrent Execution") multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) -run2 = Executor(exec_context=multi_proc_ctx, configs=configs) +run = Executor(exec_context=multi_proc_ctx, configs=configs) i = 0 config_names = ['config1', 'config2'] -for raw_result, tensor_field in run2.main(): +for raw_result, tensor_field in run.main(): result = pd.DataFrame(raw_result) print() print("Tensor Field: " + config_names[i]) @@ -21,4 +21,4 @@ for raw_result, tensor_field in run2.main(): print("Output:") print(tabulate(result, headers='keys', tablefmt='psql')) print() - i += 1 \ No newline at end of file + i += 1 diff --git a/simulations/param_sweep_run.py b/simulations/param_sweep_run.py index ecf620f..328e774 100644 --- a/simulations/param_sweep_run.py +++ b/simulations/param_sweep_run.py @@ -9,11 +9,11 @@ exec_mode = ExecutionMode() print("Simulation Execution: Concurrent Execution") multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) -run2 = Executor(exec_context=multi_proc_ctx, configs=configs) +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 run2.main(): +for raw_result, tensor_field in run.main(): result = pd.DataFrame(raw_result) print() print("Tensor Field: " + config_names[i]) @@ -21,4 +21,4 @@ for raw_result, tensor_field in run2.main(): print("Output:") print(tabulate(result, headers='keys', tablefmt='psql')) print() - i += 1 \ No newline at end of file + i += 1 diff --git a/simulations/single_config_run.py b/simulations/single_config_run.py index 38b1307..6326c52 100644 --- a/simulations/single_config_run.py +++ b/simulations/single_config_run.py @@ -11,12 +11,13 @@ print("Simulation Execution: Single Configuration") print() first_config = configs # only contains config1 single_proc_ctx = ExecutionContext(context=exec_mode.single_proc) -run1 = Executor(exec_context=single_proc_ctx, configs=first_config) -run1_raw_result, tensor_field = run1.main() -result = pd.DataFrame(run1_raw_result) +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 +print() From 7fc2e6503cfef5ed1f63150366e1a2c427b5349b Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Wed, 27 Feb 2019 14:43:22 -0500 Subject: [PATCH 10/18] trying to fix cadCAD/engine/__init__.py --- cadCAD/engine/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cadCAD/engine/__init__.py b/cadCAD/engine/__init__.py index ee36088..87d44ac 100644 --- a/cadCAD/engine/__init__.py +++ b/cadCAD/engine/__init__.py @@ -14,7 +14,7 @@ EnvProcessesType = Dict[str, Callable] SimulationType = Callable[ [ - SimExecutor, + # SimExecutor, VarDictType, StatesListsType, ConfigsType, From d56e843fccf476178993475793641f4d27f3bee1 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Fri, 1 Mar 2019 11:34:50 -0500 Subject: [PATCH 11/18] type savety skipe ended --- cadCAD/configuration/__init__.py | 26 +++++++++++++++----------- cadCAD/engine/__init__.py | 17 ++--------------- cadCAD/engine/simulation.py | 3 +-- cadCAD/utils/__init__.py | 2 +- 4 files changed, 19 insertions(+), 29 deletions(-) diff --git a/cadCAD/configuration/__init__.py b/cadCAD/configuration/__init__.py index 6175d6d..8c04248 100644 --- a/cadCAD/configuration/__init__.py +++ b/cadCAD/configuration/__init__.py @@ -1,9 +1,10 @@ +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 from cadCAD import configs - from cadCAD.utils import key_filter from cadCAD.configuration.utils import exo_update_per_ts from cadCAD.configuration.utils.policyAggregation import dict_elemwise_sum @@ -12,7 +13,8 @@ from cadCAD.configuration.utils.depreciationHandler import sanitize_partial_stat 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())], **kwargs): + exogenous_states={}, partial_state_update_blocks={}, policy_ops=[foldr(dict_elemwise_sum())], + **kwargs) -> None: self.sim_config = sim_config self.initial_state = initial_state self.seeds = seeds @@ -25,7 +27,8 @@ class Configuration(object): sanitize_config(self) -def append_configs(sim_configs={}, initial_state={}, seeds={}, raw_exogenous_states={}, env_processes={}, partial_state_update_blocks={}, _exo_update_per_ts=True): +def append_configs(sim_configs={}, initial_state={}, seeds={}, raw_exogenous_states={}, env_processes={}, + partial_state_update_blocks={}, _exo_update_per_ts: bool = True) -> None: if _exo_update_per_ts is True: exogenous_states = exo_update_per_ts(raw_exogenous_states) else: @@ -55,22 +58,22 @@ def append_configs(sim_configs={}, initial_state={}, seeds={}, raw_exogenous_sta class Identity: - def __init__(self, policy_id={'identity': 0}): + def __init__(self, policy_id: Dict[str, int] = {'identity': 0}) -> None: self.beh_id_return_val = policy_id def p_identity(self, var_dict, sub_step, sL, s): return self.beh_id_return_val - def policy_identity(self, k): + def policy_identity(self, k: str) -> Callable: return self.p_identity def no_state_identity(self, var_dict, sub_step, sL, s, _input): return None - def state_identity(self, k): + def state_identity(self, k: str) -> Callable: return lambda var_dict, sub_step, sL, s, _input: (k, s[k]) - def apply_identity_funcs(self, identity, df, cols): + def apply_identity_funcs(self, identity: Callable, df: DataFrame, cols: List[str]) -> List[DataFrame]: def fillna_with_id_func(identity, df, col): return df[[col]].fillna(value=identity(col)) @@ -78,7 +81,7 @@ class Identity: class Processor: - def __init__(self, id=Identity()): + def __init__(self, id: Identity = Identity()) -> None: self.id = id self.p_identity = id.p_identity self.policy_identity = id.policy_identity @@ -86,7 +89,7 @@ class Processor: self.state_identity = id.state_identity self.apply_identity_funcs = id.apply_identity_funcs - def create_matrix_field(self, partial_state_updates, key): + def create_matrix_field(self, partial_state_updates, key: str) -> DataFrame: if key == 'variables': identity = self.state_identity elif key == 'policies': @@ -99,7 +102,8 @@ class Processor: else: return pd.DataFrame({'empty': []}) - def generate_config(self, initial_state, partial_state_updates, exo_proc): + def generate_config(self, initial_state, partial_state_updates, exo_proc + ) -> List[Tuple[List[Callable], List[Callable]]]: def no_update_handler(bdf, sdf): if (bdf.empty == False) and (sdf.empty == True): @@ -135,4 +139,4 @@ class Processor: sdf_values, bdf_values = only_ep_handler(initial_state) zipped_list = list(zip(sdf_values, bdf_values)) - return list(map(lambda x: (x[0] + exo_proc, x[1]), zipped_list)) \ No newline at end of file + return list(map(lambda x: (x[0] + exo_proc, x[1]), zipped_list)) diff --git a/cadCAD/engine/__init__.py b/cadCAD/engine/__init__.py index 87d44ac..57c98a0 100644 --- a/cadCAD/engine/__init__.py +++ b/cadCAD/engine/__init__.py @@ -12,19 +12,6 @@ StatesListsType = List[Dict[str, Any]] ConfigsType = List[Tuple[List[Callable], List[Callable]]] EnvProcessesType = Dict[str, Callable] -SimulationType = Callable[ - [ - # SimExecutor, - VarDictType, - StatesListsType, - ConfigsType, - EnvProcessesType, - range, - int - ], - List[List[Dict[str, Any]]] -] - class ExecutionMode: single_proc = 'single_proc' @@ -32,7 +19,7 @@ class ExecutionMode: def single_proc_exec( - simulation_execs: List[SimulationType], + simulation_execs: List[Callable], var_dict_list: List[VarDictType], states_lists: List[StatesListsType], configs_structs: List[ConfigsType], @@ -48,7 +35,7 @@ def single_proc_exec( def parallelize_simulations( - simulation_execs: List[SimulationType], + simulation_execs: List[Callable], var_dict_list: List[VarDictType], states_lists: List[StatesListsType], configs_structs: List[ConfigsType], diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index ed0b466..60acd66 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -1,14 +1,13 @@ +from typing import Any, Callable, Dict, List, Tuple from copy import deepcopy from fn.op import foldr, call from cadCAD.engine.utils import engine_exception -from typing import Any, Callable, Dict, List, Tuple id_exception: Callable = engine_exception(KeyError, KeyError, None) class Executor: - def __init__( self, policy_ops: List[Callable], diff --git a/cadCAD/utils/__init__.py b/cadCAD/utils/__init__.py index 59fc9c8..8e1220c 100644 --- a/cadCAD/utils/__init__.py +++ b/cadCAD/utils/__init__.py @@ -1,7 +1,7 @@ +from typing import Dict, List from collections import defaultdict from itertools import product import warnings -from typing import Dict, List def pipe(x): From e37601ae2277de02a5fd4aa938b83dace23be5ff Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Fri, 1 Mar 2019 17:30:37 -0500 Subject: [PATCH 12/18] parallelized runs --- cadCAD/engine/__init__.py | 4 ++-- cadCAD/engine/simulation.py | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/cadCAD/engine/__init__.py b/cadCAD/engine/__init__.py index 57c98a0..aeed31a 100644 --- a/cadCAD/engine/__init__.py +++ b/cadCAD/engine/__init__.py @@ -1,5 +1,5 @@ from typing import Callable, Dict, List, Any, Tuple -from pathos.multiprocessing import ProcessingPool as Pool +from pathos.multiprocessing import ProcessingPool as PPool from pandas.core.frame import DataFrame from cadCAD.utils import flatten @@ -44,7 +44,7 @@ def parallelize_simulations( Ns: List[int] ): l = list(zip(simulation_execs, var_dict_list, states_lists, configs_structs, env_processes_list, Ts, Ns)) - with Pool(len(configs_structs)) as p: + with PPool(len(configs_structs)) as p: results = p.map(lambda t: t[0](t[1], t[2], t[3], t[4], t[5], t[6]), l) return results diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 60acd66..4c65ba6 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -1,8 +1,10 @@ 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 id_exception: Callable = engine_exception(KeyError, KeyError, None) @@ -142,7 +144,6 @@ class Executor: return simulation_list - # ToDo: Muiltithreaded Runs def simulation( self, var_dict: Dict[str, List[Any]], @@ -153,8 +154,7 @@ class Executor: runs: int ) -> List[List[Dict[str, Any]]]: - pipe_run: List[List[Dict[str, Any]]] = [] - for run in range(runs): + 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) @@ -163,6 +163,13 @@ class Executor: 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) - pipe_run += [first_timestep_per_run] + tail + return [first_timestep_per_run] + tail + + pipe_run: List[List[Dict[str, Any]]] = flatten( + TPool().map( + lambda run: execute_run(var_dict, states_list, configs, env_processes, time_seq, run), + list(range(runs)) + ) + ) return pipe_run From 5f2d0801ca39ed2eafa52281ac149c8c3721a1c5 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Mon, 4 Mar 2019 09:12:06 -0500 Subject: [PATCH 13/18] multithreaded runs for uat --- cadCAD/engine/simulation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 4c65ba6..a62d1bb 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -93,7 +93,7 @@ class Executor: return sL - # mech_pipeline + # mech_pipeline - state_update_block def state_update_pipeline( self, var_dict: Dict[str, List[Any]], @@ -123,6 +123,7 @@ class Executor: return states_list + # state_update_pipeline def run_pipeline( self, var_dict: Dict[str, List[Any]], From b3b0356a8f7c2b6602583ec60503126fc6540965 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Wed, 6 Mar 2019 15:11:25 -0500 Subject: [PATCH 14/18] moved config_sim --- cadCAD/configuration/utils/__init__.py | 23 ++++++++++++++++++-- cadCAD/configuration/utils/parameterSweep.py | 20 ----------------- cadCAD/engine/simulation.py | 1 + simulations/validation/config1.py | 3 +-- simulations/validation/config2.py | 4 ++-- simulations/validation/config4.py | 3 +-- simulations/validation/sweep_config.py | 3 +-- 7 files changed, 27 insertions(+), 30 deletions(-) delete mode 100644 cadCAD/configuration/utils/parameterSweep.py diff --git a/cadCAD/configuration/utils/__init__.py b/cadCAD/configuration/utils/__init__.py index d0d9aec..1ef09f3 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,23 @@ 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 a62d1bb..9c3eeec 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -86,6 +86,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) diff --git a/simulations/validation/config1.py b/simulations/validation/config1.py index 2c26f91..d2a8f2b 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 = { diff --git a/simulations/validation/config2.py b/simulations/validation/config2.py index 0247c9b..80adcb5 100644 --- a/simulations/validation/config2.py +++ b/simulations/validation/config2.py @@ -3,8 +3,8 @@ 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/sweep_config.py b/simulations/validation/sweep_config.py index 2350d95..e014b94 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 From 295968b71f8b85836466996cdbe00b6f28746d40 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Fri, 29 Mar 2019 09:29:41 -0400 Subject: [PATCH 15/18] cadCAD==0.2.1 --- dist/cadCAD-0.2-py3-none-any.whl | Bin 10811 -> 0 bytes dist/cadCAD-0.2.1-py3-none-any.whl | Bin 0 -> 11281 bytes setup.py | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 dist/cadCAD-0.2-py3-none-any.whl create mode 100644 dist/cadCAD-0.2.1-py3-none-any.whl diff --git a/dist/cadCAD-0.2-py3-none-any.whl b/dist/cadCAD-0.2-py3-none-any.whl deleted file mode 100644 index eb56bb3e2e006e18ecc452735178216f7508f3ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10811 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 diff --git a/dist/cadCAD-0.2.1-py3-none-any.whl b/dist/cadCAD-0.2.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..354aaec7a1da3c9b9123df1e1b02f0742b95e1fb GIT binary patch literal 11281 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 Date: Fri, 29 Mar 2019 10:36:33 -0400 Subject: [PATCH 16/18] cadCAD==0.2.1 clean --- simulations/validation/sweep_config.py | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/simulations/validation/sweep_config.py b/simulations/validation/sweep_config.py index e014b94..c9c92ab 100644 --- a/simulations/validation/sweep_config.py +++ b/simulations/validation/sweep_config.py @@ -94,15 +94,12 @@ def es4p2(_g, step, sL, s, _input): 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' + y = 'timestamp' 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): @@ -115,7 +112,6 @@ genesis_states = { 's2': Decimal(0.0), 's3': Decimal(1.0), 's4': Decimal(1.0), -# 'timestep': '2018-10-01 15:16:24' } @@ -123,26 +119,13 @@ genesis_states = { 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) + "s3": env_a, + "s4": 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": { From c05cb7ad057252d210bd803f85320f787a998298 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Fri, 29 Mar 2019 10:57:43 -0400 Subject: [PATCH 17/18] cadCAD==0.2.1 clean2 --- simulations/validation/config1.py | 8 ++++---- simulations/validation/config2.py | 8 ++++---- simulations/validation/sweep_config.py | 4 +++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/simulations/validation/config1.py b/simulations/validation/config1.py index d2a8f2b..1a98822 100644 --- a/simulations/validation/config1.py +++ b/simulations/validation/config1.py @@ -79,8 +79,8 @@ def es4p2(_g, step, sL, s, _input): 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) + y = 'timestamp' + x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=t_delta) return (y, x) @@ -99,14 +99,14 @@ genesis_states = { 's2': Decimal(0.0), 's3': Decimal(1.0), 's4': Decimal(1.0), -# 'timestep': '2018-10-01 15:16:24' + 'timestamp': '2018-10-01 15:16:24' } raw_exogenous_states = { "s3": es3p1, "s4": es4p2, -# "timestep": es5p2 + "timestamp": es5p2 } diff --git a/simulations/validation/config2.py b/simulations/validation/config2.py index 80adcb5..ebba912 100644 --- a/simulations/validation/config2.py +++ b/simulations/validation/config2.py @@ -77,8 +77,8 @@ def es4p2(_g, step, sL, s, _input): 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) + y = 'timestamp' + x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=t_delta) return (y, x) @@ -97,14 +97,14 @@ genesis_states = { 's2': Decimal(0.0), 's3': Decimal(1.0), 's4': Decimal(1.0), -# 'timestep': '2018-10-01 15:16:24' + 'timestamp': '2018-10-01 15:16:24' } raw_exogenous_states = { "s3": es3p1, "s4": es4p2, -# "timestep": es5p2 + "timestamp": es5p2 } diff --git a/simulations/validation/sweep_config.py b/simulations/validation/sweep_config.py index c9c92ab..04f5698 100644 --- a/simulations/validation/sweep_config.py +++ b/simulations/validation/sweep_config.py @@ -95,7 +95,7 @@ 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 = 'timestamp' - x = ep_time_step(s, dt_str=s['timestep'], fromat_str=ts_format, _timedelta=t_delta) + x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=t_delta) return (y, x) @@ -112,6 +112,7 @@ genesis_states = { 's2': Decimal(0.0), 's3': Decimal(1.0), 's4': Decimal(1.0), + 'timestamp': '2018-10-01 15:16:24' } @@ -119,6 +120,7 @@ genesis_states = { raw_exogenous_states = { "s3": es3p1, "s4": es4p2, + 'timestamp': es5p2 } triggered_env_b = proc_trigger(1, env_b) From 2387dc071ba02cf834b3d55b0157d923e5924567 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Fri, 29 Mar 2019 10:59:55 -0400 Subject: [PATCH 18/18] cadCAD==0.2.1 clean2 --- dist/cadCAD-0.2.1-py3-none-any.whl | Bin 11281 -> 11281 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/dist/cadCAD-0.2.1-py3-none-any.whl b/dist/cadCAD-0.2.1-py3-none-any.whl index 354aaec7a1da3c9b9123df1e1b02f0742b95e1fb..cf618e8040cc62695bde4265ae6eb8386dcb2886 100644 GIT binary patch delta 65 zcmbOjF)?DpRz;@#^3B^7&oKe%$?YmrnDWXe%c^>UL^D*gSwW&#wY));lC}p(GDkZA J#M`GG3;;9$7vKN@ delta 65 zcmbOjF)?DpRz)V`tj*gM&oKe%$?YmrK$NViCrC6yHJcU0y{hF6qLj2fKva%)0EpVB G9Si{Nj}@8#