{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"name": "Monte_Carlo_Sim_of_a_200_Year_Lifespan.ipynb",
"gpuType": "T4",
"authorship_tag": "ABX9TyOBKKuD9NcjjU3AnX90XRty",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
},
"accelerator": "GPU"
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
""
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "vMBDv-sv-StU",
"outputId": "b11470e1-d18b-4a0f-a27a-c428c3d732e4"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Ran 10000 simulations of a 200-year lifespan model.\n",
"\n",
"Average age of death: 120.22\n",
"Average number of career changes: 4.22\n",
"Average number of major illnesses: 1.61\n"
]
}
],
"source": [
"import numpy as np\n",
"import random\n",
"import statistics\n",
"\n",
"def mortality_probability(age, max_age=200):\n",
" \"\"\"\n",
" A toy function that returns the probability of death at a given age.\n",
" The function is designed so that average lifespan hovers near 200,\n",
" but you can adjust it as needed.\n",
" \"\"\"\n",
" # Example: A small base rate, increasing with age.\n",
" # At age 0 --> prob ~ 0.0, at age 200 --> prob ~ 0.02\n",
" # This will lead to many individuals reaching 200,\n",
" # but is just a toy model. Tweak as necessary.\n",
" return 0.0001 * age\n",
"\n",
"def simulate_one_life(max_age=200):\n",
" \"\"\"\n",
" Simulate a single life path, returning:\n",
" - age of death (or 200 if still alive)\n",
" - record of 'career changes'\n",
" - record of 'major illnesses'\n",
" \"\"\"\n",
" current_age = 0\n",
" is_alive = True\n",
"\n",
" career_change_years = []\n",
" illness_years = []\n",
"\n",
" while is_alive and current_age <= max_age:\n",
" # Roll for mortality this year\n",
" death_chance = mortality_probability(current_age, max_age)\n",
" if random.random() < death_chance:\n",
" # Person dies this year\n",
" return current_age, career_change_years, illness_years\n",
"\n",
" # If still alive, check for events:\n",
" # 1) Career change (example ages 25-150, 5% chance each year)\n",
" if 25 <= current_age <= 150:\n",
" if random.random() < 0.05:\n",
" career_change_years.append(current_age)\n",
"\n",
" # 2) Major illness (example ages 40-190, 2% chance each year)\n",
" if 40 <= current_age <= 190:\n",
" if random.random() < 0.02:\n",
" illness_years.append(current_age)\n",
"\n",
" current_age += 1\n",
"\n",
" # If we exited the loop without dying, it means we reached max_age\n",
" return max_age, career_change_years, illness_years\n",
"\n",
"\n",
"def run_simulation(num_simulations=10000, max_age=200):\n",
" \"\"\"\n",
" Run multiple simulations and return:\n",
" - distribution of death ages\n",
" - average age of death\n",
" - average number of career changes\n",
" - average number of major illnesses\n",
" \"\"\"\n",
" death_ages = []\n",
" career_change_counts = []\n",
" illness_counts = []\n",
"\n",
" for _ in range(num_simulations):\n",
" death_age, career_changes, illnesses = simulate_one_life(max_age)\n",
" death_ages.append(death_age)\n",
" career_change_counts.append(len(career_changes))\n",
" illness_counts.append(len(illnesses))\n",
"\n",
" avg_death_age = statistics.mean(death_ages)\n",
" avg_career_changes = statistics.mean(career_change_counts)\n",
" avg_illnesses = statistics.mean(illness_counts)\n",
"\n",
" results = {\n",
" \"avg_death_age\": avg_death_age,\n",
" \"avg_career_changes\": avg_career_changes,\n",
" \"avg_illnesses\": avg_illnesses,\n",
" \"death_age_distribution\": death_ages\n",
" }\n",
" return results\n",
"\n",
"# -----------------------\n",
"# Run the simulation\n",
"# -----------------------\n",
"if __name__ == \"__main__\":\n",
" np.random.seed(42) # For reproducibility (optional)\n",
" random.seed(42)\n",
"\n",
" N = 10_000\n",
" simulation_results = run_simulation(num_simulations=N, max_age=200)\n",
"\n",
" print(f\"Ran {N} simulations of a 200-year lifespan model.\\n\")\n",
" print(f\"Average age of death: {simulation_results['avg_death_age']:.2f}\")\n",
" print(f\"Average number of career changes: {simulation_results['avg_career_changes']:.2f}\")\n",
" print(f\"Average number of major illnesses: {simulation_results['avg_illnesses']:.2f}\")\n",
" # You could also plot or further analyze death_age_distribution if desired."
]
},
{
"cell_type": "code",
"source": [
"# Monte Carlo Simulation: CPU vs GPU (CuPy) Comparison\n",
"# ----------------------------------------------------\n",
"# This code demonstrates how to run the same Monte Carlo simulation\n",
"# on both the CPU (NumPy + Python) and the GPU (CuPy) in Google Colab.\n",
"# It computes:\n",
"# - Age of death distribution\n",
"# - Average number of career changes\n",
"# - Average number of major illnesses\n",
"# for N simulated lives (max_age = 200).\n",
"# We measure runtimes for both implementations.\n",
"\n",
"import time\n",
"import numpy as np\n",
"import random\n",
"import statistics\n",
"\n",
"# -----------------------\n",
"# 1) CPU Implementation\n",
"# -----------------------\n",
"\n",
"def mortality_probability(age, max_age=200):\n",
" \"\"\"\n",
" Toy function returning probability of death at a given age.\n",
" \"\"\"\n",
" return 0.0001 * age\n",
"\n",
"\n",
"def simulate_one_life_cpu(max_age=200):\n",
" \"\"\"\n",
" Simulate one life path on CPU.\n",
" Returns: (death_age, career_changes_count, illness_count)\n",
" \"\"\"\n",
" current_age = 0\n",
" career_changes = 0\n",
" illnesses = 0\n",
" while current_age <= max_age:\n",
" # Mortality check\n",
" if random.random() < mortality_probability(current_age, max_age):\n",
" return current_age, career_changes, illnesses\n",
" # Career change (ages 25-150, 5% per year)\n",
" if 25 <= current_age <= 150:\n",
" if random.random() < 0.05:\n",
" career_changes += 1\n",
" # Major illness (ages 40-190, 2% per year)\n",
" if 40 <= current_age <= 190:\n",
" if random.random() < 0.02:\n",
" illnesses += 1\n",
" current_age += 1\n",
" # Survived full lifespan\n",
" return max_age, career_changes, illnesses\n",
"\n",
"\n",
"def run_simulation_cpu(num_simulations=10000, max_age=200):\n",
" \"\"\"\n",
" Run multiple CPU simulations. Return results dict.\n",
" \"\"\"\n",
" death_ages = []\n",
" career_counts = []\n",
" illness_counts = []\n",
" for _ in range(num_simulations):\n",
" d_age, careers, ill = simulate_one_life_cpu(max_age)\n",
" death_ages.append(d_age)\n",
" career_counts.append(careers)\n",
" illness_counts.append(ill)\n",
" avg_death_age = statistics.mean(death_ages)\n",
" avg_careers = statistics.mean(career_counts)\n",
" avg_illnesses = statistics.mean(illness_counts)\n",
" return {\n",
" \"avg_death_age\": avg_death_age,\n",
" \"avg_career_changes\": avg_careers,\n",
" \"avg_illnesses\": avg_illnesses,\n",
" \"death_age_distribution\": np.array(death_ages)\n",
" }\n",
"\n",
"# -----------------------\n",
"# 2) GPU Implementation\n",
"# -----------------------\n",
"# We use CuPy to vectorize the simulation across all lives simultaneously.\n",
"# CuPy is a drop-in replacement for NumPy that runs on CUDA GPUs.\n",
"\n",
"try:\n",
" import cupy as cp\n",
"except ImportError:\n",
" # In Colab, install CuPy if not already installed\n",
" !pip install cupy-cuda11x --quiet\n",
" import cupy as cp\n",
"\n",
"\n",
"def run_simulation_gpu(num_simulations=10000, max_age=200):\n",
" \"\"\"\n",
" Run vectorized GPU simulation using CuPy.\n",
" Returns results dict (with arrays on CPU for easy comparison).\n",
" \"\"\"\n",
" # Seed CuPy's RNG (this may differ slightly from Python/NumPy seeds)\n",
" cp.random.seed(42)\n",
"\n",
" # 1) Prepare age grid [0, 1, 2, ..., max_age]\n",
" ages = cp.arange(max_age + 1, dtype=cp.int32) # shape: (max_age+1,)\n",
" # Compute mortality probabilities for each age\n",
" probs = 0.0001 * ages.astype(cp.float32) # shape: (max_age+1,)\n",
" probs = probs[:, cp.newaxis] # shape: (max_age+1, 1)\n",
"\n",
" # 2) Generate random numbers for each (age, life) pair\n",
" # Shape: (max_age+1, num_simulations)\n",
" rand_matrix = cp.random.random((max_age + 1, num_simulations), dtype=cp.float32)\n",
"\n",
" # 3) Create a boolean mask of where death occurs (rand < prob)\n",
" death_mask = rand_matrix < probs # shape: (max_age+1, num_simulations)\n",
"\n",
" # 4) Find death ages: first index along axis=0 where death occurs\n",
" # For columns with no True, we set death_age = max_age\n",
" has_death = death_mask.any(axis=0) # shape: (num_simulations,)\n",
" # argmax returns 0 if all False, so we adjust later\n",
" first_idx = death_mask.argmax(axis=0) # shape: (num_simulations,)\n",
" death_ages_gpu = cp.where(has_death, first_idx, max_age).astype(cp.int32)\n",
"\n",
" # 5) Career changes: ages 25-150, 5% per year\n",
" career_age_start, career_age_end = 25, 150\n",
" num_career_years = career_age_end - career_age_start + 1\n",
" career_probs = 0.05 * cp.ones((num_career_years, 1), dtype=cp.float32)\n",
" career_rand = cp.random.random((num_career_years, num_simulations), dtype=cp.float32)\n",
" career_occurrences = career_rand < career_probs # boolean mask\n",
" career_counts_gpu = career_occurrences.sum(axis=0) # shape: (num_simulations,)\n",
"\n",
" # 6) Major illnesses: ages 40-190, 2% per year\n",
" ill_age_start, ill_age_end = 40, 190\n",
" num_ill_years = ill_age_end - ill_age_start + 1\n",
" ill_probs = 0.02 * cp.ones((num_ill_years, 1), dtype=cp.float32)\n",
" ill_rand = cp.random.random((num_ill_years, num_simulations), dtype=cp.float32)\n",
" ill_occurrences = ill_rand < ill_probs\n",
" ill_counts_gpu = ill_occurrences.sum(axis=0)\n",
"\n",
" # 7) Transfer results back to CPU (NumPy) arrays\n",
" death_ages = cp.asnumpy(death_ages_gpu)\n",
" career_counts = cp.asnumpy(career_counts_gpu)\n",
" ill_counts = cp.asnumpy(ill_counts_gpu)\n",
"\n",
" avg_death_age = death_ages.mean()\n",
" avg_careers = career_counts.mean()\n",
" avg_illnesses = ill_counts.mean()\n",
"\n",
" return {\n",
" \"avg_death_age\": float(avg_death_age),\n",
" \"avg_career_changes\": float(avg_careers),\n",
" \"avg_illnesses\": float(avg_illnesses),\n",
" \"death_age_distribution\": death_ages\n",
" }\n",
"\n",
"# -----------------------\n",
"# 3) Run & Compare Timings\n",
"# -----------------------\n",
"\n",
"if __name__ == \"__main__\":\n",
" N = 50_000 # Increase N for more pronounced GPU speedup\n",
" max_age = 200\n",
"\n",
" # Seed CPU RNGs for reproducibility\n",
" np.random.seed(42)\n",
" random.seed(42)\n",
"\n",
" # 3a) CPU timing\n",
" start_cpu = time.time()\n",
" cpu_results = run_simulation_cpu(num_simulations=N, max_age=max_age)\n",
" end_cpu = time.time()\n",
" cpu_time = end_cpu - start_cpu\n",
"\n",
" print(\"CPU Results:\")\n",
" print(f\" - Avg age of death: {cpu_results['avg_death_age']:.2f}\")\n",
" print(f\" - Avg career changes: {cpu_results['avg_career_changes']:.2f}\")\n",
" print(f\" - Avg illnesses: {cpu_results['avg_illnesses']:.2f}\")\n",
" print(f\" - CPU runtime: {cpu_time:.3f} seconds\\n\")\n",
"\n",
" # 3b) GPU timing (with CuPy kernels executed synchronously)\n",
" # Warm-up: run once to initialize CUDA context\n",
" _ = run_simulation_gpu(num_simulations=1_000, max_age=max_age)\n",
" cp.cuda.Stream.null.synchronize()\n",
"\n",
" start_gpu = time.time()\n",
" gpu_results = run_simulation_gpu(num_simulations=N, max_age=max_age)\n",
" cp.cuda.Stream.null.synchronize()\n",
" end_gpu = time.time()\n",
" gpu_time = end_gpu - start_gpu\n",
"\n",
" print(\"GPU (CuPy) Results:\")\n",
" print(f\" - Avg age of death: {gpu_results['avg_death_age']:.2f}\")\n",
" print(f\" - Avg career changes: {gpu_results['avg_career_changes']:.2f}\")\n",
" print(f\" - Avg illnesses: {gpu_results['avg_illnesses']:.2f}\")\n",
" print(f\" - GPU runtime: {gpu_time:.3f} seconds\\n\")\n",
"\n",
" # 3c) Speedup ratio\n",
" speedup = cpu_time / gpu_time if gpu_time > 0 else float('inf')\n",
" print(f\"Speedup (CPU_time / GPU_time): {speedup:.2f}x\")"
],
"metadata": {
"id": "8sVQ0FevUb2v",
"outputId": "f46ba9f1-7236-4085-d654-1a001be5a0dc",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"CPU Results:\n",
" - Avg age of death: 119.95\n",
" - Avg career changes: 4.22\n",
" - Avg illnesses: 1.60\n",
" - CPU runtime: 2.170 seconds\n",
"\n",
"GPU (CuPy) Results:\n",
" - Avg age of death: 119.72\n",
" - Avg career changes: 6.30\n",
" - Avg illnesses: 3.02\n",
" - GPU runtime: 0.005 seconds\n",
"\n",
"Speedup (CPU_time / GPU_time): 450.75x\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"# Monte Carlo Sankey Pipeline: CPU vs GPU (CuPy) Comparison\n",
"# --------------------------------------------------------\n",
"# This code demonstrates how to:\n",
"# 1) Run the same Monte Carlo life simulation on CPU and GPU\n",
"# 2) Build Sankey flow counts (career->illness, illness->death_age)\n",
"# 3) Plot a Sankey diagram via Plotly\n",
"# 4) Compare runtimes for CPU vs GPU implementations\n",
"\n",
"import time\n",
"import numpy as np\n",
"import random\n",
"import statistics\n",
"import plotly.graph_objects as go\n",
"from collections import Counter\n",
"\n",
"# ----------------------\n",
"# 1) Common Definitions\n",
"# ----------------------\n",
"\n",
"def mortality_probability(age, max_age=200):\n",
" return 0.0001 * age\n",
"\n",
"# Binning functions (used by both CPU and GPU paths)\n",
"def bin_career_changes(count):\n",
" if count == 0:\n",
" return \"Career=0\"\n",
" elif count == 1:\n",
" return \"Career=1\"\n",
" elif count == 2:\n",
" return \"Career=2\"\n",
" else:\n",
" return \"Career=3+\"\n",
"\n",
"def bin_illnesses(count):\n",
" if count == 0:\n",
" return \"Ill=0\"\n",
" elif count == 1:\n",
" return \"Ill=1\"\n",
" elif count == 2:\n",
" return \"Ill=2\"\n",
" else:\n",
" return \"Ill=3+\"\n",
"\n",
"def bin_death_age(age):\n",
" if age <= 50:\n",
" return \"Age=0-50\"\n",
" elif age <= 100:\n",
" return \"Age=51-100\"\n",
" elif age <= 150:\n",
" return \"Age=101-150\"\n",
" else:\n",
" return \"Age=151-200\"\n",
"\n",
"# Build Sankey data from flow counters\n",
"def build_sankey_data(flow1_counter, flow2_counter):\n",
" career_bins = [\"Career=0\", \"Career=1\", \"Career=2\", \"Career=3+\"]\n",
" illness_bins = [\"Ill=0\", \"Ill=1\", \"Ill=2\", \"Ill=3+\"]\n",
" age_bins = [\"Age=0-50\", \"Age=51-100\", \"Age=101-150\", \"Age=151-200\"]\n",
"\n",
" all_nodes = career_bins + illness_bins + age_bins\n",
" label_to_index = {label: i for i, label in enumerate(all_nodes)}\n",
"\n",
" source_list = []\n",
" target_list = []\n",
" value_list = []\n",
"\n",
" # (career -> illness)\n",
" for (c_bin, i_bin), val in flow1_counter.items():\n",
" source_list.append(label_to_index[c_bin])\n",
" target_list.append(label_to_index[i_bin])\n",
" value_list.append(val)\n",
"\n",
" # (illness -> age)\n",
" for (i_bin, a_bin), val in flow2_counter.items():\n",
" source_list.append(label_to_index[i_bin])\n",
" target_list.append(label_to_index[a_bin])\n",
" value_list.append(val)\n",
"\n",
" return all_nodes, source_list, target_list, value_list\n",
"\n",
"# Plot Sankey via Plotly\n",
"def plot_sankey(all_nodes, source_list, target_list, value_list, title=\"Life Simulation Sankey\"):\n",
" fig = go.Figure(data=[go.Sankey(\n",
" node=dict(\n",
" pad=15,\n",
" thickness=20,\n",
" line=dict(color=\"black\", width=0.5),\n",
" label=all_nodes\n",
" ),\n",
" link=dict(\n",
" source=source_list,\n",
" target=target_list,\n",
" value=value_list\n",
" )\n",
" )])\n",
" fig.update_layout(title_text=title, font_size=12)\n",
" fig.show()\n",
"\n",
"# ----------------------\n",
"# 2) CPU Implementation\n",
"# ----------------------\n",
"\n",
"def simulate_one_life_cpu(max_age=200):\n",
" current_age = 0\n",
" career_change_years = []\n",
" illness_years = []\n",
" while current_age <= max_age:\n",
" if random.random() < mortality_probability(current_age, max_age):\n",
" return current_age, len(career_change_years), len(illness_years)\n",
" if 25 <= current_age <= 150 and random.random() < 0.05:\n",
" career_change_years.append(current_age)\n",
" if 40 <= current_age <= 190 and random.random() < 0.02:\n",
" illness_years.append(current_age)\n",
" current_age += 1\n",
" return max_age, len(career_change_years), len(illness_years)\n",
"\n",
"\n",
"def run_simulation_cpu(num_simulations=10000, max_age=200):\n",
" death_ages = []\n",
" career_counts = []\n",
" illness_counts = []\n",
" for _ in range(num_simulations):\n",
" d_age, careers, ills = simulate_one_life_cpu(max_age)\n",
" death_ages.append(d_age)\n",
" career_counts.append(careers)\n",
" illness_counts.append(ills)\n",
" return {\n",
" \"death_ages\": np.array(death_ages, dtype=np.int32),\n",
" \"career_counts\": np.array(career_counts, dtype=np.int32),\n",
" \"illness_counts\": np.array(illness_counts, dtype=np.int32)\n",
" }\n",
"\n",
"# Build flow counters (career->illness, illness->death_age)\n",
"def build_flow_counts_cpu(death_ages, career_counts, illness_counts):\n",
" combos = []\n",
" for c_count, i_count, d_age in zip(career_counts, illness_counts, death_ages):\n",
" c_bin = bin_career_changes(int(c_count))\n",
" i_bin = bin_illnesses(int(i_count))\n",
" a_bin = bin_death_age(int(d_age))\n",
" combos.append((c_bin, i_bin, a_bin))\n",
" combo_counter = Counter(combos)\n",
" flow1_counter = Counter()\n",
" flow2_counter = Counter()\n",
" for (c_bin, i_bin, a_bin), cnt in combo_counter.items():\n",
" flow1_counter[(c_bin, i_bin)] += cnt\n",
" flow2_counter[(i_bin, a_bin)] += cnt\n",
" return flow1_counter, flow2_counter\n",
"\n",
"# ----------------------\n",
"# 3) GPU Implementation (CuPy)\n",
"# ----------------------\n",
"try:\n",
" import cupy as cp\n",
"except ImportError:\n",
" # Install matching CuPy for Colab's CUDA version (e.g., CUDA 11.6)\n",
" !pip install --quiet cupy-cuda116\n",
" import cupy as cp\n",
"\n",
"\n",
"def run_simulation_gpu(num_simulations=10000, max_age=200):\n",
" cp.random.seed(42)\n",
" # Age grid\n",
" ages = cp.arange(max_age + 1, dtype=cp.int32) # (max_age+1,)\n",
" probs = (0.0001 * ages.astype(cp.float32))[:, cp.newaxis] # (max_age+1,1)\n",
"\n",
" # Mortality randoms: shape (max_age+1, num_simulations)\n",
" rand_mort = cp.random.random((max_age + 1, num_simulations), dtype=cp.float32)\n",
" death_mask = rand_mort < probs # boolean mask\n",
" has_death = death_mask.any(axis=0)\n",
" first_idx = death_mask.argmax(axis=0)\n",
" death_ages_gpu = cp.where(has_death, first_idx, max_age).astype(cp.int32)\n",
"\n",
" # Career changes (ages 25–150)\n",
" c_start, c_end = 25, 150\n",
" num_c_years = c_end - c_start + 1\n",
" career_probs = 0.05 * cp.ones((num_c_years, 1), dtype=cp.float32)\n",
" rand_career = cp.random.random((num_c_years, num_simulations), dtype=cp.float32)\n",
" career_counts_gpu = (rand_career < career_probs).sum(axis=0).astype(cp.int32)\n",
"\n",
" # Illnesses (ages 40–190)\n",
" i_start, i_end = 40, 190\n",
" num_i_years = i_end - i_start + 1\n",
" illness_probs = 0.02 * cp.ones((num_i_years, 1), dtype=cp.float32)\n",
" rand_ill = cp.random.random((num_i_years, num_simulations), dtype=cp.float32)\n",
" illness_counts_gpu = (rand_ill < illness_probs).sum(axis=0).astype(cp.int32)\n",
"\n",
" # Transfer to CPU (NumPy)\n",
" death_ages = cp.asnumpy(death_ages_gpu)\n",
" career_counts = cp.asnumpy(career_counts_gpu)\n",
" illness_counts = cp.asnumpy(illness_counts_gpu)\n",
"\n",
" return death_ages, career_counts, illness_counts\n",
"\n",
"# Build flow counters on CPU from GPU-generated arrays (reuse build_flow_counts_cpu)\n",
"# ----------------------\n",
"\n",
"if __name__ == \"__main__\":\n",
" N = 10000 # Number of simulated lives (adjust as needed)\n",
" max_age = 200\n",
"\n",
" # -------- CPU Path --------\n",
" np.random.seed(42)\n",
" random.seed(42)\n",
" start_cpu = time.time()\n",
" cpu_death, cpu_career, cpu_ill = run_simulation_cpu(num_simulations=N, max_age=max_age).values()\n",
" flow1_cpu, flow2_cpu = build_flow_counts_cpu(cpu_death, cpu_career, cpu_ill)\n",
" all_nodes_cpu, src_cpu, tgt_cpu, val_cpu = build_sankey_data(flow1_cpu, flow2_cpu)\n",
" cpu_time = time.time() - start_cpu\n",
"\n",
" print(\"CPU Path Completed\")\n",
" print(f\" - CPU runtime (simulation + flow counts): {cpu_time:.3f} seconds\")\n",
"\n",
" # -------- GPU Path --------\n",
" # Warm up CUDA context\n",
" _ = run_simulation_gpu(num_simulations=1000, max_age=max_age)\n",
" cp.cuda.Stream.null.synchronize()\n",
" np.random.seed(42)\n",
" random.seed(42)\n",
" start_gpu = time.time()\n",
" gpu_death, gpu_career, gpu_ill = run_simulation_gpu(num_simulations=N, max_age=max_age)\n",
" flow1_gpu, flow2_gpu = build_flow_counts_cpu(gpu_death, gpu_career, gpu_ill)\n",
" all_nodes_gpu, src_gpu, tgt_gpu, val_gpu = build_sankey_data(flow1_gpu, flow2_gpu)\n",
" cp.cuda.Stream.null.synchronize()\n",
" gpu_time = time.time() - start_gpu\n",
"\n",
" print(\"GPU Path Completed\")\n",
" print(f\" - GPU runtime (simulation + flow counts): {gpu_time:.3f} seconds\")\n",
"\n",
" speedup = cpu_time / gpu_time if gpu_time > 0 else float('inf')\n",
" print(f\"Speedup (CPU_time / GPU_time): {speedup:.2f}x\")\n",
"\n",
" # -------- Sankey Plot (use CPU-generated Sankey data) --------\n",
" # Plot only once, using CPU Sankey data (same topology as GPU)\n",
" plot_sankey(all_nodes_cpu, src_cpu, tgt_cpu, val_cpu,\n",
" title=\"Life Simulation Sankey (CPU-generated data)\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 629
},
"id": "h5Ei77Vb_LZf",
"outputId": "47429ab6-ee74-454a-eff1-86cad32b17c5"
},
"execution_count": 3,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"CPU Path Completed\n",
" - CPU runtime (simulation + flow counts): 0.270 seconds\n",
"GPU Path Completed\n",
" - GPU runtime (simulation + flow counts): 0.008 seconds\n",
"Speedup (CPU_time / GPU_time): 32.58x\n"
]
},
{
"output_type": "display_data",
"data": {
"text/html": [
"\n",
"