diff --git a/.gitignore b/.gitignore index 5e2a723..a7f13f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ .idea .ipynb_checkpoints .DS_Store +.idea +notebooks +SimCAD.egg-info __pycache__ Pipfile Pipfile.lock @@ -9,6 +12,8 @@ results *.csv *.txt simulations/.ipynb_checkpoints +dist/*.gz +cadCAD.egg-info build cadCAD.egg-info diff --git a/README.md b/README.md index 20056a1..c9d573a 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ SimCAD is written in Python 3. **1. Install Dependencies:** ```bash -pip install -r requirements.txt +pip3 install -r requirements.txt python3 setup.py sdist bdist_wheel -pip install dist/cadCAD-0.2-py3-none-any.whl +pip3 install dist/cadCAD-0.2-py3-none-any.whl ``` **2. Configure Simulation:** diff --git a/Simulation.md b/Simulation.md index 4e04f54..a6e1347 100644 --- a/Simulation.md +++ b/Simulation.md @@ -1,4 +1,4 @@ -# SimmCAD Documentation +# SimCAD Documentation ## Introduction diff --git a/cadCAD/configuration/__init__.py b/cadCAD/configuration/__init__.py index 27cafdc..645f9df 100644 --- a/cadCAD/configuration/__init__.py +++ b/cadCAD/configuration/__init__.py @@ -2,59 +2,84 @@ from functools import reduce from fn.op import foldr import pandas as pd + +from cadCAD import configs from cadCAD.utils import key_filter -from cadCAD.configuration.utils.behaviorAggregation import dict_elemwise_sum +from cadCAD.configuration.utils.policyAggregation import dict_elemwise_sum +from cadCAD.configuration.utils import exo_update_per_ts -class Configuration: - def __init__(self, sim_config, - # default initial_conditions to empty dict because - # user may be using the state_dict argument - initial_conditions={}, - seeds={}, - exogenous_states={}, env_processes={}, - partial_state_update_blocks={}, - behavior_ops=[foldr(dict_elemwise_sum())], - **kwargs): +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): self.sim_config = sim_config - self.state_dict = initial_conditions - self.seed = seeds - self.exogenous_states = exogenous_states + self.initial_state = initial_state + self.seeds = seeds self.env_processes = env_processes - self.behavior_ops = behavior_ops - self.mechanisms = partial_state_update_blocks + self.exogenous_states = exogenous_states + self.partial_state_updates = partial_state_update_blocks + self.policy_ops = policy_ops # for backwards compatibility, we accept old arguments via **kwargs # TODO: raise deprecation warnings for key, value in kwargs.items(): if (key=='state_dict'): - self.state_dict = value + self.initial_state = value elif (key=='seed'): - self.seed = value + self.seeds = value elif (key=='mechanisms'): - self.mechanisms = value + self.partial_state_updates = value if (self.state_dict == {}): raise Exception('The initial conditions of the system have not been set') +def append_configs(sim_configs, initial_state, seeds, raw_exogenous_states, env_processes, partial_state_update_blocks, _exo_update_per_ts=True): + if _exo_update_per_ts is True: + exogenous_states = exo_update_per_ts(raw_exogenous_states) + else: + exogenous_states = raw_exogenous_states + + if isinstance(sim_configs, list): + for sim_config in sim_configs: + configs.append( + Configuration( + sim_config=sim_config, + initial_state=initial_state, + seeds=seeds, + exogenous_states=exogenous_states, + env_processes=env_processes, + partial_state_updates=partial_state_update_blocks + ) + ) + elif isinstance(sim_configs, dict): + configs.append( + Configuration( + sim_config=sim_configs, + initial_state=initial_state, + seeds=seeds, + exogenous_states=exogenous_states, + env_processes=env_processes, + partial_state_updates=partial_state_update_blocks + ) + ) class Identity: - def __init__(self, behavior_id={'identity': 0}): - self.beh_id_return_val = behavior_id + def __init__(self, policy_id={'identity': 0}): + self.beh_id_return_val = policy_id - def b_identity(self, step, sL, s): + def p_identity(self, var_dict, sub_step, sL, s): return self.beh_id_return_val - def behavior_identity(self, k): - return self.b_identity + def policy_identity(self, k): + return self.p_identity - def no_state_identity(self, step, sL, s, _input): + def no_state_identity(self, var_dict, sub_step, sL, s, _input): return None def state_identity(self, k): - return lambda step, sL, s, _input: (k, s[k]) + return lambda var_dict, sub_step, sL, s, _input: (k, s[k]) def apply_identity_funcs(self, identity, df, cols): def fillna_with_id_func(identity, df, col): @@ -66,34 +91,34 @@ class Identity: class Processor: def __init__(self, id=Identity()): self.id = id - self.b_identity = id.b_identity - self.behavior_identity = id.behavior_identity + self.p_identity = id.p_identity + self.policy_identity = id.policy_identity self.no_state_identity = id.no_state_identity self.state_identity = id.state_identity self.apply_identity_funcs = id.apply_identity_funcs - def create_matrix_field(self, mechanisms, key): + def create_matrix_field(self, partial_state_updates, key): if key == 'state_update_functions': identity = self.state_identity elif key == 'policies': - identity = self.behavior_identity - df = pd.DataFrame(key_filter(mechanisms, key)) + identity = self.policy_identity + df = pd.DataFrame(key_filter(partial_state_updates, key)) col_list = self.apply_identity_funcs(identity, df, list(df.columns)) if len(col_list) != 0: return reduce((lambda x, y: pd.concat([x, y], axis=1)), col_list) else: return pd.DataFrame({'empty': []}) - def generate_config(self, state_dict, mechanisms, exo_proc): + def generate_config(self, initial_state, partial_state_updates, exo_proc): def no_update_handler(bdf, sdf): if (bdf.empty == False) and (sdf.empty == True): bdf_values = bdf.values.tolist() - sdf_values = [[self.no_state_identity] * len(bdf_values) for m in range(len(mechanisms))] + sdf_values = [[self.no_state_identity] * len(bdf_values) for m in range(len(partial_state_updates))] return sdf_values, bdf_values elif (bdf.empty == True) and (sdf.empty == False): sdf_values = sdf.values.tolist() - bdf_values = [[self.b_identity] * len(sdf_values) for m in range(len(mechanisms))] + bdf_values = [[self.b_identity] * len(sdf_values) for m in range(len(partial_state_updates))] return sdf_values, bdf_values else: sdf_values = sdf.values.tolist() @@ -102,13 +127,13 @@ class Processor: def only_ep_handler(state_dict): sdf_functions = [ - lambda step, sL, s, _input: (k, v) for k, v in zip(state_dict.keys(), state_dict.values()) + lambda sub_step, sL, s, _input: (k, v) for k, v in zip(state_dict.keys(), state_dict.values()) ] sdf_values = [sdf_functions] - bdf_values = [[self.b_identity] * len(sdf_values)] + bdf_values = [[self.p_identity] * len(sdf_values)] return sdf_values, bdf_values - def sanitize_mechanisms(m): + def sanitize_partial_state_updates(m): # for backwards compatibility we accept the old keys # ('behaviors' and 'states') and rename them def rename_keys(d): @@ -130,15 +155,15 @@ class Processor: for k, v in mechanisms.items(): rename_keys(v) return - - if len(mechanisms) != 0: - sanitize_mechanisms(mechanisms) - bdf = self.create_matrix_field(mechanisms, 'policies') - sdf = self.create_matrix_field(mechanisms, 'state_update_functions') + + if len(partial_state_updates) != 0: + sanitize_partial_state_updates(partial_state_updates) + bdf = self.create_matrix_field(partial_state_updates, 'policies') + sdf = self.create_matrix_field(partial_state_updates, 'states') sdf_values, bdf_values = no_update_handler(bdf, sdf) zipped_list = list(zip(sdf_values, bdf_values)) else: - sdf_values, bdf_values = only_ep_handler(state_dict) + 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)) diff --git a/cadCAD/configuration/utils/__init__.py b/cadCAD/configuration/utils/__init__.py index 56d04bf..2eb750b 100644 --- a/cadCAD/configuration/utils/__init__.py +++ b/cadCAD/configuration/utils/__init__.py @@ -1,15 +1,18 @@ from datetime import datetime, timedelta from decimal import Decimal +from copy import deepcopy from fn.func import curried import pandas as pd +from cadCAD.utils import dict_filter, contains_type + class TensorFieldReport: def __init__(self, config_proc): self.config_proc = config_proc - def create_tensor_field(self, mechanisms, exo_proc, keys=['policies', 'state_update_functions']): - dfs = [self.config_proc.create_matrix_field(mechanisms, k) for k in keys] + def create_tensor_field(self, partial_state_updates, exo_proc, keys=['policies', 'state_update_functions']): + dfs = [self.config_proc.create_matrix_field(partial_state_updates, k) for k in keys] df = pd.concat(dfs, axis=1) for es, i in zip(exo_proc, range(len(exo_proc))): df['es' + str(i + 1)] = es @@ -17,41 +20,103 @@ class TensorFieldReport: return df +# def s_update(y, x): +# return lambda step, sL, s, _input: (y, x) +# +# +def state_update(y, x): + return lambda sub_step, sL, s, _input: (y, x) + + def bound_norm_random(rng, low, high): - res = rng.normal((high+low)/2,(high-low)/6) - if (reshigh): + 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) @curried -def proc_trigger(trigger_step, update_f, step): - if step == trigger_step: +def proc_trigger(trigger_time, update_f, time): + if time == trigger_time: return update_f else: return lambda x: x -t_delta = timedelta(days=0, minutes=0, seconds=30) -def time_step(dt_str, dt_format='%Y-%m-%d %H:%M:%S', _timedelta = t_delta): +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): dt = datetime.strptime(dt_str, dt_format) t = dt + _timedelta return t.strftime(dt_format) -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 = t_delta): - if s['substep'] == 0: +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['sub_step'] == 0: return time_step(dt_str, fromat_str, _timedelta) else: return dt_str +# mech_sweep_filter +def partial_state_sweep_filter(state_field, partial_state_updates): + partial_state_dict = dict([(k, v[state_field]) for k, v in partial_state_updates.items()]) + return dict([ + (k, dict_filter(v, lambda v: isinstance(v, list))) for k, v in partial_state_dict.items() + if contains_type(list(v.values()), list) + ]) + + +def state_sweep_filter(raw_exogenous_states): + return dict([(k, v) for k, v in raw_exogenous_states.items() if isinstance(v, list)]) + +# sweep_mech_states +@curried +def sweep_partial_states(_type, in_config): + configs = [] + # filtered_mech_states + filtered_partial_states = partial_state_sweep_filter(_type, in_config.partial_state_updates) + if len(filtered_partial_states) > 0: + for partial_state, state_dict in filtered_partial_states.items(): + for state, state_funcs in state_dict.items(): + for f in state_funcs: + config = deepcopy(in_config) + config.partial_state_updates[partial_state][_type][state] = f + configs.append(config) + del config + else: + configs = [in_config] + + return configs + + +@curried +def sweep_states(state_type, states, in_config): + configs = [] + filtered_states = state_sweep_filter(states) + if len(filtered_states) > 0: + for state, state_funcs in filtered_states.items(): + for f in state_funcs: + config = deepcopy(in_config) + exploded_states = deepcopy(states) + exploded_states[state] = f + if state_type == 'exogenous': + config.exogenous_states = exploded_states + elif state_type == 'environmental': + config.env_processes = exploded_states + configs.append(config) + del config, exploded_states + else: + configs = [in_config] + + return configs + def exo_update_per_ts(ep): @curried - def ep_decorator(f, y, step, sL, s, _input): - if s['substep'] + 1 == 1: - return f(step, sL, s, _input) + def ep_decorator(f, y, var_dict, sub_step, sL, s, _input): + if s['sub_step'] + 1 == 1: + return f(var_dict, sub_step, sL, s, _input) else: - return (y, s[y]) + return y, s[y] + return {es: ep_decorator(f, es) for es, f in ep.items()} diff --git a/cadCAD/configuration/utils/parameterSweep.py b/cadCAD/configuration/utils/parameterSweep.py new file mode 100644 index 0000000..9e5d6ab --- /dev/null +++ b/cadCAD/configuration/utils/parameterSweep.py @@ -0,0 +1,20 @@ +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 diff --git a/cadCAD/configuration/utils/policyAggregation.py b/cadCAD/configuration/utils/policyAggregation.py new file mode 100644 index 0000000..eac845d --- /dev/null +++ b/cadCAD/configuration/utils/policyAggregation.py @@ -0,0 +1,46 @@ +from fn.op import foldr +from fn.func import curried + + +def get_base_value(x): + if isinstance(x, str): + return '' + elif isinstance(x, int): + return 0 + elif isinstance(x, list): + return [] + else: + return 0 + + +def policy_to_dict(v): + return dict(list(zip(map(lambda n: 'b' + str(n + 1), list(range(len(v)))), v))) + + +add = lambda a, b: a + b + + +@curried +def foldr_dict_vals(f, d): + return foldr(f)(list(d.values())) + + +def sum_dict_values(): + return foldr_dict_vals(add) + + +@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())) + + return {k: f(set_base_value(d1, d2, k), set_base_value(d2, d1, k)) for k in key_set} + + +def dict_elemwise_sum(): + return dict_op(add) diff --git a/cadCAD/engine/__init__.py b/cadCAD/engine/__init__.py index 68db2c3..5a1d4ad 100644 --- a/cadCAD/engine/__init__.py +++ b/cadCAD/engine/__init__.py @@ -16,16 +16,16 @@ class ExecutionContext: self.name = context self.method = None - def single_proc_exec(simulation_execs, states_lists, configs_structs, env_processes_list, Ts, Ns): + 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(states_list, config, env_processes, T, N) + result = simulation(var_dict, states_list, config, env_processes, T, N) return flatten(result) - def parallelize_simulations(fs, states_list, configs, env_processes, Ts, Ns): - l = list(zip(fs, states_list, configs, env_processes, Ts, Ns)) + 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]), l) + 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': @@ -46,31 +46,33 @@ class Executor: config_proc = Processor() create_tensor_field = TensorFieldReport(config_proc).create_tensor_field - #print(self.exec_context+": "+str(self.configs)) - states_lists, Ts, Ns, eps, configs_structs, env_processes_list, mechanisms, simulation_execs = \ - [], [], [], [], [], [], [], [] + print(self.exec_context+": "+str(self.configs)) + var_dict_list, states_lists, Ts, Ns, eps, configs_structs, env_processes_list, partial_state_updates, simulation_execs = \ + [], [], [], [], [], [], [], [], [] config_idx = 0 for x in self.configs: - states_lists.append([x.state_dict]) + Ts.append(x.sim_config['T']) Ns.append(x.sim_config['N']) + var_dict_list.append(x.sim_config['M']) + states_lists.append([x.initial_state]) eps.append(list(x.exogenous_states.values())) - configs_structs.append(config_proc.generate_config(x.state_dict, x.mechanisms, eps[config_idx])) + configs_structs.append(config_proc.generate_config(x.initial_state, x.partial_state_updates, eps[config_idx])) env_processes_list.append(x.env_processes) - mechanisms.append(x.mechanisms) - simulation_execs.append(SimExecutor(x.behavior_ops).simulation) + partial_state_updates.append(x.partial_state_updates) + simulation_execs.append(SimExecutor(x.policy_ops).simulation) config_idx += 1 if self.exec_context == ExecutionMode.single_proc: - tensor_field = create_tensor_field(mechanisms.pop(), eps.pop()) - result = self.exec_method(simulation_execs, states_lists, configs_structs, env_processes_list, Ts, Ns) + 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 elif self.exec_context == ExecutionMode.multi_proc: if len(self.configs) > 1: - simulations = self.exec_method(simulation_execs, states_lists, configs_structs, env_processes_list, Ts, Ns) + simulations = self.exec_method(simulation_execs, var_dict_list, states_lists, configs_structs, env_processes_list, Ts, Ns) results = [] - for result, mechanism, ep in list(zip(simulations, mechanisms, eps)): - results.append((flatten(result), create_tensor_field(mechanism, ep))) + 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 diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 052ed63..2808cb4 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -1,39 +1,48 @@ from copy import deepcopy from fn.op import foldr, call + from cadCAD.engine.utils import engine_exception id_exception = engine_exception(KeyError, KeyError, None) class Executor: - def __init__(self, behavior_ops, behavior_update_exception=id_exception, state_update_exception=id_exception): - self.behavior_ops = behavior_ops + + 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.behavior_update_exception = behavior_update_exception + self.policy_update_exception = policy_update_exception # behavior_update_exception - def get_behavior_input(self, step, sL, s, funcs): - ops = self.behavior_ops[::-1] + # get_behavior_input + def get_policy_input(self, var_dict, sub_step, sL, s, funcs): + ops = self.policy_ops[::-1] - def get_col_results(step, sL, s, funcs): - return list(map(lambda f: f(step, sL, s), funcs)) + 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(step, sL, s, funcs))(ops) + return foldr(call, get_col_results(var_dict, sub_step, sL, s, funcs))(ops) - def apply_env_proc(self, env_processes, state_dict, step): + def apply_env_proc(self, env_processes, state_dict, sub_step): for state in state_dict.keys(): if state in list(env_processes.keys()): env_state = env_processes[state] if (env_state.__name__ == '_curried') or (env_state.__name__ == 'proc_trigger'): - state_dict[state] = env_state(step)(state_dict[state]) + state_dict[state] = env_state(sub_step)(state_dict[state]) else: state_dict[state] = env_state(state_dict[state]) - def mech_step(self, m_step, sL, state_funcs, behavior_funcs, env_processes, t_step, run): + # 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] - _input = self.state_update_exception(self.get_behavior_input(m_step, sL, last_in_obj, behavior_funcs)) + _input = self.policy_update_exception(self.get_policy_input(var_dict, sub_step, sL, last_in_obj, policy_funcs)) - last_in_copy = dict([self.behavior_update_exception(f(m_step, sL, last_in_obj, _input)) for f in state_funcs]) + # ToDo: add env_proc generator to `last_in_copy` iterator as wrapper function + last_in_copy = dict( + [ + self.state_update_exception(f(var_dict, sub_step, sL, last_in_obj, _input)) for f in state_funcs + ] + ) for k in last_in_obj: if k not in last_in_copy: @@ -43,45 +52,48 @@ class Executor: 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'] = m_step, t_step, run + 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 - def mech_pipeline(self, states_list, configs, env_processes, t_step, run): - m_step = 0 + + # 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] - genesis_states['substep'], genesis_states['timestep'] = m_step, t_step + genesis_states['substep'], genesis_states['timestep'] = sub_step, time_step states_list = [genesis_states] - m_step += 1 + sub_step += 1 for config in configs: - s_conf, b_conf = config[0], config[1] - states_list = self.mech_step(m_step, states_list, s_conf, b_conf, env_processes, t_step, run) - m_step += 1 + 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) + sub_step += 1 - t_step += 1 + time_step += 1 return states_list - def block_pipeline(self, states_list, configs, env_processes, time_seq, run): + 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] for time_step in time_seq: - pipe_run = self.mech_pipeline(simulation_list[-1], configs, env_processes, time_step, run) + pipe_run = 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 - def simulation(self, states_list, configs, env_processes, time_seq, runs): + # ToDo: Muiltithreaded Runs + def simulation(self, var_dict, states_list, configs, env_processes, time_seq, runs): pipe_run = [] for run in range(runs): run += 1 states_list_copy = deepcopy(states_list) - head, *tail = self.block_pipeline(states_list_copy, configs, env_processes, time_seq, run) + 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) diff --git a/cadCAD/engine/utils.py b/cadCAD/engine/utils.py index bcf1507..c0f3a76 100644 --- a/cadCAD/engine/utils.py +++ b/cadCAD/engine/utils.py @@ -24,6 +24,8 @@ def retrieve_state(l, offset): return l[last_index(l) + offset + 1] +# exception_function = f(sub_step, sL, sL[-2], _input) +# try_function = f(sub_step, sL, last_mut_obj, _input) @curried def engine_exception(ErrorType, error_message, exception_function, try_function): try: @@ -31,3 +33,10 @@ def engine_exception(ErrorType, error_message, exception_function, try_function) except ErrorType: print(error_message) return exception_function + + +@curried +def fit_param(param, x): + return x + param + +# fit_param = lambda param: lambda x: x + param diff --git a/cadCAD/utils/__init__.py b/cadCAD/utils/__init__.py index 8b620cc..1535b1c 100644 --- a/cadCAD/utils/__init__.py +++ b/cadCAD/utils/__init__.py @@ -1,3 +1,7 @@ +from collections import defaultdict +from itertools import product +import warnings + def pipe(x): return x @@ -7,19 +11,125 @@ def print_pipe(x): return x -def flatten(l): - return [item for sublist in l for item in sublist] +def flattenDict(l): + def tupalize(k, vs): + l = [] + if isinstance(vs, list): + for v in vs: + l.append((k, v)) + else: + l.append((k, vs)) + return l + + flat_list = [tupalize(k, vs) for k, vs in l.items()] + flat_dict = [dict(items) for items in product(*flat_list)] + return flat_dict + + +def flatten(l): + if isinstance(l, list): + return [item for sublist in l for item in sublist] + elif isinstance(l, dict): + return flattenDict(l) + + +def flatMap(f, collection): + return flatten(list(map(f, collection))) + + +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): + return len(max(g.values(), key=len)) + + +def tabulate_dict(d): + max_len = get_max_dict_val_len(d) + _d = {} + for k, vl in d.items(): + if len(vl) != max_len: + _d[k] = vl + list([vl[-1]] * (max_len-1)) + else: + _d[k] = vl + + return _d + + +def flatten_tabulated_dict(d): + max_len = get_max_dict_val_len(d) + dl = [{} for i in range(max_len)] + + for k, vl in d.items(): + for v, i in zip(vl, list(range(len(vl)))): + dl[i][k] = v + + return dl + + +def contains_type(_collection, type): + return any(isinstance(x, type) for x in _collection) + + +def drop_right(l, n): + return l[:len(l) - n] + -import warnings def key_filter(l, keyname): - if (type(l)==list): + if (type(l) == list): return [v[keyname] for v in l] - # Keeping support to dictionaries for backwards compatibility - # Should be removed in the future - warnings.warn("The use of a dictionary to describe Partial State Update Blocks will be deprecated. Use a list instead.", FutureWarning) + # Keeping support to dictionaries for backwards compatibility + # Should be removed in the future + warnings.warn( + "The use of a dictionary to describe Partial State Update Blocks will be deprecated. Use a list instead.", + FutureWarning) return [v[keyname] for k, v in l.items()] +def groupByKey(l): + d = defaultdict(list) + for key, value in l: + d[key].append(value) + return list(dict(d).items()).pop() + + +# @curried def rename(new_name, f): f.__name__ = new_name return f + + +def curry_pot(f, *argv): + sweep_ind = f.__name__[0:5] == 'sweep' + arg_len = len(argv) + if sweep_ind is True and arg_len == 4: + return f(argv[0])(argv[1])(argv[2])(argv[3]) + elif sweep_ind is False and arg_len == 4: + return f(argv[0], argv[1], argv[2], argv[3]) + elif sweep_ind is True and arg_len == 3: + return f(argv[0])(argv[1])(argv[2]) + elif sweep_ind is False and arg_len == 3: + return f(argv[0], argv[1], argv[2]) + else: + raise TypeError('curry_pot() needs 3 or 4 positional arguments') + +# def curry_pot(f, *argv): +# sweep_ind = f.__name__[0:5] == 'sweep' +# arg_len = len(argv) +# if sweep_ind is True and arg_len == 4: +# return f(argv[0])(argv[1])(argv[2])(argv[3]) +# elif sweep_ind is False and arg_len == 4: +# return f(argv[0])(argv[1])(argv[2])(argv[3]) +# elif sweep_ind is True and arg_len == 3: +# return f(argv[0])(argv[1])(argv[2]) +# elif sweep_ind is False and arg_len == 3: +# return f(argv[0])(argv[1])(argv[2]) +# else: +# raise TypeError('curry_pot() needs 3 or 4 positional arguments') + +# def rename(newname): +# def decorator(f): +# f.__name__ = newname +# return f +# return decorator \ No newline at end of file diff --git a/dist/SimCAD-0.1-py3-none-any.whl b/dist/SimCAD-0.1-py3-none-any.whl deleted file mode 100644 index d6a18cf..0000000 Binary files a/dist/SimCAD-0.1-py3-none-any.whl and /dev/null differ diff --git a/dist/cadCAD-0.1-py3-none-any.whl b/dist/cadCAD-0.1-py3-none-any.whl new file mode 100644 index 0000000..dce3979 Binary files /dev/null and b/dist/cadCAD-0.1-py3-none-any.whl differ diff --git a/notebooks/jjodesty/multithreading.ipynb b/notebooks/jjodesty/multithreading.ipynb new file mode 100644 index 0000000..c3a41ad --- /dev/null +++ b/notebooks/jjodesty/multithreading.ipynb @@ -0,0 +1,576 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import _thread\n", + "import time\n", + "from fn.func import curried" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Define a function for the thread\n", + "def f(threadName, delay):\n", + " count = 0\n", + " print(count)\n", + " # while count < 5:\n", + " # time.sleep(delay)\n", + " # count += 1\n", + " # print(count)\n", + " \n", + "def pipe(x):\n", + " print(x)\n", + " return x" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "00\n", + "\n" + ] + } + ], + "source": [ + "# Create two threads as follows\n", + "try:\n", + " _thread.start_new_thread( f, (\"Thread-1\", 2, ) )\n", + " _thread.start_new_thread( f, (\"Thread-2\", 4, ) )\n", + "except:\n", + " print (\"Error: unable to start thread\")\n", + "\n", + "while 1:\n", + " pass\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2]\n", + "('s2', . at 0x1099efae8>)\n", + "('s2', . at 0x1099ef9d8>)\n" + ] + } + ], + "source": [ + "from SimCAD.engine.utils import sweep\n", + "from SimCAD.utils import rename\n", + "from SimCAD.configuration.utils import s_update\n", + "\n", + "# @curried\n", + "def fit_param(param):\n", + " return lambda x: x + param\n", + "\n", + "# xf = lambda param: lambda x: x + param\n", + "\n", + "def sweep(params, y, xf):\n", + " op = [rename('sweep', s_update(y, xf(param))) for param in params]\n", + " print(params)\n", + " # print()\n", + " return op\n", + "\n", + "for f in sweep([1,2], 's2', fit_param):\n", + " print(f(1,2,3,4))\n", + "# sweep([1,2], 's2', xf)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 64, 2187, 65536]\n" + ] + } + ], + "source": [ + "# instantiate and configure the worker pool\n", + "from pathos.threading import ThreadPool\n", + "pool = ThreadPool(nodes=4)\n", + "\n", + "# do a blocking map on the chosen function\n", + "print(pool.map(pow, [1,2,3,4], [5,6,7,8]))" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (, line 3)", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m3\u001b[0m\n\u001b[0;31m [for f in fs]\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ], + "output_type": "error" + } + ], + "source": [ + "with Pool(len(configs)) as p:\n", + " results = p.map(lambda t: t[0](t[1], t[2], t[3], t[4], t[5]), l)\n", + " \n", + "\n", + "def state_multithreading(self, fs, m_step, sL, last_in_obj, _input):\n", + " if type(fs) == 'list':\n", + " pool.map(f(m_step, sL, last_in_obj, _input), fs)\n", + " else:\n", + " f(m_step, sL, last_in_obj, _input)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('s2', [11, 23])]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from itertools import groupby\n", + "l = [('s2', 11), ('s2', 23)]\n", + "l.sort(key = lambda i : i[0])\n", + "[(key, [i[1] for i in values]) for key, values in groupby(l, lambda i: i[0])]" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "def groupByKV(l):\n", + " l.sort(key = lambda i : i[0])\n", + " return [(key, [i[1] for i in values]) for key, values in groupby(l, lambda i: i[0])]" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('s2', [11, 23])]" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "groupByKV(l)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (, line 2)", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m collect = lambda tuplist: reduce(lambda acc, (k,v): acc[k].append(v) or acc,tuplist, defaultdict(list))\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ], + "output_type": "error" + } + ], + "source": [ + "from collections import defaultdict \n", + "collect = lambda tuplist: reduce(lambda acc, (k,v): acc[k].append(v) or acc,tuplist, defaultdict(list))" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "from collections import defaultdict\n", + "d = defaultdict(list)\n", + "for key, value in [('s2', 11), ('s2', 23)]:\n", + " d[key].append(value)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on defaultdict object:\n", + "\n", + "class defaultdict(builtins.dict)\n", + " | defaultdict(default_factory[, ...]) --> dict with default factory\n", + " | \n", + " | The default factory is called without arguments to produce\n", + " | a new value when a key is not present, in __getitem__ only.\n", + " | A defaultdict compares equal to a dict with the same items.\n", + " | All remaining arguments are treated the same as if they were\n", + " | passed to the dict constructor, including keyword arguments.\n", + " | \n", + " | Method resolution order:\n", + " | defaultdict\n", + " | builtins.dict\n", + " | builtins.object\n", + " | \n", + " | Methods defined here:\n", + " | \n", + " | __copy__(...)\n", + " | D.copy() -> a shallow copy of D.\n", + " | \n", + " | __getattribute__(self, name, /)\n", + " | Return getattr(self, name).\n", + " | \n", + " | __init__(self, /, *args, **kwargs)\n", + " | Initialize self. See help(type(self)) for accurate signature.\n", + " | \n", + " | __missing__(...)\n", + " | __missing__(key) # Called by __getitem__ for missing key; pseudo-code:\n", + " | if self.default_factory is None: raise KeyError((key,))\n", + " | self[key] = value = self.default_factory()\n", + " | return value\n", + " | \n", + " | __reduce__(...)\n", + " | Return state information for pickling.\n", + " | \n", + " | __repr__(self, /)\n", + " | Return repr(self).\n", + " | \n", + " | copy(...)\n", + " | D.copy() -> a shallow copy of D.\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data descriptors defined here:\n", + " | \n", + " | default_factory\n", + " | Factory for default value called by __missing__().\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Methods inherited from builtins.dict:\n", + " | \n", + " | __contains__(self, key, /)\n", + " | True if D has a key k, else False.\n", + " | \n", + " | __delitem__(self, key, /)\n", + " | Delete self[key].\n", + " | \n", + " | __eq__(self, value, /)\n", + " | Return self==value.\n", + " | \n", + " | __ge__(self, value, /)\n", + " | Return self>=value.\n", + " | \n", + " | __getitem__(...)\n", + " | x.__getitem__(y) <==> x[y]\n", + " | \n", + " | __gt__(self, value, /)\n", + " | Return self>value.\n", + " | \n", + " | __iter__(self, /)\n", + " | Implement iter(self).\n", + " | \n", + " | __le__(self, value, /)\n", + " | Return self<=value.\n", + " | \n", + " | __len__(self, /)\n", + " | Return len(self).\n", + " | \n", + " | __lt__(self, value, /)\n", + " | Return self size of D in memory, in bytes\n", + " | \n", + " | clear(...)\n", + " | D.clear() -> None. Remove all items from D.\n", + " | \n", + " | fromkeys(iterable, value=None, /) from builtins.type\n", + " | Returns a new dict with keys from iterable and values equal to value.\n", + " | \n", + " | get(...)\n", + " | D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.\n", + " | \n", + " | items(...)\n", + " | D.items() -> a set-like object providing a view on D's items\n", + " | \n", + " | keys(...)\n", + " | D.keys() -> a set-like object providing a view on D's keys\n", + " | \n", + " | pop(...)\n", + " | D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n", + " | If key is not found, d is returned if given, otherwise KeyError is raised\n", + " | \n", + " | popitem(...)\n", + " | D.popitem() -> (k, v), remove and return some (key, value) pair as a\n", + " | 2-tuple; but raise KeyError if D is empty.\n", + " | \n", + " | setdefault(...)\n", + " | D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D\n", + " | \n", + " | update(...)\n", + " | D.update([E, ]**F) -> None. Update D from dict/iterable E and F.\n", + " | If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]\n", + " | If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v\n", + " | In either case, this is followed by: for k in F: D[k] = F[k]\n", + " | \n", + " | values(...)\n", + " | D.values() -> an object providing a view on D's values\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data and other attributes inherited from builtins.dict:\n", + " | \n", + " | __hash__ = None\n", + "\n" + ] + } + ], + "source": [ + "help(d)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'s2': [11, 23]}" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(d)" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [], + "source": [ + "def groupByKey(l):\n", + " d = defaultdict(list)\n", + " for key, value in l:\n", + " d[key].append(value)\n", + " return list(dict(d).items()).pop()" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "('s2', [11, 23])" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "r = groupByKey([('s2', 11), ('s2', 23)])\n", + "r" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# xf = lambda param: 1.0 + param\n", + "# def xf(y, param, s):\n", + "# return s[y] + param\n", + "\n", + "# def fit_param(param):\n", + "# y = 's2'\n", + "# x = 1 + param\n", + "# return lambda step, sL, s, _input: (y, x)\n", + "#\n", + "# def fit_param(param):\n", + "# return lambda step, sL, s, _input: (\n", + "# 's2',\n", + "# s['s2'] + param\n", + "# )\n", + "#\n", + "# s2m1 = sweep(\n", + "# params = [Decimal(11.0), Decimal(22.0)],\n", + "# sweep_f = fit_param\n", + "# )" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "from decimal import Decimal\n", + "from itertools import product\n", + "\n", + "# def \n", + "\n", + "l = {\n", + " 's1': 1, \n", + " 's2': [Decimal('11'), Decimal('22')], \n", + " 's3': [Decimal('12'), Decimal('23')], \n", + " 's4': 10, \n", + " 'timestamp': '2018-10-01 15:16:25', \n", + " 'mech_step': 0, \n", + " 'time_step': 1\n", + "}\n", + "\n", + "def flattenDict(l):\n", + " def tupalize(k, vs):\n", + " l = []\n", + " if isinstance(vs, list):\n", + " for v in vs:\n", + " l.append((k, v))\n", + " else:\n", + " l.append((k, vs))\n", + " return l\n", + "\n", + " flat_list = [tupalize(k, vs) for k, vs in l.items()]\n", + " flat_dict = [dict(items) for items in product(*flat_list)]\n", + " return flat_dict" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'s1': 1,\n", + " 's2': Decimal('11'),\n", + " 's3': Decimal('12'),\n", + " 's4': 10,\n", + " 'timestamp': '2018-10-01 15:16:25',\n", + " 'mech_step': 0,\n", + " 'time_step': 1},\n", + " {'s1': 1,\n", + " 's2': Decimal('11'),\n", + " 's3': Decimal('23'),\n", + " 's4': 10,\n", + " 'timestamp': '2018-10-01 15:16:25',\n", + " 'mech_step': 0,\n", + " 'time_step': 1},\n", + " {'s1': 1,\n", + " 's2': Decimal('22'),\n", + " 's3': Decimal('12'),\n", + " 's4': 10,\n", + " 'timestamp': '2018-10-01 15:16:25',\n", + " 'mech_step': 0,\n", + " 'time_step': 1},\n", + " {'s1': 1,\n", + " 's2': Decimal('22'),\n", + " 's3': Decimal('23'),\n", + " 's4': 10,\n", + " 'timestamp': '2018-10-01 15:16:25',\n", + " 'mech_step': 0,\n", + " 'time_step': 1}]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "flattenDict(l)" + ] + }, + { + "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": 1 +} diff --git a/requirements.txt b/requirements.txt index 47937eb..0418284 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ wheel +pandas pathos fn tabulate \ No newline at end of file diff --git a/setup.py b/setup.py index 927996c..54d0ae4 100644 --- a/setup.py +++ b/setup.py @@ -15,9 +15,9 @@ setup(name='cadCAD', description="cadCAD: a differential games based simulation software package for research, validation, and \ Computer Aided Design of economic systems", long_description = long_description, - url='https://github.com/BlockScience/DiffyQ-SimCAD', + url='https://github.com/BlockScience/DiffyQ-cadCAD', author='Joshua E. Jodesty', author_email='joshua@block.science', # license='LICENSE', - packages=find_packages() #['SimCAD'] + packages=find_packages() ) diff --git a/simulations/example_run.py b/simulations/example_run.py index 3a18378..3c80d44 100644 --- a/simulations/example_run.py +++ b/simulations/example_run.py @@ -1,29 +1,27 @@ 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 sweep_config, config1, config2 +from cadCAD import configs exec_mode = ExecutionMode() -print("Simulation Execution 1") -print() -first_config = [configs[0]] # from 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(tabulate(tensor_field, headers='keys', tablefmt='psql')) -print("Output:") -print(tabulate(result, headers='keys', tablefmt='psql')) -print() +# print("Simulation Execution 1") +# print() +# first_config = [configs[0]] # from 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(tabulate(tensor_field, headers='keys', tablefmt='psql')) +# print("Output:") +# print(tabulate(result, headers='keys', tablefmt='psql')) +# print() -print("Simulation Execution 2: Pairwise Execution") -print() +print("Simulation Execution 2: Concurrent Execution") multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) run2 = Executor(exec_context=multi_proc_ctx, configs=configs) for raw_result, tensor_field in run2.main(): diff --git a/simulations/validation/base_config1.py b/simulations/validation/base_config1.py deleted file mode 100644 index 3bf83ba..0000000 --- a/simulations/validation/base_config1.py +++ /dev/null @@ -1,171 +0,0 @@ -from decimal import Decimal -import numpy as np -from datetime import timedelta - -from SimCAD import configs -from SimCAD.configuration import Configuration -from SimCAD.configuration.utils import exo_update_per_ts, proc_trigger, bound_norm_random, \ - ep_time_step - -seed = { - 'z': np.random.RandomState(1), - 'a': np.random.RandomState(2), - 'b': np.random.RandomState(3), - 'c': np.random.RandomState(3) -} - -# Behaviors per Mechanism -# Different return types per mechanism ?? *** No *** -def b1m1(step, sL, s): - return {'param1': 1} -def b2m1(step, sL, s): - return {'param1': 1} - -def b1m2(step, sL, s): - return {'param1': 1, 'param2': 2} -def b2m2(step, sL, s): - return {'param1': 1, 'param2': 4} - -def b1m3(step, sL, s): - return {'param1': 1, 'param2': np.array([10, 100])} -def b2m3(step, sL, s): - return {'param1': 1, 'param2': np.array([20, 200])} - -# deff not more than 2 -# Internal States per Mechanism -def s1m1(step, sL, s, _input): - y = 's1' - x = s['s1'] + _input['param1'] - return (y, x) -def s2m1(step, sL, s, _input): - y = 's2' - x = s['s2'] + _input['param1'] - return (y, x) - -def s1m2(step, sL, s, _input): - y = 's1' - x = s['s1'] + _input['param1'] - return (y, x) -def s2m2(step, sL, s, _input): - y = 's2' - x = s['s2'] + _input['param1'] - return (y, x) - -def s1m3(step, sL, s, _input): - y = 's1' - x = s['s1'] + _input['param1'] - return (y, x) -def s2m3(step, sL, s, _input): - y = 's2' - x = s['s2'] + _input['param1'] - return (y, x) - -# Exogenous States -proc_one_coef_A = 0.7 -proc_one_coef_B = 1.3 - -def es3p1(step, sL, s, _input): - y = 's3' - x = s['s3'] * bound_norm_random(seed['a'], proc_one_coef_A, proc_one_coef_B) - return (y, x) - -def es4p2(step, sL, s, _input): - y = 's4' - x = s['s4'] * bound_norm_random(seed['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(step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], 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 - -# Genesis States -genesis_states = { - 's1': Decimal(0.0), - 's2': Decimal(0.0), - 's3': Decimal(1.0), - 's4': Decimal(1.0), - 'timestamp': '2018-10-01 15:16:24' -} - -# remove `exo_update_per_ts` to update every ts -exogenous_states = exo_update_per_ts( - { - "s3": es3p1, - "s4": es4p2, - "timestamp": es5p2 - } -) - -# make env proc trigger field agnostic - -# ToDo: Bug - Can't use environments without proc_trigger. TypeError: 'int' object is not callable -# "/Users/jjodesty/Projects/DiffyQ-SimCAD/SimCAD/engine/simulation.py" -env_processes = { - # "s3": env_a, - # "s4": env_b - "s3": proc_trigger('2018-10-01 15:16:25', env_a), - "s4": proc_trigger('2018-10-01 15:16:25', env_b) -} - -# need at least 1 behaviour and 1 state function for the 1st mech with behaviors -# mechanisms = {} -mechanisms = { - "m1": { - "behaviors": { - "b1": b1m1, # lambda step, sL, s: s['s1'] + 1, - "b2": b2m1 - }, - "states": { # exclude only. TypeError: reduce() of empty sequence with no initial value - "s1": s1m1, - "s2": s2m1 - } - }, - "m2": { - "behaviors": { - "b1": b1m2, - "b2": b2m2 - }, - "states": { - "s1": s1m2, - "s2": s2m2 - } - }, - "m3": { - "behaviors": { - "b1": b1m3, - "b2": b2m3 - }, - "states": { - "s1": s1m3, - "s2": s2m3 - } - } -} - -sim_config = { - "N": 2, - "T": range(5) -} - -configs.append( - Configuration( - sim_config=sim_config, - state_dict=genesis_states, - seed=seed, - exogenous_states=exogenous_states, - env_processes=env_processes, - mechanisms=mechanisms - ) -) \ No newline at end of file diff --git a/simulations/validation/base_config2.py b/simulations/validation/base_config2.py deleted file mode 100644 index 6b9469e..0000000 --- a/simulations/validation/base_config2.py +++ /dev/null @@ -1,180 +0,0 @@ -from decimal import Decimal -import numpy as np -from datetime import timedelta - -from SimCAD import configs -from SimCAD.configuration import Configuration -from SimCAD.configuration.utils import exo_update_per_ts, proc_trigger, bound_norm_random, \ - ep_time_step - - -seed = { - 'z': np.random.RandomState(1), - 'a': np.random.RandomState(2), - 'b': np.random.RandomState(3), - 'c': np.random.RandomState(3) -} - -# Behaviors per Mechanism -# Different return types per mechanism ?? *** No *** -def b1m1(step, sL, s): - return {'param1': 1} -def b2m1(step, sL, s): - return {'param2': 4} - -def b1m2(step, sL, s): - return {'param1': 'a', 'param2': 2} -def b2m2(step, sL, s): - return {'param1': 'b', 'param2': 4} - - -def b1m3(step, sL, s): - return {'param1': ['c'], 'param2': np.array([10, 100])} -def b2m3(step, sL, s): - return {'param1': ['d'], 'param2': np.array([20, 200])} - - -# Internal States per Mechanism -def s1m1(step, sL, s, _input): - y = 's1' - x = _input['param1'] - return (y, x) -def s2m1(step, sL, s, _input): - y = 's2' - x = _input['param2'] - return (y, x) - -def s1m2(step, sL, s, _input): - y = 's1' - x = _input['param1'] - return (y, x) -def s2m2(step, sL, s, _input): - y = 's2' - x = _input['param2'] - return (y, x) - -def s1m3(step, sL, s, _input): - y = 's1' - x = _input['param1'] - return (y, x) -def s2m3(step, sL, s, _input): - y = 's2' - x = _input['param2'] - return (y, x) - -# Exogenous States -proc_one_coef_A = 0.7 -proc_one_coef_B = 1.3 - -def es3p1(step, sL, s, _input): - y = 's3' - x = s['s3'] * bound_norm_random(seed['a'], proc_one_coef_A, proc_one_coef_B) - return (y, x) - -def es4p2(step, sL, s, _input): - y = 's4' - x = s['s4'] * bound_norm_random(seed['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(step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], 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 - -# Genesis States -genesis_states = { - 's1': Decimal(0.0), - 's2': Decimal(0.0), - 's3': Decimal(1.0), - 's4': Decimal(1.0), - 'timestamp': '2018-10-01 15:16:24' -} - -# remove `exo_update_per_ts` to update every ts -# why `exo_update_per_ts` here instead of `env_processes` -exogenous_states = exo_update_per_ts( - { - "s3": es3p1, - "s4": es4p2, - "timestamp": es5p2 - } -) - -# make env proc trigger field agnostic -env_processes = { - "s3": proc_trigger('2018-10-01 15:16:25', env_a), - "s4": proc_trigger('2018-10-01 15:16:25', env_b) -} - -# lambdas -# genesis Sites should always be there -# [1, 2] -# behavior_ops = [ foldr(_ + _), lambda x: x + 0 ] - - -# [1, 2] = {'b1': ['a'], 'b2', [1]} = -# behavior_ops = [behavior_to_dict, print_fwd, sum_dict_values] -# behavior_ops = [foldr(dict_elemwise_sum())] -# behavior_ops = [] - -# need at least 1 behaviour and 1 state function for the 1st mech with behaviors -# mechanisms = {} -mechanisms = { - "m1": { - "behaviors": { - "b1": b1m1, # lambda step, sL, s: s['s1'] + 1, - # "b2": b2m1 - }, - "states": { # exclude only. TypeError: reduce() of empty sequence with no initial value - "s1": s1m1, - # "s2": s2m1 - } - }, - "m2": { - "behaviors": { - "b1": b1m2, - # "b2": b2m2 - }, - "states": { - "s1": s1m2, - # "s2": s2m2 - } - }, - "m3": { - "behaviors": { - "b1": b1m3, - "b2": b2m3 - }, - "states": { - "s1": s1m3, - "s2": s2m3 - } - } -} - -sim_config = { - "N": 2, - "T": range(5) -} - -configs.append( - Configuration( - sim_config=sim_config, - state_dict=genesis_states, - seed=seed, - exogenous_states=exogenous_states, - env_processes=env_processes, - mechanisms=mechanisms - ) -) \ No newline at end of file diff --git a/simulations/validation/config1.py b/simulations/validation/config1.py index a016842..4539208 100644 --- a/simulations/validation/config1.py +++ b/simulations/validation/config1.py @@ -2,13 +2,12 @@ from decimal import Decimal import numpy as np from datetime import timedelta -from SimCAD import configs -from SimCAD.configuration import Configuration -from SimCAD.configuration.utils import exo_update_per_ts, proc_trigger, bound_norm_random, \ - ep_time_step +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 -seed = { +seeds = { 'z': np.random.RandomState(1), 'a': np.random.RandomState(2), 'b': np.random.RandomState(3), @@ -16,47 +15,47 @@ seed = { } -# Behaviors per Mechanism -def b1m1(step, sL, s): +# Policies per Mechanism +def p1m1(_g, step, sL, s): return {'param1': 1} -def b2m1(step, sL, s): +def p2m1(_g, step, sL, s): return {'param2': 4} -def b1m2(step, sL, s): +def p1m2(_g, step, sL, s): return {'param1': 'a', 'param2': 2} -def b2m2(step, sL, s): +def p2m2(_g, step, sL, s): return {'param1': 'b', 'param2': 4} -def b1m3(step, sL, s): +def p1m3(_g, step, sL, s): return {'param1': ['c'], 'param2': np.array([10, 100])} -def b2m3(step, sL, s): +def p2m3(_g, step, sL, s): return {'param1': ['d'], 'param2': np.array([20, 200])} # Internal States per Mechanism -def s1m1(step, sL, s, _input): +def s1m1(_g, step, sL, s, _input): y = 's1' x = _input['param1'] return (y, x) -def s2m1(step, sL, s, _input): +def s2m1(_g, step, sL, s, _input): y = 's2' x = _input['param2'] return (y, x) -def s1m2(step, sL, s, _input): +def s1m2(_g, step, sL, s, _input): y = 's1' x = _input['param1'] return (y, x) -def s2m2(step, sL, s, _input): +def s2m2(_g, step, sL, s, _input): y = 's2' x = _input['param2'] return (y, x) -def s1m3(step, sL, s, _input): +def s1m3(_g, step, sL, s, _input): y = 's1' x = _input['param1'] return (y, x) -def s2m3(step, sL, s, _input): +def s2m3(_g, step, sL, s, _input): y = 's2' x = _input['param2'] return (y, x) @@ -66,21 +65,21 @@ def s2m3(step, sL, s, _input): proc_one_coef_A = 0.7 proc_one_coef_B = 1.3 -def es3p1(step, sL, s, _input): +def es3p1(_g, step, sL, s, _input): y = 's3' - x = s['s3'] * bound_norm_random(seed['a'], proc_one_coef_A, proc_one_coef_B) + x = s['s3'] * bound_norm_random(seeds['a'], proc_one_coef_A, proc_one_coef_B) return (y, x) -def es4p2(step, sL, s, _input): +def es4p2(_g, step, sL, s, _input): y = 's4' - x = s['s4'] * bound_norm_random(seed['b'], proc_one_coef_A, proc_one_coef_B) + 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(step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=t_delta) +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) @@ -99,18 +98,15 @@ genesis_states = { 's2': Decimal(0.0), 's3': Decimal(1.0), 's4': Decimal(1.0), - 'timestamp': '2018-10-01 15:16:24' + 'timestep': '2018-10-01 15:16:24' } -# remove `exo_update_per_ts` to update every ts -exogenous_states = exo_update_per_ts( - { +raw_exogenous_states = { "s3": es3p1, "s4": es4p2, - "timestamp": es5p2 - } -) + "timestep": es5p2 +} env_processes = { @@ -119,11 +115,11 @@ env_processes = { } -mechanisms = { +partial_state_update_block = { "m1": { - "behaviors": { - "b1": b1m1, - "b2": b2m1 + "policies": { + "b1": p1m1, + "b2": p2m1 }, "states": { "s1": s1m1, @@ -131,9 +127,9 @@ mechanisms = { } }, "m2": { - "behaviors": { - "b1": b1m2, - "b2": b2m2 + "policies": { + "b1": p1m2, + "b2": p2m2 }, "states": { "s1": s1m2, @@ -141,9 +137,9 @@ mechanisms = { } }, "m3": { - "behaviors": { - "b1": b1m3, - "b2": b2m3 + "policies": { + "b1": p1m3, + "b2": p2m3 }, "states": { "s1": s1m3, @@ -153,19 +149,19 @@ mechanisms = { } -sim_config = { - "N": 2, - "T": range(5) -} - - -configs.append( - Configuration( - sim_config=sim_config, - state_dict=genesis_states, - seed=seed, - exogenous_states=exogenous_states, - env_processes=env_processes, - mechanisms=mechanisms - ) +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_updates=partial_state_update_block ) diff --git a/simulations/validation/config2.py b/simulations/validation/config2.py index 703dace..5925c6c 100644 --- a/simulations/validation/config2.py +++ b/simulations/validation/config2.py @@ -2,13 +2,11 @@ from decimal import Decimal import numpy as np from datetime import timedelta -from SimCAD import configs -from SimCAD.configuration import Configuration -from SimCAD.configuration.utils import exo_update_per_ts, proc_trigger, bound_norm_random, \ - ep_time_step +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 - -seed = { +seeds = { 'z': np.random.RandomState(1), 'a': np.random.RandomState(2), 'b': np.random.RandomState(3), @@ -16,47 +14,47 @@ seed = { } -# Behaviors per Mechanism -def b1m1(step, sL, s): +# Policies per Mechanism +def p1m1(_g, step, sL, s): return {'param1': 1} -def b2m1(step, sL, s): +def p2m1(_g, step, sL, s): return {'param2': 4} -def b1m2(step, sL, s): +def p1m2(_g, step, sL, s): return {'param1': 'a', 'param2': 2} -def b2m2(step, sL, s): +def p2m2(_g, step, sL, s): return {'param1': 'b', 'param2': 4} -def b1m3(step, sL, s): +def p1m3(_g, step, sL, s): return {'param1': ['c'], 'param2': np.array([10, 100])} -def b2m3(step, sL, s): +def p2m3(_g, step, sL, s): return {'param1': ['d'], 'param2': np.array([20, 200])} # Internal States per Mechanism -def s1m1(step, sL, s, _input): +def s1m1(_g, step, sL, s, _input): y = 's1' x = _input['param1'] return (y, x) -def s2m1(step, sL, s, _input): +def s2m1(_g, step, sL, s, _input): y = 's2' x = _input['param2'] return (y, x) -def s1m2(step, sL, s, _input): +def s1m2(_g, step, sL, s, _input): y = 's1' x = _input['param1'] return (y, x) -def s2m2(step, sL, s, _input): +def s2m2(_g, step, sL, s, _input): y = 's2' x = _input['param2'] return (y, x) -def s1m3(step, sL, s, _input): +def s1m3(_g, step, sL, s, _input): y = 's1' x = _input['param1'] return (y, x) -def s2m3(step, sL, s, _input): +def s2m3(_g, step, sL, s, _input): y = 's2' x = _input['param2'] return (y, x) @@ -66,21 +64,21 @@ def s2m3(step, sL, s, _input): proc_one_coef_A = 0.7 proc_one_coef_B = 1.3 -def es3p1(step, sL, s, _input): +def es3p1(_g, step, sL, s, _input): y = 's3' - x = s['s3'] * bound_norm_random(seed['a'], proc_one_coef_A, proc_one_coef_B) + x = s['s3'] * bound_norm_random(seeds['a'], proc_one_coef_A, proc_one_coef_B) return (y, x) -def es4p2(step, sL, s, _input): +def es4p2(_g, step, sL, s, _input): y = 's4' - x = s['s4'] * bound_norm_random(seed['b'], proc_one_coef_A, proc_one_coef_B) + 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(step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], fromat_str=ts_format, _timedelta=t_delta) +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) @@ -99,18 +97,15 @@ genesis_states = { 's2': Decimal(0.0), 's3': Decimal(1.0), 's4': Decimal(1.0), - 'timestamp': '2018-10-01 15:16:24' + 'timestep': '2018-10-01 15:16:24' } -# remove `exo_update_per_ts` to update every ts -exogenous_states = exo_update_per_ts( - { +raw_exogenous_states = { "s3": es3p1, "s4": es4p2, - "timestamp": es5p2 - } -) + "timestep": es5p2 +} env_processes = { @@ -119,11 +114,11 @@ env_processes = { } -mechanisms = { +partial_state_update_block = { "m1": { - "behaviors": { - "b1": b1m1, - # "b2": b2m1 + "policies": { + "b1": p1m1, + # "b2": p2m1 }, "states": { "s1": s1m1, @@ -131,9 +126,9 @@ mechanisms = { } }, "m2": { - "behaviors": { - "b1": b1m2, - # "b2": b2m2 + "policies": { + "b1": p1m2, + # "b2": p2m2 }, "states": { "s1": s1m2, @@ -141,9 +136,9 @@ mechanisms = { } }, "m3": { - "behaviors": { - "b1": b1m3, - "b2": b2m3 + "policies": { + "b1": p1m3, + "b2": p2m3 }, "states": { "s1": s1m3, @@ -153,19 +148,19 @@ mechanisms = { } -sim_config = { - "N": 2, - "T": range(5) -} - - -configs.append( - Configuration( - sim_config=sim_config, - state_dict=genesis_states, - seed=seed, - exogenous_states=exogenous_states, - env_processes=env_processes, - mechanisms=mechanisms - ) +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_updates=partial_state_update_block ) diff --git a/simulations/validation/config_1.py b/simulations/validation/config_1.py deleted file mode 100644 index 7bdd5ac..0000000 --- a/simulations/validation/config_1.py +++ /dev/null @@ -1,178 +0,0 @@ -from decimal import Decimal -import numpy as np -from datetime import timedelta - -from SimCAD import configs -from SimCAD.configuration import Configuration -from SimCAD.configuration.utils import exo_update_per_ts, proc_trigger, bound_norm_random, \ - ep_time_step - -seed = { - 'z': np.random.RandomState(1), - 'a': np.random.RandomState(2), - 'b': np.random.RandomState(3), - 'c': np.random.RandomState(3) -} - -# Behaviors per Mechanism -# Different return types per mechanism ?? *** No *** -def b1m1(step, sL, s): - return {'param1': 1} -def b2m1(step, sL, s): - return {'param2': 4} - -def b1m2(step, sL, s): - return {'param1': 'a', 'param2': 2} -def b2m2(step, sL, s): - return {'param1': 'b', 'param2': 4} - -def b1m3(step, sL, s): - return {'param1': ['c'], 'param2': np.array([10, 100])} -def b2m3(step, sL, s): - return {'param1': ['d'], 'param2': np.array([20, 200])} - -# deff not more than 2 -# Internal States per Mechanism -def s1m1(step, sL, s, _input): - y = 's1' - x = _input['param1'] #+ [Coef1 x 5] - return (y, x) -def s2m1(step, sL, s, _input): - y = 's2' - x = _input['param2'] #+ [Coef2 x 5] - return (y, x) - -def s1m2(step, sL, s, _input): - y = 's1' - x = _input['param1'] - return (y, x) -def s2m2(step, sL, s, _input): - y = 's2' - x = _input['param2'] - return (y, x) - -def s1m3(step, sL, s, _input): - y = 's1' - x = _input['param1'] - return (y, x) -def s2m3(step, sL, s, _input): - y = 's2' - x = _input['param2'] - return (y, x) - -# Exogenous States -proc_one_coef_A = 0.7 -proc_one_coef_B = 1.3 - -def es3p1(step, sL, s, _input): - y = 's3' - x = s['s3'] * bound_norm_random(seed['a'], proc_one_coef_A, proc_one_coef_B) - return (y, x) - -def es4p2(step, sL, s, _input): - y = 's4' - x = s['s4'] * bound_norm_random(seed['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(step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], 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 - -# Genesis States -genesis_states = { - 's1': Decimal(0.0), - 's2': Decimal(0.0), - 's3': Decimal(1.0), - 's4': Decimal(1.0), - 'timestamp': '2018-10-01 15:16:24' -} - -# remove `exo_update_per_ts` to update every ts -exogenous_states = exo_update_per_ts( - { - "s3": es3p1, - "s4": es4p2, - "timestamp": es5p2 - } -) - -# ToDo: make env proc trigger field agnostic -# ToDo: input json into function renaming __name__ -env_processes = { - "s3": env_a, - "s4": proc_trigger('2018-10-01 15:16:25', env_b) -} - -# lambdas -# genesis Sites should always be there -# [1, 2] -# behavior_ops = [ foldr(_ + _), lambda x: x + 0 ] - -# [1, 2] = {'b1': ['a'], 'b2', [1]} = -# behavior_ops = [ behavior_to_dict, print_fwd, sum_dict_values ] -# behavior_ops = [foldr(dict_elemwise_sum())] -# behavior_ops = [foldr(lambda a, b: a + b)] - -# need at least 1 behaviour and 1 state function for the 1st mech with behaviors -# mechanisms = {} - -mechanisms = { - "m1": { - "behaviors": { - "b1": b1m1, # lambda step, sL, s: s['s1'] + 1, - "b2": b2m1 - }, - "states": { # exclude only. TypeError: reduce() of empty sequence with no initial value - "s1": s1m1, - "s2": s2m1 - } - }, - "m2": { - "behaviors": { - "b1": b1m2, - "b2": b2m2 - }, - "states": { - "s1": s1m2, - "s2": s2m2 - } - }, - "m3": { - "behaviors": { - "b1": b1m3, - "b2": b2m3 - }, - "states": { - "s1": s1m3, - "s2": s2m3 - } - } -} - -sim_config = { - "N": 2, - "T": range(5) -} - -configs.append( - Configuration( - sim_config=sim_config, - state_dict=genesis_states, - seed=seed, - exogenous_states=exogenous_states, - env_processes=env_processes, - mechanisms=mechanisms - ) -) \ No newline at end of file diff --git a/simulations/validation/config_2.py b/simulations/validation/config_2.py deleted file mode 100644 index 6b9469e..0000000 --- a/simulations/validation/config_2.py +++ /dev/null @@ -1,180 +0,0 @@ -from decimal import Decimal -import numpy as np -from datetime import timedelta - -from SimCAD import configs -from SimCAD.configuration import Configuration -from SimCAD.configuration.utils import exo_update_per_ts, proc_trigger, bound_norm_random, \ - ep_time_step - - -seed = { - 'z': np.random.RandomState(1), - 'a': np.random.RandomState(2), - 'b': np.random.RandomState(3), - 'c': np.random.RandomState(3) -} - -# Behaviors per Mechanism -# Different return types per mechanism ?? *** No *** -def b1m1(step, sL, s): - return {'param1': 1} -def b2m1(step, sL, s): - return {'param2': 4} - -def b1m2(step, sL, s): - return {'param1': 'a', 'param2': 2} -def b2m2(step, sL, s): - return {'param1': 'b', 'param2': 4} - - -def b1m3(step, sL, s): - return {'param1': ['c'], 'param2': np.array([10, 100])} -def b2m3(step, sL, s): - return {'param1': ['d'], 'param2': np.array([20, 200])} - - -# Internal States per Mechanism -def s1m1(step, sL, s, _input): - y = 's1' - x = _input['param1'] - return (y, x) -def s2m1(step, sL, s, _input): - y = 's2' - x = _input['param2'] - return (y, x) - -def s1m2(step, sL, s, _input): - y = 's1' - x = _input['param1'] - return (y, x) -def s2m2(step, sL, s, _input): - y = 's2' - x = _input['param2'] - return (y, x) - -def s1m3(step, sL, s, _input): - y = 's1' - x = _input['param1'] - return (y, x) -def s2m3(step, sL, s, _input): - y = 's2' - x = _input['param2'] - return (y, x) - -# Exogenous States -proc_one_coef_A = 0.7 -proc_one_coef_B = 1.3 - -def es3p1(step, sL, s, _input): - y = 's3' - x = s['s3'] * bound_norm_random(seed['a'], proc_one_coef_A, proc_one_coef_B) - return (y, x) - -def es4p2(step, sL, s, _input): - y = 's4' - x = s['s4'] * bound_norm_random(seed['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(step, sL, s, _input): - y = 'timestamp' - x = ep_time_step(s, dt_str=s['timestamp'], 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 - -# Genesis States -genesis_states = { - 's1': Decimal(0.0), - 's2': Decimal(0.0), - 's3': Decimal(1.0), - 's4': Decimal(1.0), - 'timestamp': '2018-10-01 15:16:24' -} - -# remove `exo_update_per_ts` to update every ts -# why `exo_update_per_ts` here instead of `env_processes` -exogenous_states = exo_update_per_ts( - { - "s3": es3p1, - "s4": es4p2, - "timestamp": es5p2 - } -) - -# make env proc trigger field agnostic -env_processes = { - "s3": proc_trigger('2018-10-01 15:16:25', env_a), - "s4": proc_trigger('2018-10-01 15:16:25', env_b) -} - -# lambdas -# genesis Sites should always be there -# [1, 2] -# behavior_ops = [ foldr(_ + _), lambda x: x + 0 ] - - -# [1, 2] = {'b1': ['a'], 'b2', [1]} = -# behavior_ops = [behavior_to_dict, print_fwd, sum_dict_values] -# behavior_ops = [foldr(dict_elemwise_sum())] -# behavior_ops = [] - -# need at least 1 behaviour and 1 state function for the 1st mech with behaviors -# mechanisms = {} -mechanisms = { - "m1": { - "behaviors": { - "b1": b1m1, # lambda step, sL, s: s['s1'] + 1, - # "b2": b2m1 - }, - "states": { # exclude only. TypeError: reduce() of empty sequence with no initial value - "s1": s1m1, - # "s2": s2m1 - } - }, - "m2": { - "behaviors": { - "b1": b1m2, - # "b2": b2m2 - }, - "states": { - "s1": s1m2, - # "s2": s2m2 - } - }, - "m3": { - "behaviors": { - "b1": b1m3, - "b2": b2m3 - }, - "states": { - "s1": s1m3, - "s2": s2m3 - } - } -} - -sim_config = { - "N": 2, - "T": range(5) -} - -configs.append( - Configuration( - sim_config=sim_config, - state_dict=genesis_states, - seed=seed, - exogenous_states=exogenous_states, - env_processes=env_processes, - mechanisms=mechanisms - ) -) \ No newline at end of file diff --git a/simulations/validation/sweep_config.py b/simulations/validation/sweep_config.py new file mode 100644 index 0000000..f23e4ba --- /dev/null +++ b/simulations/validation/sweep_config.py @@ -0,0 +1,196 @@ +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 +from cadCAD.configuration.utils.parameterSweep import config_sim + +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) +} + + +g = { + '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): + y = 's1' + x = 0 + return (y, x) + +def s2m1(_g, step, sL, s, _input): + y = 's2' + x = _g['beta'] + return (y, x) + +def s1m2(_g, step, sL, s, _input): + y = 's1' + x = _input['param2'] + return (y, x) + +def s2m2(_g, step, sL, s, _input): + y = 's2' + x = _input['param2'] + return (y, x) + +def s1m3(_g, step, sL, s, _input): + y = 's1' + x = 0 + return (y, x) + +def s2m3(_g, step, sL, s, _input): + y = 's2' + x = 0 + return (y, x) + + +# Exogenous States +proc_one_coef_A = 0.7 +proc_one_coef_B = 1.3 + + +def es3p1(_g, step, sL, s, _input): + y = 's3' + x = _g['gamma'] + return (y, x) +# @curried +def es4p2(_g, step, sL, s, _input): + y = 's4' + x = _g['gamma'] + 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 +# @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('2018-10-01 15:16:25', 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 + }, + "states": { + "s1": s1m1, + "s2": s2m1 + } + }, + "m2": { + "policies": { + "b1": p1m2, + "b2": p2m2, + }, + "states": { + "s1": s1m2, + "s2": s2m2 + } + }, + "m3": { + "policies": { + "b1": p1m3, + "b2": p2m3 + }, + "states": { + "s1": s1m3, + "s2": s2m3 + } + } +} + + +sim_config = config_sim( + { + "N": 2, + "T": range(5), + "M": g + } +) + + +append_configs( + sim_configs=sim_config, + initial_state=genesis_states, + seeds=seeds, + raw_exogenous_states=raw_exogenous_states, + env_processes=env_processes, + partial_state_updates=partial_state_update_block +) \ No newline at end of file