tensnap 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. tensnap-0.1.0/MANIFEST.in +4 -0
  2. tensnap-0.1.0/PKG-INFO +75 -0
  3. tensnap-0.1.0/README.md +41 -0
  4. tensnap-0.1.0/pyproject.toml +65 -0
  5. tensnap-0.1.0/setup.cfg +4 -0
  6. tensnap-0.1.0/tensnap/__init__.py +13 -0
  7. tensnap-0.1.0/tensnap/bindings/__init__.py +0 -0
  8. tensnap-0.1.0/tensnap/bindings/basic/__init__.py +56 -0
  9. tensnap-0.1.0/tensnap/bindings/basic/accessor.py +260 -0
  10. tensnap-0.1.0/tensnap/bindings/basic/action.py +59 -0
  11. tensnap-0.1.0/tensnap/bindings/basic/chart.py +204 -0
  12. tensnap-0.1.0/tensnap/bindings/basic/parameter.py +520 -0
  13. tensnap-0.1.0/tensnap/bindings/mesa/__init__.py +20 -0
  14. tensnap-0.1.0/tensnap/bindings/mesa/accessor.py +98 -0
  15. tensnap-0.1.0/tensnap/bindings/mesa/datacollector.py +204 -0
  16. tensnap-0.1.0/tensnap/bindings/mesa/handler.py +163 -0
  17. tensnap-0.1.0/tensnap/examples/cgol.py +58 -0
  18. tensnap-0.1.0/tensnap/examples/cgol_viz.py +35 -0
  19. tensnap-0.1.0/tensnap/examples/flock.py +190 -0
  20. tensnap-0.1.0/tensnap/examples/flock_viz.py +60 -0
  21. tensnap-0.1.0/tensnap/examples/hk.py +191 -0
  22. tensnap-0.1.0/tensnap/examples/hk_viz.py +95 -0
  23. tensnap-0.1.0/tensnap/examples/mushroom.py +125 -0
  24. tensnap-0.1.0/tensnap/examples/sirs.py +370 -0
  25. tensnap-0.1.0/tensnap/examples/sirs_viz_graph.py +48 -0
  26. tensnap-0.1.0/tensnap/examples/sirs_viz_grid.py +50 -0
  27. tensnap-0.1.0/tensnap/examples/sugarscape.py +190 -0
  28. tensnap-0.1.0/tensnap/examples/sugarscape_viz.py +70 -0
  29. tensnap-0.1.0/tensnap/models/__init__.py +36 -0
  30. tensnap-0.1.0/tensnap/models/agent.py +191 -0
  31. tensnap-0.1.0/tensnap/models/environment.py +530 -0
  32. tensnap-0.1.0/tensnap/models/types.py +78 -0
  33. tensnap-0.1.0/tensnap/scenario.py +317 -0
  34. tensnap-0.1.0/tensnap/server.py +544 -0
  35. tensnap-0.1.0/tensnap/sim_loop.py +229 -0
  36. tensnap-0.1.0/tensnap/utils/__init__.py +2 -0
  37. tensnap-0.1.0/tensnap/utils/attr.py +204 -0
  38. tensnap-0.1.0/tensnap/utils/func.py +21 -0
  39. tensnap-0.1.0/tensnap/utils/model.py +30 -0
  40. tensnap-0.1.0/tensnap/utils/object.py +357 -0
  41. tensnap-0.1.0/tensnap/utils/ws.py +130 -0
  42. tensnap-0.1.0/tensnap.egg-info/PKG-INFO +75 -0
  43. tensnap-0.1.0/tensnap.egg-info/SOURCES.txt +50 -0
  44. tensnap-0.1.0/tensnap.egg-info/dependency_links.txt +1 -0
  45. tensnap-0.1.0/tensnap.egg-info/requires.txt +13 -0
  46. tensnap-0.1.0/tensnap.egg-info/top_level.txt +1 -0
  47. tensnap-0.1.0/tests/test_basic.py +15 -0
  48. tensnap-0.1.0/tests/test_environment.py +398 -0
  49. tensnap-0.1.0/tests/test_parameters.py +334 -0
  50. tensnap-0.1.0/tests/test_scenario.py +394 -0
  51. tensnap-0.1.0/tests/test_server.py +329 -0
  52. tensnap-0.1.0/tests/test_sim_loop.py +252 -0
