{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# 0. Ρυθμίσεις και βιβλιοθήκες\n",
        "\n",
        "Σε αυτό το κελί φορτώνουμε τις βασικές βιβλιοθήκες που θα χρησιμοποιήσουμε για τη μελέτη της στοχαστικής διαδικασίας Poisson:\n",
        "\n",
        "- `numpy` για αριθμητικούς υπολογισμούς και τυχαία δείγματα\n",
        "- `matplotlib` για γραφικές παραστάσεις\n",
        "\n",
        "Θα χρησιμοποιήσουμε μια γεννήτρια τυχαίων αριθμών (`rng`) από τη `NumPy` ώστε τα αποτελέσματα να είναι επαναλήψιμα (ίδια κάθε φορά που τρέχουμε το notebook).\n",
        "\n",
        "Θυμόμαστε ότι η διαδικασία Poisson με ένταση (ρυθμό) $ \\lambda > 0 $ είναι μια στοχαστική διαδικασία $ \\{N(t): t \\ge 0\\} $ με:\n",
        "\n",
        "- $N(0) = 0$\n",
        "- στάσιμες και ανεξάρτητες προσαυξήσεις\n",
        "- $N(t) \\sim \\text{Poisson}(\\lambda t)$ για κάθε $t \\ge 0$.\n"
      ],
      "metadata": {
        "id": "XgFwILGvGhE0"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "from math import factorial\n",
        "\n",
        "# Δημιουργία γεννήτριας τυχαίων αριθμών (για αναπαραγωγιμότητα)\n",
        "rng = np.random.default_rng(123)\n",
        "\n",
        "# Ρύθμιση ώστε τα γραφήματα να εμφανίζονται μέσα στο notebook\n",
        "# (στο Colab είναι ουσιαστικά το default, αλλά το κρατάμε)\n",
        "%matplotlib inline\n"
      ],
      "metadata": {
        "id": "vDROOgjSGkED"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 1. Εκθετική κατανομή και χρόνοι μεταξύ αφίξεων\n",
        "\n",
        "Θυμόμαστε ότι σε μια διαδικασία Poisson με ένταση $ \\lambda $ οι χρόνοι μεταξύ διαδοχικών αφίξεων είναι ανεξάρτητες εκθετικές τυχαίες μεταβλητές με πυκνότητα\n",
        "\n",
        "$$\n",
        "f_T(t) = \\lambda e^{-\\lambda t}, \\quad t \\ge 0.\n",
        "$$\n",
        "\n",
        "Σε αυτό το κελί:\n",
        "\n",
        "1. Προσομοιώνουμε $n$ δείγματα από κατανομή $\\text{Exp}(\\lambda)$.\n",
        "2. Συγκρίνουμε το ιστόγραμμα των δειγμάτων με τη θεωρητική πυκνότητα.\n",
        "3. Ελέγχουμε προσεγγιστικά τη **ιδιότητα μνήμης** (memoryless property):\n",
        "\n",
        "$$\n",
        "\\mathbb{P}(T > t + s \\mid T > t) = \\mathbb{P}(T > s).\n",
        "$$\n"
      ],
      "metadata": {
        "id": "cG6Il9P_GrsP"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Παράμετρος λ και αριθμός δειγμάτων\n",
        "lam = 2.0\n",
        "n_samples = 10_000\n",
        "\n",
        "# Δείγματα από Exp(λ)\n",
        "samples = rng.exponential(scale=1/lam, size=n_samples)\n",
        "\n",
        "print(\"Εμπειρικός μέσος:\", samples.mean())\n",
        "print(\"Θεωρητικός μέσος:\", 1/lam)\n",
        "\n",
        "# Ιστόγραμμα + θεωρητική πυκνότητα\n",
        "x = np.linspace(0, samples.max(), 200)\n",
        "pdf = lam * np.exp(-lam * x)\n",
        "\n",
        "plt.figure()\n",
        "plt.hist(samples, bins=40, density=True, alpha=0.6, label=\"Εμπειρική πυκνότητα\")\n",
        "plt.plot(x, pdf, linewidth=2, label=\"Θεωρητική Exp(λ)\")\n",
        "plt.xlabel(\"t\")\n",
        "plt.ylabel(\"density\")\n",
        "plt.legend()\n",
        "plt.title(\"Εκθετική κατανομή χρόνων μεταξύ αφίξεων (λ = {:.1f})\".format(lam))\n",
        "plt.show()\n",
        "\n",
        "# Έλεγχος ιδιότητας μνήμης\n",
        "t = 1.0\n",
        "s = 0.5\n",
        "\n",
        "cond = samples[samples > t]           # κρατάμε όσες είναι > t\n",
        "lhs = np.mean(cond > t + s)           # P(T > t + s | T > t)\n",
        "rhs = np.mean(samples > s)            # P(T > s)\n",
        "\n",
        "print(\"P(T > t+s | T>t) ≈\", lhs)\n",
        "print(\"P(T > s)        ≈\", rhs)\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 541
        },
        "id": "LO56o4zAGtD0",
        "outputId": "666b283c-2875-47fa-91dd-fa5c4314e3a2"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Εμπειρικός μέσος: 0.48825936284546717\n",
            "Θεωρητικός μέσος: 0.5\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "[encoded data, 41216 characters]\n"
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "P(T > t+s | T>t) ≈ 0.34686064318529863\n",
            "P(T > s)        ≈ 0.3577\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 2. Κατασκευή της διαδικασίας Poisson από εκθετικούς χρόνους\n",
        "\n",
        "Μπορούμε να κατασκευάσουμε μια διαδικασία Poisson ως εξής:\n",
        "\n",
        "1. Παίρνουμε ανεξάρτητους εκθετικούς χρόνους $T_1, T_2, \\dots$ με παράμετρο $\\lambda$.\n",
        "2. Ορίζουμε τους χρόνους αφίξεων\n",
        "   $$\n",
        "   S_n = T_1 + T_2 + \\dots + T_n.\n",
        "   $$\n",
        "3. Ορίζουμε\n",
        "   $$\n",
        "   N(t) = \\max\\{ n \\ge 0 : S_n \\le t \\}.\n",
        "   $$\n",
        "\n",
        "Σε αυτό το κελί υλοποιούμε αυτή την κατασκευή και σχεδιάζουμε ένα τυπικό **sample path** της διαδικασίας Poisson $N(t)$.\n"
      ],
      "metadata": {
        "id": "kMhfak6nG2L0"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "lam = 1.0\n",
        "T_max = 10.0\n",
        "n_jumps_max = 50  # μέγιστος αριθμός αφίξεων για ασφάλεια\n",
        "\n",
        "# Εκθετικοί χρόνοι αναμονής\n",
        "inter_arrivals = rng.exponential(scale=1/lam, size=n_jumps_max)\n",
        "arrival_times = np.cumsum(inter_arrivals)\n",
        "\n",
        "# Κρατάμε μόνο τους χρόνους αφίξεων μέχρι τον χρόνο T_max\n",
        "arrival_times = arrival_times[arrival_times <= T_max]\n",
        "n_jumps = len(arrival_times)\n",
        "\n",
        "# Φτιάχνουμε το N(t) ως step function\n",
        "times = np.concatenate(([0.0], np.repeat(arrival_times, 2), [T_max]))\n",
        "values = np.concatenate(([0], np.repeat(np.arange(1, n_jumps+1), 2), [n_jumps]))\n",
        "\n",
        "plt.figure()\n",
        "plt.step(times, values, where=\"post\")\n",
        "plt.xlabel(\"t\")\n",
        "plt.ylabel(\"N(t)\")\n",
        "plt.title(\"Διαδικασία Poisson από εκθετικούς χρόνους (λ = {:.1f})\".format(lam))\n",
        "plt.grid(True)\n",
        "plt.show()\n",
        "\n",
        "print(\"Χρόνοι αφίξεων:\", arrival_times)\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 507
        },
        "id": "omCLeY4PG6-w",
        "outputId": "3fce32e3-6dd3-41fa-f0e3-82612e716be5"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "[encoded data, 22188 characters]\n"
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Χρόνοι αφίξεων: [2.43890924 2.46329978 4.75488742 4.87175112 6.1436801  8.39439841\n",
            " 8.51570813 9.68687184 9.92526245]\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 3. Κατανομή του $N(t)$ και σύγκριση με $\\text{Poisson}(\\lambda t)$\n",
        "\n",
        "Για μια διαδικασία Poisson με ένταση $\\lambda$ γνωρίζουμε ότι\n",
        "\n",
        "$$\n",
        "N(t) \\sim \\text{Poisson}(\\lambda t), \\quad t \\ge 0.\n",
        "$$\n",
        "\n",
        "Σε αυτό το κελί:\n",
        "\n",
        "1. Προσομοιώνουμε πολλές φορές τη διαδικασία μέχρι χρόνο $t_0$ χρησιμοποιώντας εκθετικούς χρόνους.\n",
        "2. Καταγράφουμε τις τιμές $N(t_0)$.\n",
        "3. Συγκρίνουμε το εμπειρικό ιστόγραμμα με την θεωρητική κατανομή Poisson με μέση τιμή $\\lambda t_0$.\n"
      ],
      "metadata": {
        "id": "5qlE4vdnG_nv"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "lam = 1.5\n",
        "t0 = 3.0\n",
        "n_paths = 10_000\n",
        "\n",
        "def simulate_N_t_by_exponentials(lam, t0, rng):\n",
        "    \"\"\"Προσομοιώνει N(t0) συσσωρεύοντας εκθετικούς χρόνους.\"\"\"\n",
        "    n = 0\n",
        "    t = 0.0\n",
        "    while True:\n",
        "        t += rng.exponential(scale=1/lam)\n",
        "        if t > t0:\n",
        "            break\n",
        "        n += 1\n",
        "    return n\n",
        "\n",
        "values = np.array([simulate_N_t_by_exponentials(lam, t0, rng) for _ in range(n_paths)])\n",
        "\n",
        "# Εύρος τιμών που εμφανίστηκαν\n",
        "k_min = values.min()\n",
        "k_max = values.max()\n",
        "k_vals = np.arange(k_min, k_max + 1)\n",
        "\n",
        "# Εμπειρικές συχνότητες\n",
        "emp_counts = np.array([np.sum(values == k) for k in k_vals]) / n_paths\n",
        "\n",
        "# Θεωρητικές πιθανότητες Poisson(λ t0)\n",
        "mu = lam * t0\n",
        "theo_probs = np.array([\n",
        "    np.exp(-mu) * mu**k / factorial(k) for k in k_vals\n",
        "])\n",
        "\n",
        "plt.figure()\n",
        "plt.bar(k_vals - 0.2, emp_counts, width=0.4, label=\"Εμπειρική P(N(t0)=k)\")\n",
        "plt.bar(k_vals + 0.2, theo_probs, width=0.4, label=\"Θεωρητική Poisson(λ t0)\")\n",
        "plt.xlabel(\"k\")\n",
        "plt.ylabel(\"πιθανότητα\")\n",
        "plt.legend()\n",
        "plt.title(\"Κατανομή του N(t0), t0 = {:.1f}, λ = {:.1f}\".format(t0, lam))\n",
        "plt.show()\n",
        "\n",
        "print(\"Εμπειρικός μέσος:\", values.mean(), \"  Θεωρητικός:\", mu)\n",
        "print(\"Εμπειρική διασπορά:\", values.var(), \"  Θεωρητική:\", mu)\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 507
        },
        "id": "KV2sIRCMHDP4",
        "outputId": "1fb79ebf-47ff-401e-929a-10252cd4ce2d"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "[encoded data, 34000 characters]\n"
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Εμπειρικός μέσος: 4.4961   Θεωρητικός: 4.5\n",
            "Εμπειρική διασπορά: 4.63218479   Θεωρητική: 4.5\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 4. Στάσιμες προσαυξήσεις\n",
        "\n",
        "Μια διαδικασία Poisson έχει **στάσιμες προσαυξήσεις**, δηλαδή για κάθε $t$ και $h > 0$ η κατανομή του\n",
        "\n",
        "$$\n",
        "N(t+h) - N(t)\n",
        "$$\n",
        "\n",
        "εξαρτάται μόνο από το μήκος του διαστήματος $h$ και όχι από το σημείο εκκίνησης $t$.\n",
        "\n",
        "Σε αυτό το κελί:\n",
        "\n",
        "- Παίρνουμε διάφορα σημεία $t$ (π.χ. $0$, $2$, $5$) με σταθερό $h = 1$.\n",
        "- Προσομοιώνουμε πολλές πορείες μέχρι έναν μέγιστο χρόνο $T_{\\max}$.\n",
        "- Υπολογίζουμε τη μέση τιμή των προσαυξήσεων σε κάθε διάστημα και τις συγκρίνουμε.\n"
      ],
      "metadata": {
        "id": "Hxd3RQ9NHISG"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "lam = 1.0\n",
        "h = 1.0\n",
        "t_list = [0.0, 2.0, 5.0]\n",
        "n_paths = 5000\n",
        "T_max = max(t_list) + h + 2  # λίγο παραπάνω για ασφάλεια\n",
        "\n",
        "def simulate_arrivals(lam, T, rng):\n",
        "    \"\"\"Επιστρέφει τους χρόνους αφίξεων μέχρι τον χρόνο T.\"\"\"\n",
        "    times = []\n",
        "    t = 0.0\n",
        "    while True:\n",
        "        t += rng.exponential(1/lam)\n",
        "        if t > T:\n",
        "            break\n",
        "        times.append(t)\n",
        "    return np.array(times)\n",
        "\n",
        "increments = {t: [] for t in t_list}\n",
        "\n",
        "for _ in range(n_paths):\n",
        "    arr = simulate_arrivals(lam, T_max, rng)\n",
        "    for t in t_list:\n",
        "        n2 = np.sum((arr > t) & (arr <= t + h))\n",
        "        increments[t].append(n2)\n",
        "\n",
        "for t in t_list:\n",
        "    arr_inc = np.array(increments[t])\n",
        "    print(f\"t = {t:.1f}: μέση προσαύξηση ≈ {arr_inc.mean():.4f}, διασπορά ≈ {arr_inc.var():.4f}\")\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "qQYtbYX5HL3k",
        "outputId": "26945e4b-4408-415a-a3ed-e50de1857688"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "t = 0.0: μέση προσαύξηση ≈ 1.0014, διασπορά ≈ 1.0162\n",
            "t = 2.0: μέση προσαύξηση ≈ 0.9898, διασπορά ≈ 0.9861\n",
            "t = 5.0: μέση προσαύξηση ≈ 0.9928, διασπορά ≈ 1.0031\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 5. Ανεξάρτητες προσαυξήσεις\n",
        "\n",
        "Μια διαδικασία Poisson έχει επίσης **ανεξάρτητες προσαυξήσεις**: για ασύμπλεκτα χρονικά διαστήματα, οι αντίστοιχες προσαυξήσεις είναι ανεξάρτητες.\n",
        "\n",
        "Παράδειγμα:\n",
        "\n",
        "- Θεωρούμε τα διαστήματα $[0,1]$ και $[1,2]$.\n",
        "- Ορίζουμε\n",
        "  $$\n",
        "  X_1 = N(1) - N(0), \\quad X_2 = N(2) - N(1).\n",
        "  $$\n",
        "- Αν οι προσαυξήσεις είναι ανεξάρτητες, τότε η συσχέτιση $\\text{corr}(X_1, X_2)$ πρέπει να είναι κοντά στο $0$.\n",
        "\n",
        "Σε αυτό το κελί υπολογίζουμε εμπειρικά αυτή τη συσχέτιση.\n"
      ],
      "metadata": {
        "id": "oNkmIkPGHQlq"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "lam = 1.0\n",
        "n_paths = 5000\n",
        "\n",
        "def simulate_arrivals(lam, T, rng):\n",
        "    t = 0.0\n",
        "    times = []\n",
        "    while True:\n",
        "        t += rng.exponential(1/lam)\n",
        "        if t > T:\n",
        "            break\n",
        "        times.append(t)\n",
        "    return np.array(times)\n",
        "\n",
        "X1 = np.empty(n_paths)\n",
        "X2 = np.empty(n_paths)\n",
        "\n",
        "for i in range(n_paths):\n",
        "    arr = simulate_arrivals(lam, T=2.0, rng=rng)\n",
        "    X1[i] = np.sum((arr > 0.0) & (arr <= 1.0))\n",
        "    X2[i] = np.sum((arr > 1.0) & (arr <= 2.0))\n",
        "\n",
        "corr = np.corrcoef(X1, X2)[0, 1]\n",
        "print(\"corr(X1, X2) ≈\", corr)\n",
        "print(\"E[X1] ≈\", X1.mean(), \"  E[X2] ≈\", X2.mean())\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "0KEwSw8SHTb7",
        "outputId": "fd80d023-9d1b-4825-b651-befc5d9bc04f"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "corr(X1, X2) ≈ -0.027263598785801747\n",
            "E[X1] ≈ 0.9992   E[X2] ≈ 1.011\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 6. Markov ιδιότητα και πίνακας μετάβασης $P(t)$\n",
        "\n",
        "Η διαδικασία Poisson είναι **αλυσίδα Markov συνεχούς χρόνου** με χώρο καταστάσεων $\\{0,1,2,\\dots\\}$.\n",
        "\n",
        "Για $i \\le j$ ισχύει\n",
        "$$\n",
        "P_{ij}(t) = \\mathbb{P}(N(t) = j \\mid N(0) = i)\n",
        "= e^{-\\lambda t}\\frac{(\\lambda t)^{j-i}}{(j-i)!},\n",
        "$$\n",
        "και $P_{ij}(t) = 0$ αν $j < i$.\n",
        "\n",
        "Σε αυτό το κελί:\n",
        "\n",
        "- Υποθέτουμε ότι ξεκινάμε από $N(0)=0$.\n",
        "- Προσομοιώνουμε πολλές φορές $N(t)$ (χρησιμοποιώντας την Poisson κατανομή).\n",
        "- Εκτιμούμε τις πιθανότητες $P_{0j}(t)$ και τις συγκρίνουμε με τις θεωρητικές.\n"
      ],
      "metadata": {
        "id": "Lp2qJ8RjHWqU"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "lam = 1.0\n",
        "t = 1.0\n",
        "n_paths = 20_000\n",
        "max_state = 8  # θα κοιτάξουμε τις καταστάσεις 0,...,8\n",
        "\n",
        "# Προσομοίωση N(t) ως Poisson(λ t)\n",
        "N_t = rng.poisson(lam * t, size=n_paths)\n",
        "\n",
        "# Εμπειρικές πιθανότητες P(N(t)=j)\n",
        "emp_probs = np.bincount(N_t, minlength=max_state+1) / n_paths\n",
        "\n",
        "# Θεωρητικές πιθανότητες P_{0j}(t)\n",
        "k = np.arange(0, max_state+1)\n",
        "mu = lam * t\n",
        "theo_probs = np.array([\n",
        "    np.exp(-mu) * mu**j / factorial(j) for j in k\n",
        "])\n",
        "\n",
        "for j in range(max_state+1):\n",
        "    print(f\"j={j}: emp={emp_probs[j]:.4f}, theor={theo_probs[j]:.4f}\")\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "iPsLekoWHZow",
        "outputId": "ff2a9e1c-8b6c-4a63-90a1-089f6edcd3b5"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "j=0: emp=0.3659, theor=0.3679\n",
            "j=1: emp=0.3624, theor=0.3679\n",
            "j=2: emp=0.1880, theor=0.1839\n",
            "j=3: emp=0.0646, theor=0.0613\n",
            "j=4: emp=0.0157, theor=0.0153\n",
            "j=5: emp=0.0029, theor=0.0031\n",
            "j=6: emp=0.0004, theor=0.0005\n",
            "j=7: emp=0.0001, theor=0.0001\n",
            "j=8: emp=0.0000, theor=0.0000\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 7. Martingale $$M(t) = N(t) - \\lambda t$$\n",
        "\n",
        "Για τη διαδικασία Poisson με ένταση $\\lambda$ η στοχαστική διαδικασία\n",
        "\n",
        "$$\n",
        "M(t) = N(t) - \\lambda t\n",
        "$$\n",
        "\n",
        "είναι martingale ως προς τη φυσική της φιλτράτσια. Αυτό σημαίνει (μεταξύ άλλων) ότι\n",
        "\n",
        "$$\n",
        "\\mathbb{E}[M(t)] = 0 \\quad \\text{για κάθε } t \\ge 0.\n",
        "$$\n",
        "\n",
        "Σε αυτό το κελί:\n",
        "\n",
        "1. Προσομοιώνουμε πολλές πορείες της διαδικασίας Poisson πάνω σε ένα πλέγμα χρονικών στιγμών.\n",
        "2. Υπολογίζουμε $M(t)$ για κάθε πορεία.\n",
        "3. Ελέγχουμε ότι ο εμπειρικός μέσος της $M(t)$ είναι κοντά στο $0$.\n",
        "4. Σχεδιάζουμε μερικά sample paths του $M(t)$.\n"
      ],
      "metadata": {
        "id": "JBYG5LK5HdTh"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "lam = 1.0\n",
        "T_max = 10.0\n",
        "n_paths = 2000\n",
        "grid = np.linspace(0, T_max, 21)  # 0,0.5,1,...,10\n",
        "\n",
        "def simulate_poisson_on_grid(lam, grid, rng):\n",
        "    \"\"\"Προσομοιώνει μια πορεία Poisson και επιστρέφει N(t) πάνω στο grid.\"\"\"\n",
        "    N_values = np.zeros_like(grid, dtype=int)\n",
        "    t_jump = rng.exponential(1/lam)  # πρώτη άφιξη\n",
        "    N = 0\n",
        "    for idx, g in enumerate(grid):\n",
        "        while t_jump <= g:\n",
        "            N += 1\n",
        "            t_jump += rng.exponential(1/lam)\n",
        "        N_values[idx] = N\n",
        "    return N_values\n",
        "\n",
        "# Συλλέγουμε τις τιμές του M(t) για πολλές πορείες\n",
        "M_values = []\n",
        "for _ in range(n_paths):\n",
        "    N_vals = simulate_poisson_on_grid(lam, grid, rng)\n",
        "    M_vals = N_vals - lam * grid\n",
        "    M_values.append(M_vals)\n",
        "\n",
        "M_values = np.array(M_values)\n",
        "mean_M = M_values.mean(axis=0)\n",
        "\n",
        "print(\"Εμπειρικός μέσος του M(t) σε διάφορα t:\")\n",
        "for g, m in zip(grid, mean_M):\n",
        "    print(f\"t = {g:4.1f} : E[M(t)] ≈ {m: .4f}\")\n",
        "\n",
        "# Οπτικοποίηση μερικών sample paths του M(t)\n",
        "plt.figure()\n",
        "for i in range(10):\n",
        "    plt.plot(grid, M_values[i], alpha=0.6)\n",
        "plt.axhline(0, linestyle=\"--\")\n",
        "plt.xlabel(\"t\")\n",
        "plt.ylabel(\"M(t) = N(t) - λ t\")\n",
        "plt.title(\"Sample paths του martingale M(t)\")\n",
        "plt.grid(True)\n",
        "plt.show()\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 854
        },
        "id": "uP27llK7Hf3J",
        "outputId": "bafcd9d1-e17b-463f-87c8-8bec50eabfb6"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Εμπειρικός μέσος του M(t) σε διάφορα t:\n",
            "t =  0.0 : E[M(t)] ≈  0.0000\n",
            "t =  0.5 : E[M(t)] ≈ -0.0100\n",
            "t =  1.0 : E[M(t)] ≈ -0.0105\n",
            "t =  1.5 : E[M(t)] ≈ -0.0070\n",
            "t =  2.0 : E[M(t)] ≈  0.0075\n",
            "t =  2.5 : E[M(t)] ≈  0.0080\n",
            "t =  3.0 : E[M(t)] ≈ -0.0325\n",
            "t =  3.5 : E[M(t)] ≈ -0.0155\n",
            "t =  4.0 : E[M(t)] ≈ -0.0440\n",
            "t =  4.5 : E[M(t)] ≈ -0.0835\n",
            "t =  5.0 : E[M(t)] ≈ -0.0740\n",
            "t =  5.5 : E[M(t)] ≈ -0.0575\n",
            "t =  6.0 : E[M(t)] ≈ -0.0365\n",
            "t =  6.5 : E[M(t)] ≈ -0.0270\n",
            "t =  7.0 : E[M(t)] ≈ -0.0410\n",
            "t =  7.5 : E[M(t)] ≈ -0.0490\n",
            "t =  8.0 : E[M(t)] ≈ -0.0475\n",
            "t =  8.5 : E[M(t)] ≈ -0.0520\n",
            "t =  9.0 : E[M(t)] ≈ -0.0635\n",
            "t =  9.5 : E[M(t)] ≈ -0.0590\n",
            "t = 10.0 : E[M(t)] ≈ -0.0670\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "[encoded data, 105772 characters]\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 8. Ιδιότητες superposition και thinning\n",
        "\n",
        "## 8.1 Superposition\n",
        "\n",
        "Το άθροισμα δύο ανεξάρτητων διαδικασιών Poisson με εντάσεις $\\lambda_1$ και $\\lambda_2$ είναι διαδικασία Poisson με ένταση $\\lambda_1 + \\lambda_2$.\n",
        "\n",
        "Για έναν σταθερό χρόνο $t$ έχουμε\n",
        "$$\n",
        "N_1(t) \\sim \\text{Poisson}(\\lambda_1 t), \\quad\n",
        "N_2(t) \\sim \\text{Poisson}(\\lambda_2 t),\n",
        "$$\n",
        "ανεξάρτητες, και\n",
        "$$\n",
        "N(t) = N_1(t) + N_2(t) \\sim \\text{Poisson}((\\lambda_1+\\lambda_2)t).\n",
        "$$\n",
        "\n",
        "## 8.2 Thinning\n",
        "\n",
        "Αν κάθε άφιξη της διαδικασίας Poisson κρατιέται με ανεξάρτητη πιθανότητα $p$, τότε οι “διατηρημένες” αφίξεις σχηματίζουν διαδικασία Poisson με ένταση $p \\lambda$.\n",
        "\n",
        "Σε αυτό το κελί:\n",
        "\n",
        "- Δείχνουμε εμπειρικά την ιδιότητα superposition.\n",
        "- Δείχνουμε εμπειρικά το thinning: από $N(t) \\sim \\text{Poisson}(\\lambda t)$, κρατάμε κάθε άφιξη με πιθανότητα $p$ και συγκρίνουμε με $\\text{Poisson}(p \\lambda t)$.\n"
      ],
      "metadata": {
        "id": "V8aokgmTHkRj"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# 8.1 Superposition\n",
        "lam1 = 1.0\n",
        "lam2 = 2.0\n",
        "t0 = 2.0\n",
        "n_paths = 20_000\n",
        "\n",
        "N1 = rng.poisson(lam1 * t0, size=n_paths)\n",
        "N2 = rng.poisson(lam2 * t0, size=n_paths)\n",
        "N_sum = N1 + N2\n",
        "\n",
        "mu_sum = (lam1 + lam2) * t0\n",
        "\n",
        "print(\"Superposition:\")\n",
        "print(\"Εμπειρικός μέσος N_sum:\", N_sum.mean())\n",
        "print(\"Θεωρητικός μέσος (λ1+λ2)t:\", mu_sum)\n",
        "print(\"Εμπειρική διασπορά N_sum:\", N_sum.var())\n",
        "print(\"Θεωρητική διασπορά:\", mu_sum)\n",
        "print()\n",
        "\n",
        "# 8.2 Thinning\n",
        "lam = 3.0\n",
        "p = 0.4\n",
        "t0 = 2.0\n",
        "n_paths = 10_000\n",
        "\n",
        "N_total = rng.poisson(lam * t0, size=n_paths)\n",
        "\n",
        "# Για κάθε N_total = k, ο αριθμός \"κρατημένων\" αφίξεων είναι Binomial(k, p)\n",
        "N_kept = np.array([rng.binomial(k, p) for k in N_total])\n",
        "\n",
        "print(\"Thinning:\")\n",
        "print(\"Εμπειρικός μέσος N_kept:\", N_kept.mean())\n",
        "print(\"Θεωρητικός p λ t0:\", p * lam * t0)\n",
        "print(\"Εμπειρική διασπορά N_kept:\", N_kept.var())\n",
        "print(\"Θεωρητική διασπορά:\", p * lam * t0)\n",
        "\n",
        "# Προαιρετικά, μικρό ιστόγραμμα για N_kept\n",
        "k_min = N_kept.min()\n",
        "k_max = N_kept.max()\n",
        "k_vals = np.arange(k_min, k_max + 1)\n",
        "emp_probs = np.array([np.mean(N_kept == k) for k in k_vals])\n",
        "mu_thin = p * lam * t0\n",
        "theo_probs = np.array([\n",
        "    np.exp(-mu_thin) * mu_thin**k / factorial(k) for k in k_vals\n",
        "])\n",
        "\n",
        "plt.figure()\n",
        "plt.bar(k_vals - 0.2, emp_probs, width=0.4, label=\"Empirical\")\n",
        "plt.bar(k_vals + 0.2, theo_probs, width=0.4, label=\"Theoretical Poisson\")\n",
        "plt.xlabel(\"k\")\n",
        "plt.ylabel(\"Probability\")\n",
        "plt.legend()\n",
        "plt.title(\"Κατανομή του N_kept (thinning)\")\n",
        "plt.show()\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 663
        },
        "id": "RwxH3xlcHnpF",
        "outputId": "7fa3f23c-4889-4413-c05f-2c7193c03761"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Superposition:\n",
            "Εμπειρικός μέσος N_sum: 6.01\n",
            "Θεωρητικός μέσος (λ1+λ2)t: 6.0\n",
            "Εμπειρική διασπορά N_sum: 5.8831\n",
            "Θεωρητική διασπορά: 6.0\n",
            "\n",
            "Thinning:\n",
            "Εμπειρικός μέσος N_kept: 2.3913\n",
            "Θεωρητικός p λ t0: 2.4000000000000004\n",
            "Εμπειρική διασπορά N_kept: 2.35978431\n",
            "Θεωρητική διασπορά: 2.4000000000000004\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "[encoded data, 25332 characters]\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 9. Σύνθετη διαδικασία Poisson (compound Poisson) και εφαρμογή\n",
        "\n",
        "Στο αναλογιστικό πλαίσιο (π.χ. μοντέλο ζημιών ασφαλιστικής εταιρείας) χρησιμοποιούμε τη **σύνθετη διαδικασία Poisson**:\n",
        "\n",
        "- Ο αριθμός των αιτημάτων/ζημιών μέχρι τον χρόνο $t$ είναι\n",
        "  $$\n",
        "  N(t) \\sim \\text{Poisson}(\\lambda t).\n",
        "  $$\n",
        "- Τα ποσά ζημιών $Y_1, Y_2, \\dots$ είναι ανεξάρτητα και ανεξάρτητα από το $N(t)$.\n",
        "- Ορίζουμε τη συνολική ζημιά μέχρι τον χρόνο $t$ ως\n",
        "  $$\n",
        "  X(t) = \\sum_{i=1}^{N(t)} Y_i.\n",
        "  $$\n",
        "\n",
        "Σε αυτό το κελί:\n",
        "\n",
        "1. Προσομοιώνουμε μια σύνθετη διαδικασία Poisson μέχρι χρόνο $T_{\\max}$ με $Y_i$ λογαριθμοκανονικά.\n",
        "2. Σχεδιάζουμε μερικές πορείες του $X(t)$ (βηματική συνάρτηση).\n",
        "3. Προσομοιώνουμε πολλές φορές το $X(T_{\\max})$ και σχεδιάζουμε το ιστόγραμμά του.\n"
      ],
      "metadata": {
        "id": "6uDwnK0nHrcB"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "lam = 1.0      # ένταση αιτημάτων\n",
        "mu_Y = 2.0     # παράμετροι για lognormal Y\n",
        "sigma_Y = 0.5\n",
        "T_max = 10.0\n",
        "\n",
        "def simulate_compound_poisson(lam, mu_Y, sigma_Y, T_max, rng, n_grid=101):\n",
        "    \"\"\"Επιστρέφει (grid, X_grid) για σύνθετη διαδικασία Poisson.\"\"\"\n",
        "    grid = np.linspace(0, T_max, n_grid)\n",
        "    X = np.zeros_like(grid)\n",
        "    t = 0.0\n",
        "    S = 0.0    # συνολική ζημιά\n",
        "    idx = 0\n",
        "\n",
        "    while True:\n",
        "        t += rng.exponential(1/lam)\n",
        "        if t > T_max:\n",
        "            break\n",
        "        # ενημέρωση X μέχρι το t\n",
        "        while idx < len(grid) and grid[idx] < t:\n",
        "            X[idx] = S\n",
        "            idx += 1\n",
        "        # νέα ζημιά Y\n",
        "        claim = np.exp(mu_Y + sigma_Y * rng.normal())\n",
        "        S += claim\n",
        "\n",
        "    # συμπληρώνουμε μέχρι το τέλος\n",
        "    while idx < len(grid):\n",
        "        X[idx] = S\n",
        "        idx += 1\n",
        "\n",
        "    return grid, X, S\n",
        "\n",
        "# 9.1 Μερικές sample paths του X(t)\n",
        "plt.figure()\n",
        "for _ in range(5):\n",
        "    grid, X, _ = simulate_compound_poisson(lam, mu_Y, sigma_Y, T_max, rng)\n",
        "    plt.step(grid, X, where=\"post\", alpha=0.7)\n",
        "plt.xlabel(\"t\")\n",
        "plt.ylabel(\"X(t) = συνολική ζημιά\")\n",
        "plt.title(\"Sample paths σύνθετης διαδικασίας Poisson\")\n",
        "plt.grid(True)\n",
        "plt.show()\n",
        "\n",
        "# 9.2 Κατανομή του X(T_max)\n",
        "n_paths = 2000\n",
        "X_T = []\n",
        "for _ in range(n_paths):\n",
        "    _, _, S_T = simulate_compound_poisson(lam, mu_Y, sigma_Y, T_max, rng)\n",
        "    X_T.append(S_T)\n",
        "\n",
        "X_T = np.array(X_T)\n",
        "\n",
        "plt.figure()\n",
        "plt.hist(X_T, bins=40, density=True, alpha=0.7)\n",
        "plt.xlabel(\"x\")\n",
        "plt.ylabel(\"density\")\n",
        "plt.title(\"Εμπειρική κατανομή του X(T_max)\")\n",
        "plt.show()\n",
        "\n",
        "print(\"Εμπειρικός μέσος X(T_max):\", X_T.mean())\n",
        "print(\"Εμπειρική διασπορά X(T_max):\", X_T.var())\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 962
        },
        "id": "Bc0bslPpHuoN",
        "outputId": "b9b60cca-d816-472f-a454-582e10f511b2"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "[encoded data, 33504 characters]\n"
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "[encoded data, 25928 characters]\n"
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Εμπειρικός μέσος X(T_max): 83.90519760866341\n",
            "Εμπειρική διασπορά X(T_max): 900.133244473706\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [],
      "metadata": {
        "id": "y_J3IQFuHyLD"
      }
    }
  ]
}