included execution in readme
This commit is contained in:
parent
715e6f9a74
commit
176593ae0f
109
README.md
109
README.md
|
|
@ -59,90 +59,95 @@ Examples:
|
|||
|
||||
**3. Import cadCAD & Run Simulations:**
|
||||
|
||||
Examples: `/simulations/*.py` or `/simulations/*.ipynb`
|
||||
|
||||
Single Simulation: `/simulations/single_config_run.py`
|
||||
```python
|
||||
from tabulate import tabulate
|
||||
# The following imports NEED to be in the exact order
|
||||
from cadCAD.engine import ExecutionMode, ExecutionContext, Executor
|
||||
from simulations.validation import config1
|
||||
from cadCAD import configs
|
||||
|
||||
exec_mode = ExecutionMode()
|
||||
|
||||
print("Simulation Execution: Single Configuration")
|
||||
print()
|
||||
first_config = configs # only contains config1
|
||||
single_proc_ctx = ExecutionContext(context=exec_mode.single_proc)
|
||||
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: config1")
|
||||
print(tabulate(tensor_field, headers='keys', tablefmt='psql'))
|
||||
print("Output:")
|
||||
print(tabulate(result, headers='keys', tablefmt='psql'))
|
||||
print()
|
||||
```
|
||||
|
||||
Parameter Sweep Simulation (Concurrent): `/simulations/param_sweep_run.py`
|
||||
##### Single Process Execution:
|
||||
Example [System Model Configurations](link):
|
||||
* [System Model A](link): `/documentation/examples/sys_model_A.py`
|
||||
* [System Model B](link): `/documentation/examples/sys_model_B.py`
|
||||
Execution Examples:
|
||||
* [System Model A](link): `/documentation/examples/sys_model_A_exec.py`
|
||||
* [System Model B](link): `/documentation/examples/sys_model_B_exec.py`
|
||||
```python
|
||||
import pandas as pd
|
||||
from tabulate import tabulate
|
||||
# The following imports NEED to be in the exact order
|
||||
from cadCAD.engine import ExecutionMode, ExecutionContext, Executor
|
||||
from simulations.validation import sweep_config
|
||||
from documentation.examples import sys_model_A
|
||||
from cadCAD import configs
|
||||
|
||||
exec_mode = ExecutionMode()
|
||||
|
||||
print("Simulation Execution: Concurrent Execution")
|
||||
# Single Process Execution using a Single System Model Configuration:
|
||||
# sys_model_A
|
||||
sys_model_A = [configs[0]] # sys_model_A
|
||||
single_proc_ctx = ExecutionContext(context=exec_mode.single_proc)
|
||||
sys_model_A_simulation = Executor(exec_context=single_proc_ctx, configs=sys_model_A)
|
||||
|
||||
sys_model_A_raw_result, sys_model_A_tensor_field = sys_model_A_simulation.execute()
|
||||
sys_model_A_result = pd.DataFrame(sys_model_A_raw_result)
|
||||
print()
|
||||
print("Tensor Field: sys_model_A")
|
||||
print(tabulate(sys_model_A_tensor_field, headers='keys', tablefmt='psql'))
|
||||
print("Result: System Events DataFrame")
|
||||
print(tabulate(sys_model_A_result, headers='keys', tablefmt='psql'))
|
||||
print()
|
||||
```
|
||||
|
||||
### Multiple Simulations (Concurrent):
|
||||
##### Multiple Simulation Execution (Multi Process Execution)
|
||||
Documentation: [Simulation Execution](link)
|
||||
Example [System Model Configurations](link):
|
||||
* [System Model A](link): `/documentation/examples/sys_model_A.py`
|
||||
* [System Model B](link): `/documentation/examples/sys_model_B.py`
|
||||
[Execution Example:](link) `/documentation/examples/sys_model_AB_exec.py`
|
||||
```python
|
||||
import pandas as pd
|
||||
from tabulate import tabulate
|
||||
from cadCAD.engine import ExecutionMode, ExecutionContext, Executor
|
||||
from documentation.examples import sys_model_A, sys_model_B
|
||||
from cadCAD import configs
|
||||
|
||||
exec_mode = ExecutionMode()
|
||||
|
||||
# # Multiple Processes Execution using Multiple System Model Configurations:
|
||||
# # sys_model_A & sys_model_B
|
||||
multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc)
|
||||
run2 = Executor(exec_context=multi_proc_ctx, configs=configs)
|
||||
sys_model_AB_simulation = Executor(exec_context=multi_proc_ctx, configs=configs)
|
||||
|
||||
i = 0
|
||||
config_names = ['sweep_config_A', 'sweep_config_B']
|
||||
for raw_result, tensor_field in run2.main():
|
||||
result = pd.DataFrame(raw_result)
|
||||
config_names = ['sys_model_A', 'sys_model_B']
|
||||
for sys_model_AB_raw_result, sys_model_AB_tensor_field in sys_model_AB_simulation.execute():
|
||||
sys_model_AB_result = pd.DataFrame(sys_model_AB_raw_result)
|
||||
print()
|
||||
print("Tensor Field: " + config_names[i])
|
||||
print(tabulate(tensor_field, headers='keys', tablefmt='psql'))
|
||||
print("Output:")
|
||||
print(tabulate(result, headers='keys', tablefmt='psql'))
|
||||
print(f"Tensor Field: {config_names[i]}")
|
||||
print(tabulate(sys_model_AB_tensor_field, headers='keys', tablefmt='psql'))
|
||||
print("Result: System Events DataFrame:")
|
||||
print(tabulate(sys_model_AB_result, headers='keys', tablefmt='psql'))
|
||||
print()
|
||||
i += 1
|
||||
```
|
||||
|
||||
Multiple Simulations (Concurrent): `/simulations/multi_config run.py`
|
||||
### Parameter Sweep Simulation (Concurrent):
|
||||
Documentation: [System Model Parameter Sweep](link)
|
||||
[Example:](link) `/documentation/examples/param_sweep.py`
|
||||
```python
|
||||
import pandas as pd
|
||||
from tabulate import tabulate
|
||||
# The following imports NEED to be in the exact order
|
||||
from cadCAD.engine import ExecutionMode, ExecutionContext, Executor
|
||||
from simulations.validation import config1, config2
|
||||
from documentation.examples import param_sweep
|
||||
from cadCAD import configs
|
||||
|
||||
exec_mode = ExecutionMode()
|
||||
|
||||
print("Simulation Execution: Concurrent Execution")
|
||||
multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc)
|
||||
run2 = Executor(exec_context=multi_proc_ctx, configs=configs)
|
||||
run = Executor(exec_context=multi_proc_ctx, configs=configs)
|
||||
|
||||
i = 0
|
||||
config_names = ['config1', 'config2']
|
||||
for raw_result, tensor_field in run2.main():
|
||||
for raw_result, tensor_field in run.execute():
|
||||
result = pd.DataFrame(raw_result)
|
||||
print()
|
||||
print("Tensor Field: " + config_names[i])
|
||||
print("Tensor Field:")
|
||||
print(tabulate(tensor_field, headers='keys', tablefmt='psql'))
|
||||
print("Output:")
|
||||
print(tabulate(result, headers='keys', tablefmt='psql'))
|
||||
print()
|
||||
i =+ 1
|
||||
```
|
||||
|
||||
The above can be run in Jupyter.
|
||||
```bash
|
||||
jupyter notebook
|
||||
```
|
||||
|
|
|
|||
|
|
@ -125,41 +125,6 @@ class Executor:
|
|||
for f in state_funcs:
|
||||
yield self.state_update_exception(f(sweep_dict, sub_step, sH, last_in_obj, _input))
|
||||
|
||||
# def generate_record(state_funcs):
|
||||
# for f in state_funcs:
|
||||
# tmp_last_in_copy = deepcopy(last_in_obj)
|
||||
# new_kv = self.state_update_exception(f(sweep_dict, sub_step, sH, tmp_last_in_copy, _input))
|
||||
# del tmp_last_in_copy
|
||||
# yield new_kv
|
||||
#
|
||||
# # get `state` from last_in_obj.keys()
|
||||
# # vals = last_in_obj.values()
|
||||
# def generate_record(state_funcs):
|
||||
# for state, v, f in zip(states, vals, state_funcs):
|
||||
# v_copy = deepcopy(v)
|
||||
# last_in_obj[state] = v_copy
|
||||
# new_kv = self.state_update_exception(f(sweep_dict, sub_step, sH, last_in_copy, _input))
|
||||
# del v
|
||||
# yield new_kv
|
||||
|
||||
# {k: v for k, v in l}
|
||||
|
||||
# r() - r(a') -> r(a',b') -> r(a',b',c')
|
||||
|
||||
# r(f(a),b,c) -> r(a'f(b),c) -> r(a',b',f(c)) => r(a',b',c')
|
||||
# r(a',b.update(),c)
|
||||
# r1(f(a1),b1,c1) -> r2(a2,f(b1),c1) -> r3(a3,b1,f(c1)) => r(a',b',c')
|
||||
|
||||
# r1(f(a1),b,c) -> r2(a,f(b1),c) -> r3(a,b,f(c1)) => r(a',b',c')
|
||||
|
||||
# r1(f(a1),b1,c1) -> r(a2',b2.update(),c2) -> r3(a3,b1,f(c1)) => r(a',b',c')
|
||||
|
||||
|
||||
# r1(f(a1),b1,c1) -> r2(a2,f(b1),c1) -> r3(a3,b1,f(c1)) => r(a',b',c')
|
||||
|
||||
|
||||
# reduce(lambda r: F(r), [r2(f(a),b,c), r2(a,f(b),c), r3(a,b,f(c))]) => R(a',b',c')
|
||||
|
||||
def transfer_missing_fields(source, destination):
|
||||
for k in source:
|
||||
if k not in destination:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,160 @@
|
|||
Simulation Execution
|
||||
==
|
||||
System Simulations are executed with the execution engine executor (`cadCAD.engine.Executor`) given System Model
|
||||
Configurations. There are multiple simulation Execution Modes and Execution Contexts.
|
||||
|
||||
### Steps:
|
||||
1. #### *Choose Execution Mode*:
|
||||
* ##### Simulation Execution Modes:
|
||||
`cadCAD` executes a process per System Model Configuration and a thread per System Simulation.
|
||||
##### Class: `cadCAD.engine.ExecutionMode`
|
||||
##### Attributes:
|
||||
* **Single Process:** A single process Execution Mode for a single System Model Configuration (Example:
|
||||
`cadCAD.engine.ExecutionMode().single_proc`).
|
||||
* **Multi-Process:** Multiple process Execution Mode for System Model Simulations which executes on a thread per
|
||||
given System Model Configuration (Example: `cadCAD.engine.ExecutionMode().multi_proc`).
|
||||
2. #### *Create Execution Context using Execution Mode:*
|
||||
```python
|
||||
from cadCAD.engine import ExecutionMode, ExecutionContext
|
||||
exec_mode = ExecutionMode()
|
||||
single_proc_ctx = ExecutionContext(context=exec_mode.single_proc)
|
||||
```
|
||||
3. #### *Create Simulation Executor*
|
||||
```python
|
||||
from cadCAD.engine import Executor
|
||||
from cadCAD import configs
|
||||
simulation = Executor(exec_context=single_proc_ctx, configs=configs)
|
||||
```
|
||||
4. #### *Execute Simulation: Produce System Event Dataset*
|
||||
A Simulation execution produces a System Event Dataset and the Tensor Field applied to initial states used to create it.
|
||||
```python
|
||||
import pandas as pd
|
||||
raw_system_events, tensor_field = simulation.execute()
|
||||
|
||||
# Simulation Result Types:
|
||||
# raw_system_events: List[dict]
|
||||
# tensor_field: pd.DataFrame
|
||||
|
||||
# Result System Events DataFrame
|
||||
simulation_result = pd.DataFrame(raw_system_events)
|
||||
```
|
||||
|
||||
##### Example Tensor Field
|
||||
```
|
||||
+----+-----+--------------------------------+--------------------------------+
|
||||
| | m | b1 | s1 |
|
||||
|----+-----+--------------------------------+--------------------------------|
|
||||
| 0 | 1 | <function p1m1 at 0x10c458ea0> | <function s1m1 at 0x10c464510> |
|
||||
| 1 | 2 | <function p1m2 at 0x10c464048> | <function s1m2 at 0x10c464620> |
|
||||
| 2 | 3 | <function p1m3 at 0x10c464400> | <function s1m3 at 0x10c464730> |
|
||||
+----+-----+--------------------------------+--------------------------------+
|
||||
```
|
||||
|
||||
##### Example Result: System Events DataFrame
|
||||
```python
|
||||
+----+-------+------------+-----------+------+-----------+
|
||||
| | run | timestep | substep | s1 | s2 |
|
||||
|----+-------+------------+-----------+------+-----------|
|
||||
| 0 | 1 | 0 | 0 | 0 | 0.0 |
|
||||
| 1 | 1 | 1 | 1 | 1 | 4 |
|
||||
| 2 | 1 | 1 | 2 | 2 | 6 |
|
||||
| 3 | 1 | 1 | 3 | 3 | [ 30 300] |
|
||||
| 4 | 2 | 0 | 0 | 0 | 0.0 |
|
||||
| 5 | 2 | 1 | 1 | 1 | 4 |
|
||||
| 6 | 2 | 1 | 2 | 2 | 6 |
|
||||
| 7 | 2 | 1 | 3 | 3 | [ 30 300] |
|
||||
+----+-------+------------+-----------+------+-----------+
|
||||
```
|
||||
|
||||
### Execution Examples:
|
||||
##### Single Simulation Execution (Single Process Execution)
|
||||
Example [System Model Configurations](link):
|
||||
* [System Model A](link): `/documentation/examples/sys_model_A.py`
|
||||
* [System Model B](link): `/documentation/examples/sys_model_B.py`
|
||||
Execution Examples:
|
||||
* [System Model A](link): `/documentation/examples/sys_model_A_exec.py`
|
||||
* [System Model B](link): `/documentation/examples/sys_model_B_exec.py`
|
||||
```python
|
||||
import pandas as pd
|
||||
from tabulate import tabulate
|
||||
from cadCAD.engine import ExecutionMode, ExecutionContext, Executor
|
||||
from documentation.examples import sys_model_A
|
||||
from cadCAD import configs
|
||||
|
||||
exec_mode = ExecutionMode()
|
||||
|
||||
# Single Process Execution using a Single System Model Configuration:
|
||||
# sys_model_A
|
||||
sys_model_A = [configs[0]] # sys_model_A
|
||||
single_proc_ctx = ExecutionContext(context=exec_mode.single_proc)
|
||||
sys_model_A_simulation = Executor(exec_context=single_proc_ctx, configs=sys_model_A)
|
||||
|
||||
sys_model_A_raw_result, sys_model_A_tensor_field = sys_model_A_simulation.execute()
|
||||
sys_model_A_result = pd.DataFrame(sys_model_A_raw_result)
|
||||
print()
|
||||
print("Tensor Field: sys_model_A")
|
||||
print(tabulate(sys_model_A_tensor_field, headers='keys', tablefmt='psql'))
|
||||
print("Result: System Events DataFrame")
|
||||
print(tabulate(sys_model_A_result, headers='keys', tablefmt='psql'))
|
||||
print()
|
||||
```
|
||||
|
||||
##### Multiple Simulation Execution
|
||||
|
||||
* ##### *Multi Process Execution*
|
||||
Documentation: [Simulation Execution](link)
|
||||
[Execution Example:](link) `/documentation/examples/sys_model_AB_exec.py`
|
||||
Example [System Model Configurations](link):
|
||||
* [System Model A](link): `/documentation/examples/sys_model_A.py`
|
||||
* [System Model B](link): `/documentation/examples/sys_model_B.py`
|
||||
```python
|
||||
import pandas as pd
|
||||
from tabulate import tabulate
|
||||
from cadCAD.engine import ExecutionMode, ExecutionContext, Executor
|
||||
from documentation.examples import sys_model_A, sys_model_B
|
||||
from cadCAD import configs
|
||||
|
||||
exec_mode = ExecutionMode()
|
||||
|
||||
# # Multiple Processes Execution using Multiple System Model Configurations:
|
||||
# # sys_model_A & sys_model_B
|
||||
multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc)
|
||||
sys_model_AB_simulation = Executor(exec_context=multi_proc_ctx, configs=configs)
|
||||
|
||||
i = 0
|
||||
config_names = ['sys_model_A', 'sys_model_B']
|
||||
for sys_model_AB_raw_result, sys_model_AB_tensor_field in sys_model_AB_simulation.execute():
|
||||
sys_model_AB_result = pd.DataFrame(sys_model_AB_raw_result)
|
||||
print()
|
||||
print(f"Tensor Field: {config_names[i]}")
|
||||
print(tabulate(sys_model_AB_tensor_field, headers='keys', tablefmt='psql'))
|
||||
print("Result: System Events DataFrame:")
|
||||
print(tabulate(sys_model_AB_result, headers='keys', tablefmt='psql'))
|
||||
print()
|
||||
i += 1
|
||||
```
|
||||
|
||||
* ##### *Parameter Sweep*
|
||||
Documentation: [System Model Parameter Sweep](link)
|
||||
[Example:](link) `/documentation/examples/param_sweep.py`
|
||||
```python
|
||||
import pandas as pd
|
||||
from tabulate import tabulate
|
||||
# The following imports NEED to be in the exact order
|
||||
from cadCAD.engine import ExecutionMode, ExecutionContext, Executor
|
||||
from documentation.examples import param_sweep
|
||||
from cadCAD import configs
|
||||
|
||||
exec_mode = ExecutionMode()
|
||||
multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc)
|
||||
run = Executor(exec_context=multi_proc_ctx, configs=configs)
|
||||
|
||||
for raw_result, tensor_field in run.execute():
|
||||
result = pd.DataFrame(raw_result)
|
||||
print()
|
||||
print("Tensor Field:")
|
||||
print(tabulate(tensor_field, headers='keys', tablefmt='psql'))
|
||||
print("Output:")
|
||||
print(tabulate(result, headers='keys', tablefmt='psql'))
|
||||
print()
|
||||
```
|
||||
|
|
@ -1,24 +1,21 @@
|
|||
Historical State Access
|
||||
==
|
||||
The 3rd parameter of state and policy update functions (labels as `sH` of type `List[List[dict]]`) provides access to
|
||||
past Partial State Updates (PSU) given a negative offset number. `access_block` is used to access past PSUs
|
||||
(`List[dict]`) from `sH`.
|
||||
#### Motivation
|
||||
The current state (values of state variables) is accessed through the `s` list. When the user requires previous state variable values, they may be accessed through the state history list, `sH`. Accessing the state history should be implemented without creating unintended feedback loops on the current state.
|
||||
|
||||
Example: `-2` denotes to second to last PSU
|
||||
The 3rd parameter of state and policy update functions (labeled as `sH` of type `List[List[dict]]`) provides access to past Partial State Update Block (PSUB) given a negative offset number. `access_block` is used to access past PSUBs (`List[dict]`) from `sH`. For example, an offset of `-2` denotes the second to last PSUB.
|
||||
|
||||
##### Exclusion List
|
||||
#### Exclusion List
|
||||
Create a list of states to exclude from the reported PSU.
|
||||
```python
|
||||
exclusion_list = [
|
||||
'nonexsistant', 'last_x', '2nd_to_last_x', '3rd_to_last_x', '4th_to_last_x'
|
||||
'nonexistent', 'last_x', '2nd_to_last_x', '3rd_to_last_x', '4th_to_last_x'
|
||||
]
|
||||
```
|
||||
##### Example Policy Updates
|
||||
###### Last partial state update
|
||||
```python
|
||||
from cadCAD.configuration.utils import config_sim, access_block
|
||||
|
||||
def last_update(_g, substep, sH, s):
|
||||
def last_update(_params, substep, sH, s):
|
||||
return {"last_x": access_block(
|
||||
state_history=sH,
|
||||
target_field="last_x", # Add a field to the exclusion list
|
||||
|
|
@ -27,30 +24,30 @@ def last_update(_g, substep, sH, s):
|
|||
)
|
||||
}
|
||||
```
|
||||
* Note: Although `target_field` adding a field to the exclusion may seem redundant, it is useful in the case of
|
||||
the exclusion list being empty while the `target_field` is assigned to a state or a policy key.
|
||||
* Note: Although `target_field` adding a field to the exclusion may seem redundant, it is useful in the case of the exclusion list being empty while the `target_field` is assigned to a state or a policy key.
|
||||
##### Define State Updates
|
||||
###### 2nd to last partial state update
|
||||
```python
|
||||
def second2last_update(_g, substep, sH, s):
|
||||
def second2last_update(_params, substep, sH, s):
|
||||
return {"2nd_to_last_x": access_block(sH, "2nd_to_last_x", -2, exclusion_list)}
|
||||
```
|
||||
|
||||
##### Define State Updates
|
||||
|
||||
###### 3rd to last partial state update
|
||||
```python
|
||||
def third_to_last_x(_g, substep, sH, s, _input):
|
||||
def third_to_last_x(_params, substep, sH, s, _input):
|
||||
return '3rd_to_last_x', access_block(sH, "3rd_to_last_x", -3, exclusion_list)
|
||||
```
|
||||
###### 4rd to last partial state update
|
||||
```python
|
||||
def fourth_to_last_x(_g, substep, sH, s, _input):
|
||||
def fourth_to_last_x(_params, substep, sH, s, _input):
|
||||
return '4th_to_last_x', access_block(sH, "4th_to_last_x", -4, exclusion_list)
|
||||
```
|
||||
###### Non-exsistant partial state update
|
||||
* `psu_block_offset >= 0` doesn't exsist
|
||||
###### Non-exsistent partial state update
|
||||
* `psu_block_offset >= 0` doesn't exist
|
||||
```python
|
||||
def nonexsistant(_g, substep, sH, s, _input):
|
||||
return 'nonexsistant', access_block(sH, "nonexsistant", 0, exclusion_list)
|
||||
def nonexistent(_params, substep, sH, s, _input):
|
||||
return 'nonexistent', access_block(sH, "nonexistent", 0, exclusion_list)
|
||||
```
|
||||
|
||||
#### Example Simulation
|
||||
|
|
@ -91,7 +88,4 @@ Example: `last_x`
|
|||
| 8 | [{'x': 4, 'run': 1, 'substep': 1, 'timestep': 2}, {'x': 5, 'run': 1, 'substep': 2, 'timestep': 2}, {'x': 6, 'run': 1, 'substep': 3, 'timestep': 2}] |
|
||||
| 9 | [{'x': 4, 'run': 1, 'substep': 1, 'timestep': 2}, {'x': 5, 'run': 1, 'substep': 2, 'timestep': 2}, {'x': 6, 'run': 1, 'substep': 3, 'timestep': 2}] |
|
||||
+----+-----------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
#### [Example Configuration](link)
|
||||
#### [Example Results](link)
|
||||
```
|
||||
|
|
@ -15,9 +15,9 @@ policy_ops=[add, mult_by_2]
|
|||
|
||||
##### Example Policy Updates per Partial State Update (PSU)
|
||||
```python
|
||||
def p1_psu1(_g, step, sL, s):
|
||||
def p1_psu1(_params, step, sH, s):
|
||||
return {'policy1': 1}
|
||||
def p2_psu1(_g, step, sL, s):
|
||||
def p2_psu1(_params, step, sH, s):
|
||||
return {'policy2': 2}
|
||||
```
|
||||
* `add` not applicable due to lack of redundant policies
|
||||
|
|
@ -25,9 +25,9 @@ def p2_psu1(_g, step, sL, s):
|
|||
* Result: `{'policy1': 2, 'policy2': 4}`
|
||||
|
||||
```python
|
||||
def p1_psu2(_g, step, sL, s):
|
||||
def p1_psu2(_params, step, sH, s):
|
||||
return {'policy1': 2, 'policy2': 2}
|
||||
def p2_psu2(_g, step, sL, s):
|
||||
def p2_psu2(_params, step, sH, s):
|
||||
return {'policy1': 2, 'policy2': 2}
|
||||
```
|
||||
* `add` applicable due to redundant policies
|
||||
|
|
@ -35,9 +35,9 @@ def p2_psu2(_g, step, sL, s):
|
|||
* Result: `{'policy1': 8, 'policy2': 8}`
|
||||
|
||||
```python
|
||||
def p1_psu3(_g, step, sL, s):
|
||||
def p1_psu3(_params, step, sH, s):
|
||||
return {'policy1': 1, 'policy2': 2, 'policy3': 3}
|
||||
def p2_psu3(_g, step, sL, s):
|
||||
def p2_psu3(_params, step, sH, s):
|
||||
return {'policy1': 1, 'policy2': 2, 'policy3': 3}
|
||||
```
|
||||
* `add` applicable due to redundant policies
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
Simulation Configuration
|
||||
==
|
||||
|
||||
## Introduction
|
||||
|
||||
Given a **Simulation Configuration**, cadCAD produces datasets that represent the evolution of the state of a system over [discrete time](https://en.wikipedia.org/wiki/Discrete_time_and_continuous_time#Discrete_time). The state of the system is described by a set of [State Variables](#State-Variables). The dynamic of the system is described by [Policy Functions](#Policy-Functions) and [State Update Functions](#State-Update-Functions), which are evaluated by cadCAD according to the definitions set by the user in [Partial State Update Blocks](#Partial-State-Update-Blocks).
|
||||
|
||||
A Simulation Configuration is comprised of a [System Model](#System-Model) and a set of [Simulation Properties](#Simulation-Properties)
|
||||
|
||||
`append_configs`, stores a **Simulation Configuration** to be [Executed](/JS4Q9oayQASihxHBJzz4Ug) by cadCAD
|
||||
|
||||
```python
|
||||
from cadCAD.configuration import append_configs
|
||||
|
||||
append_configs(
|
||||
initial_state = ..., # System Model
|
||||
partial_state_update_blocks = .., # System Model
|
||||
policy_ops = ..., # System Model
|
||||
sim_configs = ... # Simulation Properties
|
||||
)
|
||||
```
|
||||
Parameters:
|
||||
* **initial_state** : _dict_
|
||||
[State Variables](#State-Variables) and their initial values
|
||||
* **partial_state_update_blocks** : List[dict[dict]]
|
||||
List of [Partial State Update Blocks](#Partial-State-Update-Blocks)
|
||||
* **policy_ops** : List[functions]
|
||||
See [Policy Aggregation](/63k2ncjITuqOPCUHzK7Viw)
|
||||
* **sim_configs** : _???_
|
||||
See [System Model Parameter Sweep](/4oJ_GT6zRWW8AO3yMhFKrg)
|
||||
|
||||
## Simulation Properties
|
||||
|
||||
Simulation properties are passed to `append_configs` in the `sim_configs` parameter. To construct this paramenter, we use the `config_sim` function in `cadCAD.configuration.utils`
|
||||
|
||||
```python
|
||||
from cadCAD.configuration.utils import config_sim
|
||||
|
||||
c = config_sim({
|
||||
"N": ...,
|
||||
"T": range(...),
|
||||
"M": ...
|
||||
})
|
||||
|
||||
append_configs(
|
||||
...
|
||||
sim_configs = c # Simulation Properties
|
||||
)
|
||||
```
|
||||
|
||||
### T - Simulation Length
|
||||
Computer simulations run in discrete time:
|
||||
|
||||
>Discrete time views values of variables as occurring at distinct, separate "points in time", or equivalently as being unchanged throughout each non-zero region of time ("time period")—that is, time is viewed as a discrete variable. (...) This view of time corresponds to a digital clock that gives a fixed reading of 10:37 for a while, and then jumps to a new fixed reading of 10:38, etc. ([source: Wikipedia](https://en.wikipedia.org/wiki/Discrete_time_and_continuous_time#Discrete_time))
|
||||
|
||||
As is common in many simulation tools, in cadCAD too we refer to each discrete unit of time as a **timestep**. cadCAD increments a "time counter", and at each step it updates the state variables according to the equations that describe the system.
|
||||
|
||||
The main simulation property that the user must set when creating a Simulation Configuration is the number of timesteps in the simulation. In other words, for how long do they want to simulate the system that has been modeled.
|
||||
|
||||
### N - Number of Runs
|
||||
|
||||
cadCAD facilitates running multiple simulations of the same system sequentially, reporting the results of all those runs in a single dataset. This is especially helpful for running [Monte Carlo Simulations](https://github.com/BlockScience/cadCAD-Tutorials/blob/master/01%20Tutorials/robot-marbles-part-4/robot-marbles-part-4.ipynb).
|
||||
|
||||
### M - Parameters of the System
|
||||
|
||||
Parameters of the system, passed to the state update functions and the policy functions in the `params` parameter are defined here. See [System Model Parameter Sweep](/4oJ_GT6zRWW8AO3yMhFKrg) for more information.
|
||||
|
||||
## System Model
|
||||
The System Model describes the system that will be simulated in cadCAD. It is comprised of a set of [State Variables](#Sate-Variables) and the [State Update Functions](#State-Update-Functions) that determine the evolution of the state of the system over time. [Policy Functions](#Policy-Functions) (representations of user policies or internal system control policies) may also be part of a System Model.
|
||||
|
||||
### State Variables
|
||||
>A state variable is one of the set of variables that are used to describe the mathematical "state" of a dynamical system. Intuitively, the state of a system describes enough about the system to determine its future behaviour in the absence of any external forces affecting the system. ([source: Wikipedia](https://en.wikipedia.org/wiki/State_variable))
|
||||
|
||||
cadCAD can handle state variables of any Python data type, including custom classes. It is up to the user of cadCAD to determine the state variables needed to **sufficiently and accurately** describe the system they are interested in.
|
||||
|
||||
State Variables are passed to `append_configs` along with its initial values, as a Python `dict` where the `dict_keys` are the names of the variables and the `dict_values` are their initial values.
|
||||
|
||||
```python
|
||||
from cadCAD.configuration import append_configs
|
||||
|
||||
genesis_states = {
|
||||
'state_variable_1': 0,
|
||||
'state_variable_2': 0,
|
||||
'state_variable_3': 1.5,
|
||||
'timestamp': '2019-01-01 00:00:00'
|
||||
}
|
||||
|
||||
append_configs(
|
||||
initial_state = genesis_states,
|
||||
...
|
||||
)
|
||||
```
|
||||
### State Update Functions
|
||||
State Update Functions represent equations according to which the state variables change over time. Each state update function must return a tuple containing a string with the name of the state variable being updated and its new value. Each state update function can only modify a single state variable. The general structure of a state update function is:
|
||||
```python
|
||||
def state_update_function_A(_params, substep, sH, s, _input):
|
||||
...
|
||||
return 'state_variable_name', new_value
|
||||
```
|
||||
Parameters:
|
||||
* **_params** : _dict_
|
||||
[System parameters](/4oJ_GT6zRWW8AO3yMhFKrg)
|
||||
* **substep** : _int_
|
||||
Current [substep](#Substep)
|
||||
* **sH** : _list[list[dict_]]
|
||||
Historical values of all state variables for the simulation. See [Historical State Access](/smiyQTnATtC9xPwvF8KbBQ) for details
|
||||
* **s** : _dict_
|
||||
Current state of the system, where the `dict_keys` are the names of the state variables and the `dict_values` are their current values.
|
||||
* **_input** : _dict_
|
||||
Aggregation of the signals of all policy functions in the current [Partial State Update Block](#Partial-State-Update-Block)
|
||||
|
||||
Return:
|
||||
* _tuple_ containing a string with the name of the state variable being updated and its new value.
|
||||
|
||||
State update functions should not modify any of the parameters passed to it, as those are mutable Python objects that cadCAD relies on in order to run the simulation according to the specifications.
|
||||
|
||||
### Policy Functions
|
||||
A Policy Function computes one or more signals to be passed to [State Update Functions](#State-Update-Functions) (via the _\_input_ parameter). Read [this article](https://github.com/BlockScience/cadCAD-Tutorials/blob/master/01%20Tutorials/robot-marbles-part-2/robot-marbles-part-2.ipynb) for details on why and when to use policy functions.
|
||||
|
||||
<!-- We would then expand the tutorials with these kind of concepts
|
||||
#### Policies
|
||||
Policies consist of the potential action made available through mechanisms. The action taken is expected to be the result of a conditional determination of the past state.
|
||||
|
||||
While executed the same, the modeller can approach policies dependent on the availability of a mechanism to a population.
|
||||
|
||||
- ***Control Policy***
|
||||
When the controlling or deploying entity has the ability to act in order to affect some aspect of the system, this is a control policy.
|
||||
- ***User Policy*** model agent behaviors in reaction to state variables and exogenous variables. The resulted user action will become an input to PSUs. Note that user behaviors should not directly update value of state variables.
|
||||
The action taken, as well as the potential to act, through a mechanism is a behavior. -->
|
||||
|
||||
The general structure of a policy function is:
|
||||
```python
|
||||
def policy_function_1(_params, substep, sH, s):
|
||||
...
|
||||
return {'signal_1': value_1, ..., 'signal_N': value_N}
|
||||
```
|
||||
Parameters:
|
||||
* **_params** : _dict_
|
||||
[System parameters](/4oJ_GT6zRWW8AO3yMhFKrg)
|
||||
* **substep** : _int_
|
||||
Current [substep](#Substep)
|
||||
* **sH** : _list[list[dict_]]
|
||||
Historical values of all state variables for the simulation. See [Historical State Access](/smiyQTnATtC9xPwvF8KbBQ) for details
|
||||
* **s** : _dict_
|
||||
Current state of the system, where the `dict_keys` are the names of the state variables and the `dict_values` are their current values.
|
||||
|
||||
Return:
|
||||
* _dict_ of signals to be passed to the state update functions in the same [Partial State Update Block](#Partial-State-Update-Blocks)
|
||||
|
||||
Policy functions should not modify any of the parameters passed to it, as those are mutable Python objects that cadCAD relies on in order to run the simulation according to the specifications.
|
||||
|
||||
At each [Partial State Update Block](#Partial-State-Update-Blocks) (PSUB), the `dicts` returned by all policy functions within that PSUB dictionaries are aggregated into a single `dict` using an initial reduction function (a key-wise operation, default: `dic1['keyA'] + dic2['keyA']`) and optional subsequent map functions. The resulting aggregated `dict` is then passed as the `_input` parameter to the state update functions in that PSUB. For more information on how to modify the aggregation method, see [Policy Aggregation](/63k2ncjITuqOPCUHzK7Viw).
|
||||
|
||||
### Partial State Update Blocks
|
||||
|
||||
A **Partial State Update Block** (PSUB) is a set of State Update Functions and Policy Functions such that State Update Functions in the set are independent from each other and Policies in the set are independent from each other and from the State Update Functions in the set. In other words, if a state variable is updated in a PSUB, its new value cannnot impact the State Update Functions and Policy Functions in that PSUB - only those in the next PSUB.
|
||||
|
||||

|
||||
|
||||
Partial State Update Blocks are passed to `append_configs` as a List of Python `dicts` where the `dict_keys` are named `"policies"` and `"variables"` and the values are also Python `dicts` where the keys are the names of the policy and state update functions and the values are the functions.
|
||||
|
||||
```python
|
||||
PSUBs = [
|
||||
{
|
||||
"policies": {
|
||||
"b_1": policy_function_1,
|
||||
...
|
||||
"b_J": policy_function_J
|
||||
},
|
||||
"variables": {
|
||||
"s_1": state_update_function_1,
|
||||
...
|
||||
"s_K": state_update_function_K
|
||||
}
|
||||
}, #PSUB_1,
|
||||
{...}, #PSUB_2,
|
||||
...
|
||||
{...} #PSUB_M
|
||||
]
|
||||
|
||||
append_configs(
|
||||
...
|
||||
partial_state_update_blocks = PSUBs,
|
||||
...
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
#### Substep
|
||||
At each timestep, cadCAD iterates over the `partial_state_update_blocks` list. For each Partial State Update Block, cadCAD returns a record containing the state of the system at the end of that PSUB. We refer to that subdivision of a timestep as a `substep`.
|
||||
|
||||
## Result Dataset
|
||||
|
||||
cadCAD returns a dataset containing the evolution of the state variables defined by the user over time, with three `int` indexes:
|
||||
* `run` - id of the [run](#N-Number-of-Runs)
|
||||
* `timestep` - discrete unit of time (the total number of timesteps is defined by the user in the [T Simulation Parameter](#T-Simulation-Length))
|
||||
* `substep` - subdivision of timestep (the number of [substeps](#Substeps) is the same as the number of Partial State Update Blocks)
|
||||
|
||||
Therefore, the total number of records in the resulting dataset is `N` x `T` x `len(partial_state_update_blocks)`
|
||||
|
||||
#### [System Simulation Execution](link)
|
||||
|
|
@ -31,7 +31,7 @@ Previous State:
|
|||
`y = 0`
|
||||
|
||||
```python
|
||||
def state_update(_params, step, sL, s, _input):
|
||||
def state_update(_params, step, sH, s, _input):
|
||||
y = 'state'
|
||||
x = s['state'] + _params['alpha'] + _params['gamma']
|
||||
return y, x
|
||||
|
|
@ -43,8 +43,8 @@ def state_update(_params, step, sL, s, _input):
|
|||
##### Example Policy Updates
|
||||
```python
|
||||
# Internal States per Mechanism
|
||||
def policies(_g, step, sL, s):
|
||||
return {'beta': _g['beta'], 'gamma': _g['gamma']}
|
||||
def policies(_params, step, sH, s):
|
||||
return {'beta': _params['beta'], 'gamma': _params['gamma']}
|
||||
```
|
||||
* Simulation 1: `{'beta': 2, 'gamma': 3]}`
|
||||
* Simulation 2: `{'beta': 5, 'gamma': 4}`
|
||||
|
|
@ -53,6 +53,13 @@ def policies(_g, step, sL, s):
|
|||
```python
|
||||
from cadCAD.configuration.utils import config_sim
|
||||
|
||||
g = {
|
||||
'alpha': [1],
|
||||
'beta': [2, 5],
|
||||
'gamma': [3, 4],
|
||||
'omega': [7]
|
||||
}
|
||||
|
||||
sim_config = config_sim(
|
||||
{
|
||||
"N": 2,
|
||||
|
|
@ -64,5 +71,3 @@ sim_config = config_sim(
|
|||
|
||||
#### [Example Configuration](link)
|
||||
#### [Example Results](link)
|
||||
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ PSUB = {
|
|||
"variables": variables
|
||||
}
|
||||
|
||||
partial_state_update_block = {
|
||||
psubs = {
|
||||
"PSUB1": PSUB,
|
||||
"PSUB2": PSUB,
|
||||
"PSUB3": PSUB
|
||||
|
|
@ -91,7 +91,7 @@ sim_config = config_sim(
|
|||
append_configs(
|
||||
sim_configs=sim_config,
|
||||
initial_state=genesis_states,
|
||||
partial_state_update_blocks=partial_state_update_block
|
||||
partial_state_update_blocks=psubs
|
||||
)
|
||||
|
||||
exec_mode = ExecutionMode()
|
||||
|
|
|
|||
|
|
@ -31,31 +31,31 @@ env_process = {}
|
|||
|
||||
|
||||
# Policies
|
||||
def gamma(_params, step, sL, s):
|
||||
def gamma(_params, step, sH, s):
|
||||
return {'gamma': _params['gamma']}
|
||||
|
||||
|
||||
def omega(_params, step, sL, s):
|
||||
def omega(_params, step, sH, s):
|
||||
return {'omega': _params['omega'](7)}
|
||||
|
||||
|
||||
# Internal States
|
||||
def alpha(_params, step, sL, s, _input):
|
||||
def alpha(_params, step, sH, s, _input):
|
||||
return 'alpha', _params['alpha']
|
||||
|
||||
def alpha_plus_gamma(_params, step, sL, s, _input):
|
||||
def alpha_plus_gamma(_params, step, sH, s, _input):
|
||||
return 'alpha_plus_gamma', _params['alpha'] + _params['gamma']
|
||||
|
||||
|
||||
def beta(_params, step, sL, s, _input):
|
||||
def beta(_params, step, sH, s, _input):
|
||||
return 'beta', _params['beta']
|
||||
|
||||
|
||||
def policies(_params, step, sL, s, _input):
|
||||
def policies(_params, step, sH, s, _input):
|
||||
return 'policies', _input
|
||||
|
||||
|
||||
def sweeped(_params, step, sL, s, _input):
|
||||
def sweeped(_params, step, sH, s, _input):
|
||||
return 'sweeped', {'beta': _params['beta'], 'gamma': _params['gamma']}
|
||||
|
||||
|
||||
|
|
@ -90,7 +90,7 @@ for m in psu_steps:
|
|||
psu_block[m]['variables']['policies'] = policies
|
||||
psu_block[m]["variables"]['sweeped'] = var_timestep_trigger(y='sweeped', f=sweeped)
|
||||
|
||||
partial_state_update_blocks = psub_list(psu_block, psu_steps)
|
||||
psubs = psub_list(psu_block, psu_steps)
|
||||
print()
|
||||
pp.pprint(psu_block)
|
||||
print()
|
||||
|
|
@ -99,7 +99,7 @@ append_configs(
|
|||
sim_configs=sim_config,
|
||||
initial_state=genesis_states,
|
||||
env_processes=env_process,
|
||||
partial_state_update_blocks=partial_state_update_blocks
|
||||
partial_state_update_blocks=psubs
|
||||
)
|
||||
|
||||
exec_mode = ExecutionMode()
|
||||
|
|
|
|||
|
|
@ -7,19 +7,19 @@ from cadCAD.engine import ExecutionMode, ExecutionContext, Executor
|
|||
from cadCAD import configs
|
||||
|
||||
# Policies per Mechanism
|
||||
def p1m1(_g, step, sL, s):
|
||||
def p1m1(_g, step, sH, s):
|
||||
return {'policy1': 1}
|
||||
def p2m1(_g, step, sL, s):
|
||||
def p2m1(_g, step, sH, s):
|
||||
return {'policy2': 2}
|
||||
|
||||
def p1m2(_g, step, sL, s):
|
||||
def p1m2(_g, step, sH, s):
|
||||
return {'policy1': 2, 'policy2': 2}
|
||||
def p2m2(_g, step, sL, s):
|
||||
def p2m2(_g, step, sH, s):
|
||||
return {'policy1': 2, 'policy2': 2}
|
||||
|
||||
def p1m3(_g, step, sL, s):
|
||||
def p1m3(_g, step, sH, s):
|
||||
return {'policy1': 1, 'policy2': 2, 'policy3': 3}
|
||||
def p2m3(_g, step, sL, s):
|
||||
def p2m3(_g, step, sH, s):
|
||||
return {'policy1': 1, 'policy2': 2, 'policy3': 3}
|
||||
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ variables = {
|
|||
"policies": policies
|
||||
}
|
||||
|
||||
partial_state_update_block = {
|
||||
psubs = {
|
||||
"m1": {
|
||||
"policies": {
|
||||
"p1": p1m1,
|
||||
|
|
@ -79,7 +79,7 @@ sim_config = config_sim(
|
|||
append_configs(
|
||||
sim_configs=sim_config,
|
||||
initial_state=genesis_states,
|
||||
partial_state_update_blocks=partial_state_update_block,
|
||||
partial_state_update_blocks=psubs,
|
||||
policy_ops=[lambda a, b: a + b, lambda y: y * 2] # Default: lambda a, b: a + b
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,51 +14,51 @@ seeds = {
|
|||
|
||||
|
||||
# Policies per Mechanism
|
||||
def p1m1(_g, step, sL, s):
|
||||
def p1m1(_g, step, sH, s):
|
||||
return {'param1': 1}
|
||||
def p2m1(_g, step, sL, s):
|
||||
def p2m1(_g, step, sH, s):
|
||||
return {'param1': 1, 'param2': 4}
|
||||
|
||||
def p1m2(_g, step, sL, s):
|
||||
def p1m2(_g, step, sH, s):
|
||||
return {'param1': 'a', 'param2': 2}
|
||||
def p2m2(_g, step, sL, s):
|
||||
def p2m2(_g, step, sH, s):
|
||||
return {'param1': 'b', 'param2': 4}
|
||||
|
||||
def p1m3(_g, step, sL, s):
|
||||
def p1m3(_g, step, sH, s):
|
||||
return {'param1': ['c'], 'param2': np.array([10, 100])}
|
||||
def p2m3(_g, step, sL, s):
|
||||
def p2m3(_g, step, sH, s):
|
||||
return {'param1': ['d'], 'param2': np.array([20, 200])}
|
||||
|
||||
|
||||
# Internal States per Mechanism
|
||||
def s1m1(_g, step, sL, s, _input):
|
||||
def s1m1(_g, step, sH, s, _input):
|
||||
y = 's1'
|
||||
x = s['s1'] + 1
|
||||
return (y, x)
|
||||
def s2m1(_g, step, sL, s, _input):
|
||||
def s2m1(_g, step, sH, s, _input):
|
||||
y = 's2'
|
||||
x = _input['param2']
|
||||
return (y, x)
|
||||
|
||||
def s1m2(_g, step, sL, s, _input):
|
||||
def s1m2(_g, step, sH, s, _input):
|
||||
y = 's1'
|
||||
x = s['s1'] + 1
|
||||
return (y, x)
|
||||
def s2m2(_g, step, sL, s, _input):
|
||||
def s2m2(_g, step, sH, s, _input):
|
||||
y = 's2'
|
||||
x = _input['param2']
|
||||
return (y, x)
|
||||
|
||||
def s1m3(_g, step, sL, s, _input):
|
||||
def s1m3(_g, step, sH, s, _input):
|
||||
y = 's1'
|
||||
x = s['s1'] + 1
|
||||
return (y, x)
|
||||
def s2m3(_g, step, sL, s, _input):
|
||||
def s2m3(_g, step, sH, s, _input):
|
||||
y = 's2'
|
||||
x = _input['param2']
|
||||
return (y, x)
|
||||
|
||||
def policies(_g, step, sL, s, _input):
|
||||
def policies(_g, step, sH, s, _input):
|
||||
y = 'policies'
|
||||
x = _input
|
||||
return (y, x)
|
||||
|
|
@ -68,17 +68,17 @@ def policies(_g, step, sL, s, _input):
|
|||
proc_one_coef_A = 0.7
|
||||
proc_one_coef_B = 1.3
|
||||
|
||||
def es3(_g, step, sL, s, _input):
|
||||
def es3(_g, step, sH, s, _input):
|
||||
y = 's3'
|
||||
x = s['s3'] * bound_norm_random(seeds['a'], proc_one_coef_A, proc_one_coef_B)
|
||||
return (y, x)
|
||||
|
||||
def es4(_g, step, sL, s, _input):
|
||||
def es4(_g, step, sH, s, _input):
|
||||
y = 's4'
|
||||
x = s['s4'] * bound_norm_random(seeds['b'], proc_one_coef_A, proc_one_coef_B)
|
||||
return (y, x)
|
||||
|
||||
def update_timestamp(_g, step, sL, s, _input):
|
||||
def update_timestamp(_g, step, sH, s, _input):
|
||||
y = 'timestamp'
|
||||
return y, time_step(dt_str=s[y], dt_format='%Y-%m-%d %H:%M:%S', _timedelta=timedelta(days=0, minutes=0, seconds=1))
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ env_processes = {
|
|||
}
|
||||
|
||||
|
||||
partial_state_update_block = [
|
||||
psubs = [
|
||||
{
|
||||
"policies": {
|
||||
"b1": p1m1,
|
||||
|
|
@ -154,6 +154,6 @@ append_configs(
|
|||
sim_configs=sim_config,
|
||||
initial_state=genesis_states,
|
||||
env_processes=env_processes,
|
||||
partial_state_update_blocks=partial_state_update_block,
|
||||
partial_state_update_blocks=psubs,
|
||||
policy_ops=[lambda a, b: a + b]
|
||||
)
|
||||
|
|
@ -15,7 +15,7 @@ sys_model_A_simulation = Executor(exec_context=single_proc_ctx, configs=sys_mode
|
|||
sys_model_A_raw_result, sys_model_A_tensor_field = sys_model_A_simulation.execute()
|
||||
sys_model_A_result = pd.DataFrame(sys_model_A_raw_result)
|
||||
print()
|
||||
print("Tensor Field: config1")
|
||||
print("Tensor Field: sys_model_A")
|
||||
print(tabulate(sys_model_A_tensor_field, headers='keys', tablefmt='psql'))
|
||||
print("Result: System Events DataFrame")
|
||||
print(tabulate(sys_model_A_result, headers='keys', tablefmt='psql'))
|
||||
|
|
|
|||
|
|
@ -13,46 +13,46 @@ seeds = {
|
|||
|
||||
|
||||
# Policies per Mechanism
|
||||
def p1m1(_g, step, sL, s):
|
||||
def p1m1(_g, step, sH, s):
|
||||
return {'param1': 1}
|
||||
def p2m1(_g, step, sL, s):
|
||||
def p2m1(_g, step, sH, s):
|
||||
return {'param2': 4}
|
||||
|
||||
def p1m2(_g, step, sL, s):
|
||||
def p1m2(_g, step, sH, s):
|
||||
return {'param1': 'a', 'param2': 2}
|
||||
def p2m2(_g, step, sL, s):
|
||||
def p2m2(_g, step, sH, s):
|
||||
return {'param1': 'b', 'param2': 4}
|
||||
|
||||
def p1m3(_g, step, sL, s):
|
||||
def p1m3(_g, step, sH, s):
|
||||
return {'param1': ['c'], 'param2': np.array([10, 100])}
|
||||
def p2m3(_g, step, sL, s):
|
||||
def p2m3(_g, step, sH, s):
|
||||
return {'param1': ['d'], 'param2': np.array([20, 200])}
|
||||
|
||||
|
||||
# Internal States per Mechanism
|
||||
def s1m1(_g, step, sL, s, _input):
|
||||
def s1m1(_g, step, sH, s, _input):
|
||||
y = 's1'
|
||||
x = _input['param1']
|
||||
return (y, x)
|
||||
def s2m1(_g, step, sL, s, _input):
|
||||
def s2m1(_g, step, sH, s, _input):
|
||||
y = 's2'
|
||||
x = _input['param2']
|
||||
return (y, x)
|
||||
|
||||
def s1m2(_g, step, sL, s, _input):
|
||||
def s1m2(_g, step, sH, s, _input):
|
||||
y = 's1'
|
||||
x = _input['param1']
|
||||
return (y, x)
|
||||
def s2m2(_g, step, sL, s, _input):
|
||||
def s2m2(_g, step, sH, s, _input):
|
||||
y = 's2'
|
||||
x = _input['param2']
|
||||
return (y, x)
|
||||
|
||||
def s1m3(_g, step, sL, s, _input):
|
||||
def s1m3(_g, step, sH, s, _input):
|
||||
y = 's1'
|
||||
x = _input['param1']
|
||||
return (y, x)
|
||||
def s2m3(_g, step, sL, s, _input):
|
||||
def s2m3(_g, step, sH, s, _input):
|
||||
y = 's2'
|
||||
x = _input['param2']
|
||||
return (y, x)
|
||||
|
|
@ -62,17 +62,17 @@ def s2m3(_g, step, sL, s, _input):
|
|||
proc_one_coef_A = 0.7
|
||||
proc_one_coef_B = 1.3
|
||||
|
||||
def es3(_g, step, sL, s, _input):
|
||||
def es3(_g, step, sH, s, _input):
|
||||
y = 's3'
|
||||
x = s['s3'] * bound_norm_random(seeds['a'], proc_one_coef_A, proc_one_coef_B)
|
||||
return (y, x)
|
||||
|
||||
def es4(_g, step, sL, s, _input):
|
||||
def es4(_g, step, sH, s, _input):
|
||||
y = 's4'
|
||||
x = s['s4'] * bound_norm_random(seeds['b'], proc_one_coef_A, proc_one_coef_B)
|
||||
return (y, x)
|
||||
|
||||
def update_timestamp(_g, step, sL, s, _input):
|
||||
def update_timestamp(_g, step, sH, s, _input):
|
||||
y = 'timestamp'
|
||||
return y, time_step(dt_str=s[y], dt_format='%Y-%m-%d %H:%M:%S', _timedelta=timedelta(days=0, minutes=0, seconds=1))
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ env_processes = {
|
|||
"s4": env_trigger(3)(trigger_field='timestamp', trigger_vals=trigger_timestamps, funct_list=[lambda _g, x: 10])
|
||||
}
|
||||
|
||||
partial_state_update_block = [
|
||||
psubs = [
|
||||
{
|
||||
"policies": {
|
||||
"b1": p1m1,
|
||||
|
|
@ -143,5 +143,5 @@ append_configs(
|
|||
sim_configs=sim_config,
|
||||
initial_state=genesis_states,
|
||||
env_processes=env_processes,
|
||||
partial_state_update_blocks=partial_state_update_block
|
||||
partial_state_update_blocks=psubs
|
||||
)
|
||||
|
|
@ -9,14 +9,14 @@ exec_mode = ExecutionMode()
|
|||
|
||||
print("Simulation Execution: Single Configuration")
|
||||
print()
|
||||
first_config = configs # only contains config2
|
||||
first_config = configs # only contains sys_model_B
|
||||
single_proc_ctx = ExecutionContext(context=exec_mode.single_proc)
|
||||
run = Executor(exec_context=single_proc_ctx, configs=first_config)
|
||||
|
||||
raw_result, tensor_field = run.execute()
|
||||
result = pd.DataFrame(raw_result)
|
||||
print()
|
||||
print("Tensor Field: config1")
|
||||
print("Tensor Field: sys_model_B")
|
||||
print(tabulate(tensor_field, headers='keys', tablefmt='psql'))
|
||||
print("Output:")
|
||||
print(tabulate(result, headers='keys', tablefmt='psql'))
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
Simulation Execution
|
||||
==
|
||||
System Simulations are executed with the execution engine executor (`cadCAD.engine.Executor`) given System Model
|
||||
Configurations. There are multiple simulation Execution Modes and Execution Contexts.
|
||||
|
||||
### Steps:
|
||||
1. #### *Choose Execution Mode*:
|
||||
* ##### Simulation Execution Modes:
|
||||
`cadCAD` executes a process per System Model Configuration and a thread per System Simulation.
|
||||
##### Class: `cadCAD.engine.ExecutionMode`
|
||||
##### Attributes:
|
||||
* **Single Process:** A single process Execution Mode for a single System Model Configuration (Example:
|
||||
`cadCAD.engine.ExecutionMode().single_proc`).
|
||||
* **Multi-Process:** Multiple process Execution Mode for System Model Simulations which executes on a thread per
|
||||
given System Model Configuration (Example: `cadCAD.engine.ExecutionMode().multi_proc`).
|
||||
2. #### *Create Execution Context using Execution Mode:*
|
||||
```python
|
||||
from cadCAD.engine import ExecutionMode, ExecutionContext
|
||||
exec_mode = ExecutionMode()
|
||||
single_proc_ctx = ExecutionContext(context=exec_mode.single_proc)
|
||||
```
|
||||
3. #### *Create Simulation Executor*
|
||||
```python
|
||||
from cadCAD.engine import Executor
|
||||
from cadCAD import configs
|
||||
simulation = Executor(exec_context=single_proc_ctx, configs=configs)
|
||||
```
|
||||
4. #### *Execute Simulation: Produce System Event Dataset*
|
||||
A Simulation execution produces a System Event Dataset and the Tensor Field applied to initial states used to create it.
|
||||
```python
|
||||
import pandas as pd
|
||||
raw_system_events, tensor_field = simulation.execute()
|
||||
|
||||
# Simulation Result Types:
|
||||
# raw_system_events: List[dict]
|
||||
# tensor_field: pd.DataFrame
|
||||
|
||||
# Result System Events DataFrame
|
||||
simulation_result = pd.DataFrame(raw_system_events)
|
||||
```
|
||||
|
||||
##### Example Tensor Field
|
||||
```
|
||||
+----+-----+--------------------------------+--------------------------------+
|
||||
| | m | b1 | s1 |
|
||||
|----+-----+--------------------------------+--------------------------------|
|
||||
| 0 | 1 | <function p1m1 at 0x10c458ea0> | <function s1m1 at 0x10c464510> |
|
||||
| 1 | 2 | <function p1m2 at 0x10c464048> | <function s1m2 at 0x10c464620> |
|
||||
| 2 | 3 | <function p1m3 at 0x10c464400> | <function s1m3 at 0x10c464730> |
|
||||
+----+-----+--------------------------------+--------------------------------+
|
||||
```
|
||||
|
||||
##### Example Result: System Events DataFrame
|
||||
```python
|
||||
+----+-------+------------+-----------+------+-----------+
|
||||
| | run | timestep | substep | s1 | s2 |
|
||||
|----+-------+------------+-----------+------+-----------|
|
||||
| 0 | 1 | 0 | 0 | 0 | 0.0 |
|
||||
| 1 | 1 | 1 | 1 | 1 | 4 |
|
||||
| 2 | 1 | 1 | 2 | 2 | 6 |
|
||||
| 3 | 1 | 1 | 3 | 3 | [ 30 300] |
|
||||
| 4 | 2 | 0 | 0 | 0 | 0.0 |
|
||||
| 5 | 2 | 1 | 1 | 1 | 4 |
|
||||
| 6 | 2 | 1 | 2 | 2 | 6 |
|
||||
| 7 | 2 | 1 | 3 | 3 | [ 30 300] |
|
||||
+----+-------+------------+-----------+------+-----------+
|
||||
```
|
||||
|
||||
##### [Single Process Example Execution](link)
|
||||
|
||||
##### [Multiple Process Example Execution](link)
|
||||
|
|
@ -1,220 +0,0 @@
|
|||
System Model Configuration
|
||||
==
|
||||
|
||||
#### Introduction
|
||||
|
||||
Given System Model Configurations, cadCAD produces system event datasets that conform to specified system metrics. Each
|
||||
event / record is of [Enogenous State variables](link) produced by user defined [Partial State Updates](link) (PSU /
|
||||
functions that update state); A sequence of event / record subsets that comprises the resulting system event dataset is
|
||||
produced by a [Partial State Update Block](link) (PSUB / a Tensor Field for which State, Policy, and Time are dimensions
|
||||
and PSU functions are values).
|
||||
|
||||
A **System Model Configuration** is comprised of a simulation configuration, initial endogenous states, Partial State
|
||||
Update Blocks, environmental process, and a user defined policy aggregation function.
|
||||
|
||||
Execution:
|
||||
|
||||
#### Simulation Properties
|
||||
|
||||
###### System Metrics
|
||||
The following system metrics determine the size of resulting system event datasets:
|
||||
* `run` - the number of simulations in the resulting dataset
|
||||
* `timestep` - the number of timestamps in the resulting dataset
|
||||
* `substep` - the number of PSUs per `timestep` / within PSUBS
|
||||
* Number of events / records: `run` x `timestep` x `substep`
|
||||
|
||||
###### Simulation Configuration
|
||||
For the following dictionary, `T` is assigned a `timestep` range, `N` is assigned the number of simulation runs, and
|
||||
`params` is assigned the [**Parameter Sweep**](link) dictionary.
|
||||
|
||||
```python
|
||||
from cadCAD.configuration.utils import config_sim
|
||||
|
||||
sim_config = config_sim({
|
||||
"N": 2,
|
||||
"T": range(5),
|
||||
"M": params, # Optional
|
||||
})
|
||||
```
|
||||
|
||||
#### Initial Endogenous States
|
||||
**Enogenous State variables** are read-only variables defined to capture the shape and property of the network and
|
||||
represent internal input and signal.
|
||||
|
||||
The PSUB tensor field is applied to the following states to produce a resulting system event
|
||||
dataset.
|
||||
```python
|
||||
genesis_states = {
|
||||
's1': 0.0,
|
||||
's2': 0.0,
|
||||
's3': 1.0,
|
||||
'timestamp': '2018-10-01 15:16:24'
|
||||
}
|
||||
```
|
||||
|
||||
#### Partial State Update Block:
|
||||
- ***Partial State Update Block(PSUB)*** ***(Define ?)*** Tensor Field for which State, Policy, Time are dimensions
|
||||
and Partial State Update functions are values.
|
||||
- ***Partial State Update (PSU)*** are user defined functions that encodes state updates and are executed in
|
||||
a specified order PSUBs. PSUs update states given the most recent set of states and PSU policies.
|
||||
- ***Mechanism*** ***(Define)***
|
||||
|
||||
|
||||
The PSUBs is a list of PSU dictionaries of the structure within the code block below. PSUB elements (PSU dictionaries)
|
||||
are listed / defined in order of `substeps` and **identity functions** (returning a previous state's value) are assigned
|
||||
to unreferenced states within PSUs. The number of records produced produced per `timestep` is the number of `substeps`.
|
||||
|
||||
```python
|
||||
partial_state_update_block = [
|
||||
{
|
||||
"policies": {
|
||||
"b1": p1_psu1,
|
||||
"b2": p2_psu1
|
||||
},
|
||||
"variables": {
|
||||
"s1": s1_psu1,
|
||||
"s2": s2_psu1
|
||||
}
|
||||
},
|
||||
{
|
||||
"policies": {
|
||||
"b1": p1_psu2,
|
||||
},
|
||||
"variables": {
|
||||
"s2": s2_psu2
|
||||
}
|
||||
},
|
||||
{...}
|
||||
]
|
||||
```
|
||||
*Notes:*
|
||||
1. An identity function (returning the previous state value) is assigned to `s1` in the second PSU.
|
||||
2. Currently the only names that need not correspond to the convention below are `'b1'` and `'b2'`.
|
||||
|
||||
#### Policies
|
||||
- ***Policies*** ***(Define)*** When are policies behavior ?
|
||||
- ***Behaviors*** model agent behaviors in reaction to state variables and exogenous variables. The
|
||||
resulted user action will become an input to PSUs. Note that user behaviors should not directly update value
|
||||
of state variables.
|
||||
|
||||
Policies accept parameter sweep variables [see link] `_g` (`dict`), the most recent
|
||||
`substep` integer, the state history[see link] (`sH`), the most recent state record `s` (`dict) as inputs and returns a
|
||||
set of actions (`dict`).
|
||||
|
||||
Policy functions return dictionaries as actions. Policy functions provide access to parameter sweep variables [see link]
|
||||
via dictionary `_g`.
|
||||
```python
|
||||
def p1_psu1(_g, substep, sH, s):
|
||||
return {'policy1': 1}
|
||||
def p2_psu1(_g, substep, sH, s):
|
||||
return {'policy1': 1, 'policy2': 4}
|
||||
```
|
||||
For each PSU, multiple policy dictionaries are aggregated into a single dictionary to be imputted into
|
||||
all state functions using an initial reduction function (default: `lambda a, b: a + b`) and optional subsequent map
|
||||
functions.
|
||||
Example Result: `{'policy1': 2, 'policy2': 4}`
|
||||
|
||||
#### State Updates
|
||||
State update functions provide access to parameter sweep variables [see link] `_g` (`dict`), the most recent `substep`
|
||||
integer, the state history[see link] (`sH`), the most recent state record as a dictionary (`s`), the policies of a
|
||||
PSU (`_input`), and returns a tuple of the state variable's name and the resulting new value of the variable.
|
||||
|
||||
```python
|
||||
def state_update(_g, substep, sH, s, _input):
|
||||
...
|
||||
return state, update
|
||||
```
|
||||
**Note:** Each state update function updates one state variable at a time. Changes to multiple state variables requires
|
||||
separate state update functions. A generic example of a PSU is as follows.
|
||||
|
||||
* ##### Endogenous State Updates
|
||||
They are only updated by PSUs and can be used as inputs to a PSUs.
|
||||
```python
|
||||
def s1_update(_g, substep, sH, s, _input):
|
||||
x = _input['policy1'] + 1
|
||||
return 's1', x
|
||||
|
||||
def s2_update(_g, substep, sH, s, _input):
|
||||
x = _input['policy2']
|
||||
return 's2', x
|
||||
```
|
||||
|
||||
* ##### Exogenous State Updates
|
||||
***Exogenous State variables*** ***(Review)*** are read-only variables that represent external input and signal. They
|
||||
update endogenous states and are only updated by environmental processes. Exgoneous variables can be used
|
||||
as an input to a PSU that impacts state variables. ***(Expand upon Exogenous state updates)***
|
||||
|
||||
```python
|
||||
from datetime import timedelta
|
||||
from cadCAD.configuration.utils import time_step
|
||||
def es3_update(_g, substep, sH, s, _input):
|
||||
x = ...
|
||||
return 's3'
|
||||
def es4_update(_g, substep, sH, s, _input):
|
||||
x = ...
|
||||
return 's4', x
|
||||
def update_timestamp(_g, substep, sH, s, _input):
|
||||
x = time_step(dt_str=s[y], dt_format='%Y-%m-%d %H:%M:%S', _timedelta=timedelta(days=0, minutes=0, seconds=1))
|
||||
return 'timestamp', x
|
||||
```
|
||||
Exogenous state update functions (`es3_update`, `es4_update` and `es5_update`) update once per timestamp and should be
|
||||
included as a part of the first PSU in the PSUB.
|
||||
```python
|
||||
partial_state_update_block['psu1']['variables']['s3'] = es3_update
|
||||
partial_state_update_block['psu1']['variables']['s4'] = es4_update
|
||||
partial_state_update_block['psu1']['variables']['timestamp'] = update_timestamp
|
||||
```
|
||||
|
||||
* #### Environmental Process
|
||||
- ***Environmental processes*** model external changes that directly impact exogenous states at given specific
|
||||
conditions such as market shocks at specific timestamps.
|
||||
|
||||
Create a dictionary like `env_processes` below for which the keys are exogenous states and the values are lists of user
|
||||
defined **Environment Update** functions to be composed (e.g. `[f(params, x), g(params, x)]` becomes
|
||||
`f(params, g(params, x))`).
|
||||
|
||||
Environment Updates accept the [**Parameter Sweep**](link) dictionary `params` and a state as a result of a PSU.
|
||||
```python
|
||||
def env_update(params, state):
|
||||
. . .
|
||||
return updated_state
|
||||
|
||||
# OR
|
||||
|
||||
env_update = lambda params, state: state + 5
|
||||
```
|
||||
|
||||
The `env_trigger` function is used to apply composed environment update functions to a list of specific exogenous state
|
||||
update results. `env_trigger` accepts the total number of `substeps` for the simulation / `end_substep` and returns a
|
||||
function accepting `trigger_field`, `trigger_vals`, and `funct_list`.
|
||||
|
||||
In the following example functions are used to add `5` to every `s3` update and assign `10` to `s4` at
|
||||
`timestamp`s `'2018-10-01 15:16:25'`, `'2018-10-01 15:16:27'`, and `'2018-10-01 15:16:29'`.
|
||||
```python
|
||||
from cadCAD.configuration.utils import env_trigger
|
||||
trigger_timestamps = ['2018-10-01 15:16:25', '2018-10-01 15:16:27', '2018-10-01 15:16:29']
|
||||
env_processes = {
|
||||
"s3": [lambda params, x: x + 5],
|
||||
"s4": env_trigger(end_substep=3)(
|
||||
trigger_field='timestamp', trigger_vals=trigger_timestamps, funct_list=[lambda params, x: 10]
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### System Model Configuration
|
||||
`append_configs`, stores a **System Model Configuration** to be (Executed)[url] as
|
||||
simulations producing system event dataset(s)
|
||||
|
||||
```python
|
||||
from cadCAD.configuration import append_configs
|
||||
|
||||
append_configs(
|
||||
sim_configs=sim_config,
|
||||
initial_state=genesis_states,
|
||||
env_processes=env_processes,
|
||||
partial_state_update_blocks=partial_state_update_block,
|
||||
policy_ops=[lambda a, b: a + b]
|
||||
)
|
||||
```
|
||||
|
||||
#### [System Simulation Execution](link)
|
||||
Loading…
Reference in New Issue