@@ -0,0 +1,4 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include tensnap *.py
4
+ recursive-include tensnap/examples *.py
tensnap-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: tensnap
3
+ Version: 0.1.0
4
+ Summary: Agent-based model visualization toolkit - Python bindings
5
+ Author-email: billstark001 <billstark001@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/billstark001/tensnap
8
+ Project-URL: Documentation, https://github.com/billstark001/tensnap/tree/main/docs
9
+ Project-URL: Repository, https://github.com/billstark001/tensnap
10
+ Project-URL: Issues, https://github.com/billstark001/tensnap/issues
11
+ Keywords: agent-based-modeling,simulation,visualization,abm
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Visualization
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: numpy>=1.24.0
23
+ Requires-Dist: networkx>=3.0
24
+ Requires-Dist: msgpack>=1.0.5
25
+ Requires-Dist: websockets>=12.0
26
+ Requires-Dist: typing-extensions>=4.9.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
29
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
30
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
31
+ Requires-Dist: mypy>=1.7.0; extra == "dev"
32
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
33
+ Requires-Dist: black>=23.0.0; extra == "dev"
34
+
35
+ # Tensnap Python Bindings
36
+
37
+ Python bindings for Tensnap - an agent-based model visualization toolkit.
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install tensnap
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ```python
48
+ from tensnap import Scenario
49
+ import asyncio
50
+
51
+ # Create a scenario
52
+ scenario = Scenario(name="my-simulation", port=8765)
53
+
54
+ # Define your model logic
55
+ # See examples/ directory for complete examples
56
+
57
+ # Run the server
58
+ asyncio.run(scenario.run())
59
+ ```
60
+
61
+ ## Examples
62
+
63
+ Check the `examples/` directory for complete simulations:
64
+
65
+ - `flock.py` - Flocking behavior
66
+ - `sirs.py` - SIRS epidemic model
67
+ - `sugarscape.py` - Sugarscape economic model
68
+
69
+ ## Documentation
70
+
71
+ Full documentation: <https://github.com/billstark001/tensnap>
72
+
73
+ ## License
74
+
75
+ See LICENSE file in the repository root.
@@ -0,0 +1,41 @@
1
+ # Tensnap Python Bindings
2
+
3
+ Python bindings for Tensnap - an agent-based model visualization toolkit.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install tensnap
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from tensnap import Scenario
15
+ import asyncio
16
+
17
+ # Create a scenario
18
+ scenario = Scenario(name="my-simulation", port=8765)
19
+
20
+ # Define your model logic
21
+ # See examples/ directory for complete examples
22
+
23
+ # Run the server
24
+ asyncio.run(scenario.run())
25
+ ```
26
+
27
+ ## Examples
28
+
29
+ Check the `examples/` directory for complete simulations:
30
+
31
+ - `flock.py` - Flocking behavior
32
+ - `sirs.py` - SIRS epidemic model
33
+ - `sugarscape.py` - Sugarscape economic model
34
+
35
+ ## Documentation
36
+
37
+ Full documentation: <https://github.com/billstark001/tensnap>
38
+
39
+ ## License
40
+
41
+ See LICENSE file in the repository root.
@@ -0,0 +1,65 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tensnap"
7
+ version = "0.1.0"
8
+ description = "Agent-based model visualization toolkit - Python bindings"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ {name = "billstark001", email = "billstark001@example.com"}
14
+ ]
15
+ keywords = ["agent-based-modeling", "simulation", "visualization", "abm"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Scientific/Engineering :: Visualization",
25
+ ]
26
+ dependencies = [
27
+ "numpy>=1.24.0",
28
+ "networkx>=3.0",
29
+ "msgpack>=1.0.5",
30
+ "websockets>=12.0",
31
+ "typing-extensions>=4.9.0"
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/billstark001/tensnap"
36
+ Documentation = "https://github.com/billstark001/tensnap/tree/main/docs"
37
+ Repository = "https://github.com/billstark001/tensnap"
38
+ Issues = "https://github.com/billstark001/tensnap/issues"
39
+
40
+ [project.optional-dependencies]
41
+ dev = [
42
+ "pytest>=7.4.0",
43
+ "pytest-asyncio>=0.21.0",
44
+ "pytest-cov>=4.1.0",
45
+ "mypy>=1.7.0",
46
+ "ruff>=0.1.0",
47
+ "black>=23.0.0"
48
+ ]
49
+
50
+ [tool.mypy]
51
+ python_version = "3.10"
52
+ strict = true
53
+ warn_return_any = true
54
+ warn_unused_configs = true
55
+ disallow_untyped_defs = true
56
+
57
+ [tool.ruff]
58
+ line-length = 88
59
+ target-version = "py310"
60
+ select = ["E", "F", "B", "I", "N", "UP", "PL", "RUF"]
61
+
62
+ [tool.pytest.ini_options]
63
+ testpaths = ["tests"]
64
+ python_files = ["test_*.py", "*_test.py"]
65
+ addopts = "--cov=tensnap --cov-report=html --cov-report=term-missing"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,13 @@
1
+ # tensnap/__init__.py
2
+ """TenSnap - Agent-based model visualization toolkit"""
3
+
4
+ from .server import TenSnapServer
5
+ from .sim_loop import SimulationLoop
6
+ from .scenario import SimulationScenario
7
+ from .models import *
8
+ from .bindings.basic import *
9
+ from .bindings.mesa import *
10
+ import tensnap.utils as utils
11
+
12
+
13
+ __version__ = "0.1.0"
File without changes
@@ -0,0 +1,56 @@
1
+ # tensnap/bindings/basic/__init__.py
2
+ """Basic bindings for TenSnap - parameter, chart, and button decorators"""
3
+
4
+ from .parameter import (
5
+ ParameterType,
6
+ ParameterTypeWithoutAction,
7
+ ParameterBase,
8
+ NumberParameter,
9
+ BooleanParameter,
10
+ StringParameter,
11
+ EnumParameter,
12
+ ActionParameter,
13
+ Parameter,
14
+ BindParametersConfig,
15
+ bind,
16
+ bind_parameters,
17
+ get_parameter_metadata_from_namespace,
18
+ get_parameter_metadata_from_object,
19
+ )
20
+
21
+ from .chart import (
22
+ chart,
23
+ ChartMetadata,
24
+ ChartGroupMetadata,
25
+ ChartMetadataDict,
26
+ ChartGroupMetadataDict,
27
+ ChartProperty,
28
+ SimplifiedChartMetadata,
29
+ get_chart_metadata_from_namespace,
30
+ categorize_charts,
31
+ )
32
+
33
+ from .action import (
34
+ action,
35
+ get_action_metadata_from_namespace,
36
+ )
37
+
38
+
39
+ from .accessor import (
40
+
41
+ BindUniformAgentConfig,
42
+ BindGridAgentConfig,
43
+ BindGraphAgentNXConfig,
44
+ BindGraphAgentConfig,
45
+ bind_uniform_agent,
46
+ bind_grid_agent,
47
+ bind_graph_agent_nx,
48
+ bind_graph_agent,
49
+
50
+ BindUniformEnvironmentConfig,
51
+ BindGridEnvironmentConfig,
52
+ BindGraphEnvironmentConfig,
53
+ bind_uniform_environment,
54
+ bind_grid_environment,
55
+ bind_graph_environment,
56
+ )
@@ -0,0 +1,260 @@
1
+ from abc import ABC
2
+
3
+ from typing import Any, cast, Callable
4
+
5
+ from tensnap.models.agent import (
6
+ UniformAgentAccessorDict,
7
+ GridAgentAccessorDict,
8
+ GraphAgentAccessorNXDict,
9
+ GraphAgentAccessorDict,
10
+ make_uniform_agent_accessor,
11
+ make_grid_agent_accessor,
12
+ make_graph_agent_accessor_nx,
13
+ make_graph_agent_accessor,
14
+ )
15
+
16
+ from tensnap.models.environment import (
17
+ GridEnvironmentAccessorDict,
18
+ GraphEnvironmentAccessorDict,
19
+ GraphEdgeAccessorNXDict,
20
+ GraphEdgeDict,
21
+ make_uniform_environment_accessor,
22
+ make_grid_environment_accessor,
23
+ make_graph_environment_accessor,
24
+ make_graph_edge_accessor_nx,
25
+ )
26
+
27
+ # region Agent Accessor Bindings
28
+
29
+
30
+ class BindUniformAgentConfig:
31
+
32
+ def __init__(
33
+ self,
34
+ id: str = "id",
35
+ color: str | bool | None = None,
36
+ icon: str | bool | None = None,
37
+ size: str | bool | None = None,
38
+ data: str | bool | None = None,
39
+ ) -> None:
40
+ self.accessor_dict: UniformAgentAccessorDict = {
41
+ "id": id,
42
+ "color": color,
43
+ "icon": icon,
44
+ "size": size,
45
+ "data": data,
46
+ }
47
+
48
+ def __call__(self, cls):
49
+ cls._tensnap_bind_accessor_config_uniform = self
50
+ return cls
51
+
52
+ def get_accessor(self) -> Any:
53
+ return make_uniform_agent_accessor(**self.accessor_dict)
54
+
55
+
56
+ bind_uniform_agent = BindUniformAgentConfig
57
+
58
+
59
+ class BindGridAgentConfig:
60
+
61
+ def __init__(
62
+ self,
63
+ id: str = "id",
64
+ x: str = "x",
65
+ y: str = "y",
66
+ heading: str | bool | None = None,
67
+ color: str | bool | None = None,
68
+ icon: str | bool | None = None,
69
+ size: str | bool | None = None,
70
+ data: str | bool | None = None,
71
+ trajectory_length: str | bool | None = None,
72
+ trajectory_color: str | bool | None = None,
73
+ ) -> None:
74
+ self.accessor_dict: GridAgentAccessorDict = {
75
+ "id": id,
76
+ "x": x,
77
+ "y": y,
78
+ "heading": heading,
79
+ "color": color,
80
+ "icon": icon,
81
+ "size": size,
82
+ "data": data,
83
+ "trajectory_length": trajectory_length,
84
+ "trajectory_color": trajectory_color,
85
+ }
86
+
87
+ def __call__(self, cls):
88
+ cls._tensnap_bind_accessor_config_grid = self
89
+ return cls
90
+
91
+ def get_accessor(self) -> Any:
92
+ return make_grid_agent_accessor(**self.accessor_dict)
93
+
94
+
95
+ bind_grid_agent = BindGridAgentConfig
96
+
97
+
98
+ class BindGraphAgentNXConfig:
99
+
100
+ def __init__(
101
+ self,
102
+ x: str | bool | None = None,
103
+ y: str | bool | None = None,
104
+ color: str | bool | None = None,
105
+ icon: str | bool | None = None,
106
+ size: str | bool | None = None,
107
+ data: str | bool | None = None,
108
+ ) -> None:
109
+ self.accessor_dict: GraphAgentAccessorNXDict = {
110
+ "x": x,
111
+ "y": y,
112
+ "color": color,
113
+ "icon": icon,
114
+ "size": size,
115
+ "data": data,
116
+ }
117
+
118
+ def __call__(self, cls):
119
+ cls._tensnap_bind_accessor_config_graph_nx = self
120
+ return cls
121
+
122
+ def get_accessor(self) -> Any:
123
+ return make_graph_agent_accessor_nx(**self.accessor_dict)
124
+
125
+
126
+ bind_graph_agent_nx = BindGraphAgentNXConfig
127
+
128
+
129
+ class BindGraphAgentConfig:
130
+
131
+ def __init__(
132
+ self,
133
+ id: str = "id",
134
+ x: str | bool | None = None,
135
+ y: str | bool | None = None,
136
+ color: str | bool | None = None,
137
+ icon: str | bool | None = None,
138
+ size: str | bool | None = None,
139
+ data: str | bool | None = None,
140
+ ) -> None:
141
+ self.accessor_dict: GraphAgentAccessorDict = {
142
+ "id": id,
143
+ "x": x,
144
+ "y": y,
145
+ "color": color,
146
+ "icon": icon,
147
+ "size": size,
148
+ "data": data,
149
+ }
150
+
151
+ def __call__(self, cls):
152
+ cls._tensnap_bind_accessor_config_graph = self
153
+ return cls
154
+
155
+ def get_accessor(self) -> Any:
156
+ return make_graph_agent_accessor(**self.accessor_dict)
157
+
158
+
159
+ bind_graph_agent = BindGraphAgentConfig
160
+
161
+ # endregion
162
+
163
+ # region Environment Accessor Bindings
164
+
165
+
166
+ class BindUniformEnvironmentConfig:
167
+
168
+ def __init__(
169
+ self,
170
+ ) -> None:
171
+ pass
172
+
173
+ def __call__(self, cls):
174
+ cls._tensnap_bind_accessor_config_uniform = self
175
+ return cls
176
+
177
+ def get_accessor(self, id: str) -> Any:
178
+ return make_uniform_environment_accessor(id=id)
179
+
180
+
181
+ bind_uniform_environment = BindUniformEnvironmentConfig
182
+
183
+
184
+ class BindGridEnvironmentConfig:
185
+
186
+ def __init__(
187
+ self,
188
+ width: str = "width",
189
+ height: str = "height",
190
+ coord_offset: str | bool | None = None,
191
+ background: str | bool | None = None,
192
+ trajectory_length: str | bool | None = None,
193
+ trajectory_color: str | bool | None = None,
194
+ ) -> None:
195
+ self.accessor_dict: GridEnvironmentAccessorDict = {
196
+ "id": "id",
197
+ "width": width,
198
+ "height": height,
199
+ "coord_offset": coord_offset,
200
+ "background": background,
201
+ "trajectory_length": trajectory_length,
202
+ "trajectory_color": trajectory_color,
203
+ }
204
+
205
+ def __call__(self, cls):
206
+ cls._tensnap_bind_accessor_config_grid = self
207
+ return cls
208
+
209
+ def get_accessor(self, id: str) -> Any:
210
+ self.accessor_dict["id"] = id
211
+ return make_grid_environment_accessor(**self.accessor_dict)
212
+
213
+
214
+ bind_grid_environment = BindGridEnvironmentConfig
215
+
216
+
217
+ class BindGraphEnvironmentConfig:
218
+
219
+ def __init__(
220
+ self,
221
+ edges: str = "edges",
222
+ directed: bool = False,
223
+ style: str | bool | None = None,
224
+ width: str | bool | None = None,
225
+ color: str | bool | None = None,
226
+ edge_accessor: Callable[[Any], "GraphEdgeDict"] | bool = True,
227
+ ) -> None:
228
+ self.accessor_dict: GraphEnvironmentAccessorDict = {
229
+ "id": "id",
230
+ "edges": edges,
231
+ }
232
+ self.edge_accessor_dict: GraphEdgeAccessorNXDict = {
233
+ "directed": directed,
234
+ "style": style,
235
+ "width": width,
236
+ "color": color,
237
+ }
238
+ self.edge_accessor = edge_accessor
239
+
240
+ def __call__(self, cls):
241
+ cls._tensnap_bind_accessor_config_graph = self
242
+ return cls
243
+
244
+ def get_accessor(self, id: str) -> Any:
245
+ self.accessor_dict["id"] = id
246
+ return make_graph_environment_accessor(**self.accessor_dict)
247
+
248
+ def get_edge_accessor(self) -> Any:
249
+ if self.edge_accessor is True:
250
+ # Create a default edge accessor
251
+ return make_graph_edge_accessor_nx(**self.edge_accessor_dict)
252
+ elif self.edge_accessor is False:
253
+ # No edge accessor, return identity function
254
+ return lambda x: x
255
+ else:
256
+ # Use the provided edge accessor
257
+ return self.edge_accessor
258
+
259
+
260
+ bind_graph_environment = BindGraphEnvironmentConfig
@@ -0,0 +1,59 @@
1
+ # tensnap/bindings/basic/buttons.py
2
+ """Button decorators and bindings"""
3
+
4
+ from typing import Any, Callable, TypeVar, Dict, List, Tuple
5
+
6
+ from asyncio import iscoroutinefunction
7
+ from inspect import ismethod
8
+ from functools import wraps
9
+
10
+ from .parameter import ActionParameter
11
+
12
+ F = TypeVar("F", bound=Callable[..., Any])
13
+
14
+
15
+ def action(
16
+ id: str | None = None, label: str | None = None, allow_runtime_change: bool = True
17
+ ) -> Callable[[F], F]:
18
+ """Decorator to define a button"""
19
+ orig_id = id
20
+
21
+ def decorator(func_orig: F) -> F:
22
+ if ismethod(func_orig):
23
+ if iscoroutinefunction(func_orig):
24
+
25
+ @wraps(func_orig)
26
+ async def func(*args, **kwargs) -> Any: # type: ignore
27
+ return await func_orig(*args, **kwargs)
28
+
29
+ else:
30
+
31
+ @wraps(func_orig)
32
+ def func(*args, **kwargs) -> Any: # type: ignore
33
+ return func_orig(*args, **kwargs)
34
+
35
+ else:
36
+ func = func_orig # type: ignore
37
+ id = orig_id or func.__name__
38
+ param = ActionParameter(
39
+ id=id, label=label or "", allow_runtime_change=allow_runtime_change
40
+ )
41
+
42
+ # Store parameter and action info on the function
43
+ func._tensnap_action = param # type: ignore
44
+ return func # type: ignore
45
+
46
+ return decorator
47
+
48
+
49
+ def get_action_metadata_from_namespace(namespace: Dict[str, Any]):
50
+ """Find all action-decorated functions in a given namespace"""
51
+ actions: List[Tuple[str, Callable, ActionParameter]] = []
52
+ for name, attr in namespace.items():
53
+ if name.startswith('__') and name.endswith('__'):
54
+ continue
55
+ if callable(attr) and hasattr(attr, "_tensnap_action"):
56
+ param = getattr(attr, "_tensnap_action")
57
+ if isinstance(param, ActionParameter):
58
+ actions.append((name, attr, param))
59
+ return actions