infralab 1.0.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.
- infralab-1.0.0/LICENSE +21 -0
- infralab-1.0.0/PKG-INFO +240 -0
- infralab-1.0.0/README.md +195 -0
- infralab-1.0.0/infralab/__init__.py +24 -0
- infralab-1.0.0/infralab/agents/__init__.py +1 -0
- infralab-1.0.0/infralab/agents/adapters.py +61 -0
- infralab-1.0.0/infralab/agents/agentic.py +350 -0
- infralab-1.0.0/infralab/agents/basic.py +354 -0
- infralab-1.0.0/infralab/agents/copilot.py +116 -0
- infralab-1.0.0/infralab/agents/legacy.py +1116 -0
- infralab-1.0.0/infralab/agents/protocol.py +375 -0
- infralab-1.0.0/infralab/agents/sandbox.py +448 -0
- infralab-1.0.0/infralab/agents/scripted.py +112 -0
- infralab-1.0.0/infralab/analysis.py +352 -0
- infralab-1.0.0/infralab/api.py +514 -0
- infralab-1.0.0/infralab/assets/__init__.py +14 -0
- infralab-1.0.0/infralab/assets/base.py +83 -0
- infralab-1.0.0/infralab/assets/bridges/__init__.py +44 -0
- infralab-1.0.0/infralab/assets/bridges/limit_states.py +102 -0
- infralab-1.0.0/infralab/assets/bridges/profiles.py +54 -0
- infralab-1.0.0/infralab/assets/buildings/__init__.py +54 -0
- infralab-1.0.0/infralab/assets/buildings/limit_states.py +161 -0
- infralab-1.0.0/infralab/assets/buildings/profiles.py +80 -0
- infralab-1.0.0/infralab/assets/registry.py +26 -0
- infralab-1.0.0/infralab/assets/retaining_walls/__init__.py +50 -0
- infralab-1.0.0/infralab/assets/retaining_walls/limit_states.py +122 -0
- infralab-1.0.0/infralab/assets/retaining_walls/profiles.py +59 -0
- infralab-1.0.0/infralab/assets/solar_trackers/__init__.py +52 -0
- infralab-1.0.0/infralab/assets/solar_trackers/limit_states.py +127 -0
- infralab-1.0.0/infralab/assets/solar_trackers/profiles.py +79 -0
- infralab-1.0.0/infralab/assets/wind_turbines/__init__.py +57 -0
- infralab-1.0.0/infralab/assets/wind_turbines/limit_states.py +162 -0
- infralab-1.0.0/infralab/assets/wind_turbines/profiles.py +88 -0
- infralab-1.0.0/infralab/benchmarks/__init__.py +0 -0
- infralab-1.0.0/infralab/benchmarks/regbench/configurations.yaml +602 -0
- infralab-1.0.0/infralab/benchmarks/regbench/degradation/markov_nbi.py +16 -0
- infralab-1.0.0/infralab/benchmarks/regbench/degradation/power.py +11 -0
- infralab-1.0.0/infralab/benchmarks/regbench/laws/D0.yaml +22 -0
- infralab-1.0.0/infralab/benchmarks/regbench/laws/D1.yaml +24 -0
- infralab-1.0.0/infralab/benchmarks/regbench/laws/D2.yaml +26 -0
- infralab-1.0.0/infralab/benchmarks/regbench/laws/D3.yaml +27 -0
- infralab-1.0.0/infralab/benchmarks/regbench/laws/G0.yaml +33 -0
- infralab-1.0.0/infralab/benchmarks/regbench/laws/G1.yaml +36 -0
- infralab-1.0.0/infralab/benchmarks/regbench/laws/G2.yaml +37 -0
- infralab-1.0.0/infralab/benchmarks/regbench/laws/G3.yaml +40 -0
- infralab-1.0.0/infralab/benchmarks/regbench/laws/J0.yaml +33 -0
- infralab-1.0.0/infralab/benchmarks/regbench/laws/J1.yaml +36 -0
- infralab-1.0.0/infralab/benchmarks/regbench/laws/K0.yaml +24 -0
- infralab-1.0.0/infralab/benchmarks/regbench/laws/N24.yaml +24 -0
- infralab-1.0.0/infralab/benchmarks/regbench/scenarios/aged-147.json +1 -0
- infralab-1.0.0/infralab/benchmarks/regbench/scenarios/aged-30.json +1 -0
- infralab-1.0.0/infralab/benchmarks/scenarios.json +1254 -0
- infralab-1.0.0/infralab/calibration_profiles.py +287 -0
- infralab-1.0.0/infralab/cli.py +73 -0
- infralab-1.0.0/infralab/codes/__init__.py +14 -0
- infralab-1.0.0/infralab/codes/base.py +45 -0
- infralab-1.0.0/infralab/codes/frameworks.py +81 -0
- infralab-1.0.0/infralab/codes/registry.py +24 -0
- infralab-1.0.0/infralab/core/__init__.py +0 -0
- infralab-1.0.0/infralab/core/degradation.py +57 -0
- infralab-1.0.0/infralab/core/environment.py +3614 -0
- infralab-1.0.0/infralab/core/hazards/__init__.py +0 -0
- infralab-1.0.0/infralab/core/hazards/earthquake.py +371 -0
- infralab-1.0.0/infralab/core/hazards/progressive_events.py +1071 -0
- infralab-1.0.0/infralab/core/hazards/regional_events.py +399 -0
- infralab-1.0.0/infralab/core/hazards/seismic_hazard.py +72 -0
- infralab-1.0.0/infralab/core/hazards/sudden_damage.py +633 -0
- infralab-1.0.0/infralab/core/inspection.py +54 -0
- infralab-1.0.0/infralab/core/reliability.py +687 -0
- infralab-1.0.0/infralab/data/__init__.py +0 -0
- infralab-1.0.0/infralab/data/excel_import.py +232 -0
- infralab-1.0.0/infralab/data/nbi.py +265 -0
- infralab-1.0.0/infralab/data/nbi_calibration.py +361 -0
- infralab-1.0.0/infralab/lab/__init__.py +3 -0
- infralab-1.0.0/infralab/lab/__main__.py +3 -0
- infralab-1.0.0/infralab/lab/arena.py +477 -0
- infralab-1.0.0/infralab/lab/build.py +66 -0
- infralab-1.0.0/infralab/lab/cli.py +79 -0
- infralab-1.0.0/infralab/lab/hydraulic_rate.py +90 -0
- infralab-1.0.0/infralab/lab/io.py +58 -0
- infralab-1.0.0/infralab/lab/leak_check.py +1047 -0
- infralab-1.0.0/infralab/lab/provenance.py +40 -0
- infralab-1.0.0/infralab/lab/runner.py +192 -0
- infralab-1.0.0/infralab/lab/spec.py +152 -0
- infralab-1.0.0/infralab/llm/__init__.py +2 -0
- infralab-1.0.0/infralab/llm/anthropic_backend.py +53 -0
- infralab-1.0.0/infralab/llm/openai_backend.py +58 -0
- infralab-1.0.0/infralab/llm/player.py +143 -0
- infralab-1.0.0/infralab/mcp_server.py +132 -0
- infralab-1.0.0/infralab/persistence.py +619 -0
- infralab-1.0.0/infralab/plugins/__init__.py +19 -0
- infralab-1.0.0/infralab/plugins/loader.py +127 -0
- infralab-1.0.0/infralab/plugins/registry.py +116 -0
- infralab-1.0.0/infralab/plugins/templates.py +88 -0
- infralab-1.0.0/infralab/regimes/__init__.py +16 -0
- infralab-1.0.0/infralab/regimes/base.py +350 -0
- infralab-1.0.0/infralab/regimes/builtin/hyp_risk_based_existing.yaml +21 -0
- infralab-1.0.0/infralab/regimes/builtin/it_lg2020_forced.yaml +19 -0
- infralab-1.0.0/infralab/regimes/builtin/it_lg2020_sanctioned.yaml +25 -0
- infralab-1.0.0/infralab/regimes/builtin/it_lg2020_signal.yaml +20 -0
- infralab-1.0.0/infralab/regimes/registry.py +66 -0
- infralab-1.0.0/infralab/settings/__init__.py +14 -0
- infralab-1.0.0/infralab/settings/config.py +856 -0
- infralab-1.0.0/infralab/study/__init__.py +12 -0
- infralab-1.0.0/infralab/study/battery.py +67 -0
- infralab-1.0.0/infralab/study/outcomes.py +72 -0
- infralab-1.0.0/infralab/study/questionnaire.py +122 -0
- infralab-1.0.0/infralab/study/stats.py +178 -0
- infralab-1.0.0/infralab/toolkit.py +155 -0
- infralab-1.0.0/infralab/ui/__init__.py +0 -0
- infralab-1.0.0/infralab/ui/auth.py +151 -0
- infralab-1.0.0/infralab/ui/gis_bim.py +709 -0
- infralab-1.0.0/infralab/ui/pages_legacy.py +2501 -0
- infralab-1.0.0/infralab/views.py +120 -0
- infralab-1.0.0/infralab.egg-info/PKG-INFO +240 -0
- infralab-1.0.0/infralab.egg-info/SOURCES.txt +166 -0
- infralab-1.0.0/infralab.egg-info/dependency_links.txt +1 -0
- infralab-1.0.0/infralab.egg-info/entry_points.txt +3 -0
- infralab-1.0.0/infralab.egg-info/requires.txt +35 -0
- infralab-1.0.0/infralab.egg-info/top_level.txt +1 -0
- infralab-1.0.0/pyproject.toml +47 -0
- infralab-1.0.0/setup.cfg +4 -0
- infralab-1.0.0/tests/test_agentic.py +166 -0
- infralab-1.0.0/tests/test_assessment_confidence.py +58 -0
- infralab-1.0.0/tests/test_assessment_decomposition.py +71 -0
- infralab-1.0.0/tests/test_asset_registry.py +119 -0
- infralab-1.0.0/tests/test_backend_api.py +330 -0
- infralab-1.0.0/tests/test_battery.py +208 -0
- infralab-1.0.0/tests/test_codes.py +53 -0
- infralab-1.0.0/tests/test_config_validation.py +72 -0
- infralab-1.0.0/tests/test_copilot.py +155 -0
- infralab-1.0.0/tests/test_custom_scenarios.py +109 -0
- infralab-1.0.0/tests/test_degradation_weibull.py +107 -0
- infralab-1.0.0/tests/test_detour_model.py +41 -0
- infralab-1.0.0/tests/test_env_clone_state.py +145 -0
- infralab-1.0.0/tests/test_env_snapshot.py +108 -0
- infralab-1.0.0/tests/test_excel_import.py +164 -0
- infralab-1.0.0/tests/test_experiment_platform.py +188 -0
- infralab-1.0.0/tests/test_floor_oracle.py +264 -0
- infralab-1.0.0/tests/test_floor_response.py +67 -0
- infralab-1.0.0/tests/test_hardening.py +107 -0
- infralab-1.0.0/tests/test_huggingface_and_arena.py +128 -0
- infralab-1.0.0/tests/test_lab_runner.py +201 -0
- infralab-1.0.0/tests/test_leak_sudden.py +237 -0
- infralab-1.0.0/tests/test_library_api.py +167 -0
- infralab-1.0.0/tests/test_linee_guida.py +148 -0
- infralab-1.0.0/tests/test_nbi_and_sustainability.py +113 -0
- infralab-1.0.0/tests/test_nbi_calibration.py +137 -0
- infralab-1.0.0/tests/test_new_asset_types.py +179 -0
- infralab-1.0.0/tests/test_obligation_enforcement.py +79 -0
- infralab-1.0.0/tests/test_persistence.py +126 -0
- infralab-1.0.0/tests/test_physics_extensions.py +169 -0
- infralab-1.0.0/tests/test_planner_belief.py +292 -0
- infralab-1.0.0/tests/test_plugins.py +239 -0
- infralab-1.0.0/tests/test_policy_sandbox.py +464 -0
- infralab-1.0.0/tests/test_profile_appoggi.py +15 -0
- infralab-1.0.0/tests/test_progressive_events.py +827 -0
- infralab-1.0.0/tests/test_regimes.py +446 -0
- infralab-1.0.0/tests/test_regional_events.py +277 -0
- infralab-1.0.0/tests/test_reliability_fosm.py +196 -0
- infralab-1.0.0/tests/test_retaining_walls.py +81 -0
- infralab-1.0.0/tests/test_revenue_source.py +29 -0
- infralab-1.0.0/tests/test_strict_information.py +251 -0
- infralab-1.0.0/tests/test_study.py +157 -0
- infralab-1.0.0/tests/test_sudden_damage.py +374 -0
- infralab-1.0.0/tests/test_truncated_ln.py +162 -0
- infralab-1.0.0/tests/test_typical_owner_options.py +169 -0
- infralab-1.0.0/tests/test_validation_options.py +161 -0
infralab-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Giulio Mariniello
|
|
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.
|
infralab-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: infralab
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Open library for bridge management: exchangeable stocks, degradation laws, regulations and players, compared on common random numbers.
|
|
5
|
+
Author: Giulio Mariniello
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: bridge management,infrastructure,benchmark,regulation,reinforcement learning,MCP,language-model agents
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: numpy>=1.26
|
|
16
|
+
Requires-Dist: scipy>=1.14
|
|
17
|
+
Requires-Dist: pandas>=2.2
|
|
18
|
+
Requires-Dist: PyYAML>=6.0
|
|
19
|
+
Requires-Dist: filelock>=3.13
|
|
20
|
+
Provides-Extra: mcp
|
|
21
|
+
Requires-Dist: mcp>=1.2; extra == "mcp"
|
|
22
|
+
Provides-Extra: anthropic
|
|
23
|
+
Requires-Dist: anthropic>=0.60; extra == "anthropic"
|
|
24
|
+
Provides-Extra: openai
|
|
25
|
+
Requires-Dist: openai>=1.40; extra == "openai"
|
|
26
|
+
Provides-Extra: llm
|
|
27
|
+
Requires-Dist: anthropic>=0.60; extra == "llm"
|
|
28
|
+
Requires-Dist: openai>=1.40; extra == "llm"
|
|
29
|
+
Requires-Dist: google-generativeai>=0.8; extra == "llm"
|
|
30
|
+
Requires-Dist: huggingface_hub>=0.28; extra == "llm"
|
|
31
|
+
Provides-Extra: web
|
|
32
|
+
Requires-Dist: fastapi>=0.110; extra == "web"
|
|
33
|
+
Requires-Dist: uvicorn[standard]>=0.27; extra == "web"
|
|
34
|
+
Requires-Dist: streamlit>=1.40; extra == "web"
|
|
35
|
+
Requires-Dist: plotly>=5; extra == "web"
|
|
36
|
+
Requires-Dist: matplotlib>=3.8; extra == "web"
|
|
37
|
+
Requires-Dist: seaborn>=0.13; extra == "web"
|
|
38
|
+
Requires-Dist: openpyxl>=3.1; extra == "web"
|
|
39
|
+
Requires-Dist: XlsxWriter>=3.2; extra == "web"
|
|
40
|
+
Requires-Dist: pyarrow>=15; extra == "web"
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
43
|
+
Requires-Dist: pytest-timeout>=2.3; extra == "dev"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# InfraLab
|
|
47
|
+
|
|
48
|
+
InfraLab is an open Python library in which the management of a bridge stock is a game played quarter by
|
|
49
|
+
quarter under partial information, a budget and a regulation. The four choices behind a bridge-management
|
|
50
|
+
study are exchangeable pieces:
|
|
51
|
+
|
|
52
|
+
| Piece | Form | Argument |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| Stock | a file of bridges with their inventory attributes (an importer reads National Bridge Inventory files) | `scenario` |
|
|
55
|
+
| Degradation law | a Python function `degrade(ctx)` that returns the new condition of a component | `degradation` |
|
|
56
|
+
| Regulation | a YAML file of duties: trigger, action, deadline, enforcement | `law` |
|
|
57
|
+
| Player | a function from the observation to a plan | `policy` |
|
|
58
|
+
|
|
59
|
+
Every random draw is keyed to seed, process, bridge and quarter, so two settings meet the same events and are
|
|
60
|
+
compared seed by seed in one call. A game is replayed exactly from its settings, its seed and the library version.
|
|
61
|
+
|
|
62
|
+
## Install
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install infralab # core: numpy, scipy, pandas, PyYAML
|
|
66
|
+
pip install "infralab[mcp]" # MCP server
|
|
67
|
+
pip install "infralab[anthropic]" # Claude players
|
|
68
|
+
pip install "infralab[openai]" # OpenAI and compatible endpoints
|
|
69
|
+
pip install "infralab[web]" # web platform (board for human players)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
From the source: `pip install .` in the root of the repository.
|
|
73
|
+
|
|
74
|
+
## Quick start
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
import infralab
|
|
78
|
+
|
|
79
|
+
env = infralab.make("regbench/aged-30", law="regbench/D1", seed=0, horizon_years=10)
|
|
80
|
+
obs, info = env.reset()
|
|
81
|
+
while not env.done:
|
|
82
|
+
plan = {} # {asset_id: action_name}; empty = no action
|
|
83
|
+
obs, reward, terminated, truncated, info = env.step(plan)
|
|
84
|
+
print(env.summary())
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`env.compact_observation()` gives what the owner knows in a few hundred characters per asset;
|
|
88
|
+
`env.feasible_actions(asset_id)` the actions available with their cost; `env.rules()` the rules of the run;
|
|
89
|
+
`info["report"]` what happened in the quarter (accepted and dropped requests, inspections forced by the
|
|
90
|
+
regulation, interventions of the ethical floor, collapses).
|
|
91
|
+
|
|
92
|
+
## Outcomes
|
|
93
|
+
|
|
94
|
+
`run`, `Env.summary` and `compare_laws` report outcomes computed on the true state, never shown to a player:
|
|
95
|
+
|
|
96
|
+
| Key | Meaning |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `S1` | mean over bridges of the lowest true annual reliability index reached during the game |
|
|
99
|
+
| `expected_collapses` | expected strength collapses: sum over bridges and quarters of the quarterly failure probability of the true state |
|
|
100
|
+
| `expected_hydraulic_collapses` | expected collapses from progressive hydraulic defects that matured unarrested |
|
|
101
|
+
| `bridges_below_limit`, `bridge_years_below_limit` | bridges, and bridge-years, with a true annual index below 3.02 |
|
|
102
|
+
| `collapses` | collapses drawn in the game |
|
|
103
|
+
| `cash_spend`, `user_cost`, `closed_share` | what the owner paid, the extra travel cost of road users, the closed share of bridge-years |
|
|
104
|
+
|
|
105
|
+
## Change one piece
|
|
106
|
+
|
|
107
|
+
### The player
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
def inspect_then_fix(observation):
|
|
111
|
+
bridges = observation["my_bridges"]
|
|
112
|
+
plan = {b["id"]: "inspect" for b in bridges if b["inspection_overdue"]}
|
|
113
|
+
free = [b for b in bridges if b["id"] not in plan and b["active_maintenance"] is None]
|
|
114
|
+
if free:
|
|
115
|
+
worst = min(free, key=lambda b: b["min_observed_condition"])
|
|
116
|
+
if worst["min_observed_condition"] < 0.3:
|
|
117
|
+
plan[worst["id"]] = "restore"
|
|
118
|
+
return plan
|
|
119
|
+
|
|
120
|
+
infralab.run(inspect_then_fix, "regbench/aged-30", law="regbench/D1", config="paper-short")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`infralab.policies()` lists the supplied players (`passive`, `worst_first`, `typical`, `diligent`, `heuristic`,
|
|
124
|
+
`monte_carlo`, `q_learning`, `genetic`, `memetic`); `basic:<rule>` reaches the other fixed rules
|
|
125
|
+
(`infralab.basic_policies()`); `infralab.actions()` lists the action names accepted in a plan.
|
|
126
|
+
|
|
127
|
+
### The regulation
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
r = infralab.compare_laws("regbench/D0", "regbench/D1", policy="passive",
|
|
131
|
+
scenario="regbench/aged-147", config="paper",
|
|
132
|
+
params={"ethical_floor_enabled": True}, n_seeds=10, workers=5)
|
|
133
|
+
r["difference_b_minus_a"]["S1"] # mean paired difference, lower and upper bound (95 percent)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The two regulations are played on the same stock, player and random draws. A regulation is a YAML file:
|
|
137
|
+
pass a path instead of an identifier to test your own (`examples/04_inspection_interval.py` writes nine).
|
|
138
|
+
|
|
139
|
+
### The degradation law
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
# markov.py: a Markov chain on inventory ratings
|
|
143
|
+
P = {9: 0.1483, 8: 0.0672, 7: 0.0334, 6: 0.0321, 5: 0.0355, 4: 0.0421, 3: 0.0428, 2: 0.0852, 1: 0.0193}
|
|
144
|
+
D = {9: 1.12, 8: 1.06, 7: 1.08, 6: 1.14, 5: 1.27, 4: 1.35, 3: 2.22, 2: 1.80, 1: 1.00}
|
|
145
|
+
|
|
146
|
+
def degrade(ctx):
|
|
147
|
+
c = ctx["condition"]
|
|
148
|
+
if ctx["age_increment_years"] <= 0:
|
|
149
|
+
return c
|
|
150
|
+
r = min(9, max(1, round(9 * c)))
|
|
151
|
+
if ctx["rand"]() < 1 - (1 - P[r]) ** 0.25:
|
|
152
|
+
c -= D[r] / 9
|
|
153
|
+
return c
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
infralab.compare_laws("regbench/D0", "regbench/D1", policy="passive", scenario="regbench/aged-147",
|
|
158
|
+
config="paper", degradation="markov.py", n_seeds=10, workers=5)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`infralab.degradation_laws()` lists the laws supplied: `weibull` (default), `gamma`, `regbench/power`,
|
|
162
|
+
`regbench/markov-nbi`.
|
|
163
|
+
|
|
164
|
+
### The stock
|
|
165
|
+
|
|
166
|
+
`infralab.data.nbi` reads a National Bridge Inventory file and builds a scenario
|
|
167
|
+
(`python -m infralab.data.nbi --help`); `infralab.load_scenario(path)` reads any scenario file.
|
|
168
|
+
|
|
169
|
+
## The benchmark of the paper
|
|
170
|
+
|
|
171
|
+
| Item | Identifiers |
|
|
172
|
+
|---|---|
|
|
173
|
+
| Stocks | `regbench/aged-147`, `regbench/aged-30`: aged samples of the 2023 Delaware inventory |
|
|
174
|
+
| Regulations | `regbench/D0` no obligation; `regbench/N24` routine inspection every 24 months, 12 for rating 3 or lower; `regbench/D1` intervals graded by attention class; `D2` post-event; `D3` both; `K0` flat biennial; `G0`–`G3`, `J0`–`J1` with a practice rule |
|
|
175
|
+
| Configurations | `paper` (147 bridges, 50 years; default), `paper-short` (30 bridges, 5 years), `paper-less-optimistic` (sensitivity of the reliability calibration); earlier configurations `first`, `second`, `third`, `calibrated`, `calibrated-short` |
|
|
176
|
+
|
|
177
|
+
`examples/` holds the examples of the paper: `01_change_the_regulation.py`, `02_change_the_degradation_law.py`,
|
|
178
|
+
`03_change_the_player.py` and `04_inspection_interval.py`. Called as in these scripts, the library reproduces
|
|
179
|
+
the stored games of the paper to the last digit (`tests/paper_reproduction_check.py`).
|
|
180
|
+
|
|
181
|
+
The parameter values are plausible and, apart from the matching of the reliability index to load ratings, not
|
|
182
|
+
calibrated. The reliability index serves paired comparisons between settings, not levels; results carry to a
|
|
183
|
+
real stock only through a stock and a degradation law fitted to it.
|
|
184
|
+
|
|
185
|
+
## Interfaces
|
|
186
|
+
|
|
187
|
+
**Command line**
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
infralab list
|
|
191
|
+
infralab run worst_first --law regbench/D1 --years 10
|
|
192
|
+
infralab compare regbench/D0 regbench/D1 --policy passive --seeds 10 --years 10
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**MCP server.** Any Model Context Protocol client can manage a stock through tools.
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
infralab-mcp # stdio
|
|
199
|
+
infralab-mcp --transport sse # HTTP
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{"mcpServers": {"infralab": {"command": "infralab-mcp", "env": {"INFRALAB_RUN_DIR": "runs"}}}}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Tools: `list_scenarios`, `list_laws`, `describe_law`, `list_policies`, `new_run`, `rules`, `observe`,
|
|
207
|
+
`asset_actions`, `action_catalogue`, `step`, `score`, `run_policy`, `compare_laws`. The server holds no key and
|
|
208
|
+
calls no model. During a run it returns only what the owner may know; the outcomes computed on the true state
|
|
209
|
+
are released when the run is over.
|
|
210
|
+
|
|
211
|
+
**Language-model players.**
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from infralab.llm import play
|
|
215
|
+
|
|
216
|
+
play("anthropic:claude-opus-5-5", "regbench/aged-30", law="regbench/D1", seed=0)
|
|
217
|
+
play("openai:gpt-4.1-mini", "regbench/aged-30", law="regbench/D1", seed=0)
|
|
218
|
+
play("openai:llama3.1", base_url="http://localhost:11434/v1") # any endpoint with the same protocol
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Keys are read from the environment (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or the variable named with `api_key_env`).
|
|
222
|
+
|
|
223
|
+
**Web platform.** A board for human players (FastAPI backend and React frontend, see `docs/WEBAPP.md`), and the
|
|
224
|
+
Streamlit interface `app.py`.
|
|
225
|
+
|
|
226
|
+
## Tests
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
pip install ".[dev]"
|
|
230
|
+
pytest
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Citation
|
|
234
|
+
|
|
235
|
+
See `CITATION.cff`. Model documentation: `docs/MODEL_REPORT.md`; numerical changes to the engine:
|
|
236
|
+
`docs/CHANGELOG_MODEL.md`.
|
|
237
|
+
|
|
238
|
+
## License
|
|
239
|
+
|
|
240
|
+
MIT, see `LICENSE`.
|
infralab-1.0.0/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# InfraLab
|
|
2
|
+
|
|
3
|
+
InfraLab is an open Python library in which the management of a bridge stock is a game played quarter by
|
|
4
|
+
quarter under partial information, a budget and a regulation. The four choices behind a bridge-management
|
|
5
|
+
study are exchangeable pieces:
|
|
6
|
+
|
|
7
|
+
| Piece | Form | Argument |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Stock | a file of bridges with their inventory attributes (an importer reads National Bridge Inventory files) | `scenario` |
|
|
10
|
+
| Degradation law | a Python function `degrade(ctx)` that returns the new condition of a component | `degradation` |
|
|
11
|
+
| Regulation | a YAML file of duties: trigger, action, deadline, enforcement | `law` |
|
|
12
|
+
| Player | a function from the observation to a plan | `policy` |
|
|
13
|
+
|
|
14
|
+
Every random draw is keyed to seed, process, bridge and quarter, so two settings meet the same events and are
|
|
15
|
+
compared seed by seed in one call. A game is replayed exactly from its settings, its seed and the library version.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install infralab # core: numpy, scipy, pandas, PyYAML
|
|
21
|
+
pip install "infralab[mcp]" # MCP server
|
|
22
|
+
pip install "infralab[anthropic]" # Claude players
|
|
23
|
+
pip install "infralab[openai]" # OpenAI and compatible endpoints
|
|
24
|
+
pip install "infralab[web]" # web platform (board for human players)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
From the source: `pip install .` in the root of the repository.
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import infralab
|
|
33
|
+
|
|
34
|
+
env = infralab.make("regbench/aged-30", law="regbench/D1", seed=0, horizon_years=10)
|
|
35
|
+
obs, info = env.reset()
|
|
36
|
+
while not env.done:
|
|
37
|
+
plan = {} # {asset_id: action_name}; empty = no action
|
|
38
|
+
obs, reward, terminated, truncated, info = env.step(plan)
|
|
39
|
+
print(env.summary())
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`env.compact_observation()` gives what the owner knows in a few hundred characters per asset;
|
|
43
|
+
`env.feasible_actions(asset_id)` the actions available with their cost; `env.rules()` the rules of the run;
|
|
44
|
+
`info["report"]` what happened in the quarter (accepted and dropped requests, inspections forced by the
|
|
45
|
+
regulation, interventions of the ethical floor, collapses).
|
|
46
|
+
|
|
47
|
+
## Outcomes
|
|
48
|
+
|
|
49
|
+
`run`, `Env.summary` and `compare_laws` report outcomes computed on the true state, never shown to a player:
|
|
50
|
+
|
|
51
|
+
| Key | Meaning |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `S1` | mean over bridges of the lowest true annual reliability index reached during the game |
|
|
54
|
+
| `expected_collapses` | expected strength collapses: sum over bridges and quarters of the quarterly failure probability of the true state |
|
|
55
|
+
| `expected_hydraulic_collapses` | expected collapses from progressive hydraulic defects that matured unarrested |
|
|
56
|
+
| `bridges_below_limit`, `bridge_years_below_limit` | bridges, and bridge-years, with a true annual index below 3.02 |
|
|
57
|
+
| `collapses` | collapses drawn in the game |
|
|
58
|
+
| `cash_spend`, `user_cost`, `closed_share` | what the owner paid, the extra travel cost of road users, the closed share of bridge-years |
|
|
59
|
+
|
|
60
|
+
## Change one piece
|
|
61
|
+
|
|
62
|
+
### The player
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
def inspect_then_fix(observation):
|
|
66
|
+
bridges = observation["my_bridges"]
|
|
67
|
+
plan = {b["id"]: "inspect" for b in bridges if b["inspection_overdue"]}
|
|
68
|
+
free = [b for b in bridges if b["id"] not in plan and b["active_maintenance"] is None]
|
|
69
|
+
if free:
|
|
70
|
+
worst = min(free, key=lambda b: b["min_observed_condition"])
|
|
71
|
+
if worst["min_observed_condition"] < 0.3:
|
|
72
|
+
plan[worst["id"]] = "restore"
|
|
73
|
+
return plan
|
|
74
|
+
|
|
75
|
+
infralab.run(inspect_then_fix, "regbench/aged-30", law="regbench/D1", config="paper-short")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`infralab.policies()` lists the supplied players (`passive`, `worst_first`, `typical`, `diligent`, `heuristic`,
|
|
79
|
+
`monte_carlo`, `q_learning`, `genetic`, `memetic`); `basic:<rule>` reaches the other fixed rules
|
|
80
|
+
(`infralab.basic_policies()`); `infralab.actions()` lists the action names accepted in a plan.
|
|
81
|
+
|
|
82
|
+
### The regulation
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
r = infralab.compare_laws("regbench/D0", "regbench/D1", policy="passive",
|
|
86
|
+
scenario="regbench/aged-147", config="paper",
|
|
87
|
+
params={"ethical_floor_enabled": True}, n_seeds=10, workers=5)
|
|
88
|
+
r["difference_b_minus_a"]["S1"] # mean paired difference, lower and upper bound (95 percent)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The two regulations are played on the same stock, player and random draws. A regulation is a YAML file:
|
|
92
|
+
pass a path instead of an identifier to test your own (`examples/04_inspection_interval.py` writes nine).
|
|
93
|
+
|
|
94
|
+
### The degradation law
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# markov.py: a Markov chain on inventory ratings
|
|
98
|
+
P = {9: 0.1483, 8: 0.0672, 7: 0.0334, 6: 0.0321, 5: 0.0355, 4: 0.0421, 3: 0.0428, 2: 0.0852, 1: 0.0193}
|
|
99
|
+
D = {9: 1.12, 8: 1.06, 7: 1.08, 6: 1.14, 5: 1.27, 4: 1.35, 3: 2.22, 2: 1.80, 1: 1.00}
|
|
100
|
+
|
|
101
|
+
def degrade(ctx):
|
|
102
|
+
c = ctx["condition"]
|
|
103
|
+
if ctx["age_increment_years"] <= 0:
|
|
104
|
+
return c
|
|
105
|
+
r = min(9, max(1, round(9 * c)))
|
|
106
|
+
if ctx["rand"]() < 1 - (1 - P[r]) ** 0.25:
|
|
107
|
+
c -= D[r] / 9
|
|
108
|
+
return c
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
infralab.compare_laws("regbench/D0", "regbench/D1", policy="passive", scenario="regbench/aged-147",
|
|
113
|
+
config="paper", degradation="markov.py", n_seeds=10, workers=5)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`infralab.degradation_laws()` lists the laws supplied: `weibull` (default), `gamma`, `regbench/power`,
|
|
117
|
+
`regbench/markov-nbi`.
|
|
118
|
+
|
|
119
|
+
### The stock
|
|
120
|
+
|
|
121
|
+
`infralab.data.nbi` reads a National Bridge Inventory file and builds a scenario
|
|
122
|
+
(`python -m infralab.data.nbi --help`); `infralab.load_scenario(path)` reads any scenario file.
|
|
123
|
+
|
|
124
|
+
## The benchmark of the paper
|
|
125
|
+
|
|
126
|
+
| Item | Identifiers |
|
|
127
|
+
|---|---|
|
|
128
|
+
| Stocks | `regbench/aged-147`, `regbench/aged-30`: aged samples of the 2023 Delaware inventory |
|
|
129
|
+
| Regulations | `regbench/D0` no obligation; `regbench/N24` routine inspection every 24 months, 12 for rating 3 or lower; `regbench/D1` intervals graded by attention class; `D2` post-event; `D3` both; `K0` flat biennial; `G0`–`G3`, `J0`–`J1` with a practice rule |
|
|
130
|
+
| Configurations | `paper` (147 bridges, 50 years; default), `paper-short` (30 bridges, 5 years), `paper-less-optimistic` (sensitivity of the reliability calibration); earlier configurations `first`, `second`, `third`, `calibrated`, `calibrated-short` |
|
|
131
|
+
|
|
132
|
+
`examples/` holds the examples of the paper: `01_change_the_regulation.py`, `02_change_the_degradation_law.py`,
|
|
133
|
+
`03_change_the_player.py` and `04_inspection_interval.py`. Called as in these scripts, the library reproduces
|
|
134
|
+
the stored games of the paper to the last digit (`tests/paper_reproduction_check.py`).
|
|
135
|
+
|
|
136
|
+
The parameter values are plausible and, apart from the matching of the reliability index to load ratings, not
|
|
137
|
+
calibrated. The reliability index serves paired comparisons between settings, not levels; results carry to a
|
|
138
|
+
real stock only through a stock and a degradation law fitted to it.
|
|
139
|
+
|
|
140
|
+
## Interfaces
|
|
141
|
+
|
|
142
|
+
**Command line**
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
infralab list
|
|
146
|
+
infralab run worst_first --law regbench/D1 --years 10
|
|
147
|
+
infralab compare regbench/D0 regbench/D1 --policy passive --seeds 10 --years 10
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**MCP server.** Any Model Context Protocol client can manage a stock through tools.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
infralab-mcp # stdio
|
|
154
|
+
infralab-mcp --transport sse # HTTP
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{"mcpServers": {"infralab": {"command": "infralab-mcp", "env": {"INFRALAB_RUN_DIR": "runs"}}}}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Tools: `list_scenarios`, `list_laws`, `describe_law`, `list_policies`, `new_run`, `rules`, `observe`,
|
|
162
|
+
`asset_actions`, `action_catalogue`, `step`, `score`, `run_policy`, `compare_laws`. The server holds no key and
|
|
163
|
+
calls no model. During a run it returns only what the owner may know; the outcomes computed on the true state
|
|
164
|
+
are released when the run is over.
|
|
165
|
+
|
|
166
|
+
**Language-model players.**
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from infralab.llm import play
|
|
170
|
+
|
|
171
|
+
play("anthropic:claude-opus-5-5", "regbench/aged-30", law="regbench/D1", seed=0)
|
|
172
|
+
play("openai:gpt-4.1-mini", "regbench/aged-30", law="regbench/D1", seed=0)
|
|
173
|
+
play("openai:llama3.1", base_url="http://localhost:11434/v1") # any endpoint with the same protocol
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Keys are read from the environment (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or the variable named with `api_key_env`).
|
|
177
|
+
|
|
178
|
+
**Web platform.** A board for human players (FastAPI backend and React frontend, see `docs/WEBAPP.md`), and the
|
|
179
|
+
Streamlit interface `app.py`.
|
|
180
|
+
|
|
181
|
+
## Tests
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
pip install ".[dev]"
|
|
185
|
+
pytest
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Citation
|
|
189
|
+
|
|
190
|
+
See `CITATION.cff`. Model documentation: `docs/MODEL_REPORT.md`; numerical changes to the engine:
|
|
191
|
+
`docs/CHANGELOG_MODEL.md`.
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT, see `LICENSE`.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""InfraLab — laboratorio virtuale per la gestione di asset infrastrutturali.
|
|
2
|
+
|
|
3
|
+
Pacchetto nato dal refactoring di BridgeManager: motore di affidabilità
|
|
4
|
+
FOSM/Bayesiano generico, simulatore a turni con degrado stocastico, agenti
|
|
5
|
+
decisionali (euristici, ottimizzazione, LLM) e UI Streamlit per esperimenti
|
|
6
|
+
con soggetti umani e campagne computazionali.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__version__ = "1.0.0"
|
|
10
|
+
|
|
11
|
+
# API pubblica della libreria (infralab.api): import pigri, cosi' `import infralab` resta leggero.
|
|
12
|
+
_API = ("make", "run", "actions", "compare_laws", "scenarios", "load_scenario", "laws", "describe_law", "configurations",
|
|
13
|
+
"configuration", "degradation_laws", "resolve_degradation", "policies", "basic_policies", "policy_config", "child_seeds", "Env")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def __getattr__(name):
|
|
17
|
+
if name in _API:
|
|
18
|
+
from infralab import api
|
|
19
|
+
return getattr(api, name)
|
|
20
|
+
raise AttributeError(f"module 'infralab' has no attribute {name!r}")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def __dir__():
|
|
24
|
+
return sorted(list(globals()) + list(_API))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from infralab.agents.legacy import * # noqa: F401,F403
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Adapter tra gli agenti legacy (AgentInterface.decide(env)) e il protocollo gym-like.
|
|
2
|
+
|
|
3
|
+
Gli agenti storici decidono leggendo l'intero BridgeEnv; il protocollo passa
|
|
4
|
+
l'osservazione e mette l'ambiente in info["env"] al reset. L'adapter conserva
|
|
5
|
+
quel riferimento e delega a decide().
|
|
6
|
+
|
|
7
|
+
Con policy_sandbox=True l'adapter (lato harness) resta l'unico a tenere il motore:
|
|
8
|
+
alla policy passa, a ogni decisione, un PlanningHandle nuovo (infralab.agents.sandbox).
|
|
9
|
+
Un accesso vietato (SandboxViolation) non ferma la partita: la decisione del
|
|
10
|
+
trimestre diventa "nessuna azione" e l'accesso e' registrato in `sandbox_violations`.
|
|
11
|
+
"""
|
|
12
|
+
from typing import Any, Dict, List
|
|
13
|
+
|
|
14
|
+
from infralab.agents.legacy import AgentInterface, agent_factory
|
|
15
|
+
from infralab.agents.sandbox import PlanningHandle, SandboxViolation, sandbox_enabled
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LegacyAgentAdapter:
|
|
19
|
+
"""Avvolge un AgentInterface legacy nel protocollo Agent (reset/act)."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, legacy_agent: AgentInterface, **decide_kwargs: Any):
|
|
22
|
+
self.legacy_agent = legacy_agent
|
|
23
|
+
self.decide_kwargs = decide_kwargs
|
|
24
|
+
self._env = None
|
|
25
|
+
self.last_rationale: Any = None
|
|
26
|
+
# Diagnostica del sandbox: [{"quarter", "access"}] (vuota senza sandbox).
|
|
27
|
+
self.sandbox_violations: List[Dict[str, Any]] = []
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def name(self) -> str:
|
|
31
|
+
return self.legacy_agent.name
|
|
32
|
+
|
|
33
|
+
def reset(self, observation: Dict[str, Any], info: Dict[str, Any]) -> None:
|
|
34
|
+
self._env = info.get("env")
|
|
35
|
+
if self._env is None:
|
|
36
|
+
raise ValueError("LegacyAgentAdapter richiede info['env'] al reset.")
|
|
37
|
+
|
|
38
|
+
def act(self, observation: Dict[str, Any]) -> Dict[str, Any]:
|
|
39
|
+
if self._env is None:
|
|
40
|
+
raise RuntimeError("Chiamare reset() prima di act().")
|
|
41
|
+
if not sandbox_enabled(self._env):
|
|
42
|
+
plan, rationale = self.legacy_agent.decide(self._env, **self.decide_kwargs)
|
|
43
|
+
self.last_rationale = rationale
|
|
44
|
+
return plan or {}
|
|
45
|
+
handle = PlanningHandle.build(self._env, self.name)
|
|
46
|
+
try:
|
|
47
|
+
plan, rationale = self.legacy_agent.decide(handle, **self.decide_kwargs)
|
|
48
|
+
except SandboxViolation as e:
|
|
49
|
+
plan, rationale = {}, ("sandbox_violation", str(e))
|
|
50
|
+
for access in handle.violations:
|
|
51
|
+
self.sandbox_violations.append({"quarter": handle.quarter, "access": access})
|
|
52
|
+
self.last_rationale = rationale
|
|
53
|
+
return plan if isinstance(plan, dict) else {}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def make_agent(name: str, config: Dict[str, Any], **decide_kwargs: Any) -> LegacyAgentAdapter:
|
|
57
|
+
"""Costruisce un agente protocol-compatibile dalla config legacy.
|
|
58
|
+
|
|
59
|
+
config: {"type": "Euristico" | "LLM" | "GA Orizzonte Variabile" | ...}
|
|
60
|
+
"""
|
|
61
|
+
return LegacyAgentAdapter(agent_factory(name, config), **decide_kwargs)
|