diff --git a/.gitignore b/.gitignore index 7a203b9..e4f25bd 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ monkeytype.sqlite3 build cadCAD.egg-info SimCAD.egg-info + +monkeytype.sqlite3 \ No newline at end of file diff --git a/README.md b/README.md index 6a7fe07..0719875 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 \ @@ -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 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/ @@ -41,51 +41,92 @@ 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: `/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 (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 Simulations (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. ```bash jupyter notebook -``` \ No newline at end of file +``` 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/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/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/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/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 b0727e8..aeed31a 100644 --- a/cadCAD/engine/__init__.py +++ b/cadCAD/engine/__init__.py @@ -1,33 +1,59 @@ -from pathos.multiprocessing import ProcessingPool as Pool +from typing import Callable, Dict, List, Any, Tuple +from pathos.multiprocessing import ProcessingPool as PPool +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] + class ExecutionMode: single_proc = 'single_proc' multi_proc = 'multi_proc' +def single_proc_exec( + simulation_execs: List[Callable], + 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[Callable], + 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 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 + + 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(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)) - 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 +61,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 +90,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 +104,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 0e8fb68..cc2d19b 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -1,20 +1,38 @@ +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 = engine_exception(KeyError, KeyError, None) +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: - def __init__(self, policy_ops, policy_update_exception=id_exception, state_update_exception=id_exception): - self.policy_ops = policy_ops # behavior_ops + # behavior_ops + self.policy_ops = policy_ops self.state_update_exception = state_update_exception - self.policy_update_exception = policy_update_exception # behavior_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 +40,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]) else: state_dict[state] = 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 = deepcopy(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] = deepcopy(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 ] @@ -52,52 +86,92 @@ 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) del last_in_copy return sL + # mech_pipeline - state_update_block + 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]]: - # mech_pipeline - def state_update_pipeline(self, var_dict, states_list, configs, env_processes, time_step, run): 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] + # state_update_pipeline + 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 = [] - for run in range(runs): + 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]]]: + + def execute_run(var_dict, states_list, configs, env_processes, time_seq, run) -> List[Dict[str, Any]]: 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) + 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 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..8e1220c 100644 --- a/cadCAD/utils/__init__.py +++ b/cadCAD/utils/__init__.py @@ -1,7 +1,9 @@ +from typing import Dict, List from collections import defaultdict from itertools import product import warnings + 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/dist/cadCAD-0.2-py3-none-any.whl b/dist/cadCAD-0.2-py3-none-any.whl deleted file mode 100644 index eb56bb3..0000000 Binary files a/dist/cadCAD-0.2-py3-none-any.whl and /dev/null differ 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 0000000..cf618e8 Binary files /dev/null and b/dist/cadCAD-0.2.1-py3-none-any.whl differ diff --git a/setup.py b/setup.py index a01f39c..c27fef1 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.1', 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/multi_config run.py b/simulations/multi_config_run.py similarity index 85% rename from simulations/multi_config run.py rename to 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() diff --git a/simulations/validation/config1.py b/simulations/validation/config1.py index 532f6e7..1a98822 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 = { @@ -21,6 +20,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): @@ -78,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) @@ -98,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 0247c9b..ebba912 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), @@ -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/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 09ccd6b..04f5698 100644 --- a/simulations/validation/sweep_config.py +++ b/simulations/validation/sweep_config.py @@ -4,8 +4,9 @@ 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 pp = pprint.PrettyPrinter(indent=4) @@ -17,7 +18,7 @@ seeds = { } -g = { +g: Dict[str, List[int]] = { 'alpha': [1], 'beta': [2, 5], 'gamma': [3, 4], @@ -93,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' - 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) # Environment States -# @curried -# def env_a(param, x): -# return x + param def env_a(x): return x def env_b(x): @@ -114,7 +112,7 @@ 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' } @@ -122,26 +120,14 @@ genesis_states = { raw_exogenous_states = { "s3": es3p1, "s4": es4p2, -# "timestep": es5p2 + 'timestamp': 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": {