-
Notifications
You must be signed in to change notification settings - Fork 160
feat: Add runnable MVP experiments for Q107, Q109, and Q125 #132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| { | ||
| "cells": [ | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "# WFGY MVP Experiment: Q107 - Large Scale Collective Action\n", | ||
| "\n", | ||
| "## 1. Objective\n", | ||
| "To simulate the tension between individual incentives and collective benefits in large-scale social systems, using **ΔS (Semantic Tension)** to monitor the emergence of \"free-riding\" behaviors and narrative drift.\n", | ||
| "\n", | ||
| "## 2. Theoretical Framework (WFGY 2.0)\n", | ||
| "- **G (Anchor)**: The collective goal (e.g., \"Construct a public dam\").\n", | ||
| "- **I (Current state)**: Current narrative/belief field of participants.\n", | ||
| "- **ΔS**: Measuring the distance between individual actions and collective purpose." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import numpy as np\n", | ||
| "import matplotlib.pyplot as plt\n", | ||
| "\n", | ||
| "def calculate_delta_s(vec_a, vec_b):\n", | ||
| " return 1 - np.dot(vec_a, vec_b) / (np.linalg.norm(vec_a) * np.linalg.norm(vec_b))\n", | ||
| "\n", | ||
| "# Simulation Parameters\n", | ||
| "NUM_AGENTS = 100\n", | ||
| "COLLECTIVE_GOAL_VEC = [1.0, 0.0] # Full alignment\n", | ||
| "\n", | ||
| "def simulate_turn(agent_beliefs, noise=0.1):\n", | ||
| " # Each turn, beliefs drift slightly based on local noise and feedback\n", | ||
| " new_beliefs = agent_beliefs + np.random.normal(0, noise, agent_beliefs.shape)\n", | ||
| " # Normalize vectors\n", | ||
| " new_beliefs = new_beliefs / np.linalg.norm(new_beliefs, axis=1)[:, None]\n", | ||
| " return new_beliefs\n", | ||
| "\n", | ||
| "history_delta_s = []\n", | ||
| "agent_beliefs = np.random.normal(1.0, 0.2, (NUM_AGENTS, 2))\n", | ||
| "agent_beliefs = agent_beliefs / np.linalg.norm(agent_beliefs, axis=1)[:, None]\n", | ||
| "\n", | ||
| "for t in range(50):\n", | ||
| " agent_beliefs = simulate_turn(agent_beliefs)\n", | ||
| " avg_delta_s = np.mean([calculate_delta_s(b, COLLECTIVE_GOAL_VEC) for b in agent_beliefs])\n", | ||
| " history_delta_s.append(avg_delta_s)\n", | ||
| "\n", | ||
| "print(f\"Final Avg ΔS: {history_delta_s[-1]:.4f}\")" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## 3. Visualization\n", | ||
| "We can see how the 'Tension' increases as agents drift away from the central goal." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "plt.plot(history_delta_s)\n", | ||
| "plt.title(\"Semantic Tension (ΔS) Over Time (Collective Action Drift)\")\n", | ||
| "plt.xlabel(\"Turn\")\n", | ||
| "plt.ylabel(\"Avg ΔS\")\n", | ||
| "plt.show()" | ||
| ] | ||
| } | ||
| ], | ||
| "metadata": { | ||
| "kernelspec": { | ||
| "display_name": "Python 3", | ||
| "language": "python", | ||
| "name": "python3" | ||
| }, | ||
| "language_info": { | ||
| "codemirror_mode": { | ||
| "name": "ipython", | ||
| "version": 3 | ||
| }, | ||
| "file_extension": ".py", | ||
| "mimetype": "text/x-python", | ||
| "name": "python", | ||
| "nbconvert_exporter": "python", | ||
| "pygments_lexer": "ipython3", | ||
| "version": "3.10.0" | ||
| } | ||
| }, | ||
| "nbformat": 4, | ||
| "nbformat_minor": 4 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # Simulation Model: Q107 Large Scale Collective Action | ||
|
|
||
| This document outlines the theoretical logic and mathematical modeling behind the Q107 MVP notebook. | ||
|
|
||
| ## 1. The Paradigm | ||
| In traditional game theory (e.g., Public Goods Games), tension is usually measured by utility or payoff deficits. **WFGY Tension Universe (TU Q107)** shifts this paradigm towards **Semantic Tension (ΔS)** — measuring the alignment between an agent's internal "belief state" and the collective "goal state". | ||
|
|
||
| ## 2. Core Variables | ||
| - **G (Collective Goal Vector)**: Represented as a normalized vector (e.g., `[1.0, 0.0]`). This represents perfect alignment with the public good (e.g., full cooperation). | ||
| - **$I_i$ (Agent Belief Vector)**: The current internal state of Agent $i$. In this simulation, each agent starts with a belief closely aligned with $G$, but subject to variance. | ||
| - **Noise ($\epsilon$)**: A dynamic variable representing individual incentives, miscommunication, or "free-riding" temptations. | ||
|
|
||
| ## 3. Simulation Loop | ||
| 1. **Initialization**: $N$ agents (e.g., 100) are placed in the environment, forming a tightly clustered vector field around $G$. | ||
| 2. **Turn Execution**: Each turn, agents experience a random noise $\epsilon$ added to their belief vector: | ||
| $$ I_{i, t+1} = \text{Normalize}(I_{i, t} + \epsilon) $$ | ||
| 3. **Tension Calculation**: The system continually calculates the semantic tension between the group's average belief direction and the anchor $G$: | ||
| $$ \Delta S_{i} = 1 - \cos(\theta(I_i, G)) $$ | ||
| Tension increases as the collective alignment "drifts" into a fragmented state. | ||
|
|
||
| ## 4. Academic Alignment | ||
| This model strictly adheres to the WFGY 2.0 definition of ΔS. Instead of predicting individual actions, it monitors the **structural stability** of the group's narrative. Once average ΔS breaches $0.6$, the collective action is formally categorised as entering a "Risk Zone" of dissolution. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # Data Source Information: Q109 Migration Dynamics | ||
|
|
||
| This experiment uses **real-world external data** to evaluate Semantic Tension (ΔS) across international borders. | ||
|
|
||
| ## Primary Dataset | ||
| - **Provider**: World Bank Open Data | ||
| - **Indicator**: Net Migration (`SM.POP.NETM`) | ||
| - **License**: Creative Commons Attribution 4.0 International (CC BY 4.0) | ||
| - **API Endpoint**: `https://api.worldbank.org/v2/country/all/indicator/SM.POP.NETM?format=json` | ||
|
|
||
| ## Why we use dynamic API instead of flat CSV | ||
| In alignment with WFGY's philosophy of handling "living" tension patterns: | ||
| 1. **No Static Bloat**: We avoid committing large, static `.csv` files into the repository to keep the core lightweight. | ||
| 2. **Real-time Relevancy**: By using `urllib`/`requests` to fetch standard JSON from the API, the notebook automatically adapts to the latest global demographic dividend reports without requiring manual updates. | ||
|
|
||
| ## Fallback Mechanism | ||
| If the World Bank API is inaccessible due to firewall or network restrictions during the notebook execution, users can manually download the CSV from [World Bank Data Catalog](https://data.worldbank.org/indicator/SM.POP.NETM) and feed it into the pandas dataframe. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| { | ||
| "cells": [ | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "# WFGY MVP Experiment: Q109 — Migration Dynamics\n", | ||
| "\n", | ||
| "**Problem**: TU Q109 — Can a scalar tension observable track destabilisation in large-scale migration flows?\n", | ||
| "\n", | ||
| "## Data Source\n", | ||
| "- **Dataset**: World Bank Open Data — Net Migration (`SM.POP.NETM`)\n", | ||
| "- **API**: `https://api.worldbank.org/v2/country/all/indicator/SM.POP.NETM`\n", | ||
| "- **License**: CC BY 4.0 (open access, free to use with attribution)\n", | ||
| "- **Coverage**: ~190 countries, most recent available year\n", | ||
| "\n", | ||
| "## WFGY Framework\n", | ||
| "- **G (Anchor)**: Global median net migration — baseline of a 'neutral' state\n", | ||
| "- **I (Current)**: Each country's net migration flow\n", | ||
| "- **ΔS**: Normalised deviation from the anchor — measures structural stress" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import urllib.request\n", | ||
| "import json\n", | ||
| "import math\n", | ||
| "\n", | ||
| "# ----- 1. Load Data from World Bank Open Data API -----\n", | ||
| "# SM.POP.NETM = Net migration (net number of migrants)\n", | ||
| "WB_URL = ('https://api.worldbank.org/v2/country/all/indicator/SM.POP.NETM'\n", | ||
| " '?format=json&mrv=1&per_page=50')\n", | ||
| "\n", | ||
| "print(f'Fetching from World Bank API...')\n", | ||
| "req = urllib.request.Request(WB_URL, headers={'User-Agent': 'WFGY-MVP/1.0'})\n", | ||
| "with urllib.request.urlopen(req, timeout=15) as resp:\n", | ||
| " data = json.loads(resp.read().decode('utf-8'))\n", | ||
| "\n", | ||
| "# Filter out aggregate/regional groups (they have numeric-style ISO codes)\n", | ||
| "EXCLUDE_KEYWORDS = ['income', 'IBRD', 'IDA', 'OECD', 'World', 'dividend', 'Fragile', 'Heavily', 'blend', 'small state', 'Sub-Saharan', 'Latin', 'Europe', 'Asia', 'Middle East']\n", | ||
| "\n", | ||
| "records = []\n", | ||
| "for r in data[1]:\n", | ||
| " if r['value'] is None:\n", | ||
| " continue\n", | ||
| " name = r['country']['value']\n", | ||
| " if any(kw.lower() in name.lower() for kw in EXCLUDE_KEYWORDS):\n", | ||
| " continue\n", | ||
| " records.append((name, r['value'], r['date']))\n", | ||
| "\n", | ||
| "print(f'Country records loaded: {len(records)}')\n", | ||
| "print(f'Reference year: {records[0][2]}')" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import statistics\n", | ||
| "\n", | ||
| "# ----- 2. Compute WFGY ΔS Tension -----\n", | ||
| "flows = [v for _, v, _ in records]\n", | ||
| "G_anchor = statistics.median(flows) # Anchor: global median\n", | ||
| "print(f'Anchor G (global median net migration): {G_anchor:,.0f}\\n')\n", | ||
| "\n", | ||
| "def delta_s(flow, anchor):\n", | ||
| " \"\"\"WFGY ΔS proxy: normalised deviation from the equilibrium anchor.\"\"\"\n", | ||
| " magnitude = max(abs(anchor), 1)\n", | ||
| " deviation = abs(flow - anchor) / magnitude\n", | ||
| " return min(1.0, deviation / (deviation + 1))\n", | ||
| "\n", | ||
| "def zone_label(ds):\n", | ||
| " if ds >= 0.85: return 'DANGER'\n", | ||
| " elif ds >= 0.60: return 'RISK'\n", | ||
| " elif ds >= 0.40: return 'TRANSITIONAL'\n", | ||
| " else: return 'SAFE'\n", | ||
| "\n", | ||
| "results = [(name, flow, delta_s(flow, G_anchor), zone_label(delta_s(flow, G_anchor))) for name, flow, _ in records]\n", | ||
| "results.sort(key=lambda x: x[2], reverse=True)\n", | ||
| "\n", | ||
| "print(f'{'Country':<35} {'Net Migration':>16} {'ΔS':>6} {'Zone'}')\n", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This header line is a syntax error ( Useful? React with 👍 / 👎. |
||
| "print('-' * 72)\n", | ||
| "for name, flow, ds, zone in results[:15]:\n", | ||
| " print(f'{name:<35} {flow:>16,.0f} {ds:>6.3f} {zone}')" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import matplotlib.pyplot as plt\n", | ||
| "import matplotlib.patches as mpatches\n", | ||
| "\n", | ||
| "# ----- 3. Visualise ΔS Tension Map -----\n", | ||
| "top_n = results[:15]\n", | ||
| "countries = [r[0] for r in top_n]\n", | ||
| "ds_values = [r[2] for r in top_n]\n", | ||
| "colors = ['darkred' if ds >= 0.85 else 'orange' if ds >= 0.60 else 'steelblue' for ds in ds_values]\n", | ||
| "\n", | ||
| "fig, ax = plt.subplots(figsize=(12, 6))\n", | ||
| "bars = ax.barh(countries[::-1], ds_values[::-1], color=colors[::-1])\n", | ||
| "\n", | ||
| "ax.axvline(x=0.85, color='darkred', linestyle='--', linewidth=1.2, label='Danger Zone (ΔS ≥ 0.85)')\n", | ||
| "ax.axvline(x=0.60, color='orange', linestyle='--', linewidth=1.2, label='Risk Zone (ΔS ≥ 0.60)')\n", | ||
| "ax.set_xlabel('ΔS Tension Index')\n", | ||
| "ax.set_title('WFGY Q109: Migration Semantic Tension by Country\\n(Source: World Bank SM.POP.NETM)', fontsize=13)\n", | ||
| "ax.set_xlim(0, 1.05)\n", | ||
| "ax.legend()\n", | ||
| "plt.tight_layout()\n", | ||
| "plt.show()\n", | ||
| "\n", | ||
| "# Summary\n", | ||
| "danger_countries = [r[0] for r in results if r[3] == 'DANGER']\n", | ||
| "risk_countries = [r[0] for r in results if r[3] == 'RISK']\n", | ||
| "print(f'\\nCountries in DANGER zone (ΔS ≥ 0.85): {len(danger_countries)}')\n", | ||
| "print(f'Countries in RISK zone (ΔS ≥ 0.60): {len(risk_countries)}')" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## 4. Interpretation\n", | ||
| "\n", | ||
| "| Zone | ΔS Range | Meaning |\n", | ||
| "|---|---|---|\n", | ||
| "| SAFE | < 0.40 | Migration near global equilibrium |\n", | ||
| "| TRANSITIONAL | 0.40–0.60 | Moderate drift, worth tracking |\n", | ||
| "| RISK | 0.60–0.85 | Systemic stress — policy action warranted |\n", | ||
| "| DANGER | ≥ 0.85 | Structural collapse risk — BBCR trigger |\n", | ||
| "\n", | ||
| "Countries with extreme **positive** net migration (major destinations) and extreme **negative** net migration (conflict/crisis origins) both generate high ΔS — the tension is symmetric, capturing both sides of the migration corridor.\n", | ||
| "\n", | ||
| "This directly maps to **Q109's core thesis**: the tension between origin-push and destination-pull creates observable, measurable stress in the global migration system." | ||
| ] | ||
| } | ||
| ], | ||
| "metadata": { | ||
| "kernelspec": { | ||
| "display_name": "Python 3", | ||
| "language": "python", | ||
| "name": "python3" | ||
| }, | ||
| "language_info": { | ||
| "codemirror_mode": { | ||
| "name": "ipython", | ||
| "version": 3 | ||
| }, | ||
| "file_extension": ".py", | ||
| "mimetype": "text/x-python", | ||
| "name": "python", | ||
| "nbformat": 4, | ||
| "version": "3.10.0" | ||
| } | ||
| }, | ||
| "nbformat": 4, | ||
| "nbformat_minor": 4 | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The URL hard-codes
per_page=50, so the experiment computesG_anchor, rankings, and zone counts from only a partial first page rather than the full country population described in the notebook text. This skews the global median and downstream ΔS classifications, producing systematically incomplete results for the migration-tension analysis.Useful? React with 👍 / 👎.