pyjevsim 1.3.1__tar.gz → 2.0.1__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 (40) hide show
  1. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/LICENSE +21 -21
  2. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/MANIFEST.in +11 -11
  3. pyjevsim-2.0.1/PKG-INFO +374 -0
  4. pyjevsim-2.0.1/README.md +347 -0
  5. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/__init__.py +40 -40
  6. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/atomic_model.py +38 -38
  7. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/behavior_executor.py +159 -128
  8. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/behavior_model.py +281 -259
  9. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/core_model.py +102 -102
  10. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/default_message_catcher.py +41 -41
  11. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/definition.py +101 -101
  12. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/executor.py +34 -27
  13. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/executor_factory.py +76 -76
  14. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/exgen.py +116 -116
  15. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/message_deliverer.py +56 -43
  16. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/restore_handler.py +115 -115
  17. pyjevsim-2.0.1/pyjevsim/schedule_queue.py +150 -0
  18. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/snapshot_condition.py +100 -100
  19. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/snapshot_executor.py +218 -218
  20. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/snapshot_factory.py +62 -62
  21. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/snapshot_manager.py +119 -119
  22. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/structural_executor.py +152 -136
  23. pyjevsim-2.0.1/pyjevsim/structural_model.py +133 -0
  24. pyjevsim-2.0.1/pyjevsim/system_executor.py +955 -0
  25. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/system_message.py +107 -98
  26. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/system_object.py +56 -51
  27. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim/termination_manager.py +37 -37
  28. pyjevsim-2.0.1/pyjevsim.egg-info/PKG-INFO +374 -0
  29. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyproject.toml +42 -42
  30. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/setup.cfg +4 -4
  31. pyjevsim-1.3.1/PKG-INFO +0 -158
  32. pyjevsim-1.3.1/README.md +0 -131
  33. pyjevsim-1.3.1/pyjevsim/schedule_queue.py +0 -54
  34. pyjevsim-1.3.1/pyjevsim/structural_model.py +0 -48
  35. pyjevsim-1.3.1/pyjevsim/system_executor.py +0 -648
  36. pyjevsim-1.3.1/pyjevsim.egg-info/PKG-INFO +0 -158
  37. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim.egg-info/SOURCES.txt +0 -0
  38. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim.egg-info/dependency_links.txt +0 -0
  39. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim.egg-info/requires.txt +0 -0
  40. {pyjevsim-1.3.1 → pyjevsim-2.0.1}/pyjevsim.egg-info/top_level.txt +0 -0
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 eventsim
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2023 eventsim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,11 +1,11 @@
1
- include LICENSE
2
- include README.md
3
- include pyproject.toml
4
- recursive-include pyjevsim *.py
5
- recursive-exclude tests *
6
- recursive-exclude examples *
7
- recursive-exclude docs *
8
- recursive-exclude utils *
9
- recursive-exclude venv *
10
- global-exclude *.pyc
11
- global-exclude __pycache__
1
+ include LICENSE
2
+ include README.md
3
+ include pyproject.toml
4
+ recursive-include pyjevsim *.py
5
+ recursive-exclude tests *
6
+ recursive-exclude examples *
7
+ recursive-exclude docs *
8
+ recursive-exclude utils *
9
+ recursive-exclude venv *
10
+ global-exclude *.pyc
11
+ global-exclude __pycache__
@@ -0,0 +1,374 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyjevsim
3
+ Version: 2.0.1
4
+ Summary: A DEVS(Discrete Event System Specification) Modeling & Simulation environment with journaling functionality
5
+ Author-email: Changbeom Choi <me@cbchoi.info>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/eventsim/pyjevsim
8
+ Project-URL: Documentation, https://pyjevsim.readthedocs.io/en/latest/index.html
9
+ Project-URL: Repository, https://github.com/eventsim/pyjevsim
10
+ Project-URL: Issues, https://github.com/eventsim/pyjevsim/issues
11
+ Keywords: DEVS,simulation,discrete-event,modeling
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Classifier: Intended Audience :: Science/Research
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: dill>=0.3.6
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.0; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ # pyjevsim
29
+
30
+ [![PyPI](https://img.shields.io/pypi/v/pyjevsim.svg)](https://pypi.org/project/pyjevsim/)
31
+ [![Python](https://img.shields.io/pypi/pyversions/pyjevsim.svg)](https://pypi.org/project/pyjevsim/)
32
+ [![Docs](https://readthedocs.org/projects/pyjevsim/badge/?version=latest)](https://pyjevsim.readthedocs.io/en/latest/)
33
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
34
+
35
+ ## Introduction
36
+
37
+ pyjevsim is a DEVS (discrete event system specification) modeling and
38
+ simulation environment with built-in journaling. It supports snapshot
39
+ and restore of individual models or the full simulation engine,
40
+ virtual-time and real-time execution, and HLA federate integration via
41
+ a stepped execution mode. Compatible with Python 3.10+.
42
+
43
+ Full documentation: <https://pyjevsim.readthedocs.io/en/latest/>
44
+
45
+ ### What's new in 2.0
46
+
47
+ - **Two-phase tick.** `SysExecutor` evaluates every imminent model's
48
+ `output()` first, then routes outputs and applies transitions —
49
+ fixing confluent-event ordering under Parallel-DEVS semantics.
50
+ - **HLA stepped execution.** `step(granted_time)` and
51
+ `get_next_event_time()` let an IEEE 1516-2010 RTI federate drive
52
+ pyjevsim without owning the main loop.
53
+ - **V_TIME jump-to-next-event.** The virtual-time scheduler hops
54
+ directly to the next scheduled event instead of advancing by a fixed
55
+ `time_resolution`, eliminating idle ticks on sparse models.
56
+ - **Opt-in uncaught-message tracking** for debugging dangling outputs.
57
+ - **DEVStone benchmark suite** with cross-engine comparison adapters.
58
+
59
+ ## Installing
60
+
61
+ From PyPI (recommended):
62
+
63
+ ```
64
+ pip install pyjevsim
65
+ ```
66
+
67
+ From source:
68
+
69
+ ```
70
+ git clone https://github.com/eventsim/pyjevsim
71
+ cd pyjevsim
72
+ pip install -e .
73
+ ```
74
+
75
+ ## Dependencies
76
+
77
+ - Python >= 3.10
78
+ - `dill >= 0.3.6` (installed automatically) — used for model
79
+ serialization and restoration.
80
+
81
+ `pytest` is required only to run the test suite and is declared under
82
+ the `dev` extra:
83
+
84
+ ```
85
+ pip install pyjevsim[dev]
86
+ ```
87
+
88
+ ## Quick Start
89
+
90
+ A minimal generator → sink simulation:
91
+
92
+ ```python
93
+ from pyjevsim.behavior_model import BehaviorModel
94
+ from pyjevsim.definition import ExecutionType, Infinite
95
+ from pyjevsim.system_executor import SysExecutor
96
+ from pyjevsim.system_message import SysMessage
97
+
98
+
99
+ class Gen(BehaviorModel):
100
+ def __init__(self, name):
101
+ super().__init__(name)
102
+ self.init_state("Generate")
103
+ self.insert_state("Generate", 1)
104
+ self.insert_output_port("out")
105
+
106
+ def ext_trans(self, port, msg): pass
107
+ def int_trans(self): pass
108
+ def output(self, md):
109
+ msg = SysMessage(self.get_name(), "out")
110
+ msg.insert("tick")
111
+ md.insert_message(msg)
112
+ def time_advance(self):
113
+ return 1
114
+
115
+
116
+ class Sink(BehaviorModel):
117
+ def __init__(self, name):
118
+ super().__init__(name)
119
+ self.init_state("Idle")
120
+ self.insert_state("Idle", Infinite)
121
+ self.insert_input_port("in")
122
+
123
+ def ext_trans(self, port, msg):
124
+ print(f"received: {msg.retrieve()}")
125
+ def int_trans(self): pass
126
+ def output(self, md): pass
127
+ def time_advance(self):
128
+ return Infinite
129
+
130
+
131
+ se = SysExecutor(1, ex_mode=ExecutionType.V_TIME)
132
+ gen = Gen("g")
133
+ sink = Sink("s")
134
+ se.register_entity(gen)
135
+ se.register_entity(sink)
136
+ se.coupling_relation(gen, "out", sink, "in")
137
+ se.simulate(5)
138
+ ```
139
+
140
+ See the [quick-start guide](https://pyjevsim.readthedocs.io/en/latest/pyjevsim_quick_start.html)
141
+ for structural models, snapshots, and HLA stepped execution.
142
+
143
+ ### Examples
144
+
145
+ The [`examples/`](examples/) directory contains:
146
+
147
+ - **`banksim/`** — bank queue simulation demonstrating BehaviorModel,
148
+ StructuralModel, and snapshot/restore.
149
+ - **`atsim/`** — anti-torpedo simulator with self-propelled and
150
+ stationary decoy models.
151
+ - **`mwmsim/`** — municipal waste management agent-based model.
152
+
153
+ ### Output messages are shared by reference
154
+
155
+ When a model's output port has multiple downstream subscribers, every
156
+ subscriber receives the **same** `SysMessage` object. pyjevsim does not
157
+ deep-copy outputs during propagation — and neither does any other major
158
+ Python DEVS engine (xdevs.py and PythonPDEVS share references the same
159
+ way; `benchmark/aliasing_test.py` empirically demonstrates this for all
160
+ four engines in the comparison set). Treat received messages as
161
+ immutable; if your model needs to mutate a payload, copy it on the
162
+ receiver side:
163
+
164
+ ```python
165
+ def ext_trans(self, port, msg):
166
+ payload = list(msg.retrieve()) # local copy, safe to mutate
167
+ payload.append(my_local_data)
168
+ ...
169
+ ```
170
+
171
+ See [`benchmark/results/ALIASING.md`](benchmark/results/ALIASING.md) for
172
+ the full investigation and per-engine source pointers.
173
+
174
+ ## Benchmarks
175
+
176
+ The [`benchmark/`](benchmark/) directory contains a DEVStone suite plus
177
+ adapters that run the same workload against other Python DEVS engines so the
178
+ pyjevsim baseline can be tracked over time.
179
+
180
+ ```
181
+ benchmark/
182
+ ├── devstone/ # original pyjevsim-only DEVStone (flat)
183
+ │ ├── atomic.py
184
+ │ └── topology.py
185
+ ├── engines/ # cross-engine canonical DEVStone
186
+ │ ├── common.py # shared RunResult dataclass
187
+ │ ├── pyjevsim/ # adapter for this repo
188
+ │ ├── xdevs/ # adapter for xdevs.py (pip install xdevs)
189
+ │ ├── pypdevs/ # adapter for PythonPDEVS minimal kernel
190
+ │ └── reference/ # hand-rolled flat-FEL engine (floor)
191
+ ├── run_devstone.py # pyjevsim-only runner
192
+ ├── run_compare.py # cross-engine comparison runner
193
+ └── results/
194
+ ├── BASELINE.md # captured baseline numbers
195
+ ├── baseline.csv
196
+ └── devstone_sweep.csv
197
+ ```
198
+
199
+ ### pyjevsim-only sweep
200
+
201
+ ```
202
+ python -m benchmark.run_devstone --sweep \
203
+ --output benchmark/results/devstone_sweep.csv
204
+ ```
205
+
206
+ ### Cross-engine comparison
207
+
208
+ ```
209
+ pip install xdevs # optional
210
+ python -m benchmark.run_compare --list-engines
211
+ python -m benchmark.run_compare \
212
+ --output benchmark/results/baseline.csv
213
+ ```
214
+
215
+ ### Sparse-time baseline
216
+
217
+ `run_sparse` runs a tiny periodic-generator-plus-sink topology while
218
+ sweeping the inter-event simulated period. Holds the work constant at
219
+ 100 events; only the simulated-time gap between events varies. Isolates
220
+ per-tick overhead in V_TIME mode (see
221
+ [`benchmark/results/SPARSE.md`](benchmark/results/SPARSE.md)):
222
+
223
+ ```
224
+ python -m benchmark.run_sparse --output benchmark/results/sparse.csv
225
+ ```
226
+
227
+ ### Output aliasing test
228
+
229
+ `benchmark/aliasing_test.py` empirically demonstrates that all four
230
+ engines share output value references across multiple subscribers — see
231
+ [`benchmark/results/ALIASING.md`](benchmark/results/ALIASING.md). The
232
+ prevailing convention is "treat received values as immutable; copy on
233
+ the receiver if you need to mutate".
234
+
235
+ Current baseline (best-of-three, no synthetic CPU work) — see
236
+ [`benchmark/results/BASELINE.md`](benchmark/results/BASELINE.md):
237
+
238
+ | variant | d × w | pyjevsim tr/s | xdevs tr/s | pypdevs tr/s | reference tr/s |
239
+ |---------|-------|---------------|------------|--------------|----------------|
240
+ | LI | 4 × 4 | 175 k | 689 k | 765 k | 1.68 M |
241
+ | HI | 4 × 4 | 233 k | 546 k | 888 k | 2.00 M |
242
+ | HO | 4 × 4 | 241 k | 757 k | 918 k | 1.97 M |
243
+
244
+ Use `--int-cycles N` / `--ext-cycles N` to inject synthetic CPU work per
245
+ transition and shift the measurement toward user-code cost.
246
+
247
+ ## Debugging Uncaught Output Messages
248
+
249
+ By default `SysExecutor` drops output messages that hit a port with no
250
+ downstream coupling — the simulator stays on its fast path and the
251
+ events disappear silently. When wiring up a model graph it is often
252
+ useful to know *which* events are leaking; pass `track_uncaught=True`
253
+ and they get routed to the built-in `DefaultMessageCatcher` (accessible
254
+ as `se.dmc`) so you can observe them:
255
+
256
+ ```python
257
+ se = SysExecutor(1, ex_mode=ExecutionType.V_TIME, track_uncaught=True)
258
+ ```
259
+
260
+ The flag costs ~10-15% throughput on dense graphs with many dangling
261
+ outputs (every uncoupled emit pays for one `ext_trans` + reschedule on
262
+ the catcher), so leave it off in production runs.
263
+
264
+ ## Execution Modes
265
+
266
+ SysExecutor supports three execution modes via `ExecutionType`:
267
+
268
+ | Mode | Description |
269
+ |------|-------------|
270
+ | `V_TIME` | Virtual time — simulation runs as fast as possible |
271
+ | `R_TIME` | Real time — simulation paces itself to wall-clock time |
272
+ | `HLA_TIME` | HLA/RTI-controlled time — time advancement is driven externally |
273
+
274
+ ```python
275
+ from pyjevsim.system_executor import SysExecutor
276
+ from pyjevsim.definition import ExecutionType
277
+
278
+ se = SysExecutor(1, ex_mode=ExecutionType.V_TIME)
279
+ ```
280
+
281
+ ## Multi-threading Support
282
+
283
+ SysExecutor provides thread-safe APIs for multi-threaded simulation environments where external threads inject events while the simulation runs.
284
+
285
+ ### Pause / Resume
286
+
287
+ Pause the simulation to allow external threads to accumulate events, then resume.
288
+
289
+ ```python
290
+ se.pause_sim() # Pauses the simulation loop
291
+ # External threads can safely call insert_external_event() while paused
292
+ se.resume_sim() # Resumes the simulation loop
293
+ ```
294
+
295
+ ### External Event Injection
296
+
297
+ Insert events from external threads into the simulation. Thread-safe via internal synchronization.
298
+
299
+ ```python
300
+ se.insert_external_event("port_name", message, scheduled_time=0)
301
+ ```
302
+
303
+ ### Output Event Callback
304
+
305
+ Register a callback to be notified when output events are generated, avoiding polling.
306
+
307
+ ```python
308
+ se.set_output_event_callback(lambda: print("output ready"))
309
+ events = se.handle_external_output_event()
310
+ ```
311
+
312
+ ## HLA/RTI Integration (HLA_TIME Mode)
313
+
314
+ For HLA/RTI-controlled simulations, use `HLA_TIME` mode with `step()` and `get_next_event_time()`.
315
+
316
+ ```python
317
+ se = SysExecutor(1, ex_mode=ExecutionType.HLA_TIME)
318
+ se.register_entity(model)
319
+ se.init_sim()
320
+
321
+ # RTI-driven loop
322
+ while not se.is_terminated():
323
+ next_time = se.get_next_event_time()
324
+ # ... request time advance from RTI, wait for grant ...
325
+ granted_time = ... # time granted by RTI
326
+ output_events = se.step(granted_time)
327
+ # ... publish output_events to RTI ...
328
+ ```
329
+
330
+ ### `step(granted_time)`
331
+
332
+ Runs one RTI-granted simulation step using the same Parallel-DEVS
333
+ four-phase tick that the standalone V_TIME path uses, so HLA federates
334
+ get correct `δ_int / δ_ext / δ_con` semantics:
335
+
336
+ - Every event whose `req_time <= granted_time` fires inside the call.
337
+ - Multiple cascade rounds at the same simulated instant complete in one
338
+ `step()` (sigma=0 chains do not require multiple grants).
339
+ - During each round, `global_time` reflects the actual event time so
340
+ models observe correct simulated time inside their transitions.
341
+ - Per IEEE 1516-2010, `global_time` lands at `granted_time` when the
342
+ call returns, even if the last processed event was earlier.
343
+ - Returns the `output_event_queue` contents drained during this step
344
+ (a `deque` of `(time, message)` tuples) so the federate can republish
345
+ them as RTI interactions.
346
+
347
+ ### `get_next_event_time()`
348
+
349
+ Returns the earliest scheduled event time across the FEL and the
350
+ external-event queue. Use it to compute the Time Advance Request value
351
+ for the RTI.
352
+
353
+ ### Federate ambassador
354
+
355
+ pyjevsim ships the simulator-side hooks (above) but not an RTI
356
+ ambassador. Wire `step` / `get_next_event_time` /
357
+ `insert_external_event` / `set_output_event_callback` into the federate
358
+ ambassador of your chosen IEEE 1516-2010 RTI client.
359
+
360
+ ## Graceful Termination
361
+
362
+ ```python
363
+ se.terminate_simulation() # Sets SIMULATION_TERMINATED state
364
+ se.is_terminated() # Returns True if terminated
365
+ ```
366
+
367
+ Signal handlers (SIGTERM, SIGINT) automatically invoke `terminate_simulation()` on all registered SysExecutor instances.
368
+
369
+ ## License
370
+ Author: Changbeom Choi (@cbchoi)
371
+ Copyright (c) 2014-2020 Handong Global University
372
+ Copyright (c) 2021-2024 Hanbat National University
373
+ License: MIT. The full license text is available at:
374
+ - https://github.com/eventsim/pyjevsim/blob/main/LICENSE