diff --git a/dev/Multiprocessing POC/Multiprocessing POC.ipynb b/dev/Multiprocessing POC/Multiprocessing POC.ipynb new file mode 100644 index 0000000..2b55a12 --- /dev/null +++ b/dev/Multiprocessing POC/Multiprocessing POC.ipynb @@ -0,0 +1,54 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 4, 9]\n" + ] + } + ], + "source": [ + "from multiprocessing import Pool\n", + "from functions import f\n", + "\n", + "\n", + "with Pool(5) as p:\n", + " print(p.map(f, [1, 2, 3]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "BlockScience", + "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.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/dev/Multiprocessing POC/functions.py b/dev/Multiprocessing POC/functions.py new file mode 100644 index 0000000..a3e6cc2 --- /dev/null +++ b/dev/Multiprocessing POC/functions.py @@ -0,0 +1,2 @@ +def f(x): + return x * x diff --git a/dev/cadCAD Scratch/.obsidian/app.json b/dev/cadCAD Scratch/.obsidian/app.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/dev/cadCAD Scratch/.obsidian/app.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/dev/cadCAD Scratch/.obsidian/appearance.json b/dev/cadCAD Scratch/.obsidian/appearance.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/dev/cadCAD Scratch/.obsidian/appearance.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/dev/cadCAD Scratch/.obsidian/core-plugins.json b/dev/cadCAD Scratch/.obsidian/core-plugins.json new file mode 100644 index 0000000..436f43c --- /dev/null +++ b/dev/cadCAD Scratch/.obsidian/core-plugins.json @@ -0,0 +1,30 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "properties": false, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": false +} \ No newline at end of file diff --git a/dev/cadCAD Scratch/.obsidian/workspace.json b/dev/cadCAD Scratch/.obsidian/workspace.json new file mode 100644 index 0000000..ae67d26 --- /dev/null +++ b/dev/cadCAD Scratch/.obsidian/workspace.json @@ -0,0 +1,173 @@ +{ + "main": { + "id": "ebb1f7af23941637", + "type": "split", + "children": [ + { + "id": "568d45caa507431d", + "type": "tabs", + "children": [ + { + "id": "104134b1d981b567", + "type": "leaf", + "state": { + "type": "canvas", + "state": { + "file": "MSML cadCAD Architecture.canvas", + "viewState": { + "x": 173.7321486342915, + "y": 92.17958112430901, + "zoom": -0.2988097469011942 + } + }, + "icon": "lucide-layout-dashboard", + "title": "MSML cadCAD Architecture" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "e73f36936028d6e3", + "type": "split", + "children": [ + { + "id": "029ed6d83ddc23dd", + "type": "tabs", + "children": [ + { + "id": "fe4ee760909ef1a7", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical" + }, + "icon": "lucide-folder-closed", + "title": "Files" + } + }, + { + "id": "b005d680dfdcd155", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Search" + } + }, + { + "id": "8bbe726fc7efc331", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "Bookmarks" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "a1ed7db625cdcfe6", + "type": "split", + "children": [ + { + "id": "7dc8de8390d07cad", + "type": "tabs", + "children": [ + { + "id": "9391e977b1f9e192", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "MSML cadCAD Architecture.canvas", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Backlinks for MSML cadCAD Architecture" + } + }, + { + "id": "de8681df02472c23", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "MSML cadCAD Architecture.canvas", + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Outgoing links from MSML cadCAD Architecture" + } + }, + { + "id": "e2678bce3695cc31", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + }, + "icon": "lucide-tags", + "title": "Tags" + } + }, + { + "id": "5439647b15bdca32", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "MSML cadCAD Architecture.canvas" + }, + "icon": "lucide-list", + "title": "Outline of MSML cadCAD Architecture" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Open quick switcher": false, + "graph:Open graph view": false, + "canvas:Create new canvas": false, + "daily-notes:Open today's daily note": false, + "templates:Insert template": false, + "command-palette:Open command palette": false + } + }, + "active": "104134b1d981b567", + "lastOpenFiles": [ + "Untitled.md", + "MSML cadCAD Architecture.canvas" + ] +} \ No newline at end of file diff --git a/dev/cadCAD Scratch/MSML cadCAD Architecture.canvas b/dev/cadCAD Scratch/MSML cadCAD Architecture.canvas new file mode 100644 index 0000000..9aaf705 --- /dev/null +++ b/dev/cadCAD Scratch/MSML cadCAD Architecture.canvas @@ -0,0 +1,38 @@ +{ + "nodes":[ + {"id":"a6d1c3438e3eb4e1","x":280,"y":-280,"width":500,"height":640,"type":"group","label":"cadCAD User View"}, + {"id":"b70dd39c3d751786","x":-100,"y":-280,"width":360,"height":580,"type":"group","label":"MSML-cadCAD Bridge Outputs"}, + {"id":"a66ae27ec1859b01","x":-420,"y":-280,"width":260,"height":580,"type":"group","label":"MSML Dev View"}, + {"id":"0c7ae0c823c829a9","x":-400,"y":-260,"width":220,"height":60,"type":"text","text":"MSML Object"}, + {"id":"bbb1312697692952","x":-80,"y":-260,"width":240,"height":140,"type":"text","text":"cadCAD Model\n- Acts as a factory for generating experiments"}, + {"id":"8e9e9cd0f2ed4507","x":-400,"y":160,"width":220,"height":120,"type":"text","text":"Blocks (Dynamics) -Will execute all sequentially for one \"step\" in MVP"}, + {"id":"317162b129f622fd","x":-400,"y":80,"width":220,"height":60,"type":"text","text":"Parameter Preparation Functions"}, + {"id":"cac35864b7b623bd","x":-400,"y":-20,"width":220,"height":80,"type":"text","text":"State Preparation Functions"}, + {"id":"0a251eec55fbadaf","x":-400,"y":-150,"width":220,"height":70,"type":"text","text":"build_cadCAD(...)"}, + {"id":"7a45b3e55b34f1a1","x":-40,"y":80,"width":250,"height":60,"type":"text","text":"State Space - Dictionary of state variables + types"}, + {"id":"83ac341b0997ed52","x":-40,"y":180,"width":250,"height":60,"type":"text","text":"Parameter Space"}, + {"id":"73de437b0ce0b423","x":-85,"y":-40,"width":250,"height":60,"type":"text","text":"build_experiment(state, parameters)"}, + {"id":"eed778cc1965413f","x":360,"y":-260,"width":250,"height":60,"type":"text","text":"Writes state with type of StateSpace"}, + {"id":"7a2060681be96243","x":360,"y":-175,"width":250,"height":60,"type":"text","text":"Writes parameters with type of ParameterSpace"}, + {"id":"e1005f3dcf8fd223","x":360,"y":-70,"width":250,"height":180,"type":"text","text":"ExperimentObject\n- At init, sets parameter and state variable of object and runs state prep and param prep if present"}, + {"id":"0fc9eb986c037b77","x":540,"y":190,"width":220,"height":150,"type":"text","text":"Run\n- State updated w/ multiple runs\n- Option to log all trajectory in list"}, + {"id":"dd3b639fb23c6a14","x":300,"y":190,"width":185,"height":150,"type":"text","text":"Step\n- State updated w/ one run"} + ], + "edges":[ + {"id":"e26408cf783df197","fromNode":"0c7ae0c823c829a9","fromSide":"bottom","toNode":"0a251eec55fbadaf","toSide":"top","label":"class function"}, + {"id":"1ef5bc8548306642","fromNode":"cac35864b7b623bd","fromSide":"left","toNode":"0a251eec55fbadaf","toSide":"left"}, + {"id":"3bce4a56fce2ce4f","fromNode":"8e9e9cd0f2ed4507","fromSide":"left","toNode":"0a251eec55fbadaf","toSide":"left"}, + {"id":"43ed4e3de30ccb1a","fromNode":"317162b129f622fd","fromSide":"left","toNode":"0a251eec55fbadaf","toSide":"left"}, + {"id":"3a34b35e83d033fe","fromNode":"0a251eec55fbadaf","fromSide":"right","toNode":"bbb1312697692952","toSide":"left"}, + {"id":"fd0fa4f5e8ee691a","fromNode":"0a251eec55fbadaf","fromSide":"right","toNode":"7a45b3e55b34f1a1","toSide":"left"}, + {"id":"6fa7f5ea7310aecc","fromNode":"0a251eec55fbadaf","fromSide":"right","toNode":"83ac341b0997ed52","toSide":"left"}, + {"id":"5faa2e303bc57a8a","fromNode":"bbb1312697692952","fromSide":"bottom","toNode":"73de437b0ce0b423","toSide":"top","label":"class function"}, + {"id":"810922f3beef1a5a","fromNode":"83ac341b0997ed52","fromSide":"right","toNode":"7a2060681be96243","toSide":"right"}, + {"id":"a6454fc03296dd06","fromNode":"7a45b3e55b34f1a1","fromSide":"right","toNode":"eed778cc1965413f","toSide":"right"}, + {"id":"75eae6f79f689eb8","fromNode":"7a2060681be96243","fromSide":"left","toNode":"73de437b0ce0b423","toSide":"right"}, + {"id":"daeae34e4a7c0624","fromNode":"eed778cc1965413f","fromSide":"left","toNode":"73de437b0ce0b423","toSide":"right"}, + {"id":"52f937f41817fbf3","fromNode":"73de437b0ce0b423","fromSide":"right","toNode":"e1005f3dcf8fd223","toSide":"left"}, + {"id":"02a6b575176162d1","fromNode":"e1005f3dcf8fd223","fromSide":"bottom","toNode":"dd3b639fb23c6a14","toSide":"top","label":"Class Fx"}, + {"id":"26d6af604c11e792","fromNode":"e1005f3dcf8fd223","fromSide":"bottom","toNode":"0fc9eb986c037b77","toSide":"top","label":"Class Fx"} + ] +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 3363ec4..698a2d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" [project] name = "math-spec-mapping" -version = "0.4.0" +version = "0.4.1" authors = [ { name="Sean McOwen", email="Sean@Block.Science" }, ] @@ -11,7 +11,11 @@ description = "A library for easy mapping of mathematical specifications." dependencies = [ "graphviz>=0.20.1", "ipython>=7.7.0", - "pandas>=1.4" + "pandas>=1.4", + "jsonschema>=4.21.1", + "PyGithub==2.5.0", + "dotenv", + "python-dotenv>=1.0.0" ] readme = "README.md" requires-python = ">=3.7" diff --git a/requirements.txt b/requirements.txt index 0c9c810..1fd64eb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,5 @@ graphviz>=0.20.1 ipython>=7.7.0 pandas>=1.4 jsonschema>=4.21.1 -PyGithub=2.5.0 -dotenv \ No newline at end of file +PyGithub==2.5.0 +python-dotenv>=1.0.0 \ No newline at end of file diff --git a/src/math_spec_mapping/Classes/MathSpec.py b/src/math_spec_mapping/Classes/MathSpec.py index 5143a9c..d17bd1d 100644 --- a/src/math_spec_mapping/Classes/MathSpec.py +++ b/src/math_spec_mapping/Classes/MathSpec.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict, List, Literal from .Entity import Entity from .Policy import Policy from .Mechanism import Mechanism @@ -10,6 +10,16 @@ import pandas as pd from inspect import signature, getsource, getfile, getsourcelines from IPython.display import display, Markdown +from copy import copy, deepcopy + + +class ValidKeyDict(dict): + def __getitem__(self, key): + # Add an assertion to check if the key exists + assert key in self, "Key not valid, valid options are: {}".format( + ", ".join(self.keys()) + ) + return super().__getitem__(key) class MathSpec: @@ -54,6 +64,21 @@ def __init__(self, ms_dict: Dict, json: Dict): self._crawl_spaces() self._set_source_code() + self.boundary_actions = ValidKeyDict(self.boundary_actions) + self.control_actions = ValidKeyDict(self.control_actions) + self.entities = ValidKeyDict(self.entities) + self.mechanisms = ValidKeyDict(self.mechanisms) + self.parameters.parameter_map = ValidKeyDict(self.parameters.parameter_map) + self.policies = ValidKeyDict(self.policies) + self.spaces = ValidKeyDict(self.spaces) + self.state = ValidKeyDict(self.state) + self.stateful_metrics_map = ValidKeyDict(self.stateful_metrics_map) + self.wiring = ValidKeyDict(self.wiring) + self.metrics = ValidKeyDict(self.metrics) + self.displays = ValidKeyDict(self.displays) + self.blocks = ValidKeyDict(self.blocks) + self.components = ValidKeyDict(self.components) + def _check_dictionary_names(self): for key in self.boundary_actions: assert key == self.boundary_actions[key].name @@ -986,6 +1011,50 @@ def build_implementation(self, params, domain_codomain_checking=False): self, params, domain_codomain_checking=domain_codomain_checking ) + def _build_state_space(self): + state = self.state["Global State"] + state_map = {} + for variable in state.variables: + if "python" in variable.type.type: + state_map[variable.name] = variable.type.type["python"] + else: + state_map[variable.name] = None + + return state_map + + def _build_parameter_space(self): + parameter_space = {} + for parameter in self.parameters.all_parameters: + parameter = self.parameters.parameter_map[parameter] + if "python" in parameter.variable_type.type: + parameter_space[parameter.name] = parameter.variable_type.type["python"] + else: + parameter_space[parameter.name] = None + for name in self.functional_parameters.keys(): + fp = self.functional_parameters[name] + fp = tuple(fp.keys()) + parameter_space[name] = Literal[fp] + return parameter_space + + def build_cadCAD( + self, + blocks, + state_preperation_functions=[], + parameter_preperation_functions=[], + ): + out = {} + out["StateSpace"] = self._build_state_space() + out["ParameterSpace"] = self._build_parameter_space() + out["Model"] = cadCADModel( + self, + out["StateSpace"], + out["ParameterSpace"], + blocks, + state_preperation_functions=state_preperation_functions, + parameter_preperation_functions=parameter_preperation_functions, + ) + return out + def _set_source_code(self): if "python" not in self.implementations: self.source_code = None @@ -1001,6 +1070,126 @@ def _set_source_code(self): self.source_code[x][y] = getsource(self.source_code[x][y]) +class cadCADModel: + def __init__( + self, + ms, + state_space, + parameter_space, + blocks, + state_preperation_functions=[], + parameter_preperation_functions=[], + ): + self.ms = ms + self.state_space = state_space + self.parameter_space = parameter_space + self.blocks = blocks + self.state_preperation_functions = state_preperation_functions + self.parameter_preperation_functions = parameter_preperation_functions + + def create_experiment( + self, state, params, record_trajectory=False, use_deepcopy=True + ): + return Experiment( + self, + state, + params, + self.ms, + record_trajectory=record_trajectory, + use_deepcopy=use_deepcopy, + ) + + def create_batch_experiments( + self, state, params, record_trajectory=False, use_deepcopy=True + ): + return BatchExperiments( + self, + state, + params, + self.ms, + record_trajectory=record_trajectory, + use_deepcopy=use_deepcopy, + ) + + def __repr__(self): + return f"Model({self.parameter_space}, {self.state_space}, {self.blocks})" + + +class Experiment: + def __init__(self, model, state, params, ms, record_trajectory, use_deepcopy=True): + self.model = model + self.state = deepcopy(state) + self.params = deepcopy(params) + self._use_deepcopy = use_deepcopy + self.msi = ms.build_implementation(self.params) + self.state, self.params = self.msi.prepare_state_and_params( + self.state, + self.params, + state_preperation_functions=self.model.state_preperation_functions, + parameter_preperation_functions=self.model.parameter_preperation_functions, + ) + + if record_trajectory: + if self._use_deepcopy: + self.trajectories = [deepcopy(self.state)] + else: + self.trajectories = [copy(self.state)] + self.trajectories[-1].pop("Stateful Metrics") + self.trajectories[-1].pop("Metrics") + else: + self.trajectories = None + self._record_trajectory = record_trajectory + self._iteration_count = 0 + + def step(self): + self.msi.execute_blocks(self.state, self.params, self.model.blocks) + self._iteration_count += 1 + if self._record_trajectory: + if self._use_deepcopy: + self.trajectories.append(deepcopy(self.state)) + else: + self.trajectories.append(copy(self.state)) + self.trajectories[-1].pop("Stateful Metrics") + self.trajectories[-1].pop("Metrics") + + def run(self, t): + for _ in range(t): + self.step() + + +class BatchExperiments: + def __init__(self, model, state, params, ms, record_trajectory, use_deepcopy=True): + assert type(state) == list, "Input for state must be a list of states" + assert type(params) == list, "Input for params must be a list of states" + assert len(state) == len( + params + ), "Length of state and parameters has to be the same" + + self.experiments = [ + Experiment( + model, + s, + p, + ms, + record_trajectory=record_trajectory, + use_deepcopy=use_deepcopy, + ) + for s, p in zip(state, params) + ] + + def step(self): + for experiment in self.experiments: + experiment.step() + + def run(self, t): + for experiment in self.experiments: + experiment.run(t) + + @property + def trajectories(self): + return [experiment.trajectories for experiment in self.experiments] + + class MathSpecImplementation: def __init__(self, ms: MathSpec, params, domain_codomain_checking): self.ms = deepcopy(ms) @@ -1016,6 +1205,16 @@ def __init__(self, ms: MathSpec, params, domain_codomain_checking): self.load_components() self.load_source_files() + self.boundary_actions = ValidKeyDict(self.boundary_actions) + self.control_actions = ValidKeyDict(self.control_actions) + self.mechanisms = ValidKeyDict(self.mechanisms) + self.policies = ValidKeyDict(self.policies) + self.stateful_metrics = ValidKeyDict(self.stateful_metrics) + self.wiring = ValidKeyDict(self.wiring) + self.metrics = ValidKeyDict(self.metrics) + self.blocks = ValidKeyDict(self.blocks) + self.components = ValidKeyDict(self.components) + def load_control_actions(self): control_actions = {} for ca in self.ms.control_actions: diff --git a/src/math_spec_mapping/Load/type.py b/src/math_spec_mapping/Load/type.py index 9011f79..ddabf42 100644 --- a/src/math_spec_mapping/Load/type.py +++ b/src/math_spec_mapping/Load/type.py @@ -1,7 +1,7 @@ from .general import check_json_keys from ..Classes import Type import os -from typing import _UnionGenericAlias +from typing import _UnionGenericAlias, List, _GenericAlias def convert_type(data, ms): @@ -36,6 +36,8 @@ def convert_type(data, ms): data["type_name"]["python"] = str(out) elif type(data["type"]["python"]) == _UnionGenericAlias: data["type_name"]["python"] = data["type"]["python"].__repr__() + elif type(data["type"]["python"]) == _GenericAlias: + data["type_name"]["python"] = data["type"]["python"].__repr__() else: data["type_name"]["python"] = data["type"]["python"].__name__ if "typescript" in ms["Type Keys"]: