From 10529f7a1f0cd2f68366b7bb57df1fff34244923 Mon Sep 17 00:00:00 2001
From: Wolfgang Kastaun <wolfgang.kastaun@aei.mpg.de>
Date: Wed, 12 Feb 2025 15:38:27 +0100
Subject: [PATCH] Added notebook comparing original shift inversion to new one

---
 .../shift_inversion_comparison.ipynb          | 363 ++++++++++++++++++
 1 file changed, 363 insertions(+)
 create mode 100644 docs/notebooks/shift_inversion_comparison.ipynb

diff --git a/docs/notebooks/shift_inversion_comparison.ipynb b/docs/notebooks/shift_inversion_comparison.ipynb
new file mode 100644
index 0000000..8d466c3
--- /dev/null
+++ b/docs/notebooks/shift_inversion_comparison.ipynb
@@ -0,0 +1,363 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "3efcb1ab-d4fb-4d84-aaa6-9222b0feb9a2",
+   "metadata": {},
+   "source": [
+    "# Compare original shift inversion algorithm to new one"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "7a3b9115-11a1-4f19-aaba-36d246860aa9",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%matplotlib ipympl\n",
+    "from matplotlib import pyplot as plt"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "6cca7a2a-1853-4555-9f12-c27fbb30158e",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import numpy as np"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c7043bd6-dcde-4840-bb1a-fa819d1c3936",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from lisainstrument import dsp"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "3d80234a-1eb8-4d8c-b76e-c4fb6fa0f3d8",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from lisainstrument.shift_inversion_numpy import make_shift_inverse_lagrange_numpy"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d8e70bed-5bb0-496c-a00a-730613971b2f",
+   "metadata": {},
+   "source": [
+    "Below is the original shift inversion method from the Instrument class. Only modifications are to make it self-contained, and removed logging."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "556511dc-17b4-42bd-867e-0fc9da431fd3",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def legacy_invert_scet_wrt_tps(\n",
+    "    scet_wrt_tps: np.ndarray,\n",
+    "    clockinv_tolerance: float,\n",
+    "    physics_fs: float,\n",
+    "    interpolation_order: int,\n",
+    "    clockinv_maxiter: int = 5,\n",
+    "):\n",
+    "    edge = min(100, len(scet_wrt_tps) // 2 - 1)\n",
+    "    error = 0\n",
+    "\n",
+    "    niter = 0\n",
+    "    next_inverse = scet_wrt_tps\n",
+    "    while not niter or error > clockinv_tolerance:\n",
+    "        if niter >= clockinv_maxiter:\n",
+    "            msg = \"Legacy fixed point iter did not converge\"\n",
+    "            raise RuntimeError(msg)\n",
+    "        inverse = next_inverse\n",
+    "\n",
+    "        next_inverse = dsp.timeshift(\n",
+    "            scet_wrt_tps, -inverse * physics_fs, interpolation_order\n",
+    "        )\n",
+    "    \n",
+    "        error = np.max(np.abs((inverse - next_inverse)[edge:-edge]))\n",
+    "        niter += 1\n",
+    "\n",
+    "    return inverse"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "127215c1-5082-4994-8ef4-f6eb75dcbe11",
+   "metadata": {},
+   "source": [
+    "Setting up some example data"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "ed10795d-2d67-4715-9c18-a6e3993b2605",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "order = 31\n",
+    "nsamp = 3000\n",
+    "fsample = 16.0\n",
+    "dt = 1 / fsample\n",
+    "\n",
+    "f_mod = 0.007\n",
+    "a_mod = 1e-2 / (2 * np.pi * f_mod)\n",
+    "max_it = 5\n",
+    "tol = 1e-10"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d28a7126-215b-49d5-83fd-b6d6783160fb",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "\n",
+    "def dx_from_x(x):\n",
+    "    return np.cos(2 * np.pi * f_mod * x) * a_mod\n",
+    "\n",
+    "xi = np.arange(nsamp) * dt\n",
+    "dxi = dx_from_x(xi)\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ccc111a8-3492-4569-a667-8731f60219ac",
+   "metadata": {},
+   "source": [
+    "Set up new shift inversion operator (unchunked pure numpy version)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "932daa39-b57d-4d97-b3c3-81471560c46a",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "op_np = make_shift_inverse_lagrange_numpy(\n",
+    "        order=order,\n",
+    "        fsample=fsample,\n",
+    "        max_abs_shift=a_mod * 1.01,\n",
+    "        max_iter=max_it,\n",
+    "        tolerance=tol,\n",
+    "    )\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4b15c610-c7f2-44ea-b8e6-7e7fc62c5bf5",
+   "metadata": {},
+   "source": [
+    "Compute shift inversion with both"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "5c3e3fea-5fdf-49ad-98d3-78062a04d801",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ai_np = op_np(dxi)\n",
+    "ai_leg = legacy_invert_scet_wrt_tps(dxi, tol/100, fsample, order, max_it+2)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fba2473e-3a8c-48c5-9cb5-13175098b70f",
+   "metadata": {},
+   "source": [
+    "Expected result"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "eb55600f-0cd2-4702-bd95-217d4722e248",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ai_ex = dx_from_x(xi - ai_np)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fc7a324c-4532-4c66-8317-3620d55e6d3c",
+   "metadata": {},
+   "source": [
+    "The original version seems to have more boundary artifacts"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f8b72f4b-00bb-4f57-ae42-5a4293a03d65",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.close()\n",
+    "plt.plot(xi, ai_np, \"k+\")\n",
+    "plt.plot(xi, ai_leg, \"rx\")\n",
+    "plt.plot(xi, ai_ex, \"g-\")\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c665ea55-9d2b-4e8e-8ad9-e0113541633a",
+   "metadata": {},
+   "source": [
+    "The error within the valid points is within the tolerance for both."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "74d29668-57f2-4800-8e5b-bc82253a2185",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.close()\n",
+    "plt.semilogy(xi, np.abs(ai_np-ai_ex), \"k+\")\n",
+    "plt.semilogy(xi, np.abs(ai_leg-ai_ex), \"rx\")\n",
+    "plt.axhline(y=tol)\n",
+    "plt.axvline(x=xi[op_np.margin_left])\n",
+    "plt.axvline(x=xi[-op_np.margin_right])\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "912c544e-e926-41cb-ba93-d414c538c712",
+   "metadata": {},
+   "source": [
+    "To investigate the differences inside the valid region, fix a bug in the legacy method. It used to return the second-best guess instead of the latest iteration result. Also, we make the margin size for the error measure a parameter."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "45775517-6b5b-4b78-a345-f51e5bba6439",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def modified_invert_scet_wrt_tps(\n",
+    "    scet_wrt_tps: np.ndarray,\n",
+    "    clockinv_tolerance: float,\n",
+    "    physics_fs: float,\n",
+    "    edge : int,\n",
+    "    interpolation_order: int,\n",
+    "    clockinv_maxiter: int = 15,\n",
+    "):\n",
+    "    \n",
+    "    error = 0\n",
+    "\n",
+    "    niter = 0\n",
+    "    next_inverse = scet_wrt_tps\n",
+    "    while not niter or error > clockinv_tolerance:\n",
+    "        if niter >= clockinv_maxiter:\n",
+    "            msg = \"Legacy fixed point iter did not converge\"\n",
+    "            raise RuntimeError(msg)\n",
+    "        inverse = next_inverse\n",
+    "\n",
+    "        next_inverse = dsp.timeshift(\n",
+    "            scet_wrt_tps, -inverse * physics_fs, interpolation_order\n",
+    "        )\n",
+    "    \n",
+    "        error = np.max(np.abs((inverse - next_inverse)[edge:-edge]))\n",
+    "        niter += 1\n",
+    "    print( niter )\n",
+    "    return next_inverse"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f711b48f-fffa-4732-b609-7c2d500f59ff",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#ai_mod = modified_invert_scet_wrt_tps(dxi, tol, fsample, op_np.margin_left, order)\n",
+    "ai_mod = modified_invert_scet_wrt_tps(dxi, tol, fsample, 100, order)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5566a405-1994-4f25-905c-6ed6d2ee9f6a",
+   "metadata": {},
+   "source": [
+    "Now the error inside the valid region agrees perfectly (the margin size was not the issue)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "cba3a269-ae9b-4236-87a3-be7966af0dc8",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.close()\n",
+    "plt.semilogy(xi, np.abs(ai_np-ai_ex), \"k+\")\n",
+    "plt.semilogy(xi, np.abs(ai_mod-ai_ex), \"rx\")\n",
+    "plt.axhline(y=tol)\n",
+    "plt.axvline(x=xi[op_np.margin_left])\n",
+    "plt.axvline(x=xi[-op_np.margin_right])\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9153fb52-c7de-4352-bf61-c1cda7cf7944",
+   "metadata": {},
+   "source": [
+    "The boundary artifacts are still larger for the legacy algorithm. Reason yet unkown."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "63689287-a4bf-4f23-957a-51f4046fd0ad",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.close()\n",
+    "plt.plot(xi, ai_np, \"k+\")\n",
+    "plt.plot(xi, ai_mod, \"rx\")\n",
+    "plt.plot(xi, ai_ex, \"g-\")\n",
+    "plt.xlim(xi[-20], xi[-1]+dt)\n"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "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"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
-- 
GitLab