ReForma 0.1.0__py3-none-any.whl
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.
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ReForma
|
|
3
|
+
Version: 0.1.0
|
|
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: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: networkx
|
|
14
|
+
Requires-Dist: matplotlib
|
|
15
|
+
|
|
16
|
+
# ReForma — Python bindings for the RePA/ReForma tool
|
|
17
|
+
|
|
18
|
+
A clean Python library that wraps the `ReFormaTool.jar` CLI via subprocess,
|
|
19
|
+
giving you a Pythonic interface for simulation, training, PDL/PCTL
|
|
20
|
+
verification, and export.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# From the project root (where pyproject.toml lives)
|
|
28
|
+
pip install -e .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Requires **Python 3.10+** and a working `java` on your PATH.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from ReForma import ReForma
|
|
39
|
+
|
|
40
|
+
# Point to your compiled JAR
|
|
41
|
+
ReForma = ReForma("path/to/ReFormaTool.jar")
|
|
42
|
+
|
|
43
|
+
# Load a model
|
|
44
|
+
state = ReForma.load_file("examples/recommender.r")
|
|
45
|
+
|
|
46
|
+
print(state.current_states) # ['Home']
|
|
47
|
+
print(state.enabled) # [Transition('go_work': Home → Office, p=0.500), ...]
|
|
48
|
+
|
|
49
|
+
# Simulate
|
|
50
|
+
state = ReForma.step("go_work")
|
|
51
|
+
state = ReForma.step("easy_task")
|
|
52
|
+
state = ReForma.undo() # undo last step
|
|
53
|
+
|
|
54
|
+
# Reset to initial state
|
|
55
|
+
ReForma.reset()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Simulation
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
state = ReForma.load_file("model.r")
|
|
64
|
+
|
|
65
|
+
# Check what's enabled
|
|
66
|
+
for t in state.enabled:
|
|
67
|
+
print(f"{t.label}: {t.from_state} → {t.to_state} (p={t.probability:.3f})")
|
|
68
|
+
|
|
69
|
+
# Take a step by label
|
|
70
|
+
state = ReForma.step("go_work")
|
|
71
|
+
|
|
72
|
+
# Undo / reset
|
|
73
|
+
state = ReForma.undo()
|
|
74
|
+
state = ReForma.reset()
|
|
75
|
+
|
|
76
|
+
# Inspect variables
|
|
77
|
+
print(state.variables) # {'counter': 0, 'flag': 1}
|
|
78
|
+
|
|
79
|
+
# History of labels taken
|
|
80
|
+
print(ReForma.history) # ['go_work']
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Training
|
|
86
|
+
|
|
87
|
+
Train the model on a batch of sessions (lists of event labels):
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
ReForma.train([
|
|
91
|
+
["go_work", "easy_task", "easy_task", "go_home"],
|
|
92
|
+
["battery_low", "go_charge", "finish_charge", "socialize"],
|
|
93
|
+
["no_money", "go_work", "go_home"],
|
|
94
|
+
])
|
|
95
|
+
|
|
96
|
+
# Or train directly from a log file (one session per line, comma-separated)
|
|
97
|
+
ReForma.train_from_file("logs/sessions.txt")
|
|
98
|
+
|
|
99
|
+
# sessions.txt format:
|
|
100
|
+
# go_work,easy_task,go_home
|
|
101
|
+
# battery_low,go_charge,finish_charge
|
|
102
|
+
|
|
103
|
+
# Save the updated model with new weights
|
|
104
|
+
ReForma.save_source("model_trained.r")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## PDL / PCTL Verification
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
# Quantitative: probability of eventually reaching Office
|
|
113
|
+
prob = ReForma.check_pdl_value("Home", "{P=?[F Office]}")
|
|
114
|
+
print(f"P(reach Office from Home) = {prob:.4f}")
|
|
115
|
+
|
|
116
|
+
# Qualitative: is it probable?
|
|
117
|
+
holds = ReForma.check_pdl_value("Home", "{P>=0.4[F Office]}")
|
|
118
|
+
print(f"P>=0.4? {holds}") # True / False
|
|
119
|
+
|
|
120
|
+
# PDL: is there a path via go_work to Office?
|
|
121
|
+
holds = ReForma.check_pdl_value("Home", "<go_work>Office")
|
|
122
|
+
print(holds) # True
|
|
123
|
+
|
|
124
|
+
# Get the raw string result
|
|
125
|
+
raw = ReForma.check_pdl("Home", "{P=?[F Office]}")
|
|
126
|
+
print(raw) # "Result: 0.50000"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Formula syntax reference
|
|
130
|
+
|
|
131
|
+
| Formula | Meaning |
|
|
132
|
+
|-----------------------------|------------------------------------------------|
|
|
133
|
+
| `{P=?[F target]}` | Probability of eventually reaching `target` |
|
|
134
|
+
| `{P=?[G safe]}` | Probability of staying in `safe` forever |
|
|
135
|
+
| `{P=?[X next]}` | Probability of reaching `next` in one step |
|
|
136
|
+
| `{P=?[a U b]}` | Probability of `a` until `b` |
|
|
137
|
+
| `{P>=0.5[F target]}` | Is probability of reaching target ≥ 0.5? |
|
|
138
|
+
| `<action>state` | There exists a path via `action` to `state` |
|
|
139
|
+
| `[action]state` | All paths via `action` lead to `state` |
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Export
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
# PRISM DTMC
|
|
147
|
+
prism_code = ReForma.export_prism()
|
|
148
|
+
ReForma.save_prism("output/model.pm")
|
|
149
|
+
|
|
150
|
+
# mCRL2
|
|
151
|
+
mcrl2_code = ReForma.export_mcrl2()
|
|
152
|
+
|
|
153
|
+
# GLTS (imperative translation)
|
|
154
|
+
glts_code = ReForma.export_glts()
|
|
155
|
+
|
|
156
|
+
# Mermaid diagram (initial state)
|
|
157
|
+
diagram = ReForma.export_mermaid()
|
|
158
|
+
|
|
159
|
+
# Mermaid diagram (full LTS — all reachable states)
|
|
160
|
+
full_diagram = ReForma.export_mermaid(full_lts=True)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Loading from a string
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
source = """
|
|
169
|
+
name MyModel
|
|
170
|
+
init s0
|
|
171
|
+
s0 ---> s1: a (0.6)
|
|
172
|
+
s0 ---> s2: b (0.4)
|
|
173
|
+
s1 ---> s0: back (1.0)
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
state = ReForma.load(source, name="MyModel")
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Running the tests
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
pip install pytest
|
|
185
|
+
pytest tests/ -v
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Project structure
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
ReForma/
|
|
194
|
+
├── __init__.py # Public API exports
|
|
195
|
+
├── client.py # ReForma — high-level Python API
|
|
196
|
+
├── jar_bridge.py # JarBridge — low-level subprocess wrapper
|
|
197
|
+
└── model.py # ReFormaModel, SimulationState, Transition data classes
|
|
198
|
+
tests/
|
|
199
|
+
└── test_ReForma.py # Full test suite (mocked, no JAR needed)
|
|
200
|
+
pyproject.toml
|
|
201
|
+
README.md
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Error handling
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
from ReForma.jar_bridge import JarError
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
result = ReForma.check_pdl("Home", "{P=?[F Office]}")
|
|
213
|
+
except JarError as e:
|
|
214
|
+
print(f"JAR error: {e}")
|
|
215
|
+
except RuntimeError as e:
|
|
216
|
+
print(f"Usage error: {e}") # e.g. no model loaded, invalid transition
|
|
217
|
+
```
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
reforma-0.1.0.dist-info/METADATA,sha256=rvdQrmXiHSdPIZWO19pD6R-HV-iJSU5XKDByUGo5soQ,5180
|
|
2
|
+
reforma-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
3
|
+
reforma-0.1.0.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
4
|
+
reforma-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|