bamengine 0.9.0__tar.gz → 0.9.2__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.
- {bamengine-0.9.0/src/bamengine.egg-info → bamengine-0.9.2}/PKG-INFO +12 -20
- {bamengine-0.9.0 → bamengine-0.9.2}/README.md +11 -19
- {bamengine-0.9.0 → bamengine-0.9.2}/pyproject.toml +5 -1
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/__init__.py +1 -1
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/config/__init__.py +5 -1
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/core/relationship.py +2 -2
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/logging.py +5 -1
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/simulation.py +86 -30
- {bamengine-0.9.0 → bamengine-0.9.2/src/bamengine.egg-info}/PKG-INFO +12 -20
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/engine.py +1 -1
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/robustness/internal_validity.py +36 -10
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/robustness/sensitivity.py +6 -1
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/robustness/viz.py +6 -5
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/_utils.py +44 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/baseline/__init__.py +1 -1
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/baseline/targets.yaml +8 -8
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/baseline/viz.py +14 -2
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/buffer_stock/__init__.py +1 -1
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/growth_plus/__init__.py +1 -3
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/growth_plus/viz.py +11 -38
- {bamengine-0.9.0 → bamengine-0.9.2}/LICENSE +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/setup.cfg +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/config/default_pipeline.yml +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/config/defaults.yml +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/config/schema.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/config/validator.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/core/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/core/agent.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/core/decorators.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/core/event.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/core/pipeline.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/core/registry.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/core/role.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/economy.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/_internal/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/_internal/bankruptcy.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/_internal/credit_market.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/_internal/goods_market.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/_internal/labor_market.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/_internal/planning.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/_internal/production.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/_internal/revenue.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/bankruptcy.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/credit_market.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/economy_stats.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/goods_market.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/labor_market.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/planning.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/production.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/events/revenue.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/extension.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/logging.pyi +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/ops.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/py.typed +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/relationships/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/relationships/loanbook.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/results.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/roles/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/roles/borrower.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/roles/consumer.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/roles/employer.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/roles/lender.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/roles/producer.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/roles/shareholder.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/roles/worker.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/typing.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine/utils.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine.egg-info/SOURCES.txt +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine.egg-info/dependency_links.txt +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine.egg-info/requires.txt +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/bamengine.egg-info/top_level.txt +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/__main__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/analysis.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/cli.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/cost.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/cross_eval.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/grid.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/io.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/morris.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/parameter_space.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/reporting.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/rescreen.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/screening.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/sensitivity.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/stability.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/calibration/sweep.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/extensions/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/extensions/buffer_stock/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/extensions/buffer_stock/events.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/extensions/buffer_stock/role.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/extensions/rnd/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/extensions/rnd/events.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/extensions/rnd/role.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/extensions/taxation/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/extensions/taxation/events.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/reporting.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/robustness/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/robustness/__main__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/robustness/experiments.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/robustness/reference_values.yaml +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/robustness/reporting.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/robustness/stats.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/robustness/structural.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/__init__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/baseline/__main__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/buffer_stock/__main__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/buffer_stock/targets.yaml +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/buffer_stock/viz.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/growth_plus/__main__.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scenarios/growth_plus/targets.yaml +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/scoring.py +0 -0
- {bamengine-0.9.0 → bamengine-0.9.2}/src/validation/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bamengine
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.2
|
|
4
4
|
Summary: A modular Python framework for the BAM agent-based macroeconomic model
|
|
5
5
|
Author-email: Kostas Ganitis <ganitiskostas@gmail.com>
|
|
6
6
|
Maintainer-email: Kostas Ganitis <ganitiskostas@gmail.com>
|
|
@@ -83,17 +83,10 @@ Dynamic: license-file
|
|
|
83
83
|
<p align="center">
|
|
84
84
|
A Python implementation of the BAM (Bottom-Up Adaptive Macroeconomics) model
|
|
85
85
|
from <a href="https://doi.org/10.1007/978-88-470-1971-3"><em>Macroeconomics from the Bottom-up</em></a>
|
|
86
|
-
(Delli Gatti et al., 2011).
|
|
87
|
-
interacting
|
|
88
|
-
|
|
89
|
-
</
|
|
90
|
-
|
|
91
|
-
<p align="center">
|
|
92
|
-
<em>Traditional economics models economies from the top down, assuming
|
|
93
|
-
markets automatically reach equilibrium. BAM Engine takes a different approach:
|
|
94
|
-
it simulates individual workers, firms, and banks making decisions and
|
|
95
|
-
interacting in markets, letting macroeconomic patterns emerge from the
|
|
96
|
-
bottom up.</em>
|
|
86
|
+
(Delli Gatti et al., 2011). It runs simulations of individual workers, firms, and
|
|
87
|
+
banks making decisions and interacting in labor, credit and goods markets, letting
|
|
88
|
+
macroeconomic patterns (growth, unemployment, inflation, business cycles) <b>emerge
|
|
89
|
+
from the bottom up</b>, instead of assuming them with aggregate equations.
|
|
97
90
|
</p>
|
|
98
91
|
|
|
99
92
|
<p align="center">
|
|
@@ -172,17 +165,16 @@ See the [Getting Started guide](https://bam-engine.readthedocs.io/en/latest/quic
|
|
|
172
165
|
|
|
173
166
|
## Features
|
|
174
167
|
|
|
175
|
-
- **Complete BAM
|
|
176
|
-
- **
|
|
177
|
-
- **
|
|
178
|
-
- **
|
|
179
|
-
- **Validation Framework:** Three scenario validators with scoring and robustness analysis
|
|
180
|
-
- **Calibration Pipeline:** Morris screening, grid search, and tiered stability testing
|
|
181
|
-
- **Easy Configuration:** All parameters configurable without code changes via YAML files
|
|
168
|
+
- **Complete BAM Implementation:** Baseline model from *Macroeconomics from the Bottom-up*, Chapter 3 (firms, households, and banks across labor, credit, and goods markets), three built-in extensions (R&D / Growth+, buffer-stock consumption, taxation), and a robustness analysis suite (internal validity, sensitivity, structural experiments).
|
|
169
|
+
- **Vectorized Performance:** Agent state lives in parallel NumPy arrays and behavior is expressed as array transformations, not Python loops over agent objects. Simulations scale to large populations and long horizons.
|
|
170
|
+
- **Pluggable Extension System:** Add custom roles, events, and pipeline hooks via decorators without modifying the engine. The same mechanism powers the built-in extensions.
|
|
171
|
+
- **Parameter Calibration:** Automated pipeline for tuning model parameters against validation targets.
|
|
182
172
|
|
|
183
173
|
## Architecture
|
|
184
174
|
|
|
185
|
-
BAM Engine uses an ECS (Entity-Component-System) architecture: agents are lightweight entities, state lives in Role components stored as NumPy arrays, and behavior is defined by Event systems
|
|
175
|
+
BAM Engine uses an ECS (Entity-Component-System) architecture: agents are lightweight entities, state lives in **Role** components stored as parallel NumPy arrays, and behavior is defined by **Event** systems composed into a YAML-configurable pipeline. New roles, events, and relationships can be added through decorators, without modifying core code.
|
|
176
|
+
|
|
177
|
+
The trade-off is a mindset shift: you write agent rules as systems that transform whole arrays of state at once, not as methods on per-agent objects. ECS itself does not demand vectorization, but BAM Engine treats it as the default. Every built-in system processes all agents at once, with the goods market's sequential matching rounds as the deliberate exception where strict per-agent ordering matters.
|
|
186
178
|
|
|
187
179
|
See the [User Guide](https://bam-engine.readthedocs.io/en/latest/user_guide/index.html) for a full walkthrough of the model and its architecture.
|
|
188
180
|
|
|
@@ -13,17 +13,10 @@
|
|
|
13
13
|
<p align="center">
|
|
14
14
|
A Python implementation of the BAM (Bottom-Up Adaptive Macroeconomics) model
|
|
15
15
|
from <a href="https://doi.org/10.1007/978-88-470-1971-3"><em>Macroeconomics from the Bottom-up</em></a>
|
|
16
|
-
(Delli Gatti et al., 2011).
|
|
17
|
-
interacting
|
|
18
|
-
|
|
19
|
-
</
|
|
20
|
-
|
|
21
|
-
<p align="center">
|
|
22
|
-
<em>Traditional economics models economies from the top down, assuming
|
|
23
|
-
markets automatically reach equilibrium. BAM Engine takes a different approach:
|
|
24
|
-
it simulates individual workers, firms, and banks making decisions and
|
|
25
|
-
interacting in markets, letting macroeconomic patterns emerge from the
|
|
26
|
-
bottom up.</em>
|
|
16
|
+
(Delli Gatti et al., 2011). It runs simulations of individual workers, firms, and
|
|
17
|
+
banks making decisions and interacting in labor, credit and goods markets, letting
|
|
18
|
+
macroeconomic patterns (growth, unemployment, inflation, business cycles) <b>emerge
|
|
19
|
+
from the bottom up</b>, instead of assuming them with aggregate equations.
|
|
27
20
|
</p>
|
|
28
21
|
|
|
29
22
|
<p align="center">
|
|
@@ -102,17 +95,16 @@ See the [Getting Started guide](https://bam-engine.readthedocs.io/en/latest/quic
|
|
|
102
95
|
|
|
103
96
|
## Features
|
|
104
97
|
|
|
105
|
-
- **Complete BAM
|
|
106
|
-
- **
|
|
107
|
-
- **
|
|
108
|
-
- **
|
|
109
|
-
- **Validation Framework:** Three scenario validators with scoring and robustness analysis
|
|
110
|
-
- **Calibration Pipeline:** Morris screening, grid search, and tiered stability testing
|
|
111
|
-
- **Easy Configuration:** All parameters configurable without code changes via YAML files
|
|
98
|
+
- **Complete BAM Implementation:** Baseline model from *Macroeconomics from the Bottom-up*, Chapter 3 (firms, households, and banks across labor, credit, and goods markets), three built-in extensions (R&D / Growth+, buffer-stock consumption, taxation), and a robustness analysis suite (internal validity, sensitivity, structural experiments).
|
|
99
|
+
- **Vectorized Performance:** Agent state lives in parallel NumPy arrays and behavior is expressed as array transformations, not Python loops over agent objects. Simulations scale to large populations and long horizons.
|
|
100
|
+
- **Pluggable Extension System:** Add custom roles, events, and pipeline hooks via decorators without modifying the engine. The same mechanism powers the built-in extensions.
|
|
101
|
+
- **Parameter Calibration:** Automated pipeline for tuning model parameters against validation targets.
|
|
112
102
|
|
|
113
103
|
## Architecture
|
|
114
104
|
|
|
115
|
-
BAM Engine uses an ECS (Entity-Component-System) architecture: agents are lightweight entities, state lives in Role components stored as NumPy arrays, and behavior is defined by Event systems
|
|
105
|
+
BAM Engine uses an ECS (Entity-Component-System) architecture: agents are lightweight entities, state lives in **Role** components stored as parallel NumPy arrays, and behavior is defined by **Event** systems composed into a YAML-configurable pipeline. New roles, events, and relationships can be added through decorators, without modifying core code.
|
|
106
|
+
|
|
107
|
+
The trade-off is a mindset shift: you write agent rules as systems that transform whole arrays of state at once, not as methods on per-agent objects. ECS itself does not demand vectorization, but BAM Engine treats it as the default. Every built-in system processes all agents at once, with the goods market's sequential matching rounds as the deliberate exception where strict per-agent ordering matters.
|
|
116
108
|
|
|
117
109
|
See the [User Guide](https://bam-engine.readthedocs.io/en/latest/user_guide/index.html) for a full walkthrough of the model and its architecture.
|
|
118
110
|
|
|
@@ -241,7 +241,11 @@ line-ending = "auto"
|
|
|
241
241
|
docstring-code-format = true
|
|
242
242
|
|
|
243
243
|
[tool.mypy]
|
|
244
|
-
|
|
244
|
+
# 3.12 (not the 3.11 floor) so mypy can parse PEP 695 `type` statements that
|
|
245
|
+
# numpy >= 2.5 ships in its stubs; runtime 3.11 support is guarded by the test
|
|
246
|
+
# matrix. The codebase uses `from __future__ import annotations` and no
|
|
247
|
+
# 3.12-only runtime syntax, so this does not weaken our own checks.
|
|
248
|
+
python_version = "3.12"
|
|
245
249
|
strict = true
|
|
246
250
|
files = ["src/"]
|
|
247
251
|
disallow_untyped_decorators = false # Allow @event, @role decorators
|
|
@@ -42,7 +42,11 @@ Custom pipeline:
|
|
|
42
42
|
|
|
43
43
|
>>> sim = be.Simulation.init(n_firms=100, pipeline_path="custom_pipeline.yml", seed=42)
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
Set log level:
|
|
46
|
+
|
|
47
|
+
>>> sim = be.Simulation.init(log_level="WARNING")
|
|
48
|
+
|
|
49
|
+
Advanced logging (per-event levels):
|
|
46
50
|
|
|
47
51
|
>>> log_config = {
|
|
48
52
|
... "default_level": "DEBUG",
|
|
@@ -212,7 +212,7 @@ class Relationship(
|
|
|
212
212
|
Idx1D
|
|
213
213
|
Array of edge indices where source_ids == source_id
|
|
214
214
|
"""
|
|
215
|
-
return np.
|
|
215
|
+
return np.flatnonzero(self.source_ids[: self.size] == source_id)
|
|
216
216
|
|
|
217
217
|
def query_targets(self, target_id: int) -> Idx1D:
|
|
218
218
|
"""
|
|
@@ -228,7 +228,7 @@ class Relationship(
|
|
|
228
228
|
Idx1D
|
|
229
229
|
Array of edge indices where target_ids == target_id
|
|
230
230
|
"""
|
|
231
|
-
return np.
|
|
231
|
+
return np.flatnonzero(self.target_ids[: self.size] == target_id)
|
|
232
232
|
|
|
233
233
|
def aggregate_by_source(
|
|
234
234
|
self,
|
|
@@ -24,9 +24,13 @@ Use logger in events:
|
|
|
24
24
|
>>> logger.debug("Detailed debug info")
|
|
25
25
|
>>> logger.trace("Very verbose output")
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Set log level:
|
|
28
28
|
|
|
29
29
|
>>> import bamengine as be
|
|
30
|
+
>>> sim = be.Simulation.init(log_level="WARNING")
|
|
31
|
+
|
|
32
|
+
Configure per-event log levels (advanced):
|
|
33
|
+
|
|
30
34
|
>>> log_config = {
|
|
31
35
|
... "default_level": "INFO",
|
|
32
36
|
... "events": {"firms_adjust_price": "DEBUG", "labor_market_round": "WARNING"},
|
|
@@ -62,7 +62,7 @@ from collections.abc import Mapping
|
|
|
62
62
|
from dataclasses import dataclass, field
|
|
63
63
|
from importlib import resources
|
|
64
64
|
from pathlib import Path
|
|
65
|
-
from typing import TYPE_CHECKING, Any, Literal
|
|
65
|
+
from typing import TYPE_CHECKING, Any, Literal, get_args
|
|
66
66
|
|
|
67
67
|
import numpy as np
|
|
68
68
|
import yaml
|
|
@@ -92,7 +92,7 @@ from bamengine.roles import (
|
|
|
92
92
|
from bamengine.relationships import LoanBook # Must import after roles
|
|
93
93
|
|
|
94
94
|
# isort: on
|
|
95
|
-
from bamengine.typing import Float1D
|
|
95
|
+
from bamengine.typing import Bool1D, Float1D, Int1D
|
|
96
96
|
|
|
97
97
|
__all__ = ["Simulation"]
|
|
98
98
|
|
|
@@ -110,6 +110,36 @@ _ANNOTATION_DTYPE: dict[str, tuple[type[np.generic], int]] = {
|
|
|
110
110
|
"Idx1D": (np.intp, -1),
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
# Resolved type-alias objects → (numpy dtype, fill value), used when annotations
|
|
114
|
+
# are real objects (no ``from __future__ import annotations`` in the role's module).
|
|
115
|
+
# Keying on the alias objects is stable across numpy versions, unlike the internal
|
|
116
|
+
# ``__args__`` layout of ``NDArray[...]`` (which changed in numpy 2.5). Idx1D is
|
|
117
|
+
# intentionally absent: it equals Int1D on 64-bit platforms (np.intp is np.int64),
|
|
118
|
+
# so a resolved agent-id field maps to (int, 0); the -1 sentinel is reachable via
|
|
119
|
+
# the Agent class or string annotations.
|
|
120
|
+
_ALIAS_DTYPE: dict[Any, tuple[type[np.generic], int]] = {
|
|
121
|
+
Float1D: (np.float64, 0),
|
|
122
|
+
Int1D: (np.int64, 0),
|
|
123
|
+
Bool1D: (np.bool_, 0),
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _np_scalar_from_annotation(ann: Any) -> type[np.generic] | None:
|
|
128
|
+
"""Extract the numpy scalar type from an ``NDArray[...]`` annotation.
|
|
129
|
+
|
|
130
|
+
Robust to the numpy 2.5 layout change where ``NDArray[np.int64].__args__``
|
|
131
|
+
became ``(np.int64,)`` instead of ``(Any, np.dtype[np.int64])``. Returns
|
|
132
|
+
``None`` when no numpy scalar can be found.
|
|
133
|
+
"""
|
|
134
|
+
for arg in get_args(ann):
|
|
135
|
+
if isinstance(arg, type) and issubclass(arg, np.generic):
|
|
136
|
+
return arg # numpy >= 2.5: NDArray[X] -> (X,)
|
|
137
|
+
for inner in get_args(arg):
|
|
138
|
+
if isinstance(inner, type) and issubclass(inner, np.generic):
|
|
139
|
+
return inner # numpy <= 2.4: NDArray[X] -> (Any, np.dtype[X])
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
|
|
113
143
|
log = logging.getLogger(__name__)
|
|
114
144
|
|
|
115
145
|
|
|
@@ -514,7 +544,8 @@ class Simulation:
|
|
|
514
544
|
- n_banks : int (default: 10)
|
|
515
545
|
- seed : int or None (default: 0)
|
|
516
546
|
- pipeline_path : str or None (default: None)
|
|
517
|
-
-
|
|
547
|
+
- log_level : str, e.g. "WARNING" (simple global log level)
|
|
548
|
+
- logging : dict (advanced per-event/file configuration)
|
|
518
549
|
See config/defaults.yml for all parameters.
|
|
519
550
|
|
|
520
551
|
Returns
|
|
@@ -562,7 +593,11 @@ class Simulation:
|
|
|
562
593
|
... pipeline_path="custom_pipeline.yml", seed=42
|
|
563
594
|
... ) # doctest: +SKIP
|
|
564
595
|
|
|
565
|
-
Configure
|
|
596
|
+
Configure log level:
|
|
597
|
+
|
|
598
|
+
>>> sim = bam.Simulation.init(log_level="WARNING", seed=42)
|
|
599
|
+
|
|
600
|
+
Advanced logging (per-event levels, file output):
|
|
566
601
|
|
|
567
602
|
>>> log_config = {
|
|
568
603
|
... "default_level": "DEBUG",
|
|
@@ -573,14 +608,6 @@ class Simulation:
|
|
|
573
608
|
... }
|
|
574
609
|
>>> sim = bam.Simulation.init(logging=log_config, seed=42)
|
|
575
610
|
|
|
576
|
-
Configure logging with file output:
|
|
577
|
-
|
|
578
|
-
>>> log_config = {
|
|
579
|
-
... "default_level": "DEBUG",
|
|
580
|
-
... "log_file": "simulation.log",
|
|
581
|
-
... }
|
|
582
|
-
>>> sim = bam.Simulation.init(logging=log_config, seed=42) # doctest: +SKIP
|
|
583
|
-
|
|
584
611
|
Notes
|
|
585
612
|
-----
|
|
586
613
|
- All configuration is validated before initialization
|
|
@@ -599,6 +626,16 @@ class Simulation:
|
|
|
599
626
|
cfg_dict.update(_read_yaml(config))
|
|
600
627
|
cfg_dict.update(overrides)
|
|
601
628
|
|
|
629
|
+
# log_level sugar: convert to logging dict
|
|
630
|
+
if "log_level" in overrides:
|
|
631
|
+
if "logging" in overrides:
|
|
632
|
+
raise ValueError(
|
|
633
|
+
"Cannot specify both 'log_level' and 'logging'. "
|
|
634
|
+
"Use 'log_level' for simple level setting, or 'logging' "
|
|
635
|
+
"for advanced configuration (per-event levels, log files)."
|
|
636
|
+
)
|
|
637
|
+
cfg_dict["logging"] = {"default_level": cfg_dict.pop("log_level")}
|
|
638
|
+
|
|
602
639
|
# Validate configuration (centralized validation)
|
|
603
640
|
from bamengine.config import ConfigValidator
|
|
604
641
|
|
|
@@ -942,6 +979,7 @@ class Simulation:
|
|
|
942
979
|
"savings_init",
|
|
943
980
|
"equity_base_init",
|
|
944
981
|
"pipeline_path",
|
|
982
|
+
"log_level",
|
|
945
983
|
"logging",
|
|
946
984
|
"min_wage",
|
|
947
985
|
"min_wage_rev_period",
|
|
@@ -1629,33 +1667,51 @@ class Simulation:
|
|
|
1629
1667
|
def _resolve_annotation_dtype(ann: Any) -> tuple[type[np.generic], int]:
|
|
1630
1668
|
"""Resolve a role field annotation to (numpy dtype, fill value).
|
|
1631
1669
|
|
|
1632
|
-
Handles string annotations (from ``__future__`` annotations),
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
all others use 0.
|
|
1670
|
+
Handles string annotations (from ``__future__`` annotations), the
|
|
1671
|
+
Agent class, the known resolved type aliases, and arbitrary
|
|
1672
|
+
``NDArray[np.<scalar>]`` objects. Agent (class / string) annotations
|
|
1673
|
+
use -1 fill (unassigned sentinel); all others use 0.
|
|
1674
|
+
|
|
1675
|
+
Raises
|
|
1676
|
+
------
|
|
1677
|
+
TypeError
|
|
1678
|
+
If the annotation cannot be mapped to a numpy dtype. Failing
|
|
1679
|
+
loudly avoids silently materialising the wrong dtype (e.g. an
|
|
1680
|
+
``Int`` field becoming float64), which is what a quiet fallback
|
|
1681
|
+
did before numpy 2.5 changed ``NDArray`` introspection.
|
|
1636
1682
|
"""
|
|
1637
1683
|
# String annotations (e.g., 'Float', 'Int1D')
|
|
1638
1684
|
if isinstance(ann, str):
|
|
1639
|
-
|
|
1685
|
+
try:
|
|
1686
|
+
return _ANNOTATION_DTYPE[ann]
|
|
1687
|
+
except KeyError:
|
|
1688
|
+
raise TypeError(
|
|
1689
|
+
f"Cannot resolve a numpy dtype for role field annotation "
|
|
1690
|
+
f"{ann!r}. Use Float, Int, Bool, Agent, or an "
|
|
1691
|
+
f"NDArray[np.<scalar>] annotation."
|
|
1692
|
+
) from None
|
|
1640
1693
|
|
|
1641
|
-
# Agent class (bamengine.core.agent.Agent) —
|
|
1694
|
+
# Agent class (bamengine.core.agent.Agent) — unassigned sentinel -1
|
|
1642
1695
|
from bamengine.core.agent import Agent as AgentCls
|
|
1643
1696
|
|
|
1644
1697
|
if ann is AgentCls:
|
|
1645
1698
|
return np.intp, -1
|
|
1646
1699
|
|
|
1647
|
-
#
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1700
|
+
# Known resolved type aliases (Float1D/Int1D/Bool1D and their friendly
|
|
1701
|
+
# aliases) — version-independent, no NDArray introspection needed.
|
|
1702
|
+
resolved = _ALIAS_DTYPE.get(ann)
|
|
1703
|
+
if resolved is not None:
|
|
1704
|
+
return resolved
|
|
1705
|
+
|
|
1706
|
+
# Arbitrary NDArray[np.<scalar>] — introspect robustly across numpy.
|
|
1707
|
+
scalar = _np_scalar_from_annotation(ann)
|
|
1708
|
+
if scalar is not None:
|
|
1709
|
+
return scalar, 0
|
|
1710
|
+
|
|
1711
|
+
raise TypeError(
|
|
1712
|
+
f"Cannot resolve a numpy dtype for role field annotation {ann!r}. "
|
|
1713
|
+
f"Use Float, Int, Bool, Agent, or an NDArray[np.<scalar>] annotation."
|
|
1714
|
+
)
|
|
1659
1715
|
|
|
1660
1716
|
def get_event(self, name: str) -> Any:
|
|
1661
1717
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bamengine
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.2
|
|
4
4
|
Summary: A modular Python framework for the BAM agent-based macroeconomic model
|
|
5
5
|
Author-email: Kostas Ganitis <ganitiskostas@gmail.com>
|
|
6
6
|
Maintainer-email: Kostas Ganitis <ganitiskostas@gmail.com>
|
|
@@ -83,17 +83,10 @@ Dynamic: license-file
|
|
|
83
83
|
<p align="center">
|
|
84
84
|
A Python implementation of the BAM (Bottom-Up Adaptive Macroeconomics) model
|
|
85
85
|
from <a href="https://doi.org/10.1007/978-88-470-1971-3"><em>Macroeconomics from the Bottom-up</em></a>
|
|
86
|
-
(Delli Gatti et al., 2011).
|
|
87
|
-
interacting
|
|
88
|
-
|
|
89
|
-
</
|
|
90
|
-
|
|
91
|
-
<p align="center">
|
|
92
|
-
<em>Traditional economics models economies from the top down, assuming
|
|
93
|
-
markets automatically reach equilibrium. BAM Engine takes a different approach:
|
|
94
|
-
it simulates individual workers, firms, and banks making decisions and
|
|
95
|
-
interacting in markets, letting macroeconomic patterns emerge from the
|
|
96
|
-
bottom up.</em>
|
|
86
|
+
(Delli Gatti et al., 2011). It runs simulations of individual workers, firms, and
|
|
87
|
+
banks making decisions and interacting in labor, credit and goods markets, letting
|
|
88
|
+
macroeconomic patterns (growth, unemployment, inflation, business cycles) <b>emerge
|
|
89
|
+
from the bottom up</b>, instead of assuming them with aggregate equations.
|
|
97
90
|
</p>
|
|
98
91
|
|
|
99
92
|
<p align="center">
|
|
@@ -172,17 +165,16 @@ See the [Getting Started guide](https://bam-engine.readthedocs.io/en/latest/quic
|
|
|
172
165
|
|
|
173
166
|
## Features
|
|
174
167
|
|
|
175
|
-
- **Complete BAM
|
|
176
|
-
- **
|
|
177
|
-
- **
|
|
178
|
-
- **
|
|
179
|
-
- **Validation Framework:** Three scenario validators with scoring and robustness analysis
|
|
180
|
-
- **Calibration Pipeline:** Morris screening, grid search, and tiered stability testing
|
|
181
|
-
- **Easy Configuration:** All parameters configurable without code changes via YAML files
|
|
168
|
+
- **Complete BAM Implementation:** Baseline model from *Macroeconomics from the Bottom-up*, Chapter 3 (firms, households, and banks across labor, credit, and goods markets), three built-in extensions (R&D / Growth+, buffer-stock consumption, taxation), and a robustness analysis suite (internal validity, sensitivity, structural experiments).
|
|
169
|
+
- **Vectorized Performance:** Agent state lives in parallel NumPy arrays and behavior is expressed as array transformations, not Python loops over agent objects. Simulations scale to large populations and long horizons.
|
|
170
|
+
- **Pluggable Extension System:** Add custom roles, events, and pipeline hooks via decorators without modifying the engine. The same mechanism powers the built-in extensions.
|
|
171
|
+
- **Parameter Calibration:** Automated pipeline for tuning model parameters against validation targets.
|
|
182
172
|
|
|
183
173
|
## Architecture
|
|
184
174
|
|
|
185
|
-
BAM Engine uses an ECS (Entity-Component-System) architecture: agents are lightweight entities, state lives in Role components stored as NumPy arrays, and behavior is defined by Event systems
|
|
175
|
+
BAM Engine uses an ECS (Entity-Component-System) architecture: agents are lightweight entities, state lives in **Role** components stored as parallel NumPy arrays, and behavior is defined by **Event** systems composed into a YAML-configurable pipeline. New roles, events, and relationships can be added through decorators, without modifying core code.
|
|
176
|
+
|
|
177
|
+
The trade-off is a mindset shift: you write agent rules as systems that transform whole arrays of state at once, not as methods on per-agent objects. ECS itself does not demand vectorization, but BAM Engine treats it as the default. Every built-in system processes all agents at once, with the goods market's sequential matching rounds as the deliberate exception where strict per-agent ordering matters.
|
|
186
178
|
|
|
187
179
|
See the [User Guide](https://bam-engine.readthedocs.io/en/latest/user_guide/index.html) for a full walkthrough of the model and its architecture.
|
|
188
180
|
|
|
@@ -330,8 +330,34 @@ def _analyze_seed(
|
|
|
330
330
|
) -> SeedAnalysis:
|
|
331
331
|
"""Compute co-movements, AR fit, and summary stats for one seed."""
|
|
332
332
|
if ts.collapsed:
|
|
333
|
-
#
|
|
333
|
+
# Compute basic scalar stats from pre-collapse data when enough
|
|
334
|
+
# post-burn-in data exists. This lets entry-experiment figures
|
|
335
|
+
# show degradation even when most/all seeds collapse.
|
|
334
336
|
n_lags = 2 * max_lag + 1
|
|
337
|
+
bi = burn_in
|
|
338
|
+
post_bi_len = len(ts.unemployment) - bi
|
|
339
|
+
|
|
340
|
+
if post_bi_len >= 10:
|
|
341
|
+
unemp_ss = ts.unemployment[bi:]
|
|
342
|
+
gdp_growth_ss = ts.gdp_growth[max(bi - 1, 0) :]
|
|
343
|
+
unemployment_mean = float(np.nanmean(unemp_ss))
|
|
344
|
+
unemployment_std = float(np.nanstd(unemp_ss))
|
|
345
|
+
inflation_mean = float(np.nanmean(ts.inflation[bi:]))
|
|
346
|
+
inflation_std = float(np.nanstd(ts.inflation[bi:]))
|
|
347
|
+
gdp_growth_mean = float(np.nanmean(gdp_growth_ss))
|
|
348
|
+
gdp_growth_std = float(np.nanstd(gdp_growth_ss))
|
|
349
|
+
real_wage_mean = float(np.nanmean(ts.real_wage[bi:]))
|
|
350
|
+
productivity_mean = float(np.nanmean(ts.avg_productivity[bi:]))
|
|
351
|
+
else:
|
|
352
|
+
unemployment_mean = np.nan
|
|
353
|
+
unemployment_std = np.nan
|
|
354
|
+
inflation_mean = np.nan
|
|
355
|
+
inflation_std = np.nan
|
|
356
|
+
gdp_growth_mean = np.nan
|
|
357
|
+
gdp_growth_std = np.nan
|
|
358
|
+
real_wage_mean = np.nan
|
|
359
|
+
productivity_mean = np.nan
|
|
360
|
+
|
|
335
361
|
return SeedAnalysis(
|
|
336
362
|
seed=ts.seed,
|
|
337
363
|
collapsed=True,
|
|
@@ -340,14 +366,14 @@ def _analyze_seed(
|
|
|
340
366
|
ar_order=ar_order,
|
|
341
367
|
ar_r_squared=0.0,
|
|
342
368
|
irf=np.zeros(irf_periods),
|
|
343
|
-
unemployment_mean=
|
|
344
|
-
unemployment_std=
|
|
345
|
-
inflation_mean=
|
|
346
|
-
inflation_std=
|
|
347
|
-
gdp_growth_mean=
|
|
348
|
-
gdp_growth_std=
|
|
349
|
-
real_wage_mean=
|
|
350
|
-
productivity_mean=
|
|
369
|
+
unemployment_mean=unemployment_mean,
|
|
370
|
+
unemployment_std=unemployment_std,
|
|
371
|
+
inflation_mean=inflation_mean,
|
|
372
|
+
inflation_std=inflation_std,
|
|
373
|
+
gdp_growth_mean=gdp_growth_mean,
|
|
374
|
+
gdp_growth_std=gdp_growth_std,
|
|
375
|
+
real_wage_mean=real_wage_mean,
|
|
376
|
+
productivity_mean=productivity_mean,
|
|
351
377
|
phillips_corr=np.nan,
|
|
352
378
|
okun_corr=np.nan,
|
|
353
379
|
beveridge_corr=np.nan,
|
|
@@ -569,7 +595,7 @@ def _run_seed(
|
|
|
569
595
|
sim = bam.Simulation.init(
|
|
570
596
|
seed=seed,
|
|
571
597
|
n_periods=n_periods,
|
|
572
|
-
|
|
598
|
+
log_level="ERROR",
|
|
573
599
|
**config,
|
|
574
600
|
)
|
|
575
601
|
if setup_hook is not None:
|
|
@@ -174,8 +174,13 @@ def _aggregate_seed_analyses(
|
|
|
174
174
|
|
|
175
175
|
stats_dict: dict[str, dict[str, float]] = {}
|
|
176
176
|
for attr_name in stat_fields:
|
|
177
|
+
# Use all seeds (not just valid) so that collapsed/degenerate
|
|
178
|
+
# seeds with pre-collapse data contribute to scalar stats.
|
|
179
|
+
# The NaN filter handles seeds that have no usable data.
|
|
177
180
|
values = [
|
|
178
|
-
getattr(a, attr_name)
|
|
181
|
+
getattr(a, attr_name)
|
|
182
|
+
for a in seed_analyses
|
|
183
|
+
if not np.isnan(getattr(a, attr_name))
|
|
179
184
|
]
|
|
180
185
|
if values:
|
|
181
186
|
mean_val = float(np.mean(values))
|
|
@@ -345,16 +345,17 @@ def plot_pa_gdp_comparison(
|
|
|
345
345
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
346
346
|
|
|
347
347
|
# Use log_gdp from the PA-off seed analysis and baseline for comparison
|
|
348
|
-
|
|
348
|
+
burn_in = pa_result.pa_off_validity.burn_in
|
|
349
|
+
log_gdp_off = pa_result.pa_off_validity.seed_analyses[seed].log_gdp[burn_in:]
|
|
349
350
|
if pa_result.baseline_validity is not None:
|
|
350
|
-
log_gdp_on = pa_result.baseline_validity.seed_analyses[seed].log_gdp
|
|
351
|
+
log_gdp_on = pa_result.baseline_validity.seed_analyses[seed].log_gdp[burn_in:]
|
|
351
352
|
else:
|
|
352
353
|
raise ValueError(
|
|
353
354
|
"PA GDP comparison requires baseline; re-run with include_baseline=True"
|
|
354
355
|
)
|
|
355
356
|
|
|
356
|
-
fig, ax = plt.subplots(1, 1, figsize=(
|
|
357
|
-
periods = np.arange(len(log_gdp_on))
|
|
357
|
+
fig, ax = plt.subplots(1, 1, figsize=(8, 6))
|
|
358
|
+
periods = np.arange(burn_in, burn_in + len(log_gdp_on))
|
|
358
359
|
ax.plot(periods, log_gdp_on, "b-", linewidth=1, alpha=0.8, label="PA on")
|
|
359
360
|
ax.plot(
|
|
360
361
|
periods[: len(log_gdp_off)],
|
|
@@ -507,7 +508,7 @@ def plot_entry_comparison(
|
|
|
507
508
|
]
|
|
508
509
|
collapse_rates = [vr.collapse_rate for vr in vrs]
|
|
509
510
|
|
|
510
|
-
fig, axes = plt.subplots(1, 3, figsize=(
|
|
511
|
+
fig, axes = plt.subplots(1, 3, figsize=(10, 7))
|
|
511
512
|
fig.suptitle(
|
|
512
513
|
"Entry Neutrality: Impact of Profit Taxation (Section 3.10.2)",
|
|
513
514
|
fontsize=13,
|
|
@@ -2,9 +2,53 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
5
7
|
import numpy as np
|
|
6
8
|
from numpy.typing import NDArray
|
|
7
9
|
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from matplotlib.axes import Axes
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def shade_beyond_extreme(
|
|
15
|
+
ax: Axes, extreme_min: float, extreme_max: float, axis: str = "y"
|
|
16
|
+
) -> None:
|
|
17
|
+
"""Shade areas beyond extreme bounds darker than the transition zone.
|
|
18
|
+
|
|
19
|
+
Creates a visual hierarchy:
|
|
20
|
+
- Normal zone (white): within normal bounds
|
|
21
|
+
- Transition zone (alpha=0.1 red): between extreme and normal bounds
|
|
22
|
+
- Beyond extreme (alpha=0.25 red): outside extreme bounds
|
|
23
|
+
|
|
24
|
+
Must be called AFTER all data is plotted so axis limits are set by the data.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
ax : Axes
|
|
29
|
+
Matplotlib axes to shade.
|
|
30
|
+
extreme_min, extreme_max : float
|
|
31
|
+
Extreme bound values.
|
|
32
|
+
axis : str
|
|
33
|
+
``"y"`` for horizontal bands, ``"x"`` for vertical bands.
|
|
34
|
+
"""
|
|
35
|
+
alpha = 0.25
|
|
36
|
+
color = "red"
|
|
37
|
+
if axis == "y":
|
|
38
|
+
ymin, ymax = ax.get_ylim()
|
|
39
|
+
if ymin < extreme_min:
|
|
40
|
+
ax.axhspan(ymin, extreme_min, alpha=alpha, color=color, zorder=0)
|
|
41
|
+
if ymax > extreme_max:
|
|
42
|
+
ax.axhspan(extreme_max, ymax, alpha=alpha, color=color, zorder=0)
|
|
43
|
+
ax.set_ylim(ymin, ymax)
|
|
44
|
+
else:
|
|
45
|
+
xmin, xmax = ax.get_xlim()
|
|
46
|
+
if xmin < extreme_min:
|
|
47
|
+
ax.axvspan(xmin, extreme_min, alpha=alpha, color=color, zorder=0)
|
|
48
|
+
if xmax > extreme_max:
|
|
49
|
+
ax.axvspan(extreme_max, xmax, alpha=alpha, color=color, zorder=0)
|
|
50
|
+
ax.set_xlim(xmin, xmax)
|
|
51
|
+
|
|
8
52
|
|
|
9
53
|
def compute_detrended_correlation(
|
|
10
54
|
x: NDArray[np.floating], y: NDArray[np.floating]
|
|
@@ -34,10 +34,10 @@ metadata:
|
|
|
34
34
|
time_series:
|
|
35
35
|
unemployment_rate:
|
|
36
36
|
targets:
|
|
37
|
-
normal_min: 0.
|
|
38
|
-
normal_max: 0.
|
|
39
|
-
extreme_min: 0.
|
|
40
|
-
extreme_max: 0.
|
|
37
|
+
normal_min: 0.02
|
|
38
|
+
normal_max: 0.12
|
|
39
|
+
extreme_min: 0.00
|
|
40
|
+
extreme_max: 0.15
|
|
41
41
|
mean_target: 0.065 # Book Figure 3.2b extracted
|
|
42
42
|
|
|
43
43
|
inflation_rate:
|
|
@@ -50,10 +50,10 @@ metadata:
|
|
|
50
50
|
|
|
51
51
|
log_gdp:
|
|
52
52
|
targets:
|
|
53
|
-
normal_min: 5.
|
|
54
|
-
normal_max: 5.
|
|
55
|
-
extreme_min: 5.
|
|
56
|
-
extreme_max: 5.
|
|
53
|
+
normal_min: 5.394
|
|
54
|
+
normal_max: 5.501
|
|
55
|
+
extreme_min: 5.359
|
|
56
|
+
extreme_max: 5.521
|
|
57
57
|
mean_target: 5.460
|
|
58
58
|
|
|
59
59
|
real_wage:
|