ReForma 0.1.2__tar.gz → 0.1.4__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.
- reforma-0.1.4/MANIFEST.in +2 -0
- reforma-0.1.4/PKG-INFO +390 -0
- reforma-0.1.4/README.md +376 -0
- reforma-0.1.4/ReForma.egg-info/PKG-INFO +390 -0
- {reforma-0.1.2 → reforma-0.1.4}/ReForma.egg-info/SOURCES.txt +3 -0
- {reforma-0.1.2 → reforma-0.1.4}/pyproject.toml +3 -4
- {reforma-0.1.2 → reforma-0.1.4}/reforma/__init__.py +1 -1
- {reforma-0.1.2 → reforma-0.1.4}/reforma/bin/RePATool.jar +0 -0
- reforma-0.1.4/reforma/bin/js/cytoscape-dagre.js +397 -0
- reforma-0.1.4/reforma/bin/js/cytoscape.min.js +32 -0
- reforma-0.1.4/reforma/bin/js/dagre.js +16396 -0
- {reforma-0.1.2 → reforma-0.1.4}/reforma/client.py +239 -52
- {reforma-0.1.2 → reforma-0.1.4}/reforma/jar_bridge.py +31 -12
- {reforma-0.1.2 → reforma-0.1.4}/reforma/model.py +3 -3
- reforma-0.1.2/MANIFEST.in +0 -1
- reforma-0.1.2/PKG-INFO +0 -208
- reforma-0.1.2/README.md +0 -193
- reforma-0.1.2/ReForma.egg-info/PKG-INFO +0 -208
- {reforma-0.1.2 → reforma-0.1.4}/ReForma.egg-info/dependency_links.txt +0 -0
- {reforma-0.1.2 → reforma-0.1.4}/ReForma.egg-info/requires.txt +0 -0
- {reforma-0.1.2 → reforma-0.1.4}/ReForma.egg-info/top_level.txt +0 -0
- {reforma-0.1.2 → reforma-0.1.4}/setup.cfg +0 -0
- {reforma-0.1.2 → reforma-0.1.4}/tests/test_reforma.py +0 -0
reforma-0.1.4/PKG-INFO
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ReForma
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: Python bindings for the RePA/ReForma probabilistic automaton tool
|
|
5
|
+
Author-email: Joshua Dourado <joshuadourado@ua.pt>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Topic :: Scientific/Engineering
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: networkx
|
|
13
|
+
Requires-Dist: matplotlib
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# ReForma — Reconfigurable Formal Automata in Python
|
|
17
|
+
|
|
18
|
+
**ReForma** is a powerful Python library that bridges the gap between Formal Methods (Model Checking, MDPs) and Data Science. It wraps the RePA/ReForma Java backend, providing a clean, Pythonic interface for simulating, training, visualizing, and verifying Reconfigurable Formal Automata.
|
|
19
|
+
|
|
20
|
+
With ReForma, you can define probabilistic systems where **rules dynamically change the transition weights** during execution, train these systems with real-world event logs, and verify complex properties using PDL/PCTL.
|
|
21
|
+
|
|
22
|
+
## Acknowledgements
|
|
23
|
+
|
|
24
|
+
This project relies on and interacts with several excellent open-source projects and academic tools. We would like to thank their creators and maintainers:
|
|
25
|
+
|
|
26
|
+
**Graph Rendering & Visualization:**
|
|
27
|
+
* [Cytoscape.js](https://js.cytoscape.org/) (MIT License) - Used for interactive graph rendering.
|
|
28
|
+
* [dagre](https://github.com/dagrejs/dagre) & [cytoscape-dagre](https://github.com/cytoscape/cytoscape.js-dagre) (MIT License) - Used for directed graph layout.
|
|
29
|
+
* [Mermaid.js](https://mermaid.js.org/) - Used as an export format for LTS diagrams.
|
|
30
|
+
|
|
31
|
+
**Python Dependencies:**
|
|
32
|
+
* [NetworkX](https://networkx.org/) (BSD License) - Used for internal graph processing and layout calculation.
|
|
33
|
+
* [Matplotlib](https://matplotlib.org/) (Matplotlib License) - Used for offline high-resolution graph exports.
|
|
34
|
+
|
|
35
|
+
**Formal Verification Ecosystem:**
|
|
36
|
+
* [PRISM Model Checker](https://www.prismmodelchecker.org/) - The ReForma DSL heavily supports exporting DTMC models for verification in PRISM.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
* **Interactive Visualizations:** Render drag-and-drop graphs (Base Model or Full LTS) directly inside Jupyter Notebooks.
|
|
43
|
+
* **Model Manipulation:** Merge models (union/intersection) and prune low-probability paths (`delta_cut`).
|
|
44
|
+
* **Advanced Analysis:** Find the most/least probable paths to states or variable conditions.
|
|
45
|
+
* **Formal Verification:** Evaluate PCTL and PDL formulas (e.g., *What is the probability of eventually reaching a Deadlock?*).
|
|
46
|
+
* **Training from Logs:** Automatically update transition weights by feeding real user sessions/traces.
|
|
47
|
+
* **Exports:** Export your models to PRISM, mCRL2, GLTS, or Mermaid.js.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Installation & Requirements
|
|
52
|
+
|
|
53
|
+
**Requirements:**
|
|
54
|
+
1. **Python 3.10+**
|
|
55
|
+
2. **Java (JRE or JDK)**: ReForma relies on a high-performance Scala/Java backend. You *must* have `java` accessible in your system's `PATH`.
|
|
56
|
+
|
|
57
|
+
**Installation:**
|
|
58
|
+
```bash
|
|
59
|
+
pip install reforma
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
For **visualization features** (offline PNGs and interactive Jupyter graphs), install the required extras:
|
|
63
|
+
```bash
|
|
64
|
+
pip install networkx matplotlib
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Quick Start
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from reforma import ReForma
|
|
73
|
+
|
|
74
|
+
modelo = ReForma()
|
|
75
|
+
|
|
76
|
+
# 1. Load a model
|
|
77
|
+
modelo.load_file("recommender.r")
|
|
78
|
+
|
|
79
|
+
# 2. Get a beautiful printout of the current state and probabilities
|
|
80
|
+
print(modelo.state.summary())
|
|
81
|
+
|
|
82
|
+
# 3. Simulate steps
|
|
83
|
+
modelo.step("go_work")
|
|
84
|
+
modelo.step("easy_task")
|
|
85
|
+
|
|
86
|
+
# 4. Find the most probable path to a specific state
|
|
87
|
+
path = modelo.find_best_path(target_type="state", target_value="Home", criterion="max")
|
|
88
|
+
print(path)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Visualizations (Jupyter & Offline)
|
|
94
|
+
|
|
95
|
+
ReForma provides powerful graphing tools directly integrated into your workflow. Active rules are rendered as solid lines, while disabled elements are rendered as dashed/transparent lines.
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# 1. Interactive Jupyter View (Base Model)
|
|
99
|
+
# Renders a drag-and-drop Cytoscape.js graph right inside your Notebook!
|
|
100
|
+
modelo.show_interactive()
|
|
101
|
+
|
|
102
|
+
# 2. Interactive Jupyter View (Full LTS)
|
|
103
|
+
# Renders the full Labelled Transition System tree (all reachable states)
|
|
104
|
+
modelo.show_all_steps_interactive()
|
|
105
|
+
|
|
106
|
+
# 3. Offline Static Images
|
|
107
|
+
# Renders a high-res graph using Matplotlib/NetworkX (no browser needed)
|
|
108
|
+
modelo.save_image_plt("output/my_graph.png")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Model Manipulation & Analysis
|
|
114
|
+
|
|
115
|
+
ReForma isn't just for reading models; you can actively manipulate and analyze them:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# --- Delta Cut (Pruning) ---
|
|
119
|
+
# Prunes all transitions and rules with a probability below 0.15
|
|
120
|
+
modelo.delta_cut(delta=0.15)
|
|
121
|
+
|
|
122
|
+
# --- Merge Models ---
|
|
123
|
+
# Merges the current model with another one, resolving conflicting weights
|
|
124
|
+
# by taking the maximum value ("max"), or average ("arith").
|
|
125
|
+
modelo.merge_models(other_model_source, op_type="union", agg="max")
|
|
126
|
+
|
|
127
|
+
# --- Stats & Sanity Checks ---
|
|
128
|
+
print(modelo.get_stats()) # Counts total reachable states and transitions
|
|
129
|
+
print(modelo.check_problems()) # Hunts for deadlocks, unreachability, and rule inconsistencies
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Training with Event Logs
|
|
135
|
+
|
|
136
|
+
Train the model on a batch of sessions (lists of event labels) to automatically update the transition weights. Useful for Process Mining.
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
# Train directly from a Python list
|
|
140
|
+
modelo.train([
|
|
141
|
+
["go_work", "easy_task", "easy_task", "go_home"],
|
|
142
|
+
["battery_low", "go_charge", "finish_charge", "socialize"],
|
|
143
|
+
])
|
|
144
|
+
|
|
145
|
+
# Or train from a log file (one session per line, comma-separated)
|
|
146
|
+
modelo.train_from_file("logs/sessions.txt")
|
|
147
|
+
|
|
148
|
+
# Save the updated model with the new calculated weights
|
|
149
|
+
modelo.save_source("model_trained.r")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## PDL / PCTL Verification
|
|
155
|
+
|
|
156
|
+
Evaluate probabilistic and dynamic logic formulas natively.
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
# Quantitative: Probability of eventually reaching the Office
|
|
160
|
+
prob = modelo.check_pdl_value("Home", "{P=?[F Office]}")
|
|
161
|
+
print(f"P(reach Office from Home) = {prob:.4f}")
|
|
162
|
+
|
|
163
|
+
# Qualitative: Is it probable? (Returns True/False)
|
|
164
|
+
holds = modelo.check_pdl_value("Home", "{P>=0.4[F Office]}")
|
|
165
|
+
|
|
166
|
+
# PDL: Is there a path via go_work to Office?
|
|
167
|
+
holds = modelo.check_pdl_value("Home", "<go_work>Office")
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Formula Syntax Reference
|
|
171
|
+
|
|
172
|
+
| Formula | Meaning |
|
|
173
|
+
|-----------------------------|------------------------------------------------|
|
|
174
|
+
| `{P=?[F target]}` | Probability of eventually reaching `target` |
|
|
175
|
+
| `{P=?[G safe]}` | Probability of staying in `safe` forever |
|
|
176
|
+
| `{P=?[X next]}` | Probability of reaching `next` in exactly 1 step|
|
|
177
|
+
| `{P=?[a U b]}` | Probability of `a` holding until `b` occurs |
|
|
178
|
+
| `{P>=0.5[F target]}` | Is the probability of reaching target ≥ 0.5? |
|
|
179
|
+
| `<action>state` | There exists a path via `action` to `state` |
|
|
180
|
+
| `[action]state` | All paths via `action` lead to `state` |
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Full API Reference
|
|
185
|
+
|
|
186
|
+
You can access the full API reference at any time in your Python console by running:
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
from reforma import ReForma
|
|
190
|
+
modelo = ReForma()
|
|
191
|
+
modelo.help()
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Main Methods:
|
|
195
|
+
* **Loading**: `load(source, name)`, `load_file(path)`, `reset()`
|
|
196
|
+
* **Simulation**: `step(label)`, `undo()`
|
|
197
|
+
* **Visualizations**: `show_interactive()`, `show_all_steps_interactive()`, `save_image_plt(path)`, `get_all_steps()`
|
|
198
|
+
* **Analysis**: `check_problems()`, `get_stats()`, `find_best_path(...)`
|
|
199
|
+
* **Manipulation**: `train(sessions)`, `train_from_file(path)`, `delta_cut(delta)`, `merge_models(...)`
|
|
200
|
+
* **Verification**: `check_pdl(state, form)`, `check_pdl_value(...)`
|
|
201
|
+
* **Exports**: `export_prism()`, `export_glts()`, `export_mcrl2()`, `save_source(path)`
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# Re_lang — Reconfigurable Language Guide
|
|
207
|
+
|
|
208
|
+
**Re_lang** is the Domain-Specific Language (DSL) designed to define models for the ReForma library.
|
|
209
|
+
Its main innovation lies in its ability to define **Reconfigurable Probabilistic Automata**, where transitions are not static: executing an action can dynamically enable, disable, or alter the probability of other actions in the future.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 1. Basic Structure and Global Definitions
|
|
214
|
+
|
|
215
|
+
Every model starts with a name, variables (optional), and the definition of the initial state. Optionally, you can configure the mathematical mode for weight distribution.
|
|
216
|
+
|
|
217
|
+
```text
|
|
218
|
+
name MyModel
|
|
219
|
+
|
|
220
|
+
// (Optional) Defines how residual weights are redistributed after a rule modifies a probability.
|
|
221
|
+
// Options: normalize (default), equal, proportional
|
|
222
|
+
calibration proportional
|
|
223
|
+
|
|
224
|
+
// (Optional) Ignores hyper-rules math and calculates empirical frequencies instead
|
|
225
|
+
// training
|
|
226
|
+
|
|
227
|
+
// Integer variables declaration (Optional)
|
|
228
|
+
int counter = 0
|
|
229
|
+
int flag = 1
|
|
230
|
+
|
|
231
|
+
// Initial State (Required)
|
|
232
|
+
init s0
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## 2. Base Transitions (Simple Actions)
|
|
238
|
+
|
|
239
|
+
Base transitions define the static behavioral graph of the system. You can define them using a shorthand syntax or a full syntax if you need to separate the transition's unique ID from its label.
|
|
240
|
+
|
|
241
|
+
**Shorthand Syntax:** `Source ---> Target: label (probability) [aggregation] [disabled]`
|
|
242
|
+
**Full Syntax (with ID):** `Source -act-> Target: label (probability) [aggregation] [disabled]`
|
|
243
|
+
|
|
244
|
+
```text
|
|
245
|
+
// Shorthand: Transition ID and Label are both 'work'
|
|
246
|
+
s0 ---> s1: work (0.8)
|
|
247
|
+
|
|
248
|
+
// Full Syntax: Explicitly separating the act from the Label
|
|
249
|
+
a -b-> c: d (0.5)
|
|
250
|
+
|
|
251
|
+
// In the example above:
|
|
252
|
+
// 'a' = Source state
|
|
253
|
+
// 'b' = act
|
|
254
|
+
// 'c' = Target state
|
|
255
|
+
// 'd' = Label (Used by hyper-edges/dynamic rules to buff or debuff this action)
|
|
256
|
+
|
|
257
|
+
// Transition with implicit probability (assumes 1.0 or divides evenly among available options)
|
|
258
|
+
s1 ---> s2: rest
|
|
259
|
+
|
|
260
|
+
// Transition inactive by default (will need a dynamic rule to enable it later)
|
|
261
|
+
s2 ---> s3: emergency disabled
|
|
262
|
+
```
|
|
263
|
+
*Why use full syntax?* It is extremely useful when you have multiple paths between the same (or different) states that should respond to the exact same dynamic rule (sharing the same `label`), but you need to uniquely identify each path (using the act).
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 3. Hyper-Edges (Dynamic Rules)
|
|
270
|
+
|
|
271
|
+
The core of Re_lang. Rules define how the occurrence of an event alters the structure of the model itself. A hyper-edge links a **trigger label** to a **target label**.
|
|
272
|
+
|
|
273
|
+
**Syntax (Enable / Buff):** `Trigger ->> Target: RuleName (Strength)`
|
|
274
|
+
**Syntax (Disable / Debuff):** `Trigger --! Target: RuleName (Strength)`
|
|
275
|
+
|
|
276
|
+
```text
|
|
277
|
+
// When 'battery_low' occurs, it enables and buffs the probability of 'go_charge' (strength 0.6)
|
|
278
|
+
battery_low ->> go_charge: chargeRule (0.6)
|
|
279
|
+
|
|
280
|
+
// When 'battery_low' occurs, it disables/debuffs the probability of 'go_work'
|
|
281
|
+
battery_low --! go_work: fatigueRule (0.4)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## 4. Aggregations (Rule Mathematics)
|
|
288
|
+
|
|
289
|
+
When a rule acts upon a transition, the final probability is calculated using an aggregation function that combines 3 values: the original weight of the target, the strength of the rule, and the weight of the trigger.
|
|
290
|
+
|
|
291
|
+
By default, it uses the arithmetic mean (`arith`), but you can specify others:
|
|
292
|
+
* `arith` : Arithmetic mean (Default)
|
|
293
|
+
* `max` : Chooses the maximum value.
|
|
294
|
+
* `min` : Chooses the minimum value.
|
|
295
|
+
* `prod` : Product of probabilities.
|
|
296
|
+
* `geom` : Geometric mean.
|
|
297
|
+
|
|
298
|
+
**Example:**
|
|
299
|
+
```text
|
|
300
|
+
// Uses 'max' to ensure that if the rule is very strong, it overrides the base probability.
|
|
301
|
+
stress ->> slack_off: ruleStress (0.9) max
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## 5. Guards and Updates
|
|
307
|
+
|
|
308
|
+
Any transition (simple or rule) can have conditions to occur and can alter global variables.
|
|
309
|
+
|
|
310
|
+
**Supported Operators:** `==`, `!=`, `<=`, `>=`, `<`, `>`, `AND`, `OR`.
|
|
311
|
+
|
|
312
|
+
```text
|
|
313
|
+
int tasks = 0
|
|
314
|
+
|
|
315
|
+
// Transition with Guard and Update
|
|
316
|
+
office ---> home: go_home if (tasks >= 5 AND stress_level > 2) then {
|
|
317
|
+
tasks' := 0
|
|
318
|
+
stress_level' := stress_level - 1
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// A dynamic rule can also be conditionally fired
|
|
322
|
+
finish_task ->> rest: reward if (tasks == 10)
|
|
323
|
+
```
|
|
324
|
+
*(Note: The variable being modified must have a prime `'` before the assignment operator `:=`, e.g., `tasks'`)*
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## 6. Modules (Sub-Automata)
|
|
329
|
+
|
|
330
|
+
For larger models, you can encapsulate logic inside `aut` (module) blocks, creating namespaces. This prevents state name collisions in complex systems.
|
|
331
|
+
|
|
332
|
+
```text
|
|
333
|
+
name SecuritySystem
|
|
334
|
+
|
|
335
|
+
aut Sensor {
|
|
336
|
+
init idle
|
|
337
|
+
idle ---> active: detect_motion
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
aut Alarm {
|
|
341
|
+
init off
|
|
342
|
+
off ---> on: sound_alarm disabled
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Inter-modular rule: Motion in the Sensor enables the Alarm
|
|
346
|
+
Sensor.detect_motion ->> Alarm.sound_alarm: trigger_alert
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## 7. Full Example: The Modern Worker
|
|
352
|
+
|
|
353
|
+
A model that brings all the concepts together:
|
|
354
|
+
|
|
355
|
+
```text
|
|
356
|
+
name AdvancedBot
|
|
357
|
+
calibration proportional
|
|
358
|
+
|
|
359
|
+
int energy = 10
|
|
360
|
+
int tasks_done = 0
|
|
361
|
+
|
|
362
|
+
init Home
|
|
363
|
+
|
|
364
|
+
// --- Base Behavior ---
|
|
365
|
+
Home ---> Office: go_work (0.5)
|
|
366
|
+
Home ---> Station: go_charge (0.5)
|
|
367
|
+
|
|
368
|
+
Office ---> Office: easy_task (0.7) if (energy > 0) then {
|
|
369
|
+
tasks_done' := tasks_done + 1
|
|
370
|
+
energy' := energy - 1
|
|
371
|
+
}
|
|
372
|
+
Office ---> Home: go_home (0.3)
|
|
373
|
+
|
|
374
|
+
// Maintenance states (Only available at Home)
|
|
375
|
+
Home ---> Home: battery_low
|
|
376
|
+
Station ---> Home: finish_charge (1.0) then {
|
|
377
|
+
energy' := 10
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// --- Dynamic Rules (Adaptation) ---
|
|
381
|
+
|
|
382
|
+
// If battery is low, force going to the station and block going to work
|
|
383
|
+
battery_low ->> go_charge: buff_charge (0.9) max
|
|
384
|
+
battery_low --! go_work: block_work (0.8)
|
|
385
|
+
|
|
386
|
+
// Upon finishing the charge, reset/enable going back to work
|
|
387
|
+
finish_charge ->> go_work: reset_work (0.5)
|
|
388
|
+
```
|
|
389
|
+
```
|
|
390
|
+
|