From 2de989db0ac300032906eea496d5299918bf48c2 Mon Sep 17 00:00:00 2001 From: "Joshua E. Jodesty" Date: Thu, 16 May 2019 12:51:56 -0400 Subject: [PATCH] fefactored env_process --- .gitignore | 2 +- cadCAD/configuration/__init__.py | 2 +- cadCAD/configuration/utils/__init__.py | 51 +++-- .../configuration/utils/userDefinedObject.py | 2 +- cadCAD/engine/simulation.py | 51 ++++- cadCAD/utils/sys_config.py | 28 ++- ...-any.whl => cadCAD-0.2.2-py3-none-any.whl} | Bin 14927 -> 15013 bytes requirements.txt | 3 +- simulations/historical_state_access_run.py | 1 - simulations/param_sweep_test.py | 3 +- simulations/regression_tests/sweep_config.py | 151 ++++++------- simulations/regression_tests/udo.py | 107 +++++----- .../udo_inter_substep_update.py} | 7 +- .../{validation => tutorials}/marbles.py | 0 .../{validation => tutorials}/marbles2.py | 198 ++++++++++-------- .../{validation => tutorials}/marbles2_run.py | 2 +- .../robot-marbles-agents-advanced.ipynb} | 0 simulations/udo_run.py | 3 +- simulations/udo_test.py | 1 - .../validation/historical_state_access.py | 29 ++- 20 files changed, 344 insertions(+), 297 deletions(-) rename dist/{cadCAD-0.2-py3-none-any.whl => cadCAD-0.2.2-py3-none-any.whl} (64%) rename simulations/{validation/udo.py => regression_tests/udo_inter_substep_update.py} (98%) rename simulations/{validation => tutorials}/marbles.py (100%) rename simulations/{validation => tutorials}/marbles2.py (54%) rename simulations/{validation => tutorials}/marbles2_run.py (89%) rename simulations/{validation/robot-marbles-agents-advanced-udo.ipynb => tutorials/robot-marbles-agents-advanced.ipynb} (100%) diff --git a/.gitignore b/.gitignore index 5194f30..adec4a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.idea +jupyter notebook.idea .ipynb_checkpoints .DS_Store .idea diff --git a/cadCAD/configuration/__init__.py b/cadCAD/configuration/__init__.py index 0bae909..8720047 100644 --- a/cadCAD/configuration/__init__.py +++ b/cadCAD/configuration/__init__.py @@ -28,7 +28,7 @@ class Configuration(object): sanitize_config(self) - +# ToDo: Remove Seeds def append_configs(sim_configs={}, initial_state={}, seeds={}, raw_exogenous_states={}, env_processes={}, partial_state_update_blocks={}, policy_ops=[lambda a, b: a + b], _exo_update_per_ts: bool = True) -> None: if _exo_update_per_ts is True: diff --git a/cadCAD/configuration/utils/__init__.py b/cadCAD/configuration/utils/__init__.py index 1e69c66..9345fa3 100644 --- a/cadCAD/configuration/utils/__init__.py +++ b/cadCAD/configuration/utils/__init__.py @@ -134,20 +134,43 @@ def trigger_condition(s, conditions, cond_opp): condition_bools = [s[field] in precondition_values for field, precondition_values in conditions.items()] return reduce(cond_opp, condition_bools) -def apply_state_condition(conditions, cond_opp, y, f, _g, step, sL, s, _input): - if trigger_condition(s, conditions, cond_opp): +def apply_state_condition(pre_conditions, cond_opp, y, f, _g, step, sL, s, _input): + if trigger_condition(s, pre_conditions, cond_opp): return f(_g, step, sL, s, _input) else: return y, s[y] -def proc_trigger(y, f, conditions, cond_op): - return lambda _g, step, sL, s, _input: apply_state_condition(conditions, cond_op, y, f, _g, step, sL, s, _input) +def var_trigger(y, f, pre_conditions, cond_op): + return lambda _g, step, sL, s, _input: apply_state_condition(pre_conditions, cond_op, y, f, _g, step, sL, s, _input) -def timestep_trigger(end_substep, y, f): - conditions = {'substep': [0, end_substep]} - cond_opp = lambda a, b: a and b - return proc_trigger(y, f, conditions, cond_opp) +def var_substep_trigger(substeps): + def trigger(end_substep, y, f): + pre_conditions = {'substep': substeps} + cond_opp = lambda a, b: a and b + return var_trigger(y, f, pre_conditions, cond_opp) + return lambda y, f: curry(trigger)(substeps)(y)(f) + + +def env_trigger(end_substep): + def trigger(end_substep, trigger_field, trigger_vals, funct_list): + def env_update(state_dict, target_value): + state_dict_copy = deepcopy(state_dict) + # Use supstep to simulate current sysMetrics + if state_dict_copy['substep'] == end_substep: + state_dict_copy['timestep'] = state_dict_copy['timestep'] + 1 + + if state_dict_copy[trigger_field] in trigger_vals: + for g in funct_list: + target_value = g(target_value) + + del state_dict_copy + return target_value + + return env_update + + return lambda trigger_field, trigger_vals, funct_list: \ + curry(trigger)(end_substep)(trigger_field)(trigger_vals)(funct_list) # trigger = curry(_trigger) # print(timestep_trigger) @@ -157,19 +180,15 @@ def timestep_trigger(end_substep, y, f): def config_sim(d): def process_variables(d): return flatten_tabulated_dict(tabulate_dict(d)) + if "M" in d: - return [ - { - "N": d["N"], - "T": d["T"], - "M": M - } - for M in process_variables(d["M"]) - ] + return [{"N": d["N"], "T": d["T"], "M": M} for M in process_variables(d["M"])] else: d["M"] = [{}] return d +def psub_list(psu_block, psu_steps): + return [psu_block[psu] for psu in psu_steps] def psub(policies, state_updates): return { diff --git a/cadCAD/configuration/utils/userDefinedObject.py b/cadCAD/configuration/utils/userDefinedObject.py index c013952..c1f532c 100644 --- a/cadCAD/configuration/utils/userDefinedObject.py +++ b/cadCAD/configuration/utils/userDefinedObject.py @@ -27,7 +27,7 @@ class udcView(object): } members['methods'] = [k for k, v in self.__dict__.items() if str(type(v)) == ""] members.update(variables) - return f"{members}" + return f"{members}" #[1:-1] class udcBroker(object): diff --git a/cadCAD/engine/simulation.py b/cadCAD/engine/simulation.py index 36d4067..a6e4750 100644 --- a/cadCAD/engine/simulation.py +++ b/cadCAD/engine/simulation.py @@ -3,6 +3,7 @@ from typing import Any, Callable, Dict, List, Tuple from pathos.pools import ThreadPool as TPool from copy import deepcopy from functools import reduce +from funcy import compose from cadCAD.engine.utils import engine_exception from cadCAD.utils import flatten @@ -72,19 +73,48 @@ class Executor: # return {k: reduce(f1, val_list) for k, val_list in new_dict.items()} # return foldr(call, col_results)(ops) + # def apply_env_proc( + # self, + # env_processes: Dict[str, Callable], + # state_dict: Dict[str, Any], + # time_step: int + # ) -> Dict[str, Any]: + # for state in state_dict.keys(): + # if state in list(env_processes.keys()): + # env_state: Callable = env_processes[state] + # if (env_state.__name__ == '_curried') or (env_state.__name__ == 'proc_trigger'): + # state_dict[state] = env_state(sub_step)(state_dict[state]) + # else: + # state_dict[state] = env_state(state_dict[state]) + # + # return state_dict + def apply_env_proc( self, env_processes: Dict[str, Callable], state_dict: Dict[str, Any], - sub_step: int ) -> Dict[str, Any]: - for state in state_dict.keys(): - if state in list(env_processes.keys()): - env_state: Callable = env_processes[state] - if (env_state.__name__ == '_curried') or (env_state.__name__ == 'proc_trigger'): - state_dict[state] = env_state(sub_step)(state_dict[state]) - else: - state_dict[state] = env_state(state_dict[state]) + + def env_composition(target_field, state_dict, target_value): + function_type = type(lambda x: x) + env_update = env_processes[target_field] + if isinstance(env_update, list): + target_value = compose(*env_update[::-1])(target_value) + elif isinstance(env_update, function_type): + target_value = env_update(state_dict, target_value) + else: + target_value = env_update + + return target_value + + filtered_state_dict = {k: v for k, v in state_dict.items() if k in env_processes.keys()} + env_proc_dict = { + target_field: env_composition(target_field, state_dict, target_value) + for target_field, target_value in filtered_state_dict.items() + } + + for k, v in env_proc_dict.items(): + state_dict[k] = v return state_dict @@ -137,7 +167,10 @@ class Executor: last_in_copy: Dict[str, Any] = transfer_missing_fields(last_in_obj, dict(generate_record(state_funcs))) # ToDo: Remove - last_in_copy: Dict[str, Any] = self.apply_env_proc(env_processes, last_in_copy, last_in_copy['timestep']) + # last_in_copy: Dict[str, Any] = self.apply_env_proc(env_processes, last_in_copy, last_in_copy['timestep']) + last_in_copy: Dict[str, Any] = self.apply_env_proc(env_processes, last_in_copy) + + # ToDo: make 'substep' & 'timestep' reserve fields last_in_copy['substep'], last_in_copy['timestep'], last_in_copy['run'] = sub_step, time_step, run diff --git a/cadCAD/utils/sys_config.py b/cadCAD/utils/sys_config.py index b6e3a92..738161c 100644 --- a/cadCAD/utils/sys_config.py +++ b/cadCAD/utils/sys_config.py @@ -1,5 +1,10 @@ +from copy import deepcopy + +from fn.func import curried + from cadCAD.configuration.utils import ep_time_step, time_step from funcy import curry +import pprint as pp # from fn import _ from functools import reduce @@ -25,7 +30,7 @@ def update_timestamp(y, timedelta, format): def apply(f, y: str, incr_by: int): return lambda _g, step, sL, s, _input: (y, curry(f)(s[y])(incr_by)) -def add(y: str, incr_by: int): +def add(y: str, incr_by): return apply(lambda a, b: a + b, y, incr_by) def increment_state_by_int(y: str, incr_by: int): @@ -85,15 +90,22 @@ def time_model(y, substeps, time_delta, ts_format='%Y-%m-%d %H:%M:%S'): # multi_cond_opp = lambda a, b: a and b # return proc_trigger2(y, f, multi_conditions, multi_cond_opp) +# +# @curried -def env_trigger(trigger_field, trigger_val, input, funct_list): - y, x = input - if trigger_field == trigger_val: - i = 0 - for g in funct_list: - x = g(x) - return y, x + +# print(env_trigger(3).__module__) +# pp.pprint(dir(env_trigger)) + + + +# @curried +# def env_proc_trigger(trigger_time, update_f, time): +# if time == trigger_time: +# return update_f +# else: +# return lambda x: x diff --git a/dist/cadCAD-0.2-py3-none-any.whl b/dist/cadCAD-0.2.2-py3-none-any.whl similarity index 64% rename from dist/cadCAD-0.2-py3-none-any.whl rename to dist/cadCAD-0.2.2-py3-none-any.whl index ccb0606b0f0cdd7bec143ee2f6c9aa93fdd7a47a..c460fad49fc47abda0b3754f25fd6d29afbc7f83 100644 GIT binary patch delta 3964 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 delta 3934 zcmZvfcQ71k)W%nfda-(0WpzonTV>EIZ_TJj0syq|pi@}D z$SDi2gn1e2>-G*c-?0W-m%%V;D`*ZKzO~Lh%5ysys8D3DYNOHmUi-Ys;gVunjjZnC zoUY}pC{O*%Paj=v3bN}b#;%{9`f)C>r*@CsPg;ccQ?v=qHNE%pd$x#U4}_g zirBnlsdp1NvmX5nU24|zX=}l}twBa;AuAm>HLV0zu^dS`m3_g~C+=m;@8aJn3DoxM zkXH?zFU*~I% z_WE8DvX;9OE0KOE7b1q33!CyFFX5$ zmb~Sv1GiJZpq^=~qnS`Ff(fFOzeR(&7Q`M056H-$^GHEE(rR}*n1aZ*?p2mHdOv?> ze#5R~F_|LS@-a{3>jS4zhC}yea^&}!U`w&27*J}GR)*~THxIpa6RqMq8zt^W|}dYqe7!yKiH}ooo|@VP$E-Z`?aZ z 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 0: + return 1 + else: + return 0 + +robot_strategies = [greedy_robot,fair_robot, giving_robot] + +for node in G.nodes: + nstrats = len(robot_strategies) + rv = np.random.randint(0,nstrats) + G.nodes[node]['strat'] = robot_strategies[rv] + +for e in G.edges: + owner_node = e[0] + G.edges[e]['strat'] = G.nodes[owner_node]['strat'] + +default_policy = {'nodes': [], 'edges': {}, 'quantity': {}, 'node_strats': {}, 'edge_strats': {}, 'delta': {}} +class robot(object): + def __init__(self, graph, balls, internal_policy=default_policy): + self.mem_id = str(hex(id(self))) + self.internal_policy = internal_policy + self.graph = graph + self.balls = balls + + + def robotic_network(self, graph, balls): # move balls + self.graph, self.balls = graph, balls + delta_balls = {} + for e in self.graph.edges: + src = e[0] + src_balls = self.balls[src] + dst = e[1] + dst_balls = self.balls[dst] + + # transfer balls according to specific robot strat + strat = self.graph.edges[e]['strat'] + delta_balls[e] = strat(src_balls, dst_balls) + + self.internal_policy = {'nodes': [], 'edges': {}, 'quantity': {}, 'node_strats': {}, 'edge_strats': {}, 'delta': delta_balls} + return self + + def agent_arrival(self, graph, balls): # add node + self.graph, self.balls = graph, balls + node = len(self.graph.nodes) + edge_list = self.graph.edges + + # choose a m random edges without replacement + # new = np.random.choose(edgelist,m) + new = [0, 1] # tester + + nodes = [node] + edges = [(node, new_node) for new_node in new] + + initial_balls = {node: np.random.randint(1, 25)} + + rv = np.random.randint(0, nstrats) + node_strat = {node: robot_strategies[rv]} + + edge_strats = {e: robot_strategies[rv] for e in edges} + + self.internal_policy = {'nodes': nodes, + 'edges': edges, + 'quantity': initial_balls, + 'node_strats': node_strat, + 'edge_strats': edge_strats, + 'delta': np.zeros(node + 1) + } + + return self + +robot_udo = UDO(udo=robot(G, balls), masked_members=['obj']) +initial_conditions = {'balls': balls, 'network': G, 'robot': robot_udo} def update_balls(params, step, sL, s, _input): - delta_balls = _input['delta'] + delta_balls = _input['robot'].internal_policy['delta'] new_balls = s['balls'] for e in G.edges: move_ball = delta_balls[e] @@ -42,20 +130,20 @@ def update_balls(params, step, sL, s, _input): def update_network(params, step, sL, s, _input): - new_nodes = _input['nodes'] - new_edges = _input['edges'] - new_balls = _input['quantity'] + new_nodes = _input['robot'].internal_policy['nodes'] + new_edges = _input['robot'].internal_policy['edges'] + new_balls = _input['robot'].internal_policy['quantity'] graph = s['network'] for node in new_nodes: graph.add_node(node) graph.nodes[node]['initial_balls'] = new_balls[node] - graph.nodes[node]['strat'] = _input['node_strats'][node] + graph.nodes[node]['strat'] = _input['robot'].internal_policy['node_strats'][node] for edge in new_edges: graph.add_edge(edge[0], edge[1]) - graph.edges[edge]['strat'] = _input['edge_strats'][edge] + graph.edges[edge]['strat'] = _input['robot'].internal_policy['edge_strats'][edge] key = 'network' value = graph @@ -63,8 +151,8 @@ def update_network(params, step, sL, s, _input): def update_network_balls(params, step, sL, s, _input): - new_nodes = _input['nodes'] - new_balls = _input['quantity'] + new_nodes = _input['robot'].internal_policy['nodes'] + new_balls = _input['robot'].internal_policy['quantity'] balls = np.zeros(len(s['balls']) + len(new_nodes)) for node in s['network'].nodes: @@ -79,91 +167,17 @@ def update_network_balls(params, step, sL, s, _input): return (key, value) -def greedy_robot(src_balls, dst_balls): - # robot wishes to accumlate balls at its source - # takes half of its neighbors balls - if src_balls < dst_balls: - delta = -np.floor(dst_balls / 2) - else: - delta = 0 - - return delta - - -def fair_robot(src_balls, dst_balls): - # robot follows the simple balancing rule - delta = np.sign(src_balls - dst_balls) - - return delta - - -def giving_robot(src_balls, dst_balls): - # robot wishes to gice away balls one at a time - if src_balls > 0: - delta = 1 - else: - delta = 0 - - return delta - -robot_strategies = [greedy_robot,fair_robot, giving_robot] - -for node in G.nodes: - nstrats = len(robot_strategies) - rv = np.random.randint(0,nstrats) - G.nodes[node]['strat'] = robot_strategies[rv] - -for e in G.edges: - owner_node = e[0] - G.edges[e]['strat'] = G.nodes[owner_node]['strat'] - - def robotic_network(params, step, sL, s): - graph = s['network'] - - delta_balls = {} - for e in graph.edges: - src = e[0] - src_balls = s['balls'][src] - dst = e[1] - dst_balls = s['balls'][dst] - - # transfer balls according to specific robot strat - strat = graph.edges[e]['strat'] - delta_balls[e] = strat(src_balls, dst_balls) - - return_dict = {'nodes': [], 'edges': {}, 'quantity': {}, 'node_strats': {}, 'edge_strats': {}, 'delta': delta_balls} - - return (return_dict) + s['robot'].robotic_network(s['network'], s['balls']) + return {'robot': udoPipe(s['robot'])} def agent_arrival(params, step, sL, s): - node = len(s['network'].nodes) - edge_list = s['network'].edges - - # choose a m random edges without replacement - # new = np.random.choose(edgelist,m) - new = [0, 1] # tester - - nodes = [node] - edges = [(node, new_node) for new_node in new] - - initial_balls = {node: np.random.randint(1, 25)} - - rv = np.random.randint(0, nstrats) - node_strat = {node: robot_strategies[rv]} - - edge_strats = {e: robot_strategies[rv] for e in edges} - - return_dict = {'nodes': nodes, - 'edges': edges, - 'quantity': initial_balls, - 'node_strats': node_strat, - 'edge_strats': edge_strats, - 'delta': np.zeros(node + 1) - } - return (return_dict) + s['robot'].agent_arrival(s['network'], s['balls']) + return {'robot': udoPipe(s['robot'])} +def get_robot(params, step, sL, s, _input): + return 'robot', _input['robot'] partial_state_update_blocks = [ { @@ -172,7 +186,8 @@ partial_state_update_blocks = [ 'p1': robotic_network }, 'variables': { # The following state variables will be updated simultaneously - 'balls': update_balls + 'balls': update_balls, + 'robot': get_robot } }, @@ -183,7 +198,8 @@ partial_state_update_blocks = [ }, 'variables': { # The following state variables will be updated simultaneously 'network': update_network, - 'balls': update_network_balls + 'balls': update_network_balls, + 'robot': get_robot } } ] diff --git a/simulations/validation/marbles2_run.py b/simulations/tutorials/marbles2_run.py similarity index 89% rename from simulations/validation/marbles2_run.py rename to simulations/tutorials/marbles2_run.py index b275b76..b004696 100644 --- a/simulations/validation/marbles2_run.py +++ b/simulations/tutorials/marbles2_run.py @@ -2,7 +2,6 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import marbles2 from cadCAD import configs exec_mode = ExecutionMode() @@ -20,6 +19,7 @@ print("Tensor Field: config1") print(tabulate(tensor_field, headers='keys', tablefmt='psql')) print("Output:") print(tabulate(result, headers='keys', tablefmt='psql')) +print(result[['network']]) print() print(result[['network', 'substep']]) \ No newline at end of file diff --git a/simulations/validation/robot-marbles-agents-advanced-udo.ipynb b/simulations/tutorials/robot-marbles-agents-advanced.ipynb similarity index 100% rename from simulations/validation/robot-marbles-agents-advanced-udo.ipynb rename to simulations/tutorials/robot-marbles-agents-advanced.ipynb diff --git a/simulations/udo_run.py b/simulations/udo_run.py index bbb07ba..319969d 100644 --- a/simulations/udo_run.py +++ b/simulations/udo_run.py @@ -2,10 +2,9 @@ import pandas as pd from tabulate import tabulate # The following imports NEED to be in the exact order from cadCAD.engine import ExecutionMode, ExecutionContext, Executor -from simulations.validation import udo +from simulations.regression_tests import udo_inter_substep_update from cadCAD import configs - exec_mode = ExecutionMode() print("Simulation Execution: Single Configuration") diff --git a/simulations/udo_test.py b/simulations/udo_test.py index b31713b..6bb9333 100644 --- a/simulations/udo_test.py +++ b/simulations/udo_test.py @@ -5,7 +5,6 @@ from cadCAD.engine import ExecutionMode, ExecutionContext, Executor from simulations.regression_tests import udo from cadCAD import configs - exec_mode = ExecutionMode() print("Simulation Execution: Single Configuration") diff --git a/simulations/validation/historical_state_access.py b/simulations/validation/historical_state_access.py index 05e293d..d4a86ed 100644 --- a/simulations/validation/historical_state_access.py +++ b/simulations/validation/historical_state_access.py @@ -1,25 +1,25 @@ from cadCAD.configuration import append_configs from cadCAD.configuration.utils import config_sim +# Policies per Mechanism +# def p(_g, substep, sH, s): +# return {'last_update_block': sH[-1]} + +# def policies(_g, substep, sH, s, _input): +# y = 'policies' +# x = _input +# return (y, x) + +# policies = {"p1": p, "p2": p} + + # last_partial_state_update_block def last_update_block(_g, substep, sH, s, _input): return 'sh', sH[-1] - -# Policies per Mechanism -def p(_g, substep, sH, s): - return {'last_update_block': sH[-1]} - def add(y, x): return lambda _g, substep, sH, s, _input: (y, s[y] + x) -def policies(_g, substep, sH, s, _input): - y = 'policies' - x = _input - return (y, x) - -policies = {"p1": p, "p2": p} - genesis_states = { 's': 0, 'sh': [{}], # {[], {}} @@ -34,7 +34,7 @@ variables = { PSUB = { - "policies": policies, + "policies": {}, #policies, "variables": variables } @@ -58,6 +58,5 @@ append_configs( seeds={}, raw_exogenous_states={}, env_processes={}, - partial_state_update_blocks=partial_state_update_block, - policy_ops=[lambda a, b: a + b] + partial_state_update_blocks=partial_state_update_block )