From 1503331100b0e63ecfa6649421447fbf39d57c9e Mon Sep 17 00:00:00 2001 From: kferjaoui Date: Thu, 24 Sep 2026 07:56:26 +0200 Subject: [PATCH] update the CUDA clusterfinder notebook (new API) --- python/tests/ClusterFinderCUDA.ipynb | 1183 +++++++++++++------------- 1 file changed, 601 insertions(+), 582 deletions(-) diff --git a/python/tests/ClusterFinderCUDA.ipynb b/python/tests/ClusterFinderCUDA.ipynb index a23a6bce..b2d3d3f6 100644 --- a/python/tests/ClusterFinderCUDA.ipynb +++ b/python/tests/ClusterFinderCUDA.ipynb @@ -1,596 +1,615 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "title", - "metadata": {}, - "source": [ - "# ClusterFinder — CPU vs CUDA\n", - "\n", - "Two finders over the same frames and the same pedestal:\n", - "\n", - "| | |\n", - "|---|---|\n", - "| **`ClusterFinderMT`** + `ClusterCollector` | multithreaded CPU baseline |\n", - "| **`ClusterFinderCUDA.find_clusters_batched`** | batched across CUDA streams, over a pinned input buffer, so H2D / kernel / D2H overlap |\n", - "\n", - "**Part 1** times them. **Part 2** compares what they found." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "imports", - "metadata": {}, - "outputs": [], - "source": [ - "import sys; sys.path.append('/home/ferjao_k/aare/build')\n", - "\n", - "import time\n", - "from pathlib import Path\n", - "\n", - "import boost_histogram as bh\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "from tqdm import tqdm\n", - "\n", - "from aare import (File, ClusterFinderMT, ClusterCollector, ClusterFinderCUDA)" - ] - }, - { - "cell_type": "markdown", - "id": "md-config", - "metadata": {}, - "source": [ - "## Configuration" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "config", - "metadata": {}, - "outputs": [ + { + "cells": + [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Image size: (400, 400) cluster (3, 3)\n", - "Pedestal frames: 1000\n", - "Data frames: 100000 of 100000 in file\n" - ] - } - ], - "source": [ - "base = Path('/mnt/sls_det_storage/moench_data/2603_MaxIVBeamtime/2026032408/process/xrf/')\n", - "f = File(base / 'Cu_factor_10_data_master_0.json')\n", - "pd = File(base / 'Cu_factor_10_pedestal_master_0.json')\n", - "\n", - "n_frames_pd = 1000 # pedestal frames, taken from the head of the file\n", - "N = 100000 # data frames, read after those\n", - "cluster_size = (3, 3)\n", - "rows, cols = f.rows, f.cols\n", - "image_size = (rows, cols)\n", - "\n", - "N_SIGMA = 5\n", - "N_STREAMS = 4 # H2D / kernel / D2H overlap\n", - "BATCH_SIZE = 2000 # frames per find_clusters_batched call\n", - "\n", - "# CPU workers. ClusterFinderMT gives each thread its own ClusterFinder with its\n", - "# own pedestal, and data frames are handed out round-robin -- so at high thread\n", - "# counts each pedestal tracks on only 1/n of the frames while the CUDA finder's\n", - "# single pedestal sees all of them, and the cluster counts drift apart. Keep it\n", - "# low when comparing counts; raise it to measure CPU throughput.\n", - "N_THREADS = 24\n", - "\n", - "# Fixed per-frame output slot the kernel writes into, copied WHOLE on every D2H.\n", - "# Too low does not error -- it TRUNCATES. The CUDA cell prints the peak actually\n", - "# reached, which is how you size this for a new dataset.\n", - "CAP = 3000\n", - "\n", - "capacity = 3_000 # initial ClusterVector capacity, CPU side\n", - "N_BINS = 200\n", - "E_MIN, E_MAX = -2, 4000 # spectrum range, ADU\n", - "\n", - "print(f'Image size: {image_size} cluster {cluster_size}')\n", - "print(f'Pedestal frames: {n_frames_pd}')\n", - "print(f'Data frames: {N} of {f.total_frames} in file')" - ] - }, - { - "cell_type": "markdown", - "id": "md-build", - "metadata": {}, - "source": [ - "## Build the two finders" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "build", - "metadata": {}, - "outputs": [], - "source": [ - "cf_cpu = ClusterFinderMT(image_size, cluster_size, n_sigma=N_SIGMA,\n", - " capacity=capacity, min_pedestal_samples=n_frames_pd, n_threads=N_THREADS)\n", - "sink = ClusterCollector(cf_cpu)\n", - "\n", - "cf_cuda = ClusterFinderCUDA(image_size, cluster_size, n_sigma=N_SIGMA,\n", - " max_clusters_per_frame=CAP, n_streams=N_STREAMS, \n", - " n_pedestal_frames=n_frames_pd, algorithm=\"fixed_window\", time_kernels=False)" - ] - }, - { - "cell_type": "markdown", - "id": "md-ped", - "metadata": {}, - "source": [ - "## Pedestal\n", - "\n", - "Identical frames into both, so the implementation is the only thing that differs." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "train", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Pedestal (1000 frames): 1.34s\n" - ] - } - ], - "source": [ - "pd.seek(0)\n", - "t0 = time.perf_counter()\n", - "for _ in range(n_frames_pd):\n", - " pd_img = pd.read_frame()\n", - " cf_cpu.push_pedestal_frame(pd_img.copy())\n", - " cf_cuda.push_pedestal_frame(pd_img.copy())\n", - "print(f'Pedestal ({n_frames_pd} frames): {time.perf_counter() - t0:.2f}s')" - ] - }, - { - "cell_type": "markdown", - "id": "md-io", - "metadata": {}, - "source": [ - "## Read the data frames\n", - "\n", - "Kept out of both timing loops: the two finders run over the same in-memory array." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "io", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Reading 100000 frames: 5.20s (19,219 FPS, 32.00 GB at 6.15 GB/s)\n" - ] - } - ], - "source": [ - "f.seek(0)\n", - "t0 = time.perf_counter()\n", - "data = f.read_n(N)\n", - "t_io = time.perf_counter() - t0\n", - "gb = f.bytes_per_frame * N / 1e9\n", - "print(f'Reading {N} frames: {t_io:.2f}s ({N / t_io:,.0f} FPS, '\n", - " f'{gb:.2f} GB at {gb / t_io:.2f} GB/s)')" - ] - }, - { - "cell_type": "markdown", - "id": "md-cpu", - "metadata": {}, - "source": [ - "## 1 — CPU, multithreaded" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "cpu", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████████████████████████████████████████████████████████| 100000/100000 [00:23<00:00, 4310.91it/s]\n" + "cell_type": "markdown", + "id": "title", + "metadata": {}, + "source": [ + "# ClusterFinder — CPU vs CUDA\n", + "\n", + "Two finders over the same frames and the same pedestal:\n", + "\n", + "| | |\n", + "|---|---|\n", + "| **`ClusterFinderMT`** + `ClusterCollector` | multithreaded CPU baseline |\n", + "| **`ClusterFinderCUDA.find_clusters_batched`** | batched across CUDA streams, over a pinned input buffer, so H2D / kernel / D2H overlap |\n", + "\n", + "**Part 1** times them. **Part 2** compares what they found." ] }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU MT (24 threads): 23.20s 4,310 FPS 233,099,227 clusters (2331.0/frame)\n" - ] - } - ], - "source": [ - "t0 = time.perf_counter()\n", - "# Pass the frame number: find_clusters(frame) defaults it to 0, and every\n", - "# resulting cluster is then stamped frame 0. Part 2 groups by it.\n", - "for i, frame in enumerate(tqdm(data)):\n", - " cf_cpu.find_clusters(frame, i)\n", - "t_cpu = time.perf_counter() - t0\n", - "\n", - "cf_cpu.stop(); sink.stop()\n", - "clusters_cpu = sink.steal_clusters()\n", - "\n", - "hist_cpu = bh.Histogram(bh.axis.Regular(N_BINS, E_MIN, E_MAX))\n", - "n_clusters_cpu = 0\n", - "for cv in clusters_cpu:\n", - " hist_cpu.fill(cv.sum())\n", - " n_clusters_cpu += cv.size\n", - "\n", - "print(f'CPU MT ({N_THREADS} threads): {t_cpu:.2f}s {N / t_cpu:,.0f} FPS '\n", - " f'{n_clusters_cpu:,} clusters ({n_clusters_cpu / N:.1f}/frame)')" - ] - }, - { - "cell_type": "markdown", - "id": "md-cuda", - "metadata": {}, - "source": [ - "## 2 — CUDA, batched + multi-streamed\n", - "\n", - "`register_input_buffer` page-locks the input once so H2D runs at full DMA\n", - "bandwidth instead of through the driver's staging buffer. It is paid once,\n", - "outside the loop, and released when the dataset is done." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "cuda", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CUDA batched: 3.18s 31,484 FPS 233,093,554 clusters (2330.9/frame)\n", - " input pinned in 0.84s, once, outside the loop\n", - " vs CPU: 7.30x\n", - " peak clusters in one frame: 2,545 of CAP 3,000\n" - ] - } - ], - "source": [ - "t0 = time.perf_counter()\n", - "cf_cuda.register_input_buffer(data)\n", - "t_pin = time.perf_counter() - t0\n", - "\n", - "clusters_cuda = []\n", - "t0 = time.perf_counter()\n", - "for start in range(0, N, BATCH_SIZE):\n", - " stop = min(start + BATCH_SIZE, N)\n", - " clusters_cuda.extend(\n", - " cf_cuda.find_clusters_batched(data[start:stop], first_frame=start))\n", - "t_cuda = time.perf_counter() - t0\n", - "\n", - "cf_cuda.unregister_input_buffer()\n", - "\n", - "hist_cuda = bh.Histogram(bh.axis.Regular(N_BINS, E_MIN, E_MAX))\n", - "energies = [np.asarray(cv.sum()).ravel() for cv in clusters_cuda if cv.size > 0]\n", - "if energies:\n", - " hist_cuda.fill(np.concatenate(energies))\n", - "n_clusters_cuda = sum(cv.size for cv in clusters_cuda)\n", - "\n", - "print(f'CUDA batched: {t_cuda:.2f}s {N / t_cuda:,.0f} FPS '\n", - " f'{n_clusters_cuda:,} clusters ({n_clusters_cuda / N:.1f}/frame)')\n", - "print(f' input pinned in {t_pin:.2f}s, once, outside the loop')\n", - "print(f' vs CPU: {t_cpu / t_cuda:.2f}x')\n", - "\n", - "# One ClusterVector per frame, so this IS the per-frame maximum.\n", - "peak = max((cv.size for cv in clusters_cuda), default=0)\n", - "print(f' peak clusters in one frame: {peak:,} of CAP {CAP:,}')\n", - "if peak >= CAP:\n", - " print(' !! CAP REACHED -- clusters were dropped; raise CAP and re-run.')" - ] - }, - { - "cell_type": "markdown", - "id": "md-summary", - "metadata": {}, - "source": [ - "## Summary" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "summary", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " s FPS us/frame clusters\n", - "CPU MT (24 thr) 23.20 4,310 232.0 233,099,227\n", - "CUDA batched 3.18 31,484 31.8 233,093,554\n", - "\n", - "speedup: 7.30x\n" - ] - } - ], - "source": [ - "print(f'{\"\":<22} {\"s\":>7} {\"FPS\":>10} {\"us/frame\":>10} {\"clusters\":>13}')\n", - "for name, t, n in (('CPU MT (%d thr)' % N_THREADS, t_cpu, n_clusters_cpu),\n", - " ('CUDA batched', t_cuda, n_clusters_cuda)):\n", - " print(f'{name:<22} {t:7.2f} {N / t:10,.0f} {t * 1e6 / N:10.1f} {n:>13,}')\n", - "print(f'\\nspeedup: {t_cpu / t_cuda:.2f}x')" - ] - }, - { - "cell_type": "markdown", - "id": "md-part2", - "metadata": {}, - "source": [ - "# Part 2 — Where the two disagree\n", - "\n", - "The two implementations are not bit-identical by construction: the CUDA finder\n", - "updates its pedestal once per frame, the CPU finder per pixel, so once the\n", - "pedestals drift apart a few pixels land on opposite sides of the threshold.\n", - "\n", - "Three views, cheapest first: the **spectra**, the **cluster sets**, and **where\n", - "in the run** the difference accumulates." - ] - }, - { - "cell_type": "markdown", - "id": "md-spec", - "metadata": {}, - "source": [ - "## Spectra\n", - "\n", - "Both curves should lie on top of each other; the ratio panel is the sensitive\n", - "view." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "spectra", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": - "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b8uy5NChQ7J9+3bzuEqVKkXeF0EeAAAAikVPhN0B3sknn0xuRghBnvPzu1SpUuZWAz39PhW16SYDrwAAAKBY3H3wtAYPQPG4v0fF6dtKkAcAAICwoAkhYI/vEUEeAAAAADgIQR4AAACQoC688EJ58803Y52MhPG///1PmjVrZkbtjCSCPAAAAEiijwx61113yamnniolSpSQ6tWrS/fu3eWTTz7xbFOrVi3TjE4X7TPVuHFjeeGFFzzPjxw5Us4888w8+9YBafQ1ixcvDnr8tm3bmm0ef/zxPM917drVPKf7//PPP808aklJSZ60+C+6XWECDn3vffr0MY93795t8qF+/frmPdaoUUPuvvtuSU9P93ndpZdeap4rWbKkGQHyuuuuk7///tvzvE450LlzZ6lataonP++8807Zt29fyPQcPXrUHL98+fJy/PHHm+P89ddfPtusXLlSOnToYKYZ0EF+brnlFjlw4IDPNp988om0bt1aTjzxRJO++++/XzIzM4MetyDvW/O+f//+Urt2bTM4Sp06dWTEiBFmHj236dOnB/1c3CNmduvWzTyOdGBNkAcAAICEpSfvZ511lnz66afyxBNPyI8//igfffSRtGvXTu644w6fbUePHi1btmyRH374QXr27CkDBgyQ2bNnhyUdGghNmzbNZ50GTpou91D6us2mTZvMek3Hv//9b2nUqJG5717+85//FPiYEyZMkBtuuMEEje7j6fLUU0+ZfNCgRfNCgxtvmjdvvfWW/PrrrzJnzhz5/fff5corr/Q8r/vr0aOHvPfee7Ju3Tqzn0WLFpn8CmXQoEEyd+5cmTVrlnzxxRcmeNOgSEe4dKfv4osvltNOO02++eYbk7Y1a9ZIv379PPvQz6Zr164myPz+++/NvjQdDzzwQNDjBnvfN910k2ebX375xdS+aWCvx3zmmWfk+eefl6FDh3q26d27t89noUunTp2kTZs2ZqRMN83z5557TiLKQh7p6emWZo3e2kV2dra1Z88ecwvy22ko3+S3k1G+ye9EKN+HDh2yfv75Z+vw4cNWvOnSpYt1yimnWAcOHMjznL43t5o1a1rPPPOMz/N169a1+vTpY+6PGDHCatq0acB96HnlZ599FjQNbdq0sW677Tbr5JNPtr744gvP+kcffdTq3r272a/uX/P72LFjnvPBYMcsiB07dlgul8v66aefQm731ltvWWlpaea4wcybN8/sKyMjI+g2zz77rFWtWrWgz+/du9dKTU21Zs2a5Vm3efNmKykpyfroo4/M4xdeeMGqWLGilZWV5dnm+++/N/n722+/mcdDhgyxWrRo4bPvuXPnWiVLlrT27dtnFZT7fWuZDnb+/cQTT1i1a9cOuo/t27eb9zRjxgyf9X/++adJ8++//x7wdXrMYN+ngsYp1OQBAAAgIWkzPa2x0Ro7bR7oT5sEhqLNFYszzL23tLQ0ueaaa3xq87RG6cYbb5RI0JoybZrYsGHDkNtpk8XSpUtLSkpK0Dx84403TPNIbUoaiNaSvfPOO6ZGK5gVK1aYvOzYsaNnnTb31Gaxy5Yt8zTn1Hxy1zx6zyun78e9TcmSJX32rdscOXLEHKOg8nvf7m3KlSsX9PkZM2aYPPau5VQ1a9Y0NXtLly6VSGEydADwtv4TkZk5fRMkOU1k6GbyBwCKqNFDH8mxLK14iJ7UZJesGd25QNuuX7/eTHjdoEGDQh1D+3e9/vrrpmnfbbfdJuGizSLPP/98efbZZ01AokHEJZdcUqh+doVpplqpUiWfgMmf9q17+OGH5dZbb83znPZzmzhxohw6dEjOPfdc07/P39VXXy3z5s2Tw4cPmz6OL7/8ctBjad9ADeDKli3rs17TqM+piy66SAYPHixPPvmkDBw4UA4ePOhpLqlNI5U2jxw/frzMnDlTevXqZV77yCOP+GyTH/f71v5+wWgTVW1y+fTTTwfdZurUqfKvf/3LE4h6O+WUU8xnECnU5AFAIFkZOQsAwLE0wCvMvGQa2JxwwgnmpF1r/+69996AAVBRnXHGGVK3bl3573//awIEHdAkWO1YcWng5V/j5U0HSdEA8/TTTzcDjPjT96593hYsWCDJycly/fXXe/LTTfut6UAp7777rgmKNEArLN2n+/PR/oevvvqqCay0hqxy5cpmsBwNBDUNSmsCn3zySdP/Twd9qVevnnkfyr1NKPm9b3fNpPb5u+qqq3z67Xn76quv5Oeff87Tn9FNy5AGyJFCTR4AiMjsEVeIjmY8Ilt/rGfITynXimRmSxq5AwBFVtAatVjRgEoDiLVr15qBVPKjgY0O8qEBhg6G4h0catM+/1Eo3aNrqjJlyhQoTdo8c9KkSSZA+PbbbyVSdATLPXv2BHxu//79JojRgFYHQgkUaOrrddEgSpt86qAwX3/9tbRq1cqzjQZhumhNqY6EecEFF8jw4cM9A8l40+10pEpNk3dtno5KqU1B3bRmTJdt27aZJrb6GYwbN86Meuk2ePBgueeee0zNne5La8yGDBnis01B37d70BfvAE8HntH3+eKLLwbdl9Za6mirOqhPsGauFSpUkEihJg8AROQyWSyXJ/0zvHV0GxcBAGJB+1Np8z4NqrTpX7AAzU2DGh3ZUfuK+df+aSCjw/27mxa6fffdd6ZJpL6uIDSA0Wag2hdNa5MiRedq07T6B3pak6W1Ydp0UkelDFXb5+auwdP+cEXdRoMhDaoWLlzoWadB2k8//eQT5Llp7Z0GYzq6qaZRp1Xw5nK5zOekNWbadFOD0ObNmwdNX0He9+bNm810F7of7TsZrKmrjgqqo48Gq8XT/oFas6mfQaRQkwcgYXn3FfkxRUT/Xa97tIt5nBG4hQYAwGEmT55sgoizzz7bTJGgTSa1z50GG1OmTDG1fAWhAYLWaOmcc48++qgJMHQ4f53SQJsO6pxtBaE1TxrcRKqZppsGGFqT9OWXX5ppCtw1Wfo+tBmh9jnUwMc9t51uq80dtXZRF+07qGn9448/5KGHHjLzxrlr8ebPn29q2lq2bGkCMa2VvO++++S8884z8w0GojWdGhTptBBa66cBuOZdkyZNzLQJbtoPUD8v3a9+Rlq7qvMLeg+S8+STT5oaOQ3CdMAXfV6DrmDNNYO9bw1MNR36Oq3B0wBP59DTqRZ27NjhUwvpTQNPLUM6kE4gWuOpTUm9az3DjSAPQMLSAC8jK1vSkpNMgOdtj+T8M64Uk5QBAKJFm/BpvzENzDTA0ABLAxqtWdIgr6B0FEbtn6YDgejJvTYz1FEUtc+WBjiFkd+onuGggYs2DdWRMd1Bng72ovPPKf+axw0bNpgATWvGNHDS/mpa+6lNLzWg0vnoNHBRus1LL71kmkxqzZ3Wol1++eV55qrT2jatEXPPc6d9+DQfdcAU7TPYvn17M8Kod3CmAaYeW2vLtPZU563TvovePvzwQ/N56rGbNm1qBn/p0qVL0GOHet+//fabCWD1s9WBenSpVq2azzb+fRFfeeUV8379B5Fx05pFLSPa7DdSXDqPQsT2Hqc0cterCe6hU+1APyZNj6aroJ2DQX7Hi1iV73rDPpRhSdOlb9qnOYOs6Giaw3d4nhOvmj0n4feE/HYyynds8ltP7rXfkwZMBWneh6Lnt/YR06AnHP8vtbZNBzPRIEcD0mjS8qJ9IrWWT2/teGwrzPmttAZQg9Ply5cH7SOozTk1qA70fSponEKfPABQGuDpAgBAgtB+bVrrtHHjxqgfW+cn1CkKoh3gxfrYGrxpE+H8BoEpLpprAkhoj2b3k77DZ8c6GQAAxESPHj1iclztpxgrA2J4bO37qUukEeQBSFhtXSty7+VtkrkyJad/gMi2qKYJAACguAjyACSsicnP5N57MM9zaZIZ9fQAAACEA33yAAAAAMBBCPIAAAAAwEEI8gAAAADAQQjyAAAAAMBBCPIAAAAAwEEI8gAkrG1SziyBXJM1wiwAAMSDTz/9VBo0aCDZ2dmxTkrCaNmypbzzzjtiRwR5ABJW+8wJZglktVXXLAAA59u6davcddddcuqpp0qJEiWkevXq0r17d/nkk08827hcLnn33XfzvHbQoEHStm1bz+N+/fqZbXVJTU2VSpUqSYcOHWTq1KlBA7COHTtKcnKyfP3110V+D/fdd58MGzZMkpJyTu81+NDjVqhQQUqXLi2tWrWSjz/+2Oc1uk2LFi3kpJNOkuOPP17OPPNMee2113y2mTJlipxxxhlmH+79fPjhh/mm5/PPP5ezzjpLSpYsafL1+eef93n+2LFjMnr0aKlTp47ZpmnTpvLRRx8V+9iLFy/25L/38ssvv3i2mT59esBtjhw5EnCfY8aMMc/rZ+1t+PDh8sADD9gysCbIAwAAQML6888/TTCiNWFPPPGE/PjjjybYaNeundxxxx1F2mfnzp1ly5YtZt8alOi+Bg4cKN26dZPMTN95WDdu3ChfffWV3HnnnfLKK68U6XjLli2T3377Ta666irPuiVLlpggb/78+bJixQqTBg1cv//+e8825cqVM4GhHv+HH36QG264wSzewWC1atXk8ccfl+XLl5vloosukh49esiaNWuCpmfDhg3StWtXueCCC8zxhg4dKnfffbfMmTPHs82DDz4oL7zwgjz33HPy888/y4ABA+Syyy7zSV9Rju3266+/ms/AvdSt63vhVoNG7+d10WDT33fffScvvfSSCTb9XXLJJZKenp4neLYFC3mkp6dbmjV6axfZ2dnWnj17zC3Ib6eJVfmuO3S+WQJ5f3hnyxpR2rJGl/9nebSq5QT8npDfTkb5jk1+Hzp0yPr555+tw4cPW/GmS5cu1imnnGIdOHAgz3P63tz03HDu3Ll5thk4cKDVpk0bz+O+fftaPXr0yLPdJ598Yvbx0ksv+awfOXKk1adPH2vt2rXWiSeeGDAd3vl97NixPP8v77rrLuvKK6/M972efvrp1qhRo0Ju06xZM+vBBx8MuU3ZsmWtl19+Oejz9913n9WgQQOfdbfeeqt17rnneh5XqVLFmjhxos82mm/XXHNNsY792WefmXz2/uz8TZs2zSpTpoyVn3379ll169a1FixYYD5j/az99evXz7ruuuuscNLvUbDvU0HjFGryACSsn1KuNUsgbV0rJcNK+WdFVkbOAgBwjN27d5taO62x0+aK/rQZY7hoLZQ2SfTuw6Wx47Rp0+Taa681/enq1asnb731VqH3rbV22uwyFG1SuH//flN7F4imRZunag3YhRdeGHCbrKwsmTVrlhw8eNA0nQxGawa1Caq3Tp06mdo4baapjh49mqfmrFSpUvLFF18U69huzZo1kypVqkj79u3ls88+y/P8gQMHpGbNmqa2UGtYvWsQ3bR2tUuXLnLxxRcHPc7ZZ58tS5cuFbvxOoMBALg1z5xubteN6pKz4uEKZA4AFEWo38+bFolUaZpzf9Y1Ir8tCLzdhfeKtLkv5/7K10Q+GBx4u8pNRG7+tMBJW79+vQluNMCKBj2ONot0W7RokRw6dMgEQEqDPW2yqU0mC0ObhVatWjXkNk8//bQJkHr16uWzXpsbnnLKKSbo0n6BkydPNs08vWkTVg2stM/aCSecIHPnzpXTTz89ZB9H7YvoTR9rU9WdO3ea4Evf87hx40xAqf3yNMCcN2+eCeaKc2zd94svvmia4Op70j6GGuhpXz138Kqfg/bLa9Kkiezbt0+effZZOe+882T16tWeZp0aUK5cudIErKFo3mmTWw2i3f0h7YAgDwAKovXd5BMAOExOK8ycQVWidTzvY2lA17t3b0lJyTklv/rqq+Xee+81tWn169cv8H4PHz4csD+Z28yZM2XkyJEmiKpYsaLPcyeeeKKsWrXK1GxpoDV48GAzUIr3YDKaFt1m7969pl9d3759zcAqoYIt/zz1z2sNrG6++WYTcOk6DfQ0uNWaTW+FPbZuX98r7zRA3LRpkzz11FOeIO/cc881i5sGeM2bNzf9AydMmGC21z6U2tcuVL66ax81wNOAUu/bBUEeAASRkZUt9Ya5R/FqIanJLlnTnuwCgEIZvqNg2/V5o2DbNb8uZwkDrbXRAGPt2rXSs2fPkNtqMKS1Xv40+ChTpkyBjqfHqV27tqepqI7Wqc0XdRRJN63J0pE4x44dW+D3Ub58edmzZ0/A52bPni39+/eXt99+O2CzQ619Ou2008x9HV1T06ijSXoHeWlpaZ5ttFmoDkaiQZoOnBJI5cqVTW2et+3bt5tg9uSTTzaPddRPff9aQ7dr1y5TE6kjVbrzp6jHDuTcc8+V119/Pejzmgc6HYIOXqN0oBpNr3cTWP1ctFnsxIkTPbWe7s/xuOOOs1WAp+xTpwgANqIBXVpykk/Adywr5yokAMAZtH+aNhucNGmSacoYKIBz0xonDTD8a6c0IChIrZuO3qlND6+44grz+I033jD9wbSJoNZUuZfx48fLq6++mmcUzvz6n+kIlYFq8HRKhzfffNOMBFkQ+p40iCnONlp7tnDhQp91CxYsMEGTTivhTWvKtMmjvl+tqdPRM4ubPn/a306bcYbap+a9extt3qmflb5O+xHqrab9mmuuMdu5Azz1008/mVpAu6EmDwACWDO6s8/jwQ8Oy72X20cPAOAI2getdevWZgANnbdNh8rXgEODFK1h05ot9Z///Mc0FdRgTwcV0SaS2vfr999/zzPVggYhWpOltT/btm0zg7to7ZgO8HH99dd7mmpeeeWV0rhxY5/X6mAg999/v3zwwQf5BjxuGqhqYOgf4OmxtNZLa7LcNWta4+SuedQ0afCiTSUzMjLMdAszZszwqVnU6Q908BGdO1AHbtG+atq/zX9OO286HYLWeGnTT22Sqf3a9P1qmty++eYb2bx5s6k91FttTqrNHnW+v+IcW4PkWrVqSaNGjcx70ho8DR69p28YNWqUyROtydU+edpEU4M3Dfbdtbb6uWjwp5+hBnU6MI/WQvp/Xjroiv8gM7YQ1vE+HYIpFMAQ3ImR30cfKmeWcG9rd5Rv8tvJKN+xye94nkJB/f3339Ydd9xh1axZ00pLSzNTKlx66aVmOH5vs2bNslq0aGGVLl3aqlixotWpUydr+fLlPtvoFAp6iq1LSkqKVaFCBeviiy+2pk6damVlZZlt9DX6/LfffhswPd27dzdLQadQ2L17t1WqVCnrl19+8azTIf/d6fBeNH1uw4YNs0477TSrZMmSZmqCVq1amffo7cYbb/Tki76X9u3bmykFvI0YMcJs423x4sVmOgZ9Xa1atawpU6bkeb5hw4ZWiRIlrJNPPtlMQ7B58+ZiH3vs2LFWnTp1PO/p/PPPtz744AOf1wwaNMiqUaOGZ78dO3a0li1bFjK/A02h8Ndff1mpqanWpk2bLLtNoeDSP7EKMLVd65NPPmmquXUCQh0tJ1R7aK1u9r9KobTjpXtSRB0pJ9CIRPl1SPWmEb1e4dB21zpRoh3ox6Tp0XRFq3NwIiO/EyO/ewybbG7nPXp7vttmjMjpQ5A2apfEO8o3+e1klO/Y5HeJEiXMCI/an6qg51soWn67a5b8/19qDZh+FoXpqxYueo7uPg930rGtEPmtdJAczXOt0Q0n7aeoE8oH+j4VNE6JaXNNbfus84VoUOZunxyKVjfrrPduWpWur7/qqqt8ttM3rKMSeeMHB4C/teLbuRsAgHg1bNgw09zQHZREk452qZU3sRDLY1esWNE047WjmAZ52sZWl4LSqNV79CIdkUdHEvKvudNIW0f1AQAAABKBniNrH7ZY0FqnWInlse+9916xq7geXVM7cOpQsNpBtbAz2APAxOSnzQIAAOAkcTu6pvbh+/DDD82QsN4KMoO9Px0ByXsoVn2dux1uDLss+nCnxS7pcTry25n53WTERz7TIKxOWSXawr4gx7XMlgXb1u4o3+S3k1G+Y5vfnKtEN+/hzPy2QnyfCpqOuA3yNJA76aST8gzUkt8M9oHo8LE6lKo/7dBoly+QpkNrKBUDr5DfThOt8l2hRLYcy7YkNSnnGPuTa5jb1ACT2/rbUCpnQtTaBdjW7vg9Ib+djPIdm/zWoep1+HvtD6YLIkfzGc7O76ysLHNcnTbCf05Ad2WUI4M8/UGZOnWqXHfddZKWlhZyW/8Z7AMZMmSImcfDO/N0Pg5t22yn0TUVo2uS304UrfL99yHdt0t+eSS3L/Ajuf15vfr6BtNn/93m9pcCbGt3/J6Q305G+Y5Nfuvomrt37za/4dEe9CMRkcfOzm+Xy2ViGJ2vz3/wyIKeJ8VlkKej6Kxfv1769++f77buGey1+WYw+sOkiz/NRDvVmrnTY6c0ORn57bz8dje5/OcYuTX1BThm3tfGN8o3+e1klO/o57eeR+lJqXanqVChgrkI75TfSzvJb0h/xHd+W5ZlasV37Nhhvk/6vfI/blwEeVq9r8Ga9+g4GpCVK1dOatSoYWrYNm/eLDNmzMgz4Mo555yTZ8b5gsxgDwBFcbwcIuMAIAg9IdU5vTTI+/vvv8mnCNJmfJrfcG5+H3fccSYWKs5xYxrkLV++XNq1a+d57G4y2bdvX9PnTn8oNm7cmKef3Jw5c8yAKoHs3btXbrnlFtm6datp+tWsWTMzd8bZZ58d4XcDwO5WpvSTR7P66gQuOSuyM0WSCvYz+E3KLbn38p/TEwASkdbe6YmpzmNMv7zI1fRoPy1txkdNnjPzOzk5WVJSUop9vJgGeW3btg05sEmgmes1cDt0KPgV9WeeecYsAOAvTTLlruT/isgTuT9CQ0S+DHzBCABQeHpimpqaahaEn54360Ac2k+LIC/yrDjOb+p6ASSU8uI1Omab+0SGbo5lcgAAAMKOIA8AAAAAHIQgDwAAAAAchCAPAAAAAByEIA8AAAAAHCQuJ0MHgKJYa9U0t02L8NqumU+Z20VkPQAAsDmCPAAJ46qsR83tuiK8dqNUDnt6AAAAIoHmmgAAAADgINTkAUgYZWVfkV87LTmnFlCkS9jSAwAAEAkEeQASxtKU23Pv9S70a1u61oY9PQAAAJFAc00AAAAAcBCCPAAAAABwEII8AAAAAHAQgjwAAAAAcBCCPAAAAABwEEbXBIACeCL7X+b2QXILAADYHEEegITRPvNZc7u0CK+dkd3V3BLkAQAAuyPIA5AwtsnJsU4CAABAxBHkAUABXJ80X+5KmiPy8LGcFclpIkM3k3cAAMB2CPIAJIw3kkfm3utS6NcOSnpL0iQzZ7yqrIywpw0AACBcGF0TQMJo6lpvlqJonjldGme+LjJ8R04tHgAAgE0R5AEAAACAg9BcEwAKq2Zr8gwAANgWQR4AFNb188gzAABgWwR5AFBAGVnZUm/Yh+Z+arJL1ozuTN4BAADbIcgDgALQoM7djblK9t+SSpdmAABgUwR5ABLGo9nXm9tRRXitd61dxgj3pOo3hCllAAAA4UOQByBhzMzuWOQgDwAAIF4whQIAAAAAOAg1eQASxk1J7+Xe6xLjlAAAAEQOQR6AhDEo6a3ce1NinBIAAIDIobkmAAAAADgIQR4AAAAAOAjNNQGgkM7JfNHcfk/OAQAAGyLIA4BCOijHkWcAAMC2aK4JAAAAAA5CTR6AhPGl1cTctivmfual3J97j6kYAACA/RDkAUgYt2blBGfrirmfOrI5LOkBAACIBJprAgAAAICDxDTIW7JkiXTv3l2qVq0qLpdL3n333ZDbL1682Gznv/zyyy8+282ZM0dOP/10KVGihLmdO3duhN8JgHhQR/4yCwAAgJPFtLnmwYMHpWnTpnLDDTfIFVdcUeDX/frrr1K6dGnP4woVKnjuf/XVV9K7d295+OGH5bLLLjMBXq9eveSLL76Qc845J+zvAYB9NXroIzmWZXke/5jygLjMvZtjmCoAAAAHB3ldunQxS2FVrFhRTjrppIDPjR8/Xjp06CBDhgwxj/X2888/N+tnzpxZ7DQDiB8a4GVkZUtack6jhZwADwAAwNnicuCVZs2ayZEjR0xTzAcffFDatWvnU5N3zz33+GzfqVMnE+QFc/ToUbO47du3z9xalmUWO3CnxS7pcTry2xn57RJLSiS75JdHOueseCTZc7zisHLDxXj9PlK+yW8no3yT305G+Sa/rQKee8RVkFelShV58cUX5ayzzjJB2WuvvSbt27c3ffUuvPBCs83WrVulUqVKPq/Tx7o+mDFjxsioUaPyrE9PT7fNSZym48CBA+a+9kME+e0kkSrfVY+zPN9lo1SNnFv34yIaVyLnQtLgYu4nVvg9Ib+djPJNfjsZ5Zv83pdbGeWoIK9+/fpmcWvVqpVs2rRJnnrqKU+QF+gkUb8QoU4ctUnn4MGDfTKvevXqUqZMGZ++f7HkDjY1TQR55LfTRKp8/33I5dmvcXhjzq37cRHNPtjM3I4q5n5ihd8T8tvJKN/kt5NRvslvVwHPk+IqyAvk3HPPlddff93zuHLlynlq7bZv356nds+bjsKpiz/36J124T2iKMhvp4lE+XY3q/xnn7k188U8Rt79xh9+T8hvJ6N8k99ORvlO7Px2FTAdcT9P3vfff2+acXrX7i1cuNBnmwULFkjr1q1jkDoATjQoabZZAAAA7CimNXnaB2f9+vWexxs2bJBVq1ZJuXLlpEaNGqYZ5ebNm2XGjBnmeR08pVatWtKoUSPJyMgwNXg6J54ubgMHDjRNN8eOHSs9evSQefPmyaJFi8wUCgASy8qUfpImmSIT64jc+a3IkPDMkXdT0vth2Q8AAIDjgrzly5f7jIzp7hfXt29fmT59umzZskU2bsztQyNiArv//Oc/JvArVaqUCfY++OAD6dq1q2cbrbGbNWuWGXVz+PDhUqdOHZk9ezZz5AEJSAO8NFfmPytS8jbLBgAAcBqXZZfhI21EB17RgRp0RD47Dbyi6WHgFfLbiSJVvjNGnGxu00btCts+I7nfaOH3hPx2Mso3+e1klG/ye18B45S475MHAAAAAPgHQR4AAAAAOAhBHgAAAAA4SNzPkwcAwQzOusvcTgxzFi2wzja33ch6AABgQwR5ABxrgXVORPY7OOtuc0uQBwAA7IjmmgAAAADgINTkAXCs+5Jez73XJaz7ber6LSL7BQAACAeCPACOdX3SRxHZ7xvJo3Lv5TTbBAAAsBOaawIAAACAgxDkAQAAAICDEOQBAAAAgIMQ5AEAAACAgxDkAQAAAICDMLomAMf6n9Xa3F4e5v1m5P50poV5vwAAAOFAkAfAsR7Iuj0iQV7zzOnmdl2Y9wsAABAONNcEAAAAAAehJg+AY7V0/Zx7r0vY97005TaRhw/+syI5TWTo5rAfBwAAoLAI8gA41rTkx3Lv/TuyB8rKiOz+AQAACoHmmgBQBBdkThEZviNn0Vo8AAAAmyDIAwAAAAAHobkmABTX1bPIQwAAYBsEeQBQXKe1Jw8BAIBtEOQBQBFkZGVLvWEfeh6nJrtkzejO5CUAAIg5gjwAjnVQSpnbcA+LogGdd5fmEa6XJMnSdQR5AAAg9gjyADjWOZkvmdt1Yd6vf41dxohrw3wEAACAomN0TQAAAABwEII8AAAAAHAQmmsCcKxvUm7OvfdXjFMCAAAQPQR5ABzreDkc6yQAAABEHc01AQAAAMBBqMkDgGKamX2xue1LTgIAABsgyAOAYno0u5+5JcgDAAB2QHNNAAAAAHAQavIAoJjaulbk3utCXgIAgJgjyAPgWDdkDTW3b0T4OBOTn8m992CEjwQAAJA/gjwAjvWddXqskwAAABB19MkDAAAAAAehJg+AYz2ePDn3Hn3lAABA4ohpTd6SJUuke/fuUrVqVXG5XPLuu++G3P6dd96RDh06SIUKFaR06dLSqlUr+fjjj322mT59utmX/3LkyJEIvxsAdtPNtcwsAAAAiSSmQd7BgweladOmMnHixAIHhRrkzZ8/X1asWCHt2rUzQeL333/vs50GgFu2bPFZSpYsGaF3AQAAAAD2EdPmml26dDFLQY0fP97n8WOPPSbz5s2T999/X5o1a+ZZrzV3lStXDmtaASCYbVLO3FYniwAAgA3E9cAr2dnZsn//filXLucEy+3AgQNSs2ZNqVatmnTr1i1PTR8AhFP7zAlmAQAAsIO4Hnjl6aefNk0+e/Xq5VnXoEED0y+vSZMmsm/fPnn22WflvPPOk9WrV0vdunUD7ufo0aNmcdPXKcuyzGIH7rTYJT1OR347I78tcXn2H0kuydl/vHw/Kd/kt5NRvslvJ6N8k99WAc814jbImzlzpowcOdI016xYsaJn/bnnnmsWNw3wmjdvLs8995xMmBD4SvuYMWNk1KhRedanp6fb5qRN06E1lO7mqCC/nSRS5ftYqRrmNjU9XSKp6nGW5zcjHvB7Qn47GeWb/HYyyjf5vS+3MsqRQd7s2bOlf//+8vbbb8vFF18cctukpCRp2bKl/Pbbb0G3GTJkiAwePNgn86pXry5lypQxg7jYgTvY1DQR5JHfThOp8v3KwZzJ0PuXKSOR9FFGX3ObVmaHxAN+T8hvJ6N8k99ORvkmv10FPE9KiccavBtvvNHcXnLJJQX6Mqxatco03wymRIkSZvHnnn7BLrynhAD57TSRKN9PZF9jbm+K8HfG3Vwznr6b/J6Q305G+Sa/nYzyndj57YqHIE+bZ61fv97zeMOGDSYg04FUatSoYWrYNm/eLDNmzDDPa2B3/fXXm3522iRz69atZn2pUqVMDYDSZpf6nPa/0xo5baKp+5w0aVKM3iUAAAAAJMjomsuXLzdTH7inP9Amk3r/oYceMo91fruNGzd6tn/hhRckMzNT7rjjDqlSpYpnGThwoGebvXv3yi233CINGzaUjh07miBR59c7++yzY/AOAcRSR9c3ZgEAAEgkMa3Ja9u2bciBTXSUTG+LFy/Od5/PPPOMWQBgXPJzuZkwkswAAAAJI+765AFAKI0e+kiOZeVcPPoxRfvLAQAAJJa4ngwdAPxpgJeRlW3uE+ABAIBERE0eAMdJS06SdY92EXk4Otexrsp82NzOi8rRAAAAQiPIA4BiWiu1yUMAAGAbNNcEAAAAAAehJg+Ac5WNTg3bxOSnc+91icrxAAAAQiHIA+Bcd34blcO0dX0fleMAAAAUBM01AQAAAMBBCPIAOMrKlH7yU8q1Ig9XEFk0UiTzaKyTBAAAEFUEeQAcJU0yJc2VmfPgq0kiT5wa6yQBAABEFX3yADhOhpUiacN3xDoZAAAAMUFNHgAAAAA4CDV5AFBMk7MvM7eDyEkAAGADBHkAHKVH5uPm9sMoHnNy9hXmliAPAADYAUEeAEf5XarFOgkAAAAxRZAHAMV0heuz3HtdyEsAABBzBHkAHOWF5LFRD7hGJb+SM23Dw9P+WZmcJjJ0c9TSAAAA4EaQB8BRznP9GPVjZuhPqaVz9OXKyoh6GgAAANwI8gCgmJpnTje360bl1h6+dBF5CgAAYoYgDwDC7eZPyVMAABAzTIYOAAAAAIke5K1cuVJ+/PGffi/z5s2Tnj17ytChQyUjg74oABLcwZ05CwAAQLwEebfeequsW7fO3P/jjz+kT58+ctxxx8nbb78t9913X7jTCAC2l5GVLfWGfWiWjCfqmwUAACBugjwN8M4880xzXwO7Cy+8UN58802ZPn26zJkzJ9xpBIACG5/dyyzRlJrskrTkf35OrageHQAAIAwDr1iWJdnZ2eb+okWLpFu3buZ+9erVZedOmigBiJ2Xsy81t9FsU7BmdGefxxkjonhwAACAcNTktWjRQh555BF57bXX5PPPP5dLLrnErN+wYYNUqlSpKLsEAAAAAMSqJu+ZZ56Ra6+9Vt59910ZNmyYnHbaaWb9f//7X2ndunU40gUARXJ10oLce7lz1gEAACSYIgV5TZs29Rld0+3JJ5+UlBSm3gMQO8OSZuTee4aPAQAAJKQiNdc89dRTZdeuXXnWHzlyROrVqxeOdAEAAAAAiqBI1W5//vmnZGVl5Vl/9OhR+euvv4qySwBwjPaZz5rbpbFOCAAASEiFCvLee+89z/2PP/5YypQp43msQd8nn3witWvXDm8KASDObJOTY50EAACQwAoV5PXs2dPculwu6du3r89zqampUqtWLXn66afDm0IAAAAAQGSCPPfceFpb991330n58uUL83IASAhvJI/MvccInwAAIE765Ol8eABgR6utnCldWsYwDU1d62N4dAAAkOiKPN+B9r/TZfv27Z4aPrepU6eGI20AUGjXZOXUoq0j7wAAQIIqUpA3atQoGT16tLRo0UKqVKli+ugBAAAAAOI0yHv++edl+vTpct1114U/RQBQDJUk7xyeAAAAiaRIQV5GRoa0bt06/KkBgGL6JGVg7r1ryUsAAJCQkoryoptuuknefPPN8KcGAAAAABD9IO/IkSMybtw4adOmjdx1110yePBgn6WglixZIt27d5eq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", - "text/plain": [ - "
" - ] - }, + "cell_type": "code", + "execution_count": 1, + "id": "imports", "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "fig, (ax_spec, ax_ratio) = plt.subplots(\n", - " 2, 1, figsize=(9, 6.5), sharex=True, gridspec_kw={'height_ratios': [3, 1]})\n", - "\n", - "edges = hist_cpu.axes[0].edges\n", - "cpu_vals, cuda_vals = hist_cpu.values(), hist_cuda.values()\n", - "\n", - "ax_spec.stairs(cpu_vals, edges, label=f'CPU MT ({n_clusters_cpu:,})',\n", - " color='C0', linewidth=1.4)\n", - "ax_spec.stairs(cuda_vals, edges, label=f'CUDA ({n_clusters_cuda:,})',\n", - " color='C1', linestyle='--', linewidth=1.4)\n", - "ax_spec.set_ylabel('Counts')\n", - "ax_spec.set_title(f'Cluster energy spectrum — {N:,} frames')\n", - "ax_spec.legend()\n", - "ax_spec.grid(alpha=0.2)\n", - "\n", - "with np.errstate(divide='ignore', invalid='ignore'):\n", - " ratio = np.where(cpu_vals > 0, cuda_vals / cpu_vals, np.nan)\n", - "ax_ratio.stairs(ratio, edges, color='C1', linewidth=1.2)\n", - "ax_ratio.axhline(1.0, color='gray', linewidth=0.5)\n", - "ax_ratio.set_ylabel('CUDA / CPU')\n", - "ax_ratio.set_xlabel('Energy [ADU]')\n", - "ax_ratio.set_ylim(0.9, 1.1)\n", - "ax_ratio.grid(alpha=0.3)\n", - "\n", - "plt.tight_layout()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "md-sets", - "metadata": {}, - "source": [ - "## Cluster sets\n", - "\n", - "Every cluster is keyed by `(frame, x, y)`, so the two results can be differenced\n", - "directly — no per-frame Python loop.\n", - "\n", - "A cluster whose centre moved by one pixel was *relabelled*, not gained or lost,\n", - "so those are counted separately from genuine adds and drops." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "sets", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU 233,099,227 clusters\n", - "CUDA 233,093,554 clusters net -5,673 (0.0024%)\n", + "outputs": [], + "source": [ + "import sys; sys.path.append('/home/ferjao_k/aare/build')\n", "\n", - "exact-position mismatches CPU-only 135,742 CUDA-only 130,069\n", - " of which shifted by 1 px 104,347 104,198\n", - " genuine add / drop 31,395 25,871\n", + "import time\n", + "from pathlib import Path\n", "\n", - "frames touched by a genuine difference: 41,569 of 100,000\n", - "[40.2s]\n" + "import boost_histogram as bh\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "from tqdm import tqdm\n", + "\n", + "from aare import (File, ClusterFinderMT, ClusterCollector, ClusterFinderCUDA)" ] - } - ], - "source": [ - "def cluster_keys(vectors):\n", - " \"\"\"One int64 key per cluster: frame * rows*cols + y * cols + x.\n", - "\n", - " Centres are unique within a frame, so keys are globally unique and the sets\n", - " below need only a sort, never a np.unique.\n", - " \"\"\"\n", - " n = len(vectors)\n", - " sizes = np.fromiter((cv.size for cv in vectors), np.int64, n)\n", - " frames = np.repeat(\n", - " np.fromiter((cv.frame_number for cv in vectors), np.int64, n), sizes)\n", - " xy = [np.asarray(cv) for cv in vectors if cv.size]\n", - " if not xy:\n", - " return np.empty(0, np.int64), frames\n", - " x = np.concatenate([v['x'] for v in xy]).astype(np.int64)\n", - " y = np.concatenate([v['y'] for v in xy]).astype(np.int64)\n", - " return frames * (rows * cols) + y * cols + x, frames\n", - "\n", - "\n", - "t0 = time.perf_counter()\n", - "k_cpu, fr_cpu = cluster_keys(clusters_cpu)\n", - "k_cuda, fr_cuda = cluster_keys(clusters_cuda)\n", - "\n", - "# Sort ONCE. np.setdiff1d and np.isin each re-sort their argument on every call,\n", - "# and the shift check below calls one eight times per direction -- about twenty\n", - "# sorts of a 27-million-element array. Two sorts plus binary search instead.\n", - "s_cpu = np.sort(k_cpu)\n", - "s_cuda = np.sort(k_cuda)\n", - "\n", - "\n", - "def present_in(vals, sorted_keys):\n", - " \"\"\"Which of `vals` appear in an already-sorted key array.\"\"\"\n", - " if sorted_keys.size == 0:\n", - " return np.zeros(vals.size, dtype=bool)\n", - " i = np.searchsorted(sorted_keys, vals)\n", - " np.clip(i, 0, sorted_keys.size - 1, out=i)\n", - " return sorted_keys[i] == vals\n", - "\n", - "\n", - "cpu_only = s_cpu[~present_in(s_cpu, s_cuda)]\n", - "cuda_only = s_cuda[~present_in(s_cuda, s_cpu)]\n", - "\n", - "\n", - "def drop_shifted(only, sorted_other):\n", - " \"\"\"Drop mismatches that have a counterpart within 1 px in the other set.\"\"\"\n", - " keep = np.ones(only.size, dtype=bool)\n", - " for dy in (-1, 0, 1):\n", - " for dx in (-1, 0, 1):\n", - " if dx or dy:\n", - " keep &= ~present_in(only + dy * cols + dx, sorted_other)\n", - " return only[keep]\n", - "\n", - "\n", - "cpu_real = drop_shifted(cpu_only, s_cuda)\n", - "cuda_real = drop_shifted(cuda_only, s_cpu)\n", - "touched = np.unique(np.concatenate([cpu_real, cuda_real]) // (rows * cols)).size\n", - "\n", - "print(f'CPU {k_cpu.size:>12,} clusters')\n", - "print(f'CUDA {k_cuda.size:>12,} clusters net {k_cuda.size - k_cpu.size:+,}'\n", - " f' ({abs(k_cuda.size - k_cpu.size) / k_cpu.size:.4%})')\n", - "print()\n", - "print(f'exact-position mismatches CPU-only {cpu_only.size:>8,} '\n", - " f'CUDA-only {cuda_only.size:>8,}')\n", - "print(f' of which shifted by 1 px {cpu_only.size - cpu_real.size:>17,} '\n", - " f'{cuda_only.size - cuda_real.size:>17,}')\n", - "print(f' genuine add / drop {cpu_real.size:>17,} {cuda_real.size:>17,}')\n", - "print(f'\\nframes touched by a genuine difference: {touched:,} of {N:,}')\n", - "print(f'[{time.perf_counter() - t0:.1f}s]')" - ] - }, - { - "cell_type": "markdown", - "id": "md-drift", - "metadata": {}, - "source": [ - "## Where in the run it accumulates\n", - "\n", - "Cumulative `CUDA − CPU` against frame index. A curve that rises and then flattens\n", - "means the two pedestals settled into agreement and the difference is a start-up\n", - "transient; a steadily sloping line would mean a persistent per-frame bias." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "drift", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": - "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jz92WZYzZ06Xxzdv3kSqVKkSOWciIpLcqSZPREQkDlKnTo08efK43Bo0aIAePXqgb9++yJEjBxo1amTSTpgwAffeey/Sp0+PggULonv37rh8+XLEumbOnIksWbLg+++/R+nSpZEuXTo88cQTuHLlCj755BMUKVIEWbNmxf/93//hzp07LkHkgAEDkD9/frPuatWqYcWKFfo+RUSSOdXkiYiI+BCDsm7dumH16tVwOBxmWWhoKCZNmmSCtX379pkgj8HZlClTIl539epVk2bOnDm4dOkSHnvsMXNj8Ldw4UL8888/ePzxx1GrVi089dRT5jXPPPMM9u/fb16TL18+fPvtt6aGcceOHShZsqS+VxGRZCrEYR+BRERExOs+ebNmzUKaNGkiljVr1gynTp3ChQsXsGXLlhhf//XXX5tA8PTp0xE1eQzY2KevePHiZlnXrl3x2Wef4cSJE8iQIYNZxgCOgeK0adOwd+9eE8gdPnzYBHi2hg0bomrVqhg9erS+TRGRZEo1eSIiInFQr149TJ06NeIxm0u2bdsWVapUiZJ2+fLlJujatWsXLl68iNu3b+P69eumOSZfR2yiaQd4lDt3bhPQ2QGevezkyZPm/ubNm01NYalSpVze68aNG8iePbu+UxGRZExBnoiISBwwOCtRooTH5c4OHDiA5s2bm5q5N954A9myZcOqVavQpUsX3Lp1KyJdypQpXV7H0To9LQsPDzf3+T9FihTYtGmT+e/MOTAUEZHkR0GeiIhIAtq4caOpuRs/frzpm0dfffVVvNd7//33m0FYWLNXu3ZtH+RURESChUbXFBERSUBsgskgb/LkyWbwFPazY5+6+GIzzXbt2qFjx46YN2+eGdBlw4YNePPNN81ALSIiknwpyBMREUlAFStWNFMoMPgqX748Zs+ejTFjxvhk3Zynj0Fev379zNQLrVq1wvr16800DSIiknxpdE0REREREZEgopo8ERERERGRIKIgT0REREREJIgoyBMREREREQkiCvJERERERESCiII8ERERERGRIKIgT0REREREJIgoyBMREREREQkiCvJERERERESCiII8ERERERGRIKIgT0REREREJIgoyBMREREREQkiCvJEREREREQQPP4fDMtW8Qx8sIoAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "first half: -2,032 second half: -3,641\n" + "cell_type": "markdown", + "id": "md-config", + "metadata": {}, + "source": [ + "## Configuration" ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "config", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Image size: (400, 400) cluster (3, 3)\n", + "Pedestal frames: 600\n", + "Data frames: 100000 of 100000 in file\n" + ] + } + ], + "source": [ + "base = Path('/mnt/sls_det_storage/moench_data/2603_MaxIVBeamtime/2026032408/process/xrf/')\n", + "f = File(base / 'Cu_factor_10_data_master_0.json')\n", + "pd = File(base / 'Cu_factor_10_pedestal_master_0.json')\n", + "\n", + "n_frames_pd = 600 # pedestal frames, taken from the head of the file\n", + "N = 100000 # data frames, read after those\n", + "cluster_size = (3, 3)\n", + "rows, cols = f.rows, f.cols\n", + "image_size = (rows, cols)\n", + "\n", + "N_SIGMA = 5\n", + "N_STREAMS = 4 # H2D / kernel / D2H overlap\n", + "BATCH_SIZE = 2000 # frames per find_clusters_batched call\n", + "\n", + "# CPU workers. ClusterFinderMT gives each thread its own ClusterFinder with its\n", + "# own pedestal, and data frames are handed out round-robin -- so at high thread\n", + "# counts each pedestal tracks on only 1/n of the frames while the CUDA finder's\n", + "# single pedestal sees all of them, and the cluster counts drift apart. Keep it\n", + "# low when comparing counts; raise it to measure CPU throughput.\n", + "N_THREADS = 24\n", + "\n", + "# Fixed per-frame output slot the kernel writes into, copied WHOLE on every D2H.\n", + "# Too low does not error -- it TRUNCATES. The CUDA cell prints the peak actually\n", + "# reached, which is how you size this for a new dataset.\n", + "CAP = 3000\n", + "\n", + "capacity = 3_000 # initial ClusterVector capacity, CPU side\n", + "N_BINS = 200\n", + "E_MIN, E_MAX = -2, 4000 # spectrum range, ADU\n", + "\n", + "print(f'Image size: {image_size} cluster {cluster_size}')\n", + "print(f'Pedestal frames: {n_frames_pd}')\n", + "print(f'Data frames: {N} of {f.total_frames} in file')" + ] + }, + { + "cell_type": "markdown", + "id": "md-build", + "metadata": {}, + "source": [ + "## Build the two finders" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "build", + "metadata": {}, + "outputs": [], + "source": [ + "cf_cpu = ClusterFinderMT(image_size, #only positional argument here i.e. need to use the keyword for next ones\n", + " cluster_size=cluster_size, \n", + " n_sigma=N_SIGMA,\n", + " capacity=capacity, \n", + " min_pedestal_samples=n_frames_pd, \n", + " n_threads=N_THREADS)\n", + "\n", + "sink = ClusterCollector(cf_cpu)\n", + "\n", + "cf_cuda = ClusterFinderCUDA(image_size, \n", + " cluster_size=cluster_size, \n", + " n_sigma=N_SIGMA,\n", + " max_clusters_per_frame=CAP, \n", + " n_streams=N_STREAMS, \n", + " min_pedestal_samples=n_frames_pd, \n", + " algorithm=\"fixed_window\", \n", + " time_kernels=False)" + ] + }, + { + "cell_type": "markdown", + "id": "md-ped", + "metadata": {}, + "source": [ + "## Pedestal\n", + "\n", + "Identical frames into both, so the implementation is the only thing that differs." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "train", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pedestal (600 frames): 2.39s\n" + ] + } + ], + "source": [ + "pd.seek(0)\n", + "t0 = time.perf_counter()\n", + "for _ in range(n_frames_pd):\n", + " pd_img = pd.read_frame()\n", + " cf_cpu.push_pedestal_frame(pd_img.copy())\n", + " cf_cuda.push_pedestal_frame(pd_img.copy())\n", + "print(f'Pedestal ({n_frames_pd} frames): {time.perf_counter() - t0:.2f}s')" + ] + }, + { + "cell_type": "markdown", + "id": "md-io", + "metadata": {}, + "source": [ + "## Read the data frames\n", + "\n", + "Kept out of both timing loops: the two finders run over the same in-memory array." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "io", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reading 100000 frames: 54.30s (1,842 FPS, 32.00 GB at 0.59 GB/s)\n" + ] + } + ], + "source": [ + "f.seek(0)\n", + "t0 = time.perf_counter()\n", + "data = f.read_n(N)\n", + "t_io = time.perf_counter() - t0\n", + "gb = f.bytes_per_frame * N / 1e9\n", + "print(f'Reading {N} frames: {t_io:.2f}s ({N / t_io:,.0f} FPS, '\n", + " f'{gb:.2f} GB at {gb / t_io:.2f} GB/s)')" + ] + }, + { + "cell_type": "markdown", + "id": "md-cpu", + "metadata": {}, + "source": [ + "## 1 — CPU, multithreaded" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "cpu", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████████████████████████████████████████████████████████████████████| 100000/100000 [00:23<00:00, 4235.66it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU MT (24 threads): 23.62s 4,235 FPS 233,104,694 clusters (2331.0/frame)\n" + ] + } + ], + "source": [ + "t0 = time.perf_counter()\n", + "# Pass the frame number: find_clusters(frame) defaults it to 0, and every\n", + "# resulting cluster is then stamped frame 0. Part 2 groups by it.\n", + "for i, frame in enumerate(tqdm(data)):\n", + " cf_cpu.find_clusters(frame, i)\n", + "t_cpu = time.perf_counter() - t0\n", + "\n", + "cf_cpu.stop(); sink.stop()\n", + "clusters_cpu = sink.steal_clusters()\n", + "\n", + "hist_cpu = bh.Histogram(bh.axis.Regular(N_BINS, E_MIN, E_MAX))\n", + "n_clusters_cpu = 0\n", + "for cv in clusters_cpu:\n", + " hist_cpu.fill(cv.sum())\n", + " n_clusters_cpu += cv.size\n", + "\n", + "print(f'CPU MT ({N_THREADS} threads): {t_cpu:.2f}s {N / t_cpu:,.0f} FPS '\n", + " f'{n_clusters_cpu:,} clusters ({n_clusters_cpu / N:.1f}/frame)')" + ] + }, + { + "cell_type": "markdown", + "id": "md-cuda", + "metadata": {}, + "source": [ + "## 2 — CUDA, batched + multi-streamed\n", + "\n", + "`register_input_buffer` page-locks the input once so H2D runs at full DMA\n", + "bandwidth instead of through the driver's staging buffer. It is paid once,\n", + "outside the loop, and released when the dataset is done." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "cuda", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CUDA batched: 3.35s 29,810 FPS 233,095,737 clusters (2331.0/frame)\n", + " input pinned in 0.84s, once, outside the loop\n", + " vs CPU: 7.04x\n", + " peak clusters in one frame: 2,545 of CAP 3,000\n" + ] + } + ], + "source": [ + "t0 = time.perf_counter()\n", + "cf_cuda.register_input_buffer(data)\n", + "t_pin = time.perf_counter() - t0\n", + "\n", + "clusters_cuda = []\n", + "t0 = time.perf_counter()\n", + "for start in range(0, N, BATCH_SIZE):\n", + " stop = min(start + BATCH_SIZE, N)\n", + " clusters_cuda.extend(\n", + " cf_cuda.find_clusters_batched(data[start:stop], first_frame=start))\n", + "t_cuda = time.perf_counter() - t0\n", + "\n", + "cf_cuda.unregister_input_buffer()\n", + "\n", + "hist_cuda = bh.Histogram(bh.axis.Regular(N_BINS, E_MIN, E_MAX))\n", + "energies = [np.asarray(cv.sum()).ravel() for cv in clusters_cuda if cv.size > 0]\n", + "if energies:\n", + " hist_cuda.fill(np.concatenate(energies))\n", + "n_clusters_cuda = sum(cv.size for cv in clusters_cuda)\n", + "\n", + "print(f'CUDA batched: {t_cuda:.2f}s {N / t_cuda:,.0f} FPS '\n", + " f'{n_clusters_cuda:,} clusters ({n_clusters_cuda / N:.1f}/frame)')\n", + "print(f' input pinned in {t_pin:.2f}s, once, outside the loop')\n", + "print(f' vs CPU: {t_cpu / t_cuda:.2f}x')\n", + "\n", + "# One ClusterVector per frame, so this IS the per-frame maximum.\n", + "peak = max((cv.size for cv in clusters_cuda), default=0)\n", + "print(f' peak clusters in one frame: {peak:,} of CAP {CAP:,}')\n", + "if peak >= CAP:\n", + " print(' !! CAP REACHED -- clusters were dropped; raise CAP and re-run.')" + ] + }, + { + "cell_type": "markdown", + "id": "md-summary", + "metadata": {}, + "source": [ + "## Summary" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "summary", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " s FPS us/frame clusters\n", + "CPU MT (24 thr) 23.62 4,235 236.2 233,104,694\n", + "CUDA batched 3.35 29,810 33.5 233,095,737\n", + "\n", + "speedup: 7.04x\n" + ] + } + ], + "source": [ + "print(f'{\"\":<22} {\"s\":>7} {\"FPS\":>10} {\"us/frame\":>10} {\"clusters\":>13}')\n", + "for name, t, n in (('CPU MT (%d thr)' % N_THREADS, t_cpu, n_clusters_cpu),\n", + " ('CUDA batched', t_cuda, n_clusters_cuda)):\n", + " print(f'{name:<22} {t:7.2f} {N / t:10,.0f} {t * 1e6 / N:10.1f} {n:>13,}')\n", + "print(f'\\nspeedup: {t_cpu / t_cuda:.2f}x')" + ] + }, + { + "cell_type": "markdown", + "id": "md-part2", + "metadata": {}, + "source": [ + "# Part 2 — Where the two disagree\n", + "\n", + "The two implementations are not bit-identical by construction: the CUDA finder\n", + "updates its pedestal once per frame, the CPU finder per pixel, so once the\n", + "pedestals drift apart a few pixels land on opposite sides of the threshold.\n", + "\n", + "Three views, cheapest first: the **spectra**, the **cluster sets**, and **where\n", + "in the run** the difference accumulates." + ] + }, + { + "cell_type": "markdown", + "id": "md-spec", + "metadata": {}, + "source": [ + "## Spectra\n", + "\n", + "Both curves should lie on top of each other; the ratio panel is the sensitive\n", + "view." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "spectra", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": + "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t/siyAMPPGACwvXr1/t8dnZz6NAh+f333+WKK67I85x+Hvq8BvZ169aVH3/80bPeW2pqqrk44X4+mIK8Xu9rwKwXJPzT5H5evyeadk2j/z7d286fP98EZYHKdOXKlU3zzDvuuEPS09NNGVX6/XX3K9YLE/ren376aRNk1alTx1yg0ee1HOsFAy3TWla1HGgwd+ONN/ocR4NQvQD0xBNPeMrGZZddJr169TJ5O378eHP8gQMHmmBVvzvucq9lXvenvw964UnzSr+7elHpk08+McGf6tmzp/k+6Hdb06vfR32szXUB2FSh6v8AIAq2bNlimlBde+21Yb/Gv7nm559/bvaht97Wr19v1r/66qvm8Y4dO8zjsWPHFqqJ3qhRo6ykpCTru+++81n/3//+N0/TMH1cunRpa9euXfm+nw0bNlgpKSnW3Xff7bN+3759VqVKlXyat2nzON3322+/7bNtp06drHr16nkeT58+3Ww3a9Ysn+007bp+4sSJnnU1atQwzWV//fXXPE3uXC6XtWbNGp/1HTp0yJPfmq4KFSqYprVuo0ePNvmln0Mw4X4mmkZNy6pVq/I0tStVqpR14MCBAn1GixYtMo+HDh1aqLLgLnOtWrXK89zw4cPNc/60HOp67/zQ91WiRAnr77//9qzT96jbaTNE9/tS7733nln//vvvW3YQrLnmpk2bTDr1s/CnTSX1OW2irR577DHzePPmzXm21SaYdevWDZmGgry+Tp06puz6++eff8w+Hn/8cfP466+/No/1O+RPt9Hn9DWhaJnRshPo96h27dqmaW9+zTi16W+fPn2sJk2a+Dyn+9Dfhf379+cpG2effbZpGu6m3ytd//3335vHWp7Kli1rdenSJU8z3MaNG1vnnXeeZ91JJ51kDRgwIGQ6AdgLzTXzoVfFu3TpYpoO6ZUvvRpfEO6mOv6LXg0HEDvaBKp27dqmhmzMmDHmSrjWeIVLr543atTINKnSJl3uRa+AB2ouqk2lypQpk+9+9eq57kevrnvvV2sKtFbKf796LP2N8q9h0Jo677Rq80PdznufmnatBfPfp75er9Z702aK+n69axGVDlThT2vUtm3bJu+88455rPmqtX9au+Dd3PV4PpOGDRtK48aNfdb93//9n2me6R61MdzPSAfdUNr89HhoLcnx0rRq7Y2b1jYprXHVpnT+670/50C8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uugiOfHEE800Bm3btpVDhw55nl+xYoVccskl5jkdYOjWW2+V/fv3F+jYnTt3NuuiHVgT5AEAACBhaeB07rnnymeffSZPPvmk/PDDD/Lxxx+bE/w777zTZ9uRI0fK5s2b5fvvv5du3bpJv379ZObMmRFJhwZCr776qs86DZw0Xe6h9HWbjRs3mvWajn//+9/SsGFDc9+93HfffWEfc9y4cXLTTTeZoNF9PF2efvppkw9Tp041edGnTx+f12nevP322/Lrr7/KrFmz5Pfff5err77a87zur2vXrvL+++/L2rVrzX4WLFhg8isUnT5Ag8MhQ4YE3eb//u//ZNWqVSZduuh9DfQC6dOnj5x11llh5YUGeHrs9u3by7fffivfffed+fy98+biiy+W008/XZYuXWqOvWbNGundu3eBj615/vzzz0tUWcgjPT3d0qzRW7vIzs62du/ebW5BfjsN5Zv8djLKN/mdCOX74MGD1k8//WQdOnTIijcdO3a0Tj31VGv//v15ntP35lajRg3r2Wef9Xm+Tp061rXXXmvuDx8+3GrcuHHAfeh55eeffx40Da1bt7Zuv/1265RTTrG++uorz/rHHnvM6tKli9mv7l/z++jRo57zwWDHDMf27dstl8tl/fjjjyG3e/vtt620tDRz3GDmzJlj9pWRkRF0m+eee86qWrVqWGnTvNI8885/pWVM13/zzTeedUuWLDHrfvnlF59tJ06caPL1008/Dbgvf82bN7cefPBBn3Xe+f3iiy9aFSpUsLKysjzPr1y50uz7t99+K9Cx//zzT7P+999/D5gW/R4F+z6FG6dQkwcAAICEpM0TtUZGa2y0iZ4/bZYXijZXPJ5h7r2lpaXJ9ddf71ObpzVgN998s0TDV199ZZpkNmjQIOR22lRTm0+mpKQEzcM333xTWrZsaZqSBqK1YO+++660bt36uNKstW3aRLN58+aedeeff75Zt3jxYs+6n376ydS6Tps2zVMTF4rOSae1czovnb6PihUrmrRqHrkdOXLEfEbe+3PPaee9XTjHrlGjhjnWl19+KdHCZOgA4G3dpyLTc/omSHKayJBN5A8AFFLDhz6Wo1la8VB0UpNdsmbkpWFtu27dOjPhdf369Qt0jMzMTHnjjTdMk8bbb79dIkWb+P3rX/+S5557TpYvX24CrMsuu6xA/ewK0kxVg5lQQZD2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SvHixU2+vvDCCz7PHz16VEaOHCm1a9c22zRu3Fg+/vhjn20efvhhT166l0qVKoU8rnf+u7yWhg0bFuh916xZ0+SlfoZ6697PnXfe6dlm2LBhMmjQIFsG1gR5ABDEmORxIo+UP7Y8fip5BQAO8+eff5pgRGvCnnzySfnhhx9MsNG2bVufE/qCuPTSS2Xz5s1m3xoQ6b769+8vnTt3lsxM33lYN2zYIEuWLJG77rpLJk+eXKjjLV68WH777Te55pprPOsWLVpkgry5c+fK8uXLTRo0cF25cqVnm7Jly5rAUI///fffy0033WQW72CwatWq8sQTT8iyZcvMctFFF0nXrl1lzZo1QdOzfv166dSpk1x44YXmeEOGDJF77rlHZs2a5dnmwQcflBdffFGef/55+emnn6Rfv35yxRVX+KRPaXCmeele9PMJ5bnnnvPZfuPGjeZ9eudNOO/7u+++k3/++ce8Xm/nz59v1nvv57LLLpP09PQ8wbMdMBk6AIQjK4N8AgAHuuOOO0wNzbfffmtqdbyDi5tvvrlQ+9TaQHeN06mnnirnnHOOnH/++dKuXTuZOnWq9O3b17Ptq6++aoK/22+/Xc477zwZO3asTzrCMWPGDFMbqDVibrofb48//rjMmTNHPvjgA2nSpIlZ510DqTQQfe211+Srr76SDh06mHUaGHp77LHHTO2e1jp6145501q76tWre9LQoEEDEyA+/fTTctVVV5l1WnOmgZYGg0rfvwZLzzzzjLzxxhuefaWkpORbe+etdOnSZnHT2tfdu3ebIM4tnPetNaCWZUlWVpapZR09erSpdWzdurXndbpe0z99+nTp2LGj2Ak1eQAS1o8pN5glmIFZ94gM256zJKcVadoAANG3a9cuU2unNXaBAittzhcpWgOmTRK9+3BpEKFB3g033GD609WtW1fefvvtAu9ba+20+WEo2qRw3759phYrEE2LNk/99ddfpVWrVgG30YBHA8oDBw6YZpvBaA2ZBp3eNHjSQE+baaojR474BKWqRIkSJtDypjWUVapUkVq1asm1114rf/zxhxTE5MmT5eKLL5YaNWoU+n1nZGSYwFODfr0g4E0D8y+//FLshpo8AAgiIytb6g7N6XfwY0pOe3tCPQAoIG3uHkzfBSKVG+fcn3G9yG/zAm/X6j8ire/Pub/idZEPBwbertKZIrd8FnbS1q1bZ07yNcAqCnocbR7otmDBAjl48KCn9kiDPQ1KvGudwqHNQjUQCkVryDQ46969u896bW6otY0adGnN1MSJE00zT2/aRFKDusOHD8tJJ50ks2fPljPOOCNkH0fti+hNH2tT1R07dkjlypXNex4zZowJrLSGTAMtrWnUQNKtefPmMm3aNBP8bt26VR599FFp2bKlaSp6yimn5JsvmzdvNs1l33rrrTzPhfO+vWsD9+zZY/r7+dN9aJNbDaLd/SHtwD4pAQAbSU12SVrysZ/IF7M6yyvZvk1WAADxTQM85V87E83jeR9LA7oePXqYJonquuuuk6VLl5papYI4dOhQnloxb9qcUAcxmTlzplSoUMHnuZIlS8qqVatMHzRtijlw4EBZuHChzzb16tUz22gTTW1W2atXL9OPLhT/PPXPa+07V6dOHRP4pqWlmT6JGtxqwOWmTSC1eeeZZ55pauM+/PBDs16bVoZj6tSppja2W7dueZ4L53276aA5mpZAgbTWPmqAp8GinVCTBwABrBl5qc/jukP1P5TIHeQWABSMNnkPx7VvhrfdOT1zlgjQIEODjp9//jlgIOAfFGjtjz+t4fHuAxaKHkebHbqbimoNkTZf1D5ublqTpUGF9gELV7ly5Uy/s0A0sOvTp4+88847JlDyp7VPp59+urmvo0xqGkeNGuXTb02DMPc22ixUAyMN0nTglEC0D53W5nnbtm2bCWbdNXDa503fv9YO7ty50wRQOlKlO38C0Sa1GvBpE878WJZl8rFnz54m/YV53+qvv/4yNa7BpkrQz/GEE04wwZ6dUJMHAACAhKT907TZ4IQJE0xTxkABnJvWOGlw4x9I6MiVWtOVHx29U5s9ugceefPNN83IlatXrzY1Su5FByvRmir/UThD0YFUAtWsaQ2eNjHU5oo6EmQ49D3lVyuV3zbatNM9GqXbvHnzTICoUxJ40xpIbfKo71dH39SRO4PRY2owps09w5nCYd26dSbADUew96SfhdZ+Bsu/H3/80QysYzfU5AFAGDq73B3B7TV6FgDg+GhfLO3npQNo6LxtOiecBhwapGgNmwYV6r777jPNFDXY00FFtInkSy+9JL///nueqRY0WNCaLK2V075kOriL1hLpKJo33nijp6nm1VdfLY0aNfJ5rQ4Q8sADD5imiaECHm8aqPo3YdQAT4+lNW46sqe7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AABQNGwf5Kk77rjDLIFMnTrV57HOpbdy5UpxGm1OOnz48DzNSkF+OwHlm/x2Mso3+e1klG/y28mKxfH5t60HXgEAAAAAFIw9JnwAAAAAAEQEQR4AAAAAOAhBHgAAAAA4CEFenJg4caLUqlVLihcvbuYO/PLLL2OdpLjz8MMPm8ntvZdKlSp5ntfuqbqNTi5ZokQJadOmjaxZs8ZnH0eOHJG7775bypUrJyeeeKJcfvnl8vfff8fg3djPokWLpEuXLib/NG/fe+89n+cjlb86NYpOq6JzxOii9/fs2SOJJr/87t27d57yfv755/tsQ36Hb9SoUdKsWTMpWbKkVKhQQbp162ZGcfZGGS/a/KaMR86kSZPkrLPO8swFplNWffTRR57nKdtFm9+U7ej+trhcLhkwYIDzy7cOvAJ7mzFjhpWammq9/PLL1k8//WT179/fOvHEE62//vor1kmLK8OHD7caNmxobd682bNs27bN8/wTTzxhlSxZ0po1a5b1ww8/WD169LAqV65s7d2717NNv379rFNPPdWaP3++tWLFCqtt27ZW48aNrczMTCvRzZ071xo6dKjJP/1pmT17ts/zkcrfSy+91GrUqJG1ePFis+j9zp07W4kmv/zu1auXySvv8r5z506fbcjv8HXo0MF69dVXrR9//NFatWqVddlll1nVq1e39u/f79mGMl60+U0Zj5z333/f+vDDD61ff/3VLEOGDDHnHZr/irJdtPlN2Y6Ob7/91qpZs6Z11llnmXNpN6eWb4K8OHDeeeeZwuWtfv361qBBg2KWpngN8vQLGUh2drZVqVIl80V3O3z4sFW6dGnrhRdeMI/37NljfoQ16HbbtGmTlZSUZH388cdF8A7ih3/QEan81Yscuu9vvvnGs82SJUvMul9++cVKVMGCvK5duwZ9Dfl9fPQCkeb7F198YR5Txos2vxVlPLrKlCljvfLKK5TtIs5vRdmOvH379ll16tQxQVrr1q09QZ6Tf7tprmlzGRkZsnz5cmnfvr3Pen2sk7yjYH777TdTHa9NX6+99lr5448/zPr169fLli1bfPJZ50Rp3bq1J5/1czh69KjPNrqvRo0a8VnkI1L5u2TJEtMEonnz5p5ttAmiruP7kNfChQtNU7e6devKLbfcItu2bfM8R34fn/T0dHNbtmxZyngM8tuNMh55WVlZMmPGDDlw4IBpRsjvd9HmtxtlO7LuvPNOueyyy+Tiiy/2We/k8h0Xk6Ensh07dpgfgIoVK/qs18daKBE+/eJNmzbNnPBu3bpVHn30UWnZsqVpd+3Oy0D5/Ndff5n7uk1aWpqUKVOGz6KAIpW/eqtBiz9dx/fBV8eOHeWaa66RGjVqmH9iw4YNk4suusj8s9J/YOR34Wnl6cCBA+Vf//qX+SdPGS/6/FaU8cj64YcfTJBx+PBhOemkk2T27NlyxhlneE5Q+f0umvxWlO3ImjFjhqxYsUK+++67hDo/IciLE9pJ1P+fnv86hKY/mm5nnnmm+XGtXbu2vPbaa54BKQqTz3wW4YtE/gbans8grx49enju64lx06ZNTcD34YcfypVXXkl+H4e77rpLvv/+e/nqq6/yPEcZL7r8poxHVr169WTVqlVmoIhZs2ZJr1695IsvvvA8T9kumvzWQI+yHTkbN26U/v37y7x588zghcE4sXzTXNPmdBSf5OTkPFcBtNmV/1UHFIyOjqTBnjbhdI+yGSqfdRttPqujJ/FZFEyk8le30VpYf9u3b+f7kI/KlSubIE/LO/ldeDq62vvvvy+ff/65VK1a1bOeMl60+R0IZfz4aE3F6aefbi4I6QiEjRs3lueee46yXcT5HQhlu/CWL19uziN0ZPqUlBSzaDA9btw4c999fuHE8xOCvDj4EdCCOX/+fJ/1+libGqLwdDjcn3/+2fx4ah89/YJ657N+ofWHwJ3P+jmkpqb6bLN582b58ccf+SzyEan81dpX7Zvz7bfferZZunSpWcf3IbSdO3eaK5pa3snvgtOrsVqj9O6778pnn31myjRlPHb5HQhlPPKfgf6f5Pe7aPM7EMp24bVr1840jdVaU/eigfX1119v7p922mnOPT+JyXAvKNQUCpMnTzaj9wwYMMBMofDnn3+SkwXw73//21q4cKH1xx9/mNGPdFhbHTLXnY86spKOpvTuu++aIXSvu+66gEPoVq1a1VqwYIEZQveiiy5iCgWvkatWrlxpFv1pGTNmjLnvnuojUvmrQxTr8Mc6apUuZ555ZkJOoRAqv/U5Le86hPP69eutzz//3GrRooUZ/pn8Lpzbb7/dlF/9DfGeluLgwYOebSjjRZfflPHIGjx4sLVo0SLze/H999+bIf115MB58+aZ5ynbRZfflO3oa+01uqaTyzdBXpyYMGGCVaNGDSstLc0655xzfIaRRnjc855owFylShXryiuvtNasWeN5XofR1WkWdCjdYsWKWa1atTJfdm+HDh2y7rrrLqts2bJWiRIlzJd3w4YNfASWZQIJDTb8Fx0KOpL5q3O9XX/99SZA10Xv7969O+E+g1D5rSfC7du3t8qXL2/Ku84vpuv985L8Dl+gvNZF53Jzo4wXXX5TxiPr5ptv9pxj6O9Gu3btPAGeomwXXX5Ttos+yMt26PmJS//Epg4RAAAAABBp9MkDAAAAAAchyAMAAAAAByHIAwAAAAAHIcgDAAAAAAchyAMAAAAAByHIAwAAAAAHIcgDAAAAAAchyAMAAAAAByHIAwAghtq0aSMul8ssq1ativln0bt3b0963nvvvVgnBwBQCAR5AIC44R2AeC+XXnqpxLNbbrlFNm/eLI0aNcrzXPv27SU5OVm++eabkPmRmpoqFStWlEsuuUSmTJki2dnZPtsGC9oGDBhgAk235557zqQFABC/CPIAAHFFAzoNQryX6dOnR/WYGRkZUd3/CSecIJUqVZKUlBSf9Rs2bJAlS5bIXXfdJZMnTw6ZH3/++ad89NFH0rZtW+nfv7907txZMjMzC5yW0qVLm7QAAOIXQR4AIK4UK1bMBCHeS5kyZXxqrF555RW54oorTPBUp04def/993328dNPP0mnTp3kpJNOMrVfPXv2lB07dnie15otDawGDhwo5cqVM7VjSvej+ytRooQJpl577TVzvD179siBAwekVKlS8t///tfnWB988IGceOKJsm/fvgK/11dffdUEa7fffrvMnDnTHCNYfpx66qlyzjnnyJAhQ2TOnDkm4Js6dWqBjwkAiH8EeQAAxxkxYoR0795dvv/+exPMXX/99bJr1y7znNZ6tW7dWs4++2xZtmyZfPzxx7J161azvTcN4LRm7euvv5YXX3zR1JRdffXV0q1bN9N37rbbbpOhQ4d6ttdA7tprrzWBmTd9rK8rWbJkgd6DZVnmtTfccIPUr19f6tatK2+//XZYr73oooukcePG8u677xbomAAAZyDIAwDElf/973+mBs57eeSRR/L0Vbvuuuvk9NNPl8cff9zUgH377bfmuUmTJpkaL12vwVOTJk1MH7bPP/9c1q5d69mHvvbJJ5+UevXqme1eeOEFc/+pp54ytxrQ6XG89e3bVz755BP5559/zGOtHdT03nzzzQV+nwsWLJCDBw9Khw4dzGMN9oI12QxE06yBKQAg8RDkAQDiijaT1Jo07+XOO+/02eass87yqWHTWrRt27aZx8uXLzcBnXeQqAGR+v333z2va9q0qc8+f/31V2nWrJnPuvPOOy/P44YNG8q0adPM49dff12qV68urVq1KvD71ICuR48enn56GrQuXbrUpCPcmkBtSgoASDy+PbwBALA5Ddq0li0UHWnSmwY77tEm9bZLly4yevToPK+rXLmyz3HyC5p0nT+tzRs/frwMGjTINLe86aabChxsadNSHQnz6NGjpubRLSsry9Q6Bkq7v59//llq1arleayBbnp6ep7ttD+hDrYCAHAOavIAAAlFm2quWbNGatasaYJF78U/sPOmtX3fffedzzrt0+dPm1XqqJjjxo0zx+nVq1eB0/jmm29K1apVZfXq1T41lmPHjjV9BfMbNfOzzz6TH374Qa666qqQ6dcgVWs2tfkpAMA5CPIAAHHlyJEjsmXLFp/Fe2TM/GjTTq0p0+aP2k/vjz/+kHnz5pl+c1pTFowOtPLLL7/IAw88YPru6SAo7tErvWvqdKTPK6+8Uv7zn/+YOe40WCtMU00drEXnzfNeNI1a8/bhhx/myY9NmzbJihUrTF/Drl27mlE5b7zxRs929913n9mv1jJq+jWA1BFEtYmqf3NXAEB8I8gDAMQVHQ1Tm1V6L//617/Cfn2VKlXMiJka0OmgJho86bxy2mQxKSn4v0Vt+qjTI+iIldrnT5tRukfX1GkMvPXp08fMrVeYAVe0Zk0DMO9aOO8mlxo4eg/A4s4PrZnUOfO0v6HWIuo0CjqJupuOHqpBqdYEat9C3Y8GeF9++aXUqFGjwOkEANiXywrUoQAAAOTrscceM6Nubty4MU9zSw0cdZTNtLS0kPvQOfl0OgdtimknWjs5e/ZsM2UEACC+UJMHAECYJk6caPq1aRNPHTlTp1Pw7nOnUx5oP7xRo0aZ5p35BXje+9VRPrUfXaz169fPpAUAEL+oyQMAIEz33nuvzJw50/Tp06kRevbsKYMHD/ZMc/Dwww+b2j2dMkGbS4YTLGlfukOHDpn7us9wA8No0akm9u7da+5rM9BQg9EAAOyJIA8AAAAAHITmmgAAAADgIAR5AAAAAOAgBHkAAAAA4CAEeQAAAADgIAR5AAAAAOAgBHkAAAAA4CAEeQAAAADgIAR5AAAAAOAgBHkAAAAAIM7x/+xXTgFvoafsAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, (ax_spec, ax_ratio) = plt.subplots(\n", + " 2, 1, figsize=(9, 6.5), sharex=True, gridspec_kw={'height_ratios': [3, 1]})\n", + "\n", + "edges = hist_cpu.axes[0].edges\n", + "cpu_vals, cuda_vals = hist_cpu.values(), hist_cuda.values()\n", + "\n", + "ax_spec.stairs(cpu_vals, edges, label=f'CPU MT ({n_clusters_cpu:,})',\n", + " color='C0', linewidth=1.4)\n", + "ax_spec.stairs(cuda_vals, edges, label=f'CUDA ({n_clusters_cuda:,})',\n", + " color='C1', linestyle='--', linewidth=1.4)\n", + "ax_spec.set_ylabel('Counts')\n", + "ax_spec.set_title(f'Cluster energy spectrum — {N:,} frames')\n", + "ax_spec.legend()\n", + "ax_spec.grid(alpha=0.2)\n", + "\n", + "with np.errstate(divide='ignore', invalid='ignore'):\n", + " ratio = np.where(cpu_vals > 0, cuda_vals / cpu_vals, np.nan)\n", + "ax_ratio.stairs(ratio, edges, color='C1', linewidth=1.2)\n", + "ax_ratio.axhline(1.0, color='gray', linewidth=0.5)\n", + "ax_ratio.set_ylabel('CUDA / CPU')\n", + "ax_ratio.set_xlabel('Energy [ADU]')\n", + "ax_ratio.set_ylim(0.9, 1.1)\n", + "ax_ratio.grid(alpha=0.3)\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "md-sets", + "metadata": {}, + "source": [ + "## Cluster sets\n", + "\n", + "Every cluster is keyed by `(frame, x, y)`, so the two results can be differenced\n", + "directly — no per-frame Python loop.\n", + "\n", + "A cluster whose centre moved by one pixel was *relabelled*, not gained or lost,\n", + "so those are counted separately from genuine adds and drops." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "sets", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU 233,104,694 clusters\n", + "CUDA 233,095,737 clusters net -8,957 (0.0038%)\n", + "\n", + "exact-position mismatches CPU-only 167,059 CUDA-only 158,102\n", + " of which shifted by 1 px 126,836 126,667\n", + " genuine add / drop 40,223 31,435\n", + "\n", + "frames touched by a genuine difference: 48,682 of 100,000\n", + "[43.3s]\n" + ] + } + ], + "source": [ + "def cluster_keys(vectors):\n", + " \"\"\"One int64 key per cluster: frame * rows*cols + y * cols + x.\n", + "\n", + " Centres are unique within a frame, so keys are globally unique and the sets\n", + " below need only a sort, never a np.unique.\n", + " \"\"\"\n", + " n = len(vectors)\n", + " sizes = np.fromiter((cv.size for cv in vectors), np.int64, n)\n", + " frames = np.repeat(\n", + " np.fromiter((cv.frame_number for cv in vectors), np.int64, n), sizes)\n", + " xy = [np.asarray(cv) for cv in vectors if cv.size]\n", + " if not xy:\n", + " return np.empty(0, np.int64), frames\n", + " x = np.concatenate([v['x'] for v in xy]).astype(np.int64)\n", + " y = np.concatenate([v['y'] for v in xy]).astype(np.int64)\n", + " return frames * (rows * cols) + y * cols + x, frames\n", + "\n", + "\n", + "t0 = time.perf_counter()\n", + "k_cpu, fr_cpu = cluster_keys(clusters_cpu)\n", + "k_cuda, fr_cuda = cluster_keys(clusters_cuda)\n", + "\n", + "# Sort ONCE. np.setdiff1d and np.isin each re-sort their argument on every call,\n", + "# and the shift check below calls one eight times per direction -- about twenty\n", + "# sorts of a 27-million-element array. Two sorts plus binary search instead.\n", + "s_cpu = np.sort(k_cpu)\n", + "s_cuda = np.sort(k_cuda)\n", + "\n", + "\n", + "def present_in(vals, sorted_keys):\n", + " \"\"\"Which of `vals` appear in an already-sorted key array.\"\"\"\n", + " if sorted_keys.size == 0:\n", + " return np.zeros(vals.size, dtype=bool)\n", + " i = np.searchsorted(sorted_keys, vals)\n", + " np.clip(i, 0, sorted_keys.size - 1, out=i)\n", + " return sorted_keys[i] == vals\n", + "\n", + "\n", + "cpu_only = s_cpu[~present_in(s_cpu, s_cuda)]\n", + "cuda_only = s_cuda[~present_in(s_cuda, s_cpu)]\n", + "\n", + "\n", + "def drop_shifted(only, sorted_other):\n", + " \"\"\"Drop mismatches that have a counterpart within 1 px in the other set.\"\"\"\n", + " keep = np.ones(only.size, dtype=bool)\n", + " for dy in (-1, 0, 1):\n", + " for dx in (-1, 0, 1):\n", + " if dx or dy:\n", + " keep &= ~present_in(only + dy * cols + dx, sorted_other)\n", + " return only[keep]\n", + "\n", + "\n", + "cpu_real = drop_shifted(cpu_only, s_cuda)\n", + "cuda_real = drop_shifted(cuda_only, s_cpu)\n", + "touched = np.unique(np.concatenate([cpu_real, cuda_real]) // (rows * cols)).size\n", + "\n", + "print(f'CPU {k_cpu.size:>12,} clusters')\n", + "print(f'CUDA {k_cuda.size:>12,} clusters net {k_cuda.size - k_cpu.size:+,}'\n", + " f' ({abs(k_cuda.size - k_cpu.size) / k_cpu.size:.4%})')\n", + "print()\n", + "print(f'exact-position mismatches CPU-only {cpu_only.size:>8,} '\n", + " f'CUDA-only {cuda_only.size:>8,}')\n", + "print(f' of which shifted by 1 px {cpu_only.size - cpu_real.size:>17,} '\n", + " f'{cuda_only.size - cuda_real.size:>17,}')\n", + "print(f' genuine add / drop {cpu_real.size:>17,} {cuda_real.size:>17,}')\n", + "print(f'\\nframes touched by a genuine difference: {touched:,} of {N:,}')\n", + "print(f'[{time.perf_counter() - t0:.1f}s]')" + ] + }, + { + "cell_type": "markdown", + "id": "md-drift", + "metadata": {}, + "source": [ + "## Where in the run it accumulates\n", + "\n", + "Cumulative `CUDA − CPU` against frame index. A curve that rises and then flattens\n", + "means the two pedestals settled into agreement and the difference is a start-up\n", + "transient; a steadily sloping line would mean a persistent per-frame bias." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "drift", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": + "iVBORw0KGgoAAAANSUhEUgAAA3kAAAFeCAYAAAA4xtZyAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAayVJREFUeJzt3Qd0FNXbBvAnIfQeeu9FQamCSJOuFEURBQFB0U9EFKWDSG/+QVREmqKoICgWVIoCUpQi0kFRitI7UkJv2e88d5xkd7MJSdhkS57fOQPZ2bu7d2fuzsw7t4U4HA4HREREREREJCiE+joDIiIiIiIi4j0K8kRERERERIKIgjwREREREZEgoiBPREREREQkiCjIExERERERCSIK8kRERERERIKIgjwREREREZEgoiBPREREREQkiCjIExERERERCSIK8iSobNu2DU8//TSKFSuGdOnSIVOmTKhcuTL+97//4fTp01Hp7r//frMklUmTJmHGjBlJ9v6ByFvbJCQkBEOGDIl6zPfkun379rmkGzhwIAoXLoywsDBky5bNrLt27Rq6dOmCfPnyIVWqVKhYseJt50cCn3uZiq9Lly6Z161YsSLGc7GVS3H1ySefoE2bNihTpgxCQ0NRtGjRWDfRhQsX8MorryB//vzm+M7f75w5czym3bRpExo2bGjOAfz9P/roo/jnn3/ivfkT8vp3330XZcuWRdq0ac25Z+jQobh+/XqMdCdOnECnTp2QM2dOZMiQATVq1MBPP/0U7zx5OqalJL/88ovZxvv3749aF9sxneWI2zopeeM3vmfPHnTo0MHs1/Tp06NEiRLo0aMH/v3333i9fteuXWjVqhWyZ89uylT16tXx3XffxUjH4xTz6r7wd+SMxzJP6eyF29o2ffp0FChQABcvXkz095ck5hAJEtOmTXOEhYU5ypUr53jvvfccy5cvdyxevNgxatQoR7FixRwtW7aMSlu3bl2zJBXmISnfPxB5a5vwsDV48OCoxydOnHCsXbvWceXKlah18+bNM+lee+01x6pVqxzr1683699++22z/t1333WsWbPGsW3bttvOjwQ+9zIVXydPnoz1tZ7KpcTUsGFDR/ny5R3t27d3lCxZ0lGkSJFYN1OjRo0c2bJlc0yZMsWxbNkyx7PPPmu2/6xZs1zS/fnnn47MmTM7ateu7ViwYIHjq6++Msef/Pnzm/1yKwl5/YgRIxwhISGO/v37m3PO//73P0eaNGkczz33nEs6lgN+z4IFCzpmzpxpzk0PP/ywOWetWLHilnmK7ZiWUkRGRjoqV67sePHFF13Wx3ZM37Rpk2PPnj1JmqePPvrIfPbevXsT9XqWpRw5cpjrkxkzZpgy/eabbzoyZcrkqFixouPmzZtxvp6fGx4ebsrmnDlzHPPnz3c0a9bMlMcvv/zSJS2PUczrDz/8YI5L9rJu3TqXdOfOnXN53l6eeuqpqNfbrl+/7ihVqpRj0KBBifr+kvQU5ElQ4ME9VapUjgceeMDjRdXVq1cd3377bUAHeTzJXbp0yRGokirI84QXXkx3/Phxl/W8KEyfPr3Dmy5evOjV95PgCPLEcu3aNXMxGBvnC1leoMYW5DHY4rb+7LPPYgR+DL5u3LgRta5169aOnDlzmgtW2759+xypU6d29OnT55a7Jr6vP3XqlCNdunSO//u//3N5/ciRI82F9h9//BG1jjcemX+eq2zcLnfeeaejWrVqt8xTbMc0TwL5PBGbhQsXmu//119/JfkxPbmCvPfff9+8funSpS7reWOa6xmoxuX555835e/QoUNR6/g7uOOOOxyFChVy+W3ZQR6PWYm59ihevLj5bboHnuPGjXNkzZpV50E/pSBPgkLz5s3NHdEDBw7EK717kMc7sDwA8n9nPHhzPQ/mtr///tvxxBNPOPLly2fu2ObOndtRv359x+bNm83zPBDyNc6L84ULLxx69uzpKFq0qLlo4AVK9+7dHRcuXHD5bL6Ody0nT57sKFu2rEnLv2PD4Hbo0KEmbdq0ac0dvvvvv9+xevXqqDSXL1929OvXz+Wzu3bt6jhz5kyMz/Z04crv0bFjxxgnOd6B7NKli7kryc995JFHHIcPH3Z5XVzbxBNuJ57A+X4ZM2Z0NGnSxLFz584YeXM/0Xr6LPsE577Y+5UnMV6EVahQwZw0WVvQqlUrs6+dscwwWF25cqWjRo0a5uKCZSEx+/WTTz4x+4rvcffddzu+//57jzUKbdq0MWWMZY0n7g4dOrjcyDh69Ki5yCxQoID5XH7+kCFD4rywtvHuLy+S8+bNa74389O3b98YeaZff/3V/M64P1i+eNLn90tIfu39EJ+LJe5HXvRzu/Cutp0/ezvxNXycIUMGxz333BOjZiO2Gzksv+5lz1Pt8AsvvGAullj2cuXK5ahXr57j559/jnFscF/s34en72SXn99++81Rq1Yts+95F3/06NExLp5+//13s2+YhgEHf6e8U+/pOOXJL7/8Yo5LrBXge7C88vW2LVu2mPf64IMPYr2gdr4xtmvXLkfbtm3NtuC+5bafOHGiy+vs4yjLdo8ePcxvgMEOy0V8xBXk8VjA7+Jerhn08TPt4xyf5/flBbC7xo0bm5qHuCTk9ayR42ezpsPZkSNHzHoGe841lmXKlInxnvYFvfOFurvYjmn2c9xurG3k74S/Tf6GifuHtZHcZ/ydsCbxjTfeMIG3M7tcMgBlOeFvje/74YcfmudZbipVqmS2C99j0aJFMfIYn/LBMj58+HBH6dKlzWcwOLjrrrtMbdyttGjRwvzOncV1THc/V9llk+VlwIAB5vzN2toGDRrECBxZy/rQQw+ZYyq3Z4kSJcwx1j1Aut0gj7V3fL37sWvSpElm/Y4dO+J8PfdF9erVY6znb8+9XN5OkPfTTz+Z1/K84o7nH/7Gp0+fnuD3laQXltTNQUWS2s2bN7Fs2TJUqVIFhQoVSvLPa9q0qflM9vNjO/pTp05hzZo1OHv2rHn+m2++wWOPPYasWbOafmjEfgR2H566devi0KFDGDBgAO6++2788ccfGDRoELZv346lS5eadu+2efPmmX4IfD5v3rzInTu3xzzduHEDDz74oEnLPiv169c363799VccOHAA9913H6+s0bJlS9MHpH///qhdu7bpwzh48GCsXbvWLHY+E+rZZ59Fs2bN8Nlnn+HgwYPo3bs32rdvb/bLrbaJJ3ZeuV353e+55x6sXr3afMdb4We99957pr/ADz/8YD6zYMGCeOCBBzB8+HAsX748Kl/s/0DPP/+86V/x8ssv44033jD9N4cNG2a229atW5EnT56o9z969Kj5bn369MGoUaNMP6KE7tcFCxZg/fr15jPY54dl6ZFHHsHOnTtRvHhxk4afW6tWLdN/h+lKlSplPpv9LdgPhdvv2LFjqFatmskDP4vfh/txxIgRpp/IRx99FOe22r17tynPLDMZM2bEX3/9Zb7/b7/9FrWN6Mcff0SLFi1wxx13YPz48abc8/0XL14clSY++U0ovifL6muvvWb2I/s6sW8U17Ecc/tzu/bt2xfNmzfH3r17Tb+W22X33+Vvg7879gVjuWI/Xn4u/2cfIJYvlqvOnTub3wDlypUrzvfmPmvXrh169uxp3p/vy+/DfmZPPfWUScPtxvLEfTJ58mTzu589eza6desWr/yvXLkSjRo1MuWQvwNue/7uuA/5Pk888QQqVKiASpUqmTLC/Dvjb4GfybJBO3bsML8F7vc333zTbBOWCf5eePzj93DG78P+ZlOmTDFlM7bjVkL8/vvvpvyxP5ozfkf7eebx77//xuXLl6PWu6ddsmQJrly5EqMvki0hr+dn0l133eWSjmWDvwP7eTt/POZ6ek/i8YL9mxJyTHPuP/jnn3+aPnvsE8hyY3+XJ5980qxLkyaN+T2NHDnS/M4//PDDGOWS/dl5XON7s5/hM888Y47nX375pTmu8XP52+axmf0TWWYTUj54nGPfMOazTp06pt8i82KfO2PD4wePoS+99JLLeh7rYjumx4bfo2bNmvjggw8QERFhjh38XXD7sU+fvd1Yfvmb5nfmsY7HPR7feDxPnTo1vIHbkduMxwL+PosUKWL25ZgxY6KOt7faLuHh4THW28dant/vvfdel+dYVtk3lOWzSZMm5lzBPMSF5Y6/Y5YPd9zX7I/KcxrLi/iZZAgkRZLUsWPHzF0m1iDEV2Jr8tg8h49vdecxtqaJvGMfGhoa484d28/zfXkH3cbHvNN5+vTpW34f3jlnejb/iA3b0jMN+4w4+/zzz8169mlMbE0eaxmc8TO4nnf5EtNck3eK+fp33nnHZT3vjN+qJi+uu5bMO2tmnPFuJ9OyL4SzgwcPmjvXzs2zmH+m5Z3N29mvefLkcURERLiUYb6e72NjLQxrFOPqQ8TaBtZu7N+/P0YTGn6Oc3OxW2FtJmsxWEvJ127dujXqOd7J5sKa4NjEJ78Jrcnj9neu4bBrn3gX3rmZrN1f6bvvvvNKTZ47NoHituFdf9ZSx6e5Zmw1eVzn3g+GTfZYU23r3bt3jOZ+xDTxqcm79957TW3q+fPnXb6D3SeM+5omTJhg3o815DYeb1h7wVpp58/l65ybL1K3bt1MjYx9jLKPo3Xq1HEkRlw1eaxBc95G7rVmrBEj1ujx8ezZs2OtNeNrYpOQ17PfHbeVJ6ytYs2fjbXsnmoHWXvmqRmqu9iOadxe7KrgvA89YS0ayzDPFUzvfF6xy+WGDRui1v37778mHX+Dzq0y7N8gy05CywdbArC2MaH4e+FnsuWBO0/H9Lhq8po2beqS7osvvvBYG+t+XOQx1r12+3Zr8ohlibWnzrWRbC4cn768HGeAx1zn3zmx9tb5N0Hc7zx/8lzEljdjxowxrTJ4LoqrFpmtfLgPPf32bO3atTPvI/5Ho2uKJADvmvFO4dixY82dvc2bNyMyMjLer58/fz7Kly9vRgBjTZu98I4aayTcR+ljjRxHzbKxBtH5dfZnL1q0yNxZjutOmn2n033EsdatW5s7vwkZ5c3dQw895PHutPMoaAnBO7PEGg9nvCvtbdwn3PasnXPetrxDydoO933C/cH9cjv7tV69esicOXPUY9YUsrbD3l6sGWRtzOOPPx5nzRA/l+/FO+rOn2vXePI94sK78dym/K68i8071KxBIt7Ztkdv451t1vbEVvsR3/wmFLenc+2GfWebNWkcSc59fWLLmyesheLIvPzOrD3ituFvxN4uicVtzdpX99+Lc965LVme7rzzTpd0bdu2veX7c6S7devWmZpz1hLbuH85ih9rm1ljbP++eNffedRb1vRdvXo16q49a634vVnTzG3uXM5Y08fn2WLAGUf7SwrOteG3ei4haW/ns5IzT7Fh+SldunSM9Tw/8dicI0eOqN83a4t5HuHv2r32ka1hnM91PCbxN2jX2Hn6rSWkfLDcszaxa9eupqaPNWnxceTIEfO/N2qE43OuYk0XR5FkyyD7t89aNkrM7995m3Cx7isBZ86cwcMPP2y2w6xZs/Dzzz+bGr1Vq1aZfDJtXFizf+7cObNPeSw/fvw4Xn/9ddMChlj7ZuNvn7WYPDfwnMEaTF43nDx50tSwxob54j60Wyp4wv3CbXar/EryU5AnAc8ejppNtZIaT8Q8ofHinQdGXgTyopbNUs6fP3/L1/MgzCYUPGk4L7zg54GfTVvcT7zOGGA6v45NZ4gHap6InQ/q7jgkM09Y7hfh/E688IzvkM2e8CLCU3MRNntKDDuv7u/LfHob9wm3PQMt9/3Ci5Nb7ZPE7Ff372VvM3t78eTPCzHnJlmx5f3777+P8bnlypUzz7t/rjM2QWTzMQYEbLLDQJRNSL/++mvzvJ0Xli2KKy/xzW9CuTdFYpOzuNbzYsQbeAPnhRdeMMORf/XVV6YccNuwaWZiy3R8971d/p2bCNs8rfO0L1jmPJVT+2Ld/q1zO/JiktMYcP8RAz5ejNtliGl58cbme+7lzG7OGZ/fyO3idvN0jLKb1tplwt6+saXl8S6u6QcS8nqmZZnjTQ5PaZ3LaXzznxietjeb6fP3ffjwYbzzzjumKT/LMJt9kns59vT5/F3d6reWkPLBZrzjxo0zvycGG9wmDRo0wIYNG+L8fnZeY7vJ5M1zFW+cNm7c2BwH2XSV53s2X7cD1YT+/tnU03272Dff2DR+y5Ytpgkwb7Zxf/G4w8CKTeH5f1y47djcmsEhrw14fmS+2YSVYmv+a+PvnDcH3G/SuDfV5DUDg9HYcL/wmOOt4694j/rkScDjHUoe7HhXinepE3OhaZ88eAfbmaeLZN7R44GPeDf0iy++MP0M2D6ed/9vFZCyz5B7fwjn5+O6u8sLeuc82hdtPAjz7h9PULEFejy58WTMi3bnQI8HZ/bHYL835xOf+7ag2wkEE8LOKz/P+aTMfHobtzm3sz0Hkzv3dZ7uuCd0v94KL6xYrlmeb5V33olmPxtPnO/Ae6rZ5R1yBnd27R2594+xy0pceYlvfp1/Z87bNa5gNLH4WbzL7S4+nzVz5kxTW8j+cM7icyPHG1jmGcC7i0/5Z00zjwHs1xdbjYhzeWSN3dy5c82FJvvmMBBw/t58P7sW8MUXX/T4mezz5a1aqdiwLxFrGXlccO6Xxz5SxJpP4sUuf4v2emdcV7JkyTiDhYS83u6Lx/W8IeC8n1jO7DzZaWN7T+f8J4an7c3+3KzV5UW/XQtFDCq8KSHlg/uNc8Bx4XGG/exYu8Sbpuz751w778wur85z3SYV9p1kbSNvdnTs2NFlPrvE4DGYvylnnBPS3hcMxNyDdPtc7NynMzbMI2vk2b+aASTL5+jRo02Z8NQH1B3P/7FdM7AmmAv7DMbVD5H7hcdz55YD4h9UkydBgXcIebB67rnnTLDljh28GSDFxp6Al7UxzjxNKuqMd8HYiZwncHaYju3OvI2DQ7DpGy/iqlatGmOJayJg4uc4p7cv4nlXlHfR4ppsnIGwfQHrjDUVvBiwn7e3h/u2YFDA2p/Eim2beMLmJOR+J5MDu3gb9wnLDu94e9on7oMqxPYet7Nf3fEik4EXL77jCkr4ubwQ4IWpp8+NK8izLwzdg9ipU6fGKON8fwawngL/hOQ3tt9ZXL/NxOJn8SaMc55508BuyhQXbhv37cI8c6AHb9ZYx4bbkvuVA1o4i23ib2dses2Agxf3zvniDSD+9nkTzLlpH2steKHJGgEuDGCcm4Xywpu/R17s8YaCp3LmqXbS29gckMcfHq+cffzxx6ac20EWAwkOWsHv7xyUs2aLzcA5cE9cEvJ61uxye7kfd+1JsjmwhnP+OcgIa85tDFi5T5j3uH6rieHp983j3Pvvv+/Vz0ls+WBtKJsUMzBkkBDXhOJ2E1EeY5NafI+L8cWaT/ftYTfV5z7njTGee5zZx5n43rBmmeU2YoDHG1vTpk0zNW/Owb0nrMFjcOg+OIvNvpntPjCTOzYVdW9aLv5BNXkSFDgSFu8+s60/+xWwyQObGzG448mHBz3eKeXJ2xM2c2jYsKG5A8Y7kzw4spmG3XTN+UKP7eDZj42jB/IAzuCH6/v16xeVjoEBL8g+//xzM1oiLwS4jqMY8iKFI4u9+uqr5qTIiy9eQLB5Bu+YOd8Rji9elPECjf0I2N+GJ12+Ly8oePBv06aNGW2Pd0zZFp99ADjCmD26JkfZ451YG/9m236O2MgLTl5sTpw40Yw0llixbRNPeOHJbcTmMgxAeWLk6JqffvopvI3b4f/+7/9MjQabDfFzeaHMmhDWjjKPLE9xSYr9ao/mxtexbPEEztod3njgBQcvFNhclzUwHNmOTYZ5h5jBPi+YFi5caGqWY7tQ4GtY1llmWAZ4p5ZBNe9iu2MTL/52eDHA78caH3439quxA/H45JfNt1jrx4sG5p0XJ7wg5l18b2MZ5ueyryVv/jDAYxPrLFmy3PK1DJ7Z5InbheWfvynmlzUSzv1O+J14rPj222/NTRJ+N9Y6JDSo91SeGFTz5g0/l800eYODQQLF1SybeBzj753HgV69epnjFPv6MHBkbZhzzQ9rYdinh/uP24ZBjPvvnM39uG/t5mT8fgyAWLvBAN15JNaE4rHFDmZZA8amjxzNkXjhaF88clvwO/Hzefxi+eJ34WiTDJTskRGJo7CyNoT7kWWRvwkey7hv+Ft0xhpbNp+z+0kl5PXc37zJx2Ml/+Zxi7U2bNnBPkzOF77sL83fEc8dHD2R/Zi4T1i2WKPlbdxW3O88N/A4yu/AcySb83pbfMsHjyE8D/N4zhYC7Af39ttvm98Qz6ex4TGM5wwGJTzOJSWOFMmbWtzvLBPcr/wOPM56GwNcHj+5r/h57API3yibz/M379wnnS0u+HvmMYnli9gPjqOZ8hzGYxGPDzzG8fhgN8u1sX85j4W8HuC5l01QObYAr31YPtyxvPCYw/NEXKN88jzH97pVICg+4uuRX0S8iSN/cUStwoULm7l6OOoW5/cZNGiQy6h/nkbe40iQjz32mBlxiqNatm/f3ow25jy6Jiei7dSpk5kDiO/NkQ05x9lbb73lMhkvJ87lyGqch8d9TjjOQTZw4EAzZxLzaM8V9Oqrr5pRFt3nU4svjnzI78lR6Pi+nLOOIx46T77LNJxDifnhaG8cpZDzgbnPk8fJ4zmqJOc54+hq3FbctrGNruk+qqSn0Urj2iaenD171vHMM8+Y0cM4xxPnDON8Rt4eXdPGOaE45xCf53fmaJJPPfWUy4hz9nxSntzufnXftsR5kjjSGvcl35PlmuXPeeQ1fseXX37ZzLfGfcryW6VKFcdrr73mcb47Z/a8WNy+nN+Kc5FxAl73uSGJo889+OCD5nvZc0fxuyU0v5wj7r777jPbmfNQcV9xrrbY5slz52n72aPgjh071mX9xx9/bOa64+hwHMGSI8nGZ3RNlv9evXqZ/PG1lStXNiN4enotJzLmMYbbJL7z5Lnz9L6cJ49zq/HzuU87d+5svo/7yKe3mifPLs8ccdPTXIz2HGf2yH5LlizxmIbfg79Hez5GlhfuR07S7f67nzt3riO+YpvD0tOopRxFkGWd8zqyfPHY62kUTOLvlqOhsmxnyZLFjES4Z8+eGOn4W+H7Jfb1xFGAOZqmXeaZb/e56IjHAR5TuD+5X7lPYtveCRld09PvhLi/7bk/ud84aqs9crHzsTm2cpnQ3+CtygdHMOY6zvtobyuWa54bbuX11193ZM+ePcaokwkdXdO9bHqaC5fHMZ5veK7iZ/KYxjl443PuSSgebzliL0cntecf5XHYfc5f7kt+1pQpU1xGQOU5ldua25zb86WXXvI4Fx5HHy9ZsqTZVkzL7cO5bWMbaXbWrFnm8+y5Em81h97GjRsTvQ0k6YTwH18FmCIiIhI/rHFm7RVrJe0BMCTxWNvEmhrWJsXWn0z8A/uTsiadgwRxnseUhrVt/O2zeaU3BqDxZosJNtdkSxvxP2quKSIi4mfYTJN9dthMjX3ROF0GJ3Bm80AFeN7BUQnZH5HNecW/8bfAZswcZIpNXm/VZDnYsD8omwX7U4DHPpLsfnE7zbUlaakmT0RExM+wXx37K3JgBvYDZJ8l9vPq3r17koxeKRIINa/sO8r+jey/Jr4PPFmzyBYG4p8U5ImIiIiIiASRlFXfLSIiIiIiEuQU5ImIiIiIiAQRBXkiIiIiIiJBRKNrJgFODsnhfjk5pTrIi4iIiIiIN3D2Ow5ExFFn4xppVkFeEmCAp5GfREREREQkKRw8eBAFCxaM9XkFeUmANXj2xs+SJQv8oWbxzJkzyJ49e4qbW0a8Q2VIVH7EV3T8EZUh8aVIP7uOjoiIMJVJdrwRGwV5ScBuoskAz1+CPM6zxLz4Q+GUwKMyJCo/ouOPBCqdwyQYy8+tuoT5T05FRERERETktinIExERERERCSIK8kRERERERIKIgjwREREREZEgoiBPREREREQkiCjIi8OkSZNQrFgxpEuXDlWqVMEvv/ySfHtGREREREQkERTkxeLzzz/HK6+8gtdeew2bN29G7dq18eCDD+LAgQOJ2c4iIiIiIiLJQkFeLMaPH4/OnTvj2WefxR133IG3337bTDw4efLk5NkzIiIiIiIiiaDJ0D24du0aNm7ciH79+rmsb9y4MdasWYOAc+EEUh9cB0SWAW5cAa5GADeuAmFpgUL3AmFpfJ1DERERERHxEgV5Hpw6dQo3b95Enjx5XNbz8bFjx2Kkv3r1qllsERER5v/IyEiz+Jrj7+XI+m2X2J8PLwFHlU5AqcZAztLJmjcJDCzHDofDL8qzBB6VH1H5ER2DJFBF+tk1UHzzoSAvDiEhIS6PuYPd19Ho0aMxdOjQGOvnzJmD9OnTw+euXwKy9UQqx3Ug9L9dzu8ReROhEQcRcvoGsGSDtfB7ps2Cm9mKITJDDib0bd7FL7Ds37hxA2FhYR5/AyIqP6Ljj/grncMkmMrP5cuX45UuxMGcS4zmmhkyZMDcuXPxyCOPRK3v3r07tmzZgpUrV96yJo/9986cOYMsWbL4RcTPvGTPnh2hoR66YR78DSG7fkDI6rdivrZuf6BObysolBTrlmVIROVHdPwRP6VzmART+WGcwbycO3cuzjhDNXkepEmTxkyZsGTJEpcgj48ffvjhGOnTpk1rFncsCP5QGIh3HmLNT5F7raXREJZk4OCvwMYZwLbPEbpyNMCF6g0EMuYEyrUE0mdP9u8gflyGRFR+RMcf8WM6h0mwlJ/45kFBXix69OiBDh06oGrVqqhRowamTZtmpk/o0iX2vm1BgQWnyH3WUqk9sOot4O9l1nPLR1j/z38lOn3jEUD1LkCq1L7Jr4iIiIiIuFCQF4snnngC//77L4YNG4ajR4+ifPnyWLhwIYoUKYIUo1gda4m8Cez4Fvjy6ZhpFg+0FluZpsATM4HQVMmaVRERERERsahPXhK1lc2aNest28omZ1vi06dPIzw83LvVzFvnAN887/m52r2A+gPVly9IJFkZkhRB5UdUfkTHIAlUkX52DRTfOEM1eZJ4FdpYi+3iKeD9esDZA8Av46zFlLL0QK9dQDrfB7wiIiIiIsFOQZ54DwdleWU7sPcX4OPm0etvXAbGFIp+3PwtoOoz2vIiIiIiIklAQZ54X7HawJBz1t+s1fv4IeDM3ujn579qLfTieiDiEFCkFhCWhpORqImniIiIiMhtUJAnSStbYaD7FuvvQxuAWY8Bl89EP//ePdF/ZykARByOfhwSCrR4B6jUQYGfiIiIiEg8KciT5FOwKtB3nzVa59n9wIRKrs87B3jkiAS+e8labG1mAyXqA6nTJU+eRUREREQCjII8SX6cXiG8eHSTTrp5wwryshaymnZOqgHcvBrztXPaRv99Z0sgdQbg/BHg3heBLPmA3OWAg+uAAlWi5+4LCbEmeef/XIiBJmsK7cciIiIiIkFCQZ74h1RhQPb/5iDMUQJ4/UT0c+ynx8DPveZvx7zov/9ZkfDPzFEKqNMLOLEDOLQReGAUkK9CYr+BiIiIiIhfUJAn/o+1be41f0e3AWHprIFdZrVK3Pv+u9t1nr+pdVyfr/UqULY5kPdu4PolICwtkDp9Ir+EiIiIiEjyUJAngSnf3db/uUq7Bn82Nsdks9Cb16ObbV6/DGyZBWz4CGjzGXDuIDCjWeyfseota4kNm4vy/SOvA/Vft4LAtROt9ew3yOdWjQeqPA1kznO731hEREREJF5CHA62hRNfzESfXCIjI3H69GmEh4cjNDTU19nxX+wXyIDt2kXg8Ebgs8e9994ZcwENhwAZclrBYIl6CCQqQ6LyIzr+SKDSOUyCqfzEN85QTZ6Ic79ALmySWbqJ5xpC1s5dOg2sfAPYMN31uVRpgJvXPG/PiyeBb1+MuT51RuD6Revv+gOBwvcBRWtqn4iIiIhIoinIE0kINv1k08vm460lPq6cA8YUBorVAfb+7PqcHeDRshExX9tomDVyKINP28V/rYnj02b2/HkXTgIXjgPrPwBy3wHc+TBweJMVPLLifkZz4Pj26PSFawAH1kb3Q6zbN+F9D0/tASZWiX4cXgJ4eiGQOS9wcqcVFO9bDVRsa32fTLkS9v4iIiIiEm9qrpkE1FxTbl1IjgLrJgOr3wGK1QX2rrz1a9JltQJGZwzgdnybNBv8rtZAumzA+veRZBhUZikAlGoEZC+adJ8jPuVvTV0ksKj8iMqQ6BgUTc01RfwZ5/RjLR0Xd9u/BNa+B9zXDVg5Fjj5p7XePcCjxAZ4d7Swpov4/Rsgf0Xg+O9AuUeBpYOd8jE3Ye9Z6F6gzSxg92Jg3guuz3E+Q45Q6s59YJsnZlojmmr+QhEREZFEU01eElBNnnjVuDLAhWNA7juBR6ZaI4tycvd1U4D9q4FDG4C2nwGhYbc/zx+bc7J5JYO+rzpb6xoMQmSBarixcixSp0mLkNYzotMd22o192Sz0Njez1PAxtFPF/QENs+0BrvxpEonoPnbCviCgGpiROVHdAySQBXpZ61R4htnKMjz4cZPqYVTAk+SlyGObPrzWGDlmLjTDT6roC8A6RgkKj+iY5AEqkg/u46Ob5zh+5yKiHBgmXr9rRFNe/8d+/YYyj6C04EhWaOXk7u0/UREREScaHRNEfEvGXNawR6bpB78FTi1C9jzE/Dnd9bzC3q4pn/vHuv/4vWAB8YAuctGP8c5D9lkNG2mZPwCIiIiIr6lIE9E/BObRBS5z1rYP+/oVmBqnejnW06xRijlevpnOTCpetzv2Ww8ULYZkCmP9VgDvIiIiEgQUpAnIoGBg8q4T1DPefdYW3fjKjD/lVuPNspaQPeaQA5Y03d/dG0fJ7wfUwQoWd+aQuJqBLB7qTWnYd67gIrtgB/6WWnDiwNh6YATO6zRSR/7EIi8AXz3MrD1s4R9v7afAxGHrIF0ts621lXqADw8Efj9K+C77kCjoUCGcKBEAyCd7/v7ioiIiH/SwCtJQAOvSLDxt07HCcKRPH+dbAVKRzYh6Lx+CkiVGv4soMuP+JzKj6gMiY5B0TRPnogIhaay5hzk4u74H8Dk+zxvp3ueA4rVAUJCgWyFgTP7gK//D8iUG4g4DGQtCGTODxxY4/n1lTsCO+ZZtYGc+y9veasGzhEJXDoF5CkP/LUA+LwdUKoJcHgDcOlf1+ao87pEPy54jzVhvPv8hcNzAmkyAXX7ADVespq5ioiISIqmmrwkoJo8CTZBfyd94wwgfThQqDqQ+b/+eonFAWOSYxuxmeroglbQ6G7gCSAsLfxF0JcfSVIqP6IyJL4U6WfnMNXkiYjEFwd28ZbkOgGkyQgMPgNcPAVs+wL4sX/0cyNyAxXaAg+/Z9VkioiISIqigVdERAJ9yokaXa3l3CHgrXLWeg7eYg/gQs3ftoJZjSgqIiIS9Hxf5ygiIt7BfoKDzwJ1+sR8jqOPcjJ5exJ5zh8oIiIiQUk1eSIiwYQ1dfVfs5ZtHKTFYTXZ/PIZ13QM+LIXs5p0Fq3pq9yKiIhIElCQJyISrO5uHf13+VbAoY3AB/Wj153ZC8xoav39fyuB/BWTP48iIiLidQryRERSioJVoieUP3sAePuu6Oem1bX+H3Rag7WIiIgEOPXJExFJiTj3HwO+fgeAwk5zBQ4LB+a0A27e8GXuRERE5DYoyBMRScnSZQWeWQQ0GBy97q/5wNQ61px/IiIiEnAU5ImICFC7h1Wzl6OUtTVO/AEMy26NxPnrFCDypraSiIhIgFCQJyIi0V7aAPQ/5LpFfuhrNeOc0Rw4sw+4fllbTERExI9p4BUREXGVNnP0AC27FgOf/TdK575fgHcquKYdeBIIS6MtKCIiEohB3rBhwzyuz5o1K8qUKYPGjRsjNFQVgyIiQaV0Yyvg2zIb2DbHGpXz9D/Rz4/IBWTMBXTfCqTJ6MucioiISEKDvG+++cbj+rNnz+Lw4cMoV64cfvzxR+TOnTu+bykiIoGiYltrsV08BXzUFDi1E7h4EhiV31rfZTWQp5w1KbuIiIj4d5C3efPmWJ87evQonnzySQwYMAAffPCBt/ImIiL+KmNOoNtvwJRawLHt0eun1IwetbPHX0CaDD7LooiISErllT55+fLlw4gRI9ChQwdvvJ2IiASKLqus/zndwvcvA5s/tR5fOQeMymf93WQ0UOpx3+VRREQkhfFaJ7oCBQrgxIkT3no7EREJJOyT/fBEq//e66dcn/qxP3JOLIFQTslw9bzPsigiIpJSeC3I27p1K4oWLeqttxMRkUCVKnV0sPfQRNfnRhe0pmEQERER3zfXjIiI8Lj+3LlzWL9+PXr27Ilnn33Wm3kTEZFAD/Yqd0BkxXY4c3Ancnx0r7XenoahTDOg+Xggc16fZlNERCTFBnnZsmVDSCyjpXH9888/jz59+ngzbyIiEiQcGXMhctAZhG6YDizsZa3cucBaXlwP5Crt6yyKiIikvCBv+fLlHtdnyZIFpUqVQqZMmbyZLxERCUbVngMqdQBWjAJWv2Ote+8eoPXHQLmWvs6diIhIyuqTV7duXY9LpUqVkjzA27dvHzp37oxixYohffr0KFGiBAYPHoxr1665pDtw4ABatGiBjBkzImfOnHj55ZdjpNm+fbvJN9+Hg8VwkneHw+GSZuXKlahSpQrSpUuH4sWLY8qUKUn6/UREUpTU6YBGw6x+e7nKWuvmdgSGZAWWj/Z17kRERFJOkHfp0iW8+OKLJjDihOecF+/UKdcR1JLKX3/9hcjISEydOhV//PEH3nrrLRN4cV4+282bN9GsWTNcvHgRq1atwpw5c/DVV1+ZvoLO/QobNWqE/Pnzm36E7777LsaNG4fx48dHpdm7dy+aNm2K2rVrm7kB+RkMFvleIiLiZS+uA9p9Gf145Rgr2Lt2UZtaREQkkUIc7tVYsejduzcmTZqEdu3amRqu2bNn4/7778fcuXPhC2PHjsXkyZPxzz//mMeLFi1C8+bNcfDgQRPEEQO9Tp06makd2KyU6fv374/jx48jbdq0Js2YMWNMsHfo0CHTt7Bv37747rvv8Oeff0Z9VpcuXczooWvXro1X3hhMZs2a1QxKw8/1NQbIp0+fRnh4OEI5zLmIypD42zGI8+wd+g34sEn0umeXAQWrJFs+xT/pHCYqQ6JjUMLjjHhf8X/99deYPn06pk2bhgkTJmDBggWYN2+eqUHzBX4xXjDYGICVL18+KsCjJk2a4OrVq9i4cWNUGjbVtAM8O82RI0dMk1A7TePGjV0+i2k2bNiA69evJ8M3ExFJgRj8Fb4X6LUnugnnB/WBN+8AburYKyIikiQDr7CGjE0YbdWqVUNYWJgJkAoVKoTk9Pfff5vatzfffDNq3bFjx5AnTx6XdNmzZ0eaNGnMc3Ya97n87NfwOfb58/Q+fHzjxg3TPDVfvnwx8sNAkov7dBO8+8jF15gHVtj6Q14kMKkMSbKVnww5gBfWAr9ORujiAcD5I8DwnIjkwCx3PKQdkQLp+CMqQ6JjULT4Xs/HO8hjjR0DJpcXh4WZ4CexhgwZgqFDh8aZhn3nqlatGvWYQeUDDzyA1q1bx5iXz9MUD7ywcF7vnsZurZrQNM5Gjx7t8XucOXPmtraPNwvD+fPnzfdQc01RGZKAOAaVfgIhBeoj/KMaCIEDoRyYBcCpF/fwYJy0GRa/onOYqAyJjkHReD71apDHkzP7tzk3dbxy5Yrpr8bRLJ2bdcZXt27d0KZNmzjTONe8McCrV68eatSoYZqNOsubNy/WrVsXI8hiE0u7Zo5p7Fo9G/vr0a3SMKDNkSOHxzyyn1+PHj1cavJYu8maRH/pk8cAlflRkCcqQxIwx6DwcDgGnYZjx7cI/bKTWZXzvZKIfP1fIET9i1MKncNEZUh0DIrGmMSrQd5TTz0Voyarffv2uB2c5oBLfBw+fNgEeJza4KOPPopxocDAb+TIkTh69GhUk8rFixeboJSvsdNwtExOq2DXSjIN+/HZwSTTfP/99y7vzTSsTUydOrXHvPEznINfG/PoL0EV950/5UcCj8qQ+Kz8lH8EyJQLmNHMPAx9owgw4LB2SAqi44+oDImOQZb4nkfjPbqmL7EGjwOmFC5cGJ988glSpUoV9Rxr3uzmpBUrVjQ1chx5kyO5seaxZcuWpv+ePVhLmTJlUL9+fRPs7d6926QZNGhQ1FQLnEKBA7g8//zzeO6558xALKyt5GiirVq1ild+NbqmBBuNbid+UX7+/Rt4t7L1d53eQP2B2jEpgI4/ojIkOgYl4eiaDKK2bduGy5cve5xDj88l1cAerEnbs2cPli1bhoIFC5qaOnuxMfDjiJ+c3qFmzZp4/PHHTYDHefBs3CBLliwx0yWwZq5r166mmaVzU0sOvrJw4UKsWLHCBI3Dhw83o4nGN8ATEZEkkqME8Pwv1t8/jwV+Gq5NLSIicjs1eTNmzMDEiRNNvzfnmjQ7AKxevTpeeeWV227CGQxUkyfBRnfSxa/Kz56lwMz/brzd3QZ4dKp2UBDT8UdUhkTHoCSsyeMceb169YoR4BHX9enTJ8ZgKCIiIl5XsiHQ4y/r721zgGGeB8USERFJqeId5O3cuRP33ntvrM/fc889+PPPP72VLxERkdhlyQf83wrr78gbwJCswIWT2mIiIiIJCfIuXrwYNcl3bHM2sG+eiIhIsshfCXjxt+jH40oC12P2GxcREUlp4h3klSpVCmvWrIn1+VWrVpk0IiIiySZXGaDz0ujHI/MCZ/ZrB4iISIoW7yDvySefxMCBA80omu62bt1qpiFgGhERkWRV6B6g38HoxxyU5fQ/2gkiIpJixXsy9FdffRWLFi0yE4s3bNgQZcuWNZOTsh/e0qVLzbQFTCMiIpLs0mUBBp8FvugA/Pk9MIFNOdcDuUprZ4iISIoT75q81KlTm/nqRo4ciaNHj5qRNKdMmWL+5jo+xzQiIiI+ERICPDETqNndevzePcC+VdoZIiKS4sS7Jo8YxHGqBC4iIiJ+qeFQYN9q4PAGYEYz4JkfgcKxjw4tIiISbLwwK62IiIif1eg99xNQppn1+MMmQMRRX+dKREQk2SjIExGR4NT2M6BkI+vv8WWBqxd8nSMREZFkoSBPRESC15OfA2mzWn+PLgCc+MvXORIREUlyCvJERCR4haYCeu+JfjypOnD1vC9zJCIi4t9B3urVq3H16lXv5UZERMTbwtIArx2Lfjy6IPDhg9rOIiIStG4ryHvwwQdx+PBh7+VGREQkKaROb82jd9/L1uMDa4C37lKtnoiIBKXbCvIcDof3ciIiIpLUo242Hg40HWc9PnfAqtXbPEvbXUREgor65ImISMpS7TngybnRj7/tClw44csciYiI+E+QN3XqVOTJk8d7uREREUkOpRsDQ84B+SpYj8eVAo79rm0vIiJB4baCvCeffBIZM2b0Xm5ERESS0/M/A1kKWn9PqQmsept9EbQPREQkoKm5poiIpGyv/g5kK2z9vXQwMDQbEBnp61yJiIgkmoI8ERFJ2TggyyvbgT57o9cNyw7s+cmXuRIREUk0BXkiIiKUIRzouTN6W8x8FPhpuLaNiIik3CDv5s2bmDdvnrfeTkREJPllzus6cfov44DNM7UnREQkZQV5f/31F/r06YP8+fPj8ccf906uREREfDlxOkfefPR96/G3LwK7Fmt/iIhIcAd5Fy9exIcffoiaNWuiXLly2LRpE0aOHIkjR454P4ciIiK+cPfjQNnm1t+ftQbeqaCRN0VEJPiCvLVr16Jz587ImzcvJk6ciEcffRQhISGYMGECnn32WeTMmTPpcioiIpLcnnBqqnlmH/DZEwr0REQkeIK8O++8E23btjWTn69bt87U3vXs2dMEeSIiIkGJ5zg23ez4vfV494/WFAsiIiLBEOTt2bMHderUQb169XDHHXckba5ERET8SbE6QJPR0Y/HFPFlbkRERLwT5O3duxdlypTBCy+8gIIFC6JXr17YvHmzavJERCRlqNEV6Djf+vvKWeCDhr7OkYiIyO0FeQUKFMBrr71mavQ+/fRTHDt2zAy8cuPGDcyYMQO7du2K71uJiIgEpmK1gVbTrb8PrQeGZAVuXPN1rkRERG5/dM369etj5syZZjRNDsCybNkylC1bFnfffXdi3k5ERCRw3PUYUP/16MeT7vVlbkRERLw7T162bNnQtWtXbNiwwQzEcv/999/O24mIiASGOr2A7lutv0//bdXoXb/i61yJiIgYYUigc+fOYcmSJdi3b5/pj1e8eHE0aNAAFStWNFMpiIiIpAjZiwLVngd+m2o9HpkHeO2YNZm6iIhIoAR5bKLZrVs3REREuKzPmjUrpkyZgieeeMLb+RMREfFfTf8H1HoFGP/fqNMj8wKDz1pTL4iIiPh7c002x3z66afRsmVLM6rm5cuXcenSJdNUs0WLFujQoQO2bNmStLkVERHxN1nyA4NORz/mPHpXzvkyRyIiksLFO8h79913TYDHkTQrVKiAtGnTIl26dKhcuTI++eQTPPTQQ2quKSIiKVNoKqD7tujHYwpb/fQib/oyVyIikkLFO8hbvXo1nn/++Vif79KlC1atWuWtfImIiASW7EWAgSdc1w0LB65d9FWOREQkhYp3kMfpEkqXLh3r83zu8OHD3sqXiIhI4AlLCww5B3T4JnrdqPzAzRu+zJWIiKQw8Q7y2P+OzTNjw+abV65o+GgRERGUqA+8uiN6QwzPATgc2jAiIuJ/o2v++OOPZiRNT86ePeutPImIiAS+rAWAAUesmjxaMRqoN8DXuRIRkRQgQUFex44d43ye8+aJiIjIf9JkBHrtBsaVAla+AZR+AChQWZtHRET8o7lmZGTkLZebNzWKmIiIiItMuYH2X1l/v18P2DpHG0hERPwjyBMREZFEKtkQCPuvX/s3z1vTK9y4ps0pIiK+ba7JufA8YR+9MmXKoGzZst7Ml4iISHAZeBxYPBBY8671+KtngNYfW3PsiYiI+KImr3v37h6Xxx57DOXKlUOLFi1w/vx5JLWrV6+iYsWKpv/fli1bXJ47cOCAyUfGjBmRM2dOvPzyy7h2zfVO6fbt21G3bl2kT58eBQoUwLBhw+BwG/Fs5cqVqFKlihlNtHjx4pgyZUqSfy8REUkBGo8Aevxl/f3n99Y8eoc3+jpXIiKSUoO8M2fOeFwYdP36668mwBo6dGjS5hZAnz59kD//fyOVOWF/wGbNmuHixYtmUvY5c+bgq6++Qs+ePaPSREREoFGjRub169evx7vvvotx48Zh/PjxUWn27t2Lpk2bonbt2ti8eTMGDBhggkW+l4iIyG3Lkg94eTMQlt56/H59YOlQTbEgIiL+0ycvNDQU99xzD9588018//33SEqLFi3C4sWLTWDmjut37NiBmTNnolKlSmjYsKHJ0/vvv2+CO5o1a5aZy2/GjBkoX748Hn30URPEMciza/NYa1e4cGG8/fbbuOOOO/Dss8/imWee8fiZIiIiiRJeHBh4DMiQ03q8ajzweXvg5nVtUBER8Z+BV0qWLIlDhw4hqRw/fhzPPfccPv30U2TIkCHG82vXrjWBm3MtX5MmTUxN48aNG6PSsKkmJ253TnPkyBHs27cvKk3jxo1d3ptpNmzYgOvXdfIVEREv6vM30HkJEF4C+Gs+MDynNSjLrsXazCIikjzz5MXl77//RsGCBZEUWMvWqVMndOnSBVWrVo0KyJwdO3YMefLkcVmXPXt2pEmTxjxnpylatKhLGvs1fK5YsWIe34ePb9y4gVOnTiFfvnwxPpuBJBebXXNoTy3ha8wDt6E/5EUCk8qQqPwkoQJVgS6rEDK2JEKuX7TWfdYakU/MAso0TfGFT8cfuV0qQxJM5Se++bjtII9fmn3X2PeNg54kxJAhQ27Zj49959asWWMCp/79+yd4Mnbmz3m9exq7mWZC0zgbPXq0x+/BPosMDv2hMHBQHH4PNq8VURkSHYP80PPbzH8Z1r2FDOsnIvTzdjjf6E1cLdMSKZnOYaIyJDoGRYvvQJfxDvJYK+YpyLlw4YIZ9OSBBx4wQVtCdOvWDW3atIkzDWveRowYYQZ3cW5mSazVa9euHT7++GPkzZsX69atixFksYmlXTPHNHatnu3EiRPm/1ulCQsLQ44cOTzmkcFnjx49oh4zIC1UqJDZZlmyZIE/nCC575gfBXmiMiQ6Bvm5B4cjslRdhH7WGpmX9DRLZLeNVj++FEjnMFEZEh2DojEm8WqQx4FIPGEQwznyOEhJQnGaAy63MmHCBBPo2diHjv3kPv/8c1SvXt2sq1GjBkaOHImjR49GNankYCwMDDkdgp2GA61wWgU247TTsB+f3YyTadwHkGEaBpSpU6f2mD9+hnsASgyo/CWoYpDnT/mRwKMyJCo/yah0Y+CZH4EPm5iHoROrAINOp9g59XT8EZUh0THIEt9r+RCH+yRxAYB98th/js1EOWcesTaRf7NGbuzYsTh9+rTpx9eyZUszVQKdO3fOTNxev359E+zt3r3bpBk0aFDUVAucQoEDuDz//PNmoBcOxMK+gLNnz0arVq3ilT/W5HGSeH6ev9TkcXuEh4cryBOVIdExKJBcOg38r1j04/teBhoPR0qic5ioDImOQQmPM+JdrcPas169ekUNKuKMH9K7d28zAqavpEqVCgsWLDATmNesWROPP/64CfCcpz7gBlmyZIkZBZQ1c127djXNLJ2bWjJ4XLhwIVasWGGCxuHDh5uaxPgGeCIiIl6TIRx4dUf04zUTgCm1tIFFRMQ7NXl2gDdt2jSPz7O2i0HUG2+8gZRONXkSbHQnXVR+/MCCXsD696Mfd1kF5L0LwU7HH1EZEh2DkrAm74cffsBTTz0V6/N8bv78+fF9OxEREUmIZuOAZ5zmz2ON3qJ+HAJa21FERBIX5LGvWuHChWN9nnPkeZq/TkRERLykcHVrABbbusnA0GzAtf/m1xMREUlIkJc+ffo4gzg+xzQiIiKShDjC5pBzQO+/gbT/NdUZlR+YXJNtG7XpRUQk/kEepyr49NNPY33+k08+QbVq1bRJRUREkkPGnECv3dGPj/8ODMsODMkK7FmqfSAikoLFe548DrzSqFEj09GPI2nak4dzRM3//e9/mDFjhplPTkRERJJJ6nRWrZ77VAsz/xsRusU7QJVO2h0iIilMvIO8evXq4b333kP37t3x1ltvmdFcODkpR3bhJOGci47zz4mIiIgPplpgsMdBWNhHz/Z9d2uhjt8Dxepo14iIpADxDvKIE4Q3b94cX3zxBfbs2QPOvlC6dGk89thjZuAVERER8aGQECvYu3ENmFARiDgc/dzHLYAKbYFHpvgyhyIi4m9BHhUoUACvvvpq0uRGREREbl9YGqDHDmsglkungHGlrPVbZwOFqgFVn9FWFhEJYvEeeEVEREQCTGgokCm3Vbv36H8Tqc9/1RqcZV5X4OoFX+dQRESSgII8ERGRlODux4En50Y/3jILGF0A2PuLL3MlIiJJQEGeiIhISlG6sVWr12lh9LqPm1s1e1NqARFHfZk7ERHxEgV5IiIiKU3Rmlawlylv9Lpj24HxZa2Aj8vB9b7MoYiIJHeQd/bsWXzwwQfo378/Tp8+bdZt2rQJhw87jeIlIiIi/q3XTuD1f4Fnl8V8bnpDK9ib3Ra4dtEXuRMRkeQaXXPbtm1o2LChmRR93759eO655xAeHo5vvvkG+/fvxyeffJLYvIiIiEhySxUGFKxi1ezRoY3AB07z3u5cCIzKb/3NgJDpRUQkuGryevTogU6dOmH37t1Ily5d1PoHH3wQP//8s7fzJyIiIsnJDvgGnQGyFnZ9bngOYHRh4PoV7RMRkWAK8tavX28mRfc0f96xY8e8lS8RERHx9fQLr263Ar5nf4pef/UcMDIP8NWz1qTrIiIS+EEea+8iIiJirN+5cydy5crlrXyJiIiIvyhY1Qr2nlkcvW77XGBELmD2k8DNG77MnYiI3G6Q9/DDD2PYsGG4fv26eRwSEoIDBw6gX79+aNWqVULfTkRERAJF4epWsDfgSPS6nQusZpy//TfZuoiIBF6QN27cOJw8eRK5c+fG5cuXUbduXZQsWRKZM2fGyJEjkyaXIiIi4j/SZLSCvbp9o9ct7GWNxrnjO8Dh8GXuRERSvAQPkZUlSxasWrUKy5YtM9MmREZGonLlymbETREREUlB6g2wlhVjgBWjrXVfdLD+77UHyKRuHCIiARHkcdqEokWLon79+mYRERGRFO7+ftZy8DdgeiNr3biSQKeF1sTrIiLi3801ixcvjlq1amHq1KlRE6GLiIiIoFA1YODJ6A0xoymw4g1tGBERfw/yNmzYgBo1amDEiBHInz+/GYhl7ty5uHr1atLkUERERAJHWBqrv17arNbjFaOsvnoftwD+mAdcPe/rHIqIBL0EB3nsfzd27FgzouaiRYvMACycN4//P/PMM0mTSxEREQks/Q8AraZHP977MzC3IzC6ILDrR1/mTEQk6CU4yLNx6oR69erh/fffx9KlS00zzo8//ti7uRMREZHAdddjVq1evYHW49x3Wv9/9rhVu7fqLSv4ExER3w68Yjt48CBmz56Nzz77DNu3bzdNOCdOnOjd3ImIiEjgq9vbWujXycAP/ay/lw6JTnNnS6D1DN5F9k0eRURScpA3bdo0zJo1C6tXr0aZMmXQrl07zJs3z4y4KSIiIhKne18ASj8AHNkEfOnUzWPHPGBoNqD7ViC7rilERJI1yBs+fDjatGmDd955BxUrVrytDxcREZEUKLyYtZRvZT0+vReY8N81xTsVrP8fmQZUeMJ3eRQRSUlBHgdcYX88EREREa9gwPfyFuCbLsDBX6113/wfsKg38PwvADJqQ4uIeDvI27ZtG8qXL4/Q0FDT/y4ud999d0I+X0RERMQK9Dr/N+rm8lHAyjeAK+cQ+s7dSNP4LaD6U7czXpyISIoS4nA4HLdKxODu2LFjZpoE/s2aPOeX2Y/5/82bN5HSRUREIGvWrDh37hyyZMni6+wgMjLSTFwfHh5u9p+IypDoGCR+LzISGJbddV2v3UCm3L7KkQQoXQdJMJWf+MYZ8arJ27t3L3LlyhX1t4iIiEiS4sXU4LNwfPEUQv78zlo3rpT1/8CT1qTrIiKS+CCvSJEiUX/v378f9913H8LCXF9648YNrFmzxiWtiIiISKKxpVDrj/Ev76If+AGh371orR9h3XhG4xHAfS9pA4uIuElwnSMnQGeVpTtWGfI5EREREa+r+KRVg+ds8UBrUnUuN29oo4uIJDbIs/veufv333+RMaNGvxIREZEkwiaaQ84BPf4CGjpNpE7Dc1jB3p/zgctntAtEJEWL9xQKjz76qPmfAV6nTp2QNm3aqOc42ApH4GQzThEREZEklSUfUOtVazn4GzC9UfRzn7eL/rv7NiC7upGISMoT7yCPo7jYNXmZM2dG+vTpo55LkyYN7r33Xjz33HNJk0sRERERTwpVs2r3LpwE3roTuHkt+rl37gbuehxo9b62nYikKPEO8j766CPzf9GiRdGrVy81zRQRERH/kSkX8Pp/ffaObgWm1rH+3v6FtdgK3Qt0WgCkivclkIhI8PfJGzx4sAI8ERER8V/5Kli1exXaxnzu4K9W/72fhrN5ki9yJyKS5BJ1G+vLL7/EF198gQMHDuDaNadmEQA2bdrkrbyJiIiIJN4jU4CH3gXOHgAckcBfC4Clg63nfhlnLc3GA/d01lYWkZRdkzdhwgQ8/fTTyJ07NzZv3oxq1aohR44c+Oeff/Dggw8mTS5FREREEiNVaiBHCSBnKaDWK1YN35NOzTcX9ABGFQTOHdL2FZGUG+RNmjQJ06ZNw8SJE82AK3369MGSJUvw8ssvm7nyRERERPxa6SZWsFent/X42nngrXLAe/cCB9cDVyJ8nUMRkeQN8thE054qgSNsnj9/3vzdoUMHzJ49+/ZyIyIiIpJc6g8Eev8T/fjkn8D0hsCYQtace7uXAFfOATeuAZE3geuXtW9EJDiDvLx585qJz6lIkSL49ddfzd979+410yskpQULFqB69eomuMyZM2fU3H3OAWiLFi3MwDB8nrWL7n0Gt2/fjrp165r3KFCgAIYNGxYj3ytXrkSVKlWQLl06FC9eHFOmTEnS7yUiIiI+kpGTqJ8D+h20+u9RmszW/7MeA8YUBkbkAoaFAyPzWsHfwj5AxBHtMhEJnoFX6tevj++//x6VK1dG586d8eqrr5qBWDZs2BAj6PKmr776yszDN2rUKJMHBmYM2JwnZG/WrBly5cqFVatWmUC0Y8eOJt2771oH7YiICDRq1Aj16tXD+vXrsWvXLjOxO4PCnj17RgWrTZs2NZ81c+ZMrF69Gl27djXv26pVqyT7fiIiIuJD6bIAlZ+yFvrtfWDVW0DE4Zhpf5tqLXT/AKBiWyBb4eTNr4hIHEIcCax+i4yMNEtYmBUfcpRNBlUlS5ZEly5dTD89b7tx44aZn2/o0KEmsPRk0aJFaN68OQ4ePIj8+fObdXPmzDFB3IkTJ5AlSxZMnjwZ/fv3x/Hjx5E2bVqTZsyYMSYIPHToEEJCQtC3b1989913+PPPP6Pem99r69atWLt2bbzyy2CSk8ezjyI/19e4v06fPo3w8HCEhia48lZEZUh0DBKf8atzWGQk8EUH4K/5safpuw/Y8CFw74tA6nTJmTsJhDIkASfSz8pPfOOMBOeUX84O8Ojxxx83I26yaWRSBHj2tAyHDx82n12pUiXky5fPjOT5xx9/RKVhAFa+fPmoAI+aNGmCq1evYuPGjVFp2FTTDvDsNEeOHMG+ffui0jRu3Njl85mGNZXXr19Pku8nIiIiAYAXeG1m/de88wCQKU/MNG8UBX4aBozMYzXtvHrBWn9T1xAi4mfNNbdt2xbvN7z77rvhbZyegYYMGYLx48ebWr0333zTBGxscsnI+tixY8iTx/Vgmz17dhN48jni/3ytM/s1fK5YsWIe34ePWZt46tQpE2C6YyDJxTnCdq719DXmgRW2/pAXCUwqQ6LyIzr+uGG/vR5/RT++fgkhE+9ByHm3vnqjC7g8dOS+00zW7nhoIhDi+1qBlEDnMAmm8hPffMQryKtYsaJpynirlp1Mw75x8cWgjU0w48K+c/aXee2116L6xX300UcoWLAg5s6di+effz7q890xz87r3dPY3ymhaZyNHj3a4/c4c+aMCQ59jduPo6Dye/hDNbMEHpUhUfkRHX/ioeMvUX+GXDmHHB9UjpEk5MQO4MQOhGydjdNPrUBklkIqXElM5zAJpvJjz2zglSCPg5EkhW7duqFNmzZxpmHNm/1l7rzzzqj1bHLJkS85oqY96ue6detiBFlsYmnXzDGNXatnY389ulUaNlHlpO+esJ9fjx49XGryChUqZGoS/aVPHgNU5scfCqcEHpUhUfkRHX8SKhyRg864ruJN43MHETqhgpXik/sR+fJWDdqSxHQOk2AqP87d5uJMF59EnCohKXCaAy63wukMGNTt3LkTtWrVMusYvLEfnZ23GjVqYOTIkTh69GhUk8rFixeb1/H1dpoBAwaYaRXs/oNMw358djNOpuHooc6YpmrVqkidOrXH/PEznPv52VgQ/KEwEAunP+VHAo/KkKj8iI4/XhBeFBh8Fnj7LpeADy2nWKN0SpLQOUyCpfzENw8JnkLhk08+ifP5p576b+hhL2JtGEe4HDx4sKkhY2A3duxY81zr1q3N/xwshTV9nJSdz3EUnF69epmpEOzatCeffNI0q+SImwz2du/ebaZkGDRoUFRTTH7OxIkTTc0cX8uBWKZPn66J3kVERMQ7eM3RfSvw4wBg3X9z8c7rYi1Upw9QsCpQuom2uIgkSoKDvO7du7s8Zo3apUuXTM1YhgwZkiTIIwZurJ5kEHf58mUzKfqyZctM1SmlSpXKTJbOOe1q1qxpJjtnUDdu3Lio9+Bwo0uWLMGLL75oaub4WgZzzk0tOfjKwoULzfx/7733nqnl4+ihmiNPREREvCY0FfDgG0DhGsCCHsClf6Of+/l/0X8/MhWoEHfXFhGR254nzxPWiL3wwgvo3bu3mW4gpdM8eRJs/G2OGAksKj+i8hMPvBybWgc4/jsvzwCH00B2pZoA93YB8twFZMqlAqVjkKTgc1hEPOfJS3BNnielSpUyk4q3b98ef/3lNJywiIiIiMSvCWeX6NE5jZ+GA7+MA3b/aC22B8YA1Z635u0TEfHAa0cHNpfkpOIiIiIi4gUNXgde2gRkL+a6/od+wLDswNS6wJVzwNFtwG/vA38tAFaOtSZhXzIIWDPRqiEUkRQnwTV53333nctjtvbkiJYcrIR94URERETES3KUALpvsf6+eh749BHg0Hrr8dEtwJjCnl+3+h3r/8WvWf+3mW0N5MK+gCIS9BIc5LVs2dLlMUelzJUrF+rXr48333zTm3kTEREREVvazMCzS62/100FFvXxvG3CSwCpMwDHt0evm/Pf9Az39wfq9rWah4pI0ApLTOdDEREREfGh6s9by60c2gB80CD68YrR1lK1M3D9spmrD4XvtR5nyQdE3gT+mg+c2gWEhgEHfgV2/RD9+qe+A4rXjfszL58FzuyzJnnPEH4bX1JEEssrA6+IiIiIiB/ifHtDzll/s4+e3Xxzw/ToNPt+AX625h++pU8eiv670XAgY05g3gu3fl35x4DfvwQemghUag9cOGG9Vs1HRfwjyGMfvC+//BLLly/HiRMnYtTsff31197Mn4iIiIh4w33drIW1dTsXAZ+3izv9A28A/+4GSjUGblwBvnCbC3nJ6/H/bAZ49F03a/Gk3CNA03FW8CciyT8Z+rRp01CvXj3kyZPH9MkTERERkQDB2rM7mkfX8Nlu3gBSxXFpyPQ3rwNr3wMO/gbsXGCtb/818O/f1nue2g0Uq2P1+ePInnuWAosHAkVqAjvmuU767u6Pb6yF0mS2aiHbzgFSp/PGtxZJURI8GTonApw5cyaaNm2adLkKcJoMXYKNv00EKoFF5UdUfsQjBowMAs/sB8o8ALzfALh0Kma6/JWAMk2BurEMNKNjkKSgc1hEUk2GzjctXrz47eZPRERERFKyVKmBMg9GP+7ztxX4sTZvzQTg9D7g2nngyGZrWT7SSvfCGiBPueg5ANWqTOT2g7whQ4Zg6NCh+PDDD5E+ffqEvlxEREREJPbA7+7HrYVBnGkeOhH4aWh0msn3xXwdRwdtNAwISxd3k1ORFCLBv4LWrVtj9uzZyJ07N4oWLYrUqVO7PL9p0yZv5k9EREREUiLW0IWlAWr3sJZrl4BR+Tyn5WihziOGOntsBpC3dpJmVSTgg7xOnTph48aNaN++vQZeEREREZHkkSZD9GAxrOWzm2nuWw3s+NYa1MUexdNJ6Jed4HG8zrafA6WbqLmnBKUEB3kLFizAjz/+iFq1aiVNjkRERERE4uLcD69oTWuhx6YDEUesZpuciH37l8BXnT2/x+wnXB8Xuheo1x8ILwFkzqdmn5KygrxChQrFOZKLiIiIiIjPZMkf/fddjyHyzkcQ8edyZDm5HqEMDgvfC3zycMzXHfw15np7kBeRYA/y3nzzTfTp0wdTpkwxffJERERERPxWSAhu5K0I3FkfsIfAt5t9nvgTuHwG2L0EOLMP+ONr19fag7w0GAwUvx/IWQpIndFa5wfD6Yt4LchjX7xLly6hRIkSyJAhQ4yBVziPhIiIiIiI38t9h/V/kf+CudYfWf8z4HunQnQ6ju7pPMKn7bEPgfKtkiOnIkkb5L399tsJfYmIiIiISODIXtSq7Vs7Cfixf+zpvnzGWlpOBu5uo9o9Cdwgr2PHjkmTExERERERf1Kjq7W44+iek2sCJ/6wHs97wVoY6D0yRSN2SuAFeQcOHIjz+cKFC99OfkRERERE/BsHcOm6xgr2Nn0MfN/dWr9tjjWNQ++/gfTZfJ1LScESHORxsJUQ52Fr3dy8efN28yQiIiIi4v94TVylE1DuEWDjDGDJICDyBvBGkeg0BasBnRZYE7uL+GuQt3nzZpfH169fN+vGjx+PkSNHejNvIiIiIiL+L11WoGZ34L6XgZ/HAsudrokP/QaMyAXU6QPU6QWEpfVlTiWFSHCQV6GC00hD/6latSry58+PsWPH4tFHH/VW3kREREREAqtmr24fa2FTztP/ALPbAKd2AT//z1qyFga6bwFCU/k6txLEvDbBR+nSpbF+/XpvvZ2IiIiISGAHfDlKAN3WA4WqA0VrW+vPHQCGhQOjCwGXNPWY+ElNXkREhMtjh8OBo0ePYsiQIShVqpQ38yYiIiIiEvg6L7b+v/ivNcH6hWPA1Qjgf8Wi0ww4AqT5b6J1keQO8rJlyxZj4BUGeoUKFcKcOXNuNz8iIiIiIsEpYw6g106rGef8HsA/y6OfG5Xf+r/fAauPn0hyBnnLli1zCfJCQ0ORK1culCxZEmFhCX47EREREZGUJbw48NQ8IPKm1XTT2ZjCQEgq4P5+QJ3emnNPEiXBUdn999+fuE8SEREREZFoHHxlyDnr71N7gIlVrL8dN60ROp1H6aRsRYCXNgKpUmsrincHXhk9ejQ+/PDDGOu57o033kjo24mIiIiISM6SwKDTQOORQJWngbQemmye3Q8Mzwm8U8EavVPEW0He1KlTUbZs2Rjry5UrhylTpiT07URERERExK7Zu68b0OJtoP8BoOdOoFgd4KGJQPuvgNQZrO10Zh8wNBtw4aS2m3gnyDt27Bjy5csXYz375XGUTRERERER8YLMeYGO3wOVOwAlGwKvHQXafh79/LiSwPrpwJn9qtmT2+uTx1E0V69ejWLFnIZ8Bcw6ToguIiIiIiJJpMwDwOCzVk0eLegR/VzmfMAr29VnTxIe5D377LN45ZVXcP36ddSvX9+s++mnn9CnTx/07NlTm1REREREJClxpPu++4HPngAO/hq9/vxRq88e9T8EpM2s/ZBCJTjIYzB3+vRpdO3aFdeuXTPr0qVLh759+6J///5JkUcREREREXGWPhvQ+Ufrbw7C8tv7wKLe0c+PLmj9//IWq4YvdTptvxQkxMGZzBPhwoUL+PPPP5E+fXqUKlUKadOm9X7uAlRERASyZs2Kc+fOIUuWLL7ODiIjI01gHh4ebuY1FFEZEh2DJFDoHCYqQwn+0QBvlgYuug3K0uxNoGI7IHV6FaoAPgbFN85IdE4zZcqEe+65B+XLl1eAJyIiIiLiDxiI9N5j9dtztqAnMDKv9f+Nq77KnSQT34ejIiIiIiLi/X57nGidS8f50evXfwCMyA181kZbPIgpyBMRERERCWbFalvB3iPTotftWgQMyapavSClIE9EREREJCWo8IQV7D3+SfQ61uotGQTcvO7LnImXKcgTEREREUlJ7nwY6Lsv+vHqd6ypF7Z85stciRcpyBMRERERSWnSZ7dq9So8Gb1u3gtWE04uXz4DbP/SlzmU26AgT0REREQkpXpkshXstf/Kdf3vXwFfdbYCvon3ANev+CqHkhyToYuIiIiISJAp2dAK9s4dskbgZFC3brL13KldwMg81t+NRwA1ulmjd4rfCpiavF27duHhhx9Gzpw5zcR/NWvWxPLly13SHDhwAC1atEDGjBlNupdffhnXrl1zSbN9+3bUrVvXTOJeoEABDBs2DO7zwa9cuRJVqlRBunTpULx4cUyZMiVZvqOIiIiIiE9lLQg0HAI8OAbouQto/TGQKm3084sHAkOzASd3Am7X0OI/AqYmr1mzZihdujSWLVtmArS3334bzZs3x99//428efPi5s2bJk2uXLmwatUq/Pvvv+jYsaMJ4N59992oGeIbNWqEevXqYf369SZw7NSpkwkKe/bsadLs3bsXTZs2xXPPPYeZM2di9erV6Nq1q3nfVq1a+XgriIiIiIgkk8x5gHItreXgemD/amDpYOu596rFTH9Xa2uaBk7ILj4V4nCvxvJDp06dMkHWzz//jNq1a5t158+fNzV6S5cuRYMGDbBo0SIT9B08eBD58+c3aebMmWOCuBMnTpi0kydPRv/+/XH8+HGkTWvdkRgzZowJAg8dOoSQkBD07dsX3333Hf7888+oz+/SpQu2bt2KtWvXxiu/DCazZs2Kc+fOmc/1tcjISJw+fRrh4eEI1Y9OVIZExyAJIDqHicqQn7l8FphaGzh74NZpU2cEmv4PKFYHOP4HMLsN0HoGULaF9Xwq/69vivSz6+j4xhn+v2UB5MiRA3fccQc++eQTVK5c2QRoU6dORZ48eUyzSmIAVr58+agAj5o0aYKrV69i48aNpvaOadhU0w7w7DQM/Pbt24dixYqZNI0bN3b5fKaZPn06rl+/jtSpUyfjNxcRERER8SPpswGvbLf+PrkLOH8U2PgR8Mc3MdNevwh8+6LrurmdYqbrvBTIks9qKipeERBBHmvYlixZYvrkZc6c2UTRDPB++OEHZMuWzaQ5duyYWecse/bsSJMmjXnOTlO0aFGXNPZr+ByDPE/vw8c3btwwNYr58uWLkT8GklycI2w78ufia8wDK2z9IS8SmFSGROVHdPyRQKVzWBLKUdJaitYGWn0YtTrkp2HA3hVAvgoI2Tjj1u8zvWHUn47qXeBoMBgISwd/EOln19HxzYdPg7whQ4Zg6NChcaZh3znW1rFfXO7cufHLL7+YPnkffPCBaZ7J5+3Ai8GgO+4U5/XuaezWqglN42z06NEev8eZM2dMcOgPhYHNW/k9/KGaWQKPypCo/IiOPxKodA7zgUrdrIVqvO45zY0ryLLwBcARiTQHV0WtDlk3xSx0tVRznG80HghNBV+J9LPraObF74O8bt26oU2bNnGmYc0bB1uZP3++CZrstqeTJk0ytXsff/wx+vXrZwZfWbdunctrmZ5NLO2aOaaxa/Vs7K9Ht0oTFhZmmo16wuaePXr0cKnJK1SokKlJ9Jc+eQxQmR9/KJwSeFSGROVHdPyRQKVzmB/r9K35L6pu6tB6hH4Y3W0q7e75Zonsux9I65tr6kg/u45mTBKvdPAhTnPA5VYuXbpk/nffsHxsV1nWqFEDI0eOxNGjR6Nq9hYvXmz639n99phmwIABZloFNuO007Afn92Mk2m+//57l89hmqpVq8baH4+f4dzPzzl//lAYiIXTn/IjgUdlSFR+RMcfCVQ6hwWIwtWtufpuXLP6+H3zf2Z16BtFrOebjQfu6Zyiy09oPPPg+5zGAwMvRs+cEoGjXHLqg969e5vpDjhtAnGwlDvvvBMdOnTA5s2b8dNPP6FXr15mKgS7Nu3JJ580wRhH3Pz999/xzTffYNSoUaYWzm6KyZE09+/fb9ZxhM0PP/zQDLrC9xIRERERkSQWlgao8AQw8CSQyqqYMRb0AIbl1Px8wRLksbaPg6xcuHAB9evXN7VqnAvv22+/RYUKFUyaVKlSYcGCBWYCc06U/vjjj6Nly5YYN25c1PtwuFE28eR0CXwP9vNjMOfc1JKDryxcuBArVqxAxYoVMXz4cEyYMEFz5ImIiIiIJHew9/pJ4JXfgWz/1eZFXgfGlQaObAFuRA98KAE4T16g0Tx5Emz8bY4YCSwqP6LyIzoGiVdwQnankTij9DsApMuaIs5hEfGcJ8/3ORUREREREbmVQvcAr58CGrqNaj+msFWzJ1EU5ImIiIiISGBIlRqo9Yo1QMuDY6PXT6sLDMkKXI3fFAPBTkGeiIiIiIgEnur/Bww+C9TpE71udEHgt/eR0inIExERERGRwMQR8uu/BvQ/HL1uYS+rVm/3UqRUCvJERERERCSwpc1kNeGs2zd63axWwP41SIkU5ImIiIiISHCoN8BqwhmW3no87wXg+hWkNAryREREREQkuJpwDjwGVO0MnNkHjMwDbJyBlERBnoiIiIiIBJ8Gg6L//r47MLYkJ75DSqAgT0REREREgk/6bFY/vY7zrccXTwLDsqeIQE9BnoiIiIiIBK9itYHe/0Q/ZqC3ewmCmYI8EREREREJbhlzAP0OABlzWY9nPWZNs3DxFIKRgjwREREREQl+6bICvfcAD78XvW5sCeDUbgQbBXkiIiIiIpJyVGoP9Nkb/XhiVeDSaQQTBXkiIiIiIpKyZAi3BmUpfJ/1+H/FrOabkTcRDBTkiYiIiIhIyvT0QqtmzzYsHHA4EOgU5ImIiIiISMqdOP3h94CXNkWvGxr40ywoyBMRERERkZQtRwmg7/7/HjisaRauXUSgUpAnIiIiIiKSPpvrfHqj8gOOwKzRU5AnIiIiIiJiz6fnFOiFDs+BVCd3INAoyBMREREREXEO9HrujHqY/fMWATcYS5ivMyAiIiIiIuJXMucFBp+FY+7TuJCvBjJygJYAoiBPRERERETEXUgIHI99iKunTyMjAouaa4qIiIiIiAQRBXkiIiIiIiJBREGeiIiIiIhIEFGQJyIiIiIiEkQU5ImIiIiIiAQRBXkiIiIiIiJBREGeiIiIiIhIEFGQJyIiIiIiEkQU5ImIiIiIiASRMF9nIBg5HA7zf0REBPxBZGQkzp8/j7CwMISGKq4XlSHRMUgCh85hojIkOgZFs+MLO96IjYK8JMCAigoVKpQUby8iIiIiIik83siaNWusz4c4bhUGSqLuOh45cgSZM2dGSEiIX0T8DDgPHjyILFmy+Do7EoBUhkTlR3T8kUClc5gEU/lh6MYAL3/+/HG20FNNXhLgBi9YsCD8DQumPxROCVwqQ6LyIzr+SKDSOUyCpfzEVYNnUwctERERERGRIKIgT0REREREJIgoyEsB0qZNi8GDB5v/RVSGRMcgCSQ6h4nKkOgYlHAaeEVERERERCSIqCZPREREREQkiCjIExERERERCSIK8kRERERERIKIgrwUYNKkSShWrBjSpUuHKlWq4JdffvF1liSJjR49Gvfccw8yZ86M3Llzo2XLlti5c2eMyTSHDBliJtNMnz497r//fvzxxx8uaa5evYqXXnoJOXPmRMaMGfHQQw/h0KFDLmnOnDmDDh06mDlbuPDvs2fPuqQ5cOAAWrRoYd6D7/Xyyy/j2rVrSbgFxNvlKSQkBK+88krUOpUficvhw4fRvn175MiRAxkyZEDFihWxceNGlR+5pRs3bmDgwIHmuoXnpuLFi2PYsGGIjIxU+RGPfv75Z3ONwesZnqvmzZvn8ry/na+2b9+OunXrmrwUKFDAlG/m0escEtTmzJnjSJ06teP999937Nixw9G9e3dHxowZHfv37/d11iQJNWnSxPHRRx85fv/9d8eWLVsczZo1cxQuXNhx4cKFqDRjxoxxZM6c2fHVV185tm/f7njiiScc+fLlc0RERESl6dKli6NAgQKOJUuWODZt2uSoV6+eo0KFCo4bN25EpXnggQcc5cuXd6xZs8Ys/Lt58+ZRzzMt1/G1fA++V/78+R3dunVTGQgAv/32m6No0aKOu+++2xw/bCo/EpvTp087ihQp4ujUqZNj3bp1jr179zqWLl3q2LNnj8qP3NKIESMcOXLkcMyfP9+Unblz5zoyZcrkePvtt1V+xKOFCxc6XnvtNXM9w9Dmm2++cXnen85X586dc+TJk8fRpk0bkxfmiXkbN26c1/eugrwgV61aNVNwnZUtW9bRr18/n+VJkt+JEyfMgW/lypXmcWRkpCNv3rzmwGe7cuWKI2vWrI4pU6aYx2fPnjU3CHijwHb48GFHaGio44cffjCPeeOA7/vrr79GpVm7dq1Z99dff0UdfPkavtY2e/ZsR9q0ac3BTvzX+fPnHaVKlTInqrp160YFeSo/Epe+ffs6atWqFevzKj8SF96UfOaZZ1zWPfroo4727dur/MgtuQd5kX52vTNp0iTz2cyDbfTo0SYYZF69Sc01gxirh9k8pnHjxi7r+XjNmjU+y5ckv3Pnzpn/w8PDzf979+7FsWPHXMoG56Ji8wG7bLDsXL9+3SUNmzqUL18+Ks3atWtNk4Xq1atHpbn33nvNOuc0fA1fa2vSpIlpGuHcfEv8z4svvohmzZqhYcOGLutVfiQu3333HapWrYrWrVub5uKVKlXC+++/r/Ij8VKrVi389NNP2LVrl3m8detWrFq1Ck2bNtXxRxJsr59d7zANP9t57mqmOXLkCPbt2+fVPRzm1XcTv3Lq1CncvHkTefLkcVnPxyzwkjLwxlaPHj3MiZMHH7L3v6eysX///qg0adKkQfbs2WOksV/P/3kR547rnNO4fw7fk++tcui/5syZg02bNmH9+vUxnlP5kbj8888/mDx5sjnuDBgwAL/99pvpl8KLmqeeekrlR+LUt29fc2OybNmySJUqlbmOGTlyJNq2bavjjyTYMT+73uH/RYsWjfE59nPsi+otCvJSAHZCdb/od18nwatbt27Ytm2buRPqjbLhnsZT+sSkEf9x8OBBdO/eHYsXLzYDNsVG5Uc84QAZrMkbNWqUecyaPA5ywMCPQZ7Kj8Tl888/x8yZM/HZZ5+hXLly2LJlixn0ibUjHTt2VPmRRAnxo+sdT3mJ7bW3Q801gxhH9eFdMPfakhMnTsS40yDBiSNFsenU8uXLUbBgwaj1efPmNf/HVTaYhk1+OZpUXGmOHz8e43NPnjzpksb9c/iebBqhcuif2KyE+5mj8YaFhZll5cqVmDBhgvnb+a6jM5UfoXz58uHOO+902Rh33HGHGXWOdPyRuPTu3Rv9+vVDmzZtcNddd5kRDF999VUzyq/KjyRUXj+73vGUhp9D3r4mUpAXxFg9zIu0JUuWuKzn4/vuu89n+ZKkx7tCrMH7+uuvsWzZshjV/3zMA41z2eABjhfydtlg2UmdOrVLmqNHj+L333+PSlOjRg3TrIbNsWzr1q0z65zT8DV8rY01RGy6xc8Q/9OgQQMzxDPvoNsLa2batWtn/uaQ5io/EpuaNWvGmLKF/auKFCli/tbxR+Jy6dIlhIa6Xp7yhrU9hYLKjyREMT+73mEaTvngPK0C07Cm2r0Z523z6jAu4rdTKEyfPt2MDPTKK6+YKRT27dvn66xJEnrhhRfM6E0rVqxwHD16NGq5dOlSVBqONMU0X3/9tRnGt23bth6HFC5YsKAZ/pzDAdevX9/jkMIcXp+jTHG56667PA4p3KBBA/MefC++p6ZQCCzOo2uSyo/ENe1GWFiYY+TIkY7du3c7Zs2a5ciQIYNj5syZKj9ySx07djRD2dtTKPAclTNnTkefPn1UfiTWkaA3b95sFoY248ePN3/b04X50/mKI3lyCgXmgXlhnrJkyaIpFCRx3nvvPTNnUZo0aRyVK1eOGkZfghcPcp4Wzp1n41C9gwcPNkMLc3jfOnXqmAOOs8uXL5uDU3h4uCN9+vTmYHbgwAGXNP/++6+jXbt2Zp4XLvz7zJkzLml4oOWw2HwPvhff03n4YAm8IE/lR+Ly/fffm4sdHls4bc+0adNcnlf5kdjwwpvHGs7tmi5dOkfx4sXNHGhXr15V+RGPli9f7vGap2PHjn55vNm2bZujdu3aJi/M05AhQ7w+fQKF8B/v1g2KiIiIiIiIr6hPnoiIiIiISBBRkCciIiIiIhJEFOSJiIiIiIgEEQV5IiIiIiIiQURBnoiIiIiISBBRkCciIiIiIhJEFOSJiIiIiIgEEQV5IiIiIiIiQURBnoiIiIiISBBRkCciIpJAnTp1QkhISIxlz5492pYiIuJzYb7OgIiISCB64IEH8NFHH7msy5Url8vja9euIU2aNMmcMxERSelUkyciIpIIadOmRd68eV2WBg0aoFu3bujRowdy5syJRo0ambTjx4/HXXfdhYwZM6JQoULo2rUrLly4EPVeM2bMQLZs2TB//nyUKVMGGTJkwGOPPYaLFy/i448/RtGiRZE9e3a89NJLuHnzpksQ2adPHxQoUMC8d/Xq1bFixQrtTxGRFE41eSIiIl7EoOyFF17A6tWr4XA4zLrQ0FBMmDDBBGt79+41QR6Ds0mTJkW97tKlSybNnDlzcP78eTz66KNmYfC3cOFC/PPPP2jVqhVq1aqFJ554wrzm6aefxr59+8xr8ufPj2+++cbUMG7fvh2lSpXSfhURSaFCHPYZSEREROLdJ2/mzJlIly5d1LoHH3wQJ0+exLlz57B58+Y4Xz937lwTCJ46dSqqJo8BG/v0lShRwqzr0qULPv30Uxw/fhyZMmUy6xjAMVCcMmUK/v77bxPIHTp0yAR4toYNG6JatWoYNWqU9qaISAqlmjwREZFEqFevHiZPnhz1mM0l27Zti6pVq8ZIu3z5chN07dixAxEREbhx4wauXLlimmPydcQmmnaAR3ny5DEBnR3g2etOnDhh/t60aZOpKSxdurTLZ129ehU5cuTQPhURScEU5ImIiCQCg7OSJUt6XO9s//79aNq0qamZGz58OMLDw7Fq1Sp07twZ169fj0qXOnVql9dxtE5P6yIjI83f/D9VqlTYuHGj+d+Zc2AoIiIpj4I8ERGRJLRhwwZTc/fmm2+avnn0xRdf3Pb7VqpUyQzCwpq92rVreyGnIiISLDS6poiISBJiE0wGee+++64ZPIX97Nin7naxmWa7du3w1FNP4euvvzYDuqxfvx5vvPGGGahFRERSLgV5IiIiSahixYpmCgUGX+XLl8esWbMwevRor7w35+ljkNezZ08z9cJDDz2EdevWmWkaREQk5dLomiIiIiIiIkFENXkiIiIiIiJBREGeiIiIiIhIEFGQJyIiIiIiEkQU5ImIiIiIiAQRBXkiIiIiIiJBREGeiIiIiIhIEFGQJyIiIiIiEkQU5ImIiIiIiAQRBXkiIiIiIiJBREGeiIiIiIhIEFGQJyIiIiIiEkQU5ImIiIiIiCB4/D831EGWtwATiQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "first half: -3,925 second half: -5,032\n" + ] + } + ], + "source": [ + "cnt_cpu = np.bincount(fr_cpu, minlength=N)\n", + "cnt_cuda = np.bincount(fr_cuda, minlength=N)\n", + "cum = np.cumsum(cnt_cuda.astype(np.int64) - cnt_cpu.astype(np.int64))\n", + "\n", + "fig, ax = plt.subplots(figsize=(9, 3.6))\n", + "ax.plot(cum, color='C1', linewidth=1.2)\n", + "ax.axhline(0, color='gray', linewidth=0.5)\n", + "ax.set_xlabel('Frame')\n", + "ax.set_ylabel('cumulative CUDA - CPU')\n", + "ax.set_title(f'Cluster-count difference accumulating over {N:,} frames '\n", + " f'(final {cum[-1]:+,})')\n", + "ax.grid(alpha=0.25)\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "half = N // 2\n", + "print(f'first half: {cum[half - 1]:+,} second half: {cum[-1] - cum[half - 1]:+,}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e7b0da01-1787-4260-890d-533accda0e17", + "metadata": {}, + "outputs": [], + "source": [] } ], - "source": [ - "cnt_cpu = np.bincount(fr_cpu, minlength=N)\n", - "cnt_cuda = np.bincount(fr_cuda, minlength=N)\n", - "cum = np.cumsum(cnt_cuda.astype(np.int64) - cnt_cpu.astype(np.int64))\n", - "\n", - "fig, ax = plt.subplots(figsize=(9, 3.6))\n", - "ax.plot(cum, color='C1', linewidth=1.2)\n", - "ax.axhline(0, color='gray', linewidth=0.5)\n", - "ax.set_xlabel('Frame')\n", - "ax.set_ylabel('cumulative CUDA - CPU')\n", - "ax.set_title(f'Cluster-count difference accumulating over {N:,} frames '\n", - " f'(final {cum[-1]:+,})')\n", - "ax.grid(alpha=0.25)\n", - "plt.tight_layout()\n", - "plt.show()\n", - "\n", - "half = N // 2\n", - "print(f'first half: {cum[half - 1]:+,} second half: {cum[-1] - cum[half - 1]:+,}')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.15" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + "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.11.15" + } + }, + "nbformat": 4, + "nbformat_minor": 5 + }