├── .ipynb_checkpoints └── cmb-checkpoint.ipynb ├── GaussianRealizationsMinimalNotebook.nb ├── README.md ├── SpectrumNormalization.nb └── cmb.ipynb /.ipynb_checkpoints/cmb-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Generating realizations of a 2d Gaussian random field $\\phi(x)$, with some simple analysis run on the position space results. Inspired by the [Cosmic Microwave Background (CMB)](https://en.wikipedia.org/wiki/Cosmic_microwave_background); see [my website](https://garrettgoon.com/gaussian-fields/) for more background information.\n", 8 | "\n", 9 | "TODO:\n", 10 | "\n", 11 | "- Extend to higher dimensions\n", 12 | "- Some systematic errors seem to still exist w/ theory doing increasingly poorly and over-predicting correlations as `power` increases towards 2. Need to find/understand, test more" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": 1, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "import numpy as np\n", 22 | "import matplotlib.pyplot as plt\n", 23 | "import emcee\n", 24 | "import scipy.stats as stats\n", 25 | "import corner\n", 26 | "import time\n", 27 | "\n", 28 | "import scipy.stats as stats\n", 29 | "from scipy.optimize import minimize\n", 30 | "from scipy.special import gamma" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "Create a class for generating the power spectrum. Spectra are characterized by an amplitude and power-law exponent: $\\langle \\phi(k)\\phi(-k)\\rangle'=\\frac{\\texttt{amplitude}}{k^{\\texttt{power}}}\\equiv P(k)$, corresponding in position space to $\\langle \\phi(x)\\phi(0)\\rangle\\propto \\frac{\\texttt{amplitude}}{x^{d-{\\texttt{power}}}}\\equiv G(x)$ in $d$-dimensions. \n", 38 | "\n", 39 | "Important parameters:\n", 40 | "\n", 41 | "- `amplitude`: amplitude of spectrum, defined above\n", 42 | "- `power`: power-law exponent, defined above\n", 43 | "- `dimensions`: number of spatial dimensions (can only use `dimensions`=2, currently)\n", 44 | "- `size_exponent`: grid size is 2**`size_exponent`\n", 45 | "- `max_len`: maximum separation of data points collected when forming binned correlation function\n", 46 | "- `min_len`: minimum separation of data points collected when forming binned correlation function\n", 47 | " \n", 48 | "Methods: \n", 49 | "\n", 50 | "- `generate`: generate realization of desired power spectrum, perform simple fit as sanity check\n", 51 | "- `spectrum_plot`: visualization of generated spectra\n", 52 | "- `hist`: histogram of generated values (should be normally distributed; mostly a sanity check)\n", 53 | "- `bin_pair_data`: pair data separated by less than distance `max_len` and bin to find correlation function. Produce some summary plots, perform simple linear-regression, and compare fit and theory to results.\n", 54 | "- `bayes`: simple bayesian/MCMC analysis on `amplitude` and `power` parameters using flat priors\n", 55 | " \n" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 2, 61 | "metadata": { 62 | "variables": { 63 | "fit": "

NameError: name 'fit' is not defined

\n", 64 | "theory": "

NameError: name 'theory' is not defined

\n", 65 | "{-1*correlator_lr.slope:.5f": "

SyntaxError: invalid syntax (, line 1)

\n", 66 | "{-1*spectrum_lr.slope:.5f": "

SyntaxError: invalid syntax (, line 1)

\n", 67 | "{-self.power:.5f": "

SyntaxError: invalid syntax (, line 1)

\n", 68 | "{self.power-self.dimensions:.5f": "

SyntaxError: invalid syntax (, line 1)

\n" 69 | } 70 | }, 71 | "outputs": [], 72 | "source": [ 73 | "class CmbPy():\n", 74 | " \"\"\"\n", 75 | " A class for generating 2D power specta and some analysis of the results.\n", 76 | " \"\"\"\n", 77 | " def __init__(self, amplitude=1, power=1.75, size_exponent=5, dimensions=2):\n", 78 | " self.amplitude = amplitude\n", 79 | " self.power = power\n", 80 | " self._size = int(2 ** size_exponent)\n", 81 | " self.dimensions = dimensions\n", 82 | " \n", 83 | " \"\"\"\n", 84 | " Writing P(k)=amplitude/k^{power}, then corresponding position-space correlator is\n", 85 | " G(x)=_x_amplitude/x^{dims-power} with _x_amplitude as below.\n", 86 | " \"\"\"\n", 87 | " self._x_amplitude_numerator = self.amplitude * gamma(1 - (self.power / 2))\n", 88 | " self._x_amplitude_denominator = (np.pi * (2 ** self.power)) * gamma(self.power / 2)\n", 89 | " self._x_amplitude = self._x_amplitude_numerator / self._x_amplitude_denominator\n", 90 | " \n", 91 | "\n", 92 | " def _theory_xspace(self, params, x):\n", 93 | " amplitude, power = params\n", 94 | " \"\"\"\n", 95 | " The theoretical position-space correlator, given the model parameters. \n", 96 | " Only valid for 2d right now. Fourier transform to position space is\n", 97 | " unambiguous for 2 > power > .5. Used for fitting. Might be missing some\n", 98 | " (2 * np.pi / self._size) factors?\n", 99 | "\n", 100 | " \"\"\"\n", 101 | " numerator = amplitude * gamma(1 - (power / 2))\n", 102 | " denominator = (np.pi * (2 ** power)) * gamma(power / 2)\n", 103 | " return numerator * (x ** (power - 2)) / denominator\n", 104 | "\n", 105 | " \n", 106 | " def generate(self):\n", 107 | " \"\"\"\n", 108 | " Generating the spectrum.\n", 109 | " \"\"\"\n", 110 | " # Creating a grid w/ each point pulled from std normal.\n", 111 | " gaussian_seed = np.random.normal(\n", 112 | " size=[self._size for _ in range(self.dimensions)])\n", 113 | " gaussian_seed_fourier = np.fft.fft2(gaussian_seed)\n", 114 | " # Numpy's fft algorithm automatically indexes with negative values on right half\n", 115 | " # positive on left half, as desired.\n", 116 | "\n", 117 | " # Generating the fft momenta indices and their norms.\n", 118 | " kvector = np.fft.fftfreq(self._size) * self._size\n", 119 | " kgrid = np.meshgrid(kvector, kvector)\n", 120 | " # include 2 pi / N factors\n", 121 | " knorms = np.sqrt(kgrid[0] ** 2 + kgrid[1] ** 2)\n", 122 | "\n", 123 | " # Create the desired power spectrum with the k=0 divergence regulated to zero.\n", 124 | " if self.power > 0:\n", 125 | " knorms[0][0] = np.inf\n", 126 | " # 2 pi / N factors included here\n", 127 | " power_spectrum = self.amplitude * ((knorms * (2 * np.pi / self._size)) ** (-1 * self.power))\n", 128 | " power_spectrum_sqrt = np.sqrt(power_spectrum)\n", 129 | " # Multiply by the transformed white noise to get the realization of the spectrum.\n", 130 | " fourier_realization = gaussian_seed_fourier * power_spectrum_sqrt\n", 131 | "\n", 132 | " # Create the power spectrum.\n", 133 | " # https://bertvandenbroucke.netlify.app/2019/05/24/computing-a-power-spectrum-in-python/ is useful resource for this\n", 134 | " # We need 1/N factors to match conventions for continuous case.\n", 135 | " fourier_amplitudes = (np.abs(fourier_realization) ** 2) / ((self._size) ** self.dimensions)\n", 136 | " \n", 137 | " # Flatten out and bin.\n", 138 | " fourier_amplitudes_flat = fourier_amplitudes.flatten()\n", 139 | " knorms_flat = knorms.flatten()\n", 140 | " k_bins = np.arange(.5, self._size // 2 + 1, 1) \n", 141 | " k_vals = 0.5 * (k_bins[1:] + k_bins[:-1])\n", 142 | " binned_means, _, _ = stats.binned_statistic(knorms_flat,\n", 143 | " fourier_amplitudes_flat,\n", 144 | " statistic='mean',\n", 145 | " bins=k_bins)\n", 146 | " \n", 147 | " \n", 148 | " \"\"\"\n", 149 | " In order to perform a power-law linear regression fit on the binned values,\n", 150 | " we need to take a logarithm which requires all binned values to be positive,\n", 151 | " while some are negative at large k, presumably due to low statistics.\n", 152 | " As a bit of a fudge, we isolate the terms in binned_means which are positive.\n", 153 | " \"\"\"\n", 154 | " \n", 155 | " binned_means_first_neg_index = 0\n", 156 | " for index, num in enumerate(binned_means):\n", 157 | " if num < 0:\n", 158 | " binned_means_first_neg_index = index\n", 159 | " break\n", 160 | " if binned_means_first_neg_index:\n", 161 | " binned_means_positive = binned_means[:binned_means_first_neg_index] \n", 162 | " k_vals_positive = k_vals[:binned_means_first_neg_index]\n", 163 | " else:\n", 164 | " binned_means_positive = binned_means\n", 165 | " k_vals_positive = k_vals\n", 166 | "\n", 167 | " # Simple linear regression. Need 2 pi / N factors here.\n", 168 | " spectrum_lr = stats.linregress(np.log(1 / (k_vals_positive * (2 * np.pi / self._size))),\n", 169 | " np.log(np.abs(binned_means_positive)))\n", 170 | " \n", 171 | " def spectrum_lr_fit(k):\n", 172 | " return np.exp(spectrum_lr.intercept) / ((k * (2 * np.pi / self._size)) ** (spectrum_lr.slope))\n", 173 | "\n", 174 | " # Plotting.\n", 175 | " fig, axs = plt.subplots(2)\n", 176 | " x_fit = np.linspace(k_vals_positive[0], k_vals_positive[-1], num=100)\n", 177 | " y_fit = spectrum_lr_fit(x_fit)\n", 178 | " axs[1].loglog(x_fit,\n", 179 | " y_fit,\n", 180 | " linestyle='-',\n", 181 | " color='r',\n", 182 | " label='linear-regression')\n", 183 | " axs[0].plot(k_vals_positive, binned_means_positive)\n", 184 | " axs[0].set_xlabel('$k$')\n", 185 | " axs[0].set_ylabel('$P(k)$')\n", 186 | " axs[1].loglog(k_vals_positive, binned_means_positive)\n", 187 | " axs[1].set_xlabel('$k$')\n", 188 | " axs[1].set_ylabel('$P(k)$')\n", 189 | " axs[1].set_title(\n", 190 | " f'$P^{{fit}}(k)\\\\approx {np.exp(spectrum_lr.intercept):.5f}\\\\cdot k^{{{-1*spectrum_lr.slope:.5f}}} \\\\quad P^{{theory}}(k)\\\\approx {self.amplitude:.5f}\\\\cdot k^{{{-self.power:.5f}}} $'\n", 191 | " )\n", 192 | " fig.suptitle(\n", 193 | " f'Actual values: (amp,power)=({self.amplitude:.5f},{self.power:.5f})')\n", 194 | " plt.tight_layout()\n", 195 | " plt.legend()\n", 196 | " plt.show()\n", 197 | "\n", 198 | " # Transform back and take the real part to get the spectrum.\n", 199 | " self.realization = np.real(np.fft.ifft2(fourier_realization))\n", 200 | "\n", 201 | " # Imaginary parts are from numerical errors; they're very small.\n", 202 | " im_to_re_ratio = np.imag(np.fft.ifft2(fourier_realization)) / np.real(\n", 203 | " np.fft.ifft2(fourier_realization))\n", 204 | "\n", 205 | " print(\n", 206 | " 'Sanity check: ratio of imaginary to real components in generated data:'\n", 207 | " )\n", 208 | " print(\n", 209 | " f'Average ratio: {np.mean(im_to_re_ratio)} Standard dev.: {np.std(im_to_re_ratio)}'\n", 210 | " )\n", 211 | "\n", 212 | " def spectrum_plot(self):\n", 213 | " \"\"\"\n", 214 | " Plotting the spectrum.\n", 215 | " \"\"\"\n", 216 | " if hasattr(self, 'spectrum'):\n", 217 | " fig, ax = plt.subplots()\n", 218 | " ax.imshow(self.realization,cmap='inferno')\n", 219 | " fig.suptitle('Realization')\n", 220 | " ax.set_xlabel('x')\n", 221 | " ax.set_ylabel('y')\n", 222 | " plt.show()\n", 223 | " else:\n", 224 | " print('Run generate to create spectrum first')\n", 225 | "\n", 226 | " def hist(self):\n", 227 | " \"\"\"\n", 228 | " Histogram of generated values.\n", 229 | " \"\"\"\n", 230 | "\n", 231 | " if not hasattr(self, 'spectrum'):\n", 232 | " return print('Run generate to create spectrum first')\n", 233 | "\n", 234 | " data = self.realization.flatten()\n", 235 | " std = np.std(data)\n", 236 | " self.std = std\n", 237 | " \n", 238 | " _, ax = plt.subplots()\n", 239 | " ax.hist(data, bins=100, density=True)\n", 240 | " ax.set_ylabel('counts')\n", 241 | " ax.set_xlabel('$\\phi$')\n", 242 | " ax.set_title('Distribution of generated points')\n", 243 | " # plot fit\n", 244 | " x = np.linspace(-5 * std, 5 * std, 100)\n", 245 | " y = stats.norm.pdf(x, 0, std)\n", 246 | " ax.plot(x, y, label=f'Normal(0,{std**2:.5f})')\n", 247 | " plt.legend()\n", 248 | " plt.show()\n", 249 | "\n", 250 | " def bin_pair_data(self, min_len=1, max_len=None):\n", 251 | " \"\"\"\n", 252 | " Collect all independent pairs of data points separated by less than max_len\n", 253 | " and bin them by distance, rounding to nearest integer. \n", 254 | " Can also specify a minimum separation using min_len, taken as 1 by default\n", 255 | " \n", 256 | " This is a bottleneck, use progress counter, timer. \n", 257 | " Don't think there's any native way to to this in numpy.\n", 258 | " The number of independent pairs for an N x N grid scales as O(N^4)\n", 259 | " which makes this take forever, hence the min_len and max_len params. \n", 260 | " \n", 261 | " Plot various aspects of data\n", 262 | " \"\"\"\n", 263 | " # Dynamically choosing max_len, if not specified.\n", 264 | " if max_len == None:\n", 265 | " max_len = int(self._size / 10)\n", 266 | "\n", 267 | " print('Collecting pair data...')\n", 268 | "\n", 269 | " # Put distance and field values in two separate arrays\n", 270 | " distances, fields = [], []\n", 271 | " \n", 272 | " start = time.time()\n", 273 | " prog = 20\n", 274 | " for row_1 in range(self._size):\n", 275 | " if (row_1 + 1) % int(self._size / 5) == 0:\n", 276 | " print(f'{prog}% complete')\n", 277 | " prog += 20\n", 278 | " for col_1 in range(self._size):\n", 279 | " for row_2 in range(row_1 + int(min_len / np.sqrt(2)), min(self._size, row_1 + max_len + 1)):\n", 280 | " for col_2 in range(col_1 + int(min_len / np.sqrt(2)), min(self._size, col_1 + max_len + 1)):\n", 281 | " dist = np.sqrt((row_1 - row_2) ** 2 + (col_1 - col_2) ** 2)\n", 282 | " distances.append(dist)\n", 283 | " fields.append(self.realization[row_1][col_1] * self.realization[row_2][col_2])\n", 284 | " fields = np.array(fields)\n", 285 | " distances = np.array(distances)\n", 286 | " print('Data collection time:', f'{time.time()-start:.5f}', 'seconds')\n", 287 | " print('Independent data pairs analyzed:', f'{len(distances):.3e}')\n", 288 | " \n", 289 | "\n", 290 | " x_bins = np.arange(min_len-.5, max_len+1, 1)\n", 291 | " x_vals = 0.5 * (x_bins[1:] + x_bins[:-1])\n", 292 | " binned_corr, _, _ = stats.binned_statistic(distances,\n", 293 | " fields,\n", 294 | " statistic='mean',\n", 295 | " bins=x_bins)\n", 296 | " binned_corr_std, _, _ = stats.binned_statistic(distances,\n", 297 | " fields,\n", 298 | " statistic='std',\n", 299 | " bins=x_bins)\n", 300 | " binned_corr_count, _, _ = stats.binned_statistic(distances,\n", 301 | " fields,\n", 302 | " statistic='count',\n", 303 | " bins=x_bins)\n", 304 | " \n", 305 | " \n", 306 | " \n", 307 | " # Histograms of counts and stds\n", 308 | " fig, axs = plt.subplots(2)\n", 309 | " axs[0].bar(x_vals, binned_corr_count)\n", 310 | " axs[0].set_xlabel('x')\n", 311 | " axs[0].set_title('Points per bin')\n", 312 | " axs[1].scatter(x_vals, binned_corr_std)\n", 313 | " axs[1].set_xlabel('x')\n", 314 | " axs[1].set_ylabel('$\\\\sigma$')\n", 315 | " axs[1].set_title('Std. of $\\\\phi(x)\\\\phi(0)$ values per bin')\n", 316 | " plt.tight_layout()\n", 317 | " plt.show()\n", 318 | " \n", 319 | " \"\"\"\n", 320 | " Simple linear regression.\n", 321 | " \n", 322 | " Some of the large x data can have negative correlations again,\n", 323 | " presumably due to low statistics. For the log fit, \n", 324 | " we need to throw out this part of the data to avoid\n", 325 | " imaginary numbers, as before. Obviously a bit of a fudge.\n", 326 | " \"\"\"\n", 327 | " \n", 328 | " binned_corr_first_neg_index = 0\n", 329 | " for index, num in enumerate(binned_corr):\n", 330 | " if num < 0:\n", 331 | " binned_corr_first_neg_index = index\n", 332 | " break\n", 333 | " if binned_corr_first_neg_index:\n", 334 | " binned_corr_positive = binned_corr[:binned_corr_first_neg_index] \n", 335 | " binned_corr_std_positive = binned_corr_std[:binned_corr_first_neg_index] \n", 336 | " binned_corr_count_positive = binned_corr_count[:binned_corr_first_neg_index] \n", 337 | " x_vals_positive = x_vals[:binned_corr_first_neg_index] \n", 338 | " else:\n", 339 | " binned_corr_positive = binned_corr\n", 340 | " binned_corr_std_positive = binned_corr_std\n", 341 | " binned_corr_count_positive = binned_corr_count\n", 342 | " x_vals_positive = x_vals\n", 343 | " print(binned_corr_positive)\n", 344 | "\n", 345 | " # Simple linear regression (treating all errors as equal).\n", 346 | " correlator_lr = stats.linregress(np.log(1 / x_vals_positive),\n", 347 | " np.log(binned_corr_positive))\n", 348 | " \n", 349 | " self.lr_fit = {\n", 350 | " '_x_amplitude': np.exp(correlator_lr.intercept),\n", 351 | " 'power': self.dimensions - correlator_lr.slope\n", 352 | " }\n", 353 | "\n", 354 | " \n", 355 | " def correlator_lr_fit(x):\n", 356 | " return np.exp(correlator_lr.intercept) / (x**(correlator_lr.slope))\n", 357 | "\n", 358 | " \n", 359 | " fig, axs = plt.subplots(2)\n", 360 | " axs[0].errorbar(x_vals_positive,\n", 361 | " binned_corr_positive,\n", 362 | " yerr=binned_corr_std_positive / np.sqrt(binned_corr_count_positive),\n", 363 | " marker='.',\n", 364 | " color='k')\n", 365 | " axs[1].errorbar(x_vals_positive,\n", 366 | " binned_corr_positive,\n", 367 | " yerr=binned_corr_std_positive / np.sqrt(binned_corr_count_positive),\n", 368 | " marker='.',\n", 369 | " color='k')\n", 370 | " \n", 371 | " # Plotting the fit.\n", 372 | " x_fit = np.linspace(x_vals_positive[0], x_vals_positive[-1], num=100)\n", 373 | " y_fit = correlator_lr_fit(x_fit)\n", 374 | " axs[0].plot(x_fit,\n", 375 | " y_fit,\n", 376 | " linestyle='-',\n", 377 | " color='r',\n", 378 | " label='linear-regression')\n", 379 | " axs[1].loglog(x_fit,\n", 380 | " y_fit,\n", 381 | " linestyle='-',\n", 382 | " color='r',\n", 383 | " label='linear-regression')\n", 384 | " axs[0].plot(x_vals_positive, binned_corr_positive)\n", 385 | " axs[0].set_xlabel('$x$')\n", 386 | " axs[0].set_ylabel('$\\\\langle \\\\phi(x)\\\\phi(0)\\\\rangle\\equiv G(x)$')\n", 387 | " axs[1].loglog(x_vals_positive, binned_corr_positive)\n", 388 | " axs[1].set_xlabel('$x$')\n", 389 | " axs[1].set_ylabel('$\\\\langle \\\\phi(x)\\\\phi(0)\\\\rangle\\equiv G(x)$')\n", 390 | " \n", 391 | " # Plotting the theory.\n", 392 | " y_theory = self._theory_xspace((self.amplitude, self.power), x_fit)\n", 393 | " axs[0].plot(x_fit,\n", 394 | " y_theory,\n", 395 | " linestyle=':',\n", 396 | " color='m',\n", 397 | " label='theory')\n", 398 | " axs[1].loglog(x_fit,\n", 399 | " y_theory,\n", 400 | " linestyle=':',\n", 401 | " color='m',\n", 402 | " label='theory')\n", 403 | " \n", 404 | " \n", 405 | "\n", 406 | " x_amp = self._x_amplitude_numerator / self._x_amplitude_denominator\n", 407 | " axs[1].set_title(\n", 408 | " f'$G^{{fit}}(x)\\\\approx {np.exp(correlator_lr.intercept):.5f}\\\\cdot x^{{{-1*correlator_lr.slope:.5f}}} \\\\quad G^{{theory}}(x)\\\\approx {x_amp:.5f}\\\\cdot x^{{{self.power-self.dimensions:.5f}}} $'\n", 409 | " )\n", 410 | " fig.suptitle(\n", 411 | " f'Actual values: (amp,power)=({self.amplitude:.5f},{self.power:.5f})'\n", 412 | " )\n", 413 | " plt.tight_layout()\n", 414 | " plt.legend()\n", 415 | " plt.show()\n", 416 | "\n", 417 | " # Compute summary statistics: (x,y_bar,std(y_bar))\n", 418 | " self.data_summary_positive = np.array([[\n", 419 | " x_vals_positive[i], binned_corr_positive[i],\n", 420 | " binned_corr_std_positive[i] / np.sqrt(binned_corr_count_positive[i])\n", 421 | " ] for i in range(len(x_vals_positive))])\n", 422 | " \n", 423 | "\n", 424 | " def bayes(self, steps=10**4, walkers=2**6):\n", 425 | " \"\"\"\n", 426 | " Bayesian analysis for power-spectrum parameters using binned data.\n", 427 | " \"\"\"\n", 428 | "\n", 429 | " if not hasattr(self, 'data_summary_positive'):\n", 430 | " return print('Run data_fit first')\n", 431 | "\n", 432 | " # Set flat priors on the amplitude and power over some range covering actual values.\n", 433 | " amp_max, power_max = 3 * self.amplitude, 10\n", 434 | "\n", 435 | " def log_prior(params):\n", 436 | " amplitude, power = params\n", 437 | " if 0 < amplitude < amp_max and 0 < power < power_max:\n", 438 | " return 0.0\n", 439 | " return -np.inf\n", 440 | "\n", 441 | " def log_likelihood(params, data):\n", 442 | " x, y, sigy = data\n", 443 | " return np.sum(-np.log(sigy) - .5 *\n", 444 | " (self._theory_xspace(params, x) - y)**2 / sigy**2)\n", 445 | "\n", 446 | " # Total log-prob needed for MCMC.\n", 447 | " def log_posterior(params, data):\n", 448 | " lp = log_prior(params)\n", 449 | " if not np.isfinite(lp):\n", 450 | " return -np.inf\n", 451 | " return lp + log_likelihood(params, data)\n", 452 | "\n", 453 | " # MCMC setup.\n", 454 | " # Distribute initial walker positions around position space LR best fits (so as not to cheat).\n", 455 | " initial = np.array([self.lr_fit['_x_amplitude'], self.lr_fit['power']])\n", 456 | " pos = initial.T + .1 * np.concatenate(\n", 457 | " (np.random.uniform(-initial[0], initial[0], (walkers, 1)),\n", 458 | " np.random.uniform(-initial[1], initial[1], (walkers, 1))),\n", 459 | " axis=1)\n", 460 | " walkers, dim = pos.shape\n", 461 | "\n", 462 | " sampler = emcee.EnsembleSampler(walkers,\n", 463 | " dim,\n", 464 | " log_posterior,\n", 465 | " args=[self.data_summary_positive.T])\n", 466 | " sampler.run_mcmc(pos, steps, progress=True)\n", 467 | " samples = sampler.get_chain()\n", 468 | "\n", 469 | " # Plotting chain convergence\n", 470 | " fig, axs = plt.subplots(2)\n", 471 | " for i in range(walkers):\n", 472 | " for j in range(dim):\n", 473 | " axs[j].plot(samples[:, i, j])\n", 474 | "\n", 475 | " axs[0].set_ylabel('amp')\n", 476 | " axs[1].set_ylabel('power')\n", 477 | " fig.suptitle('Chain convergence')\n", 478 | " plt.show()\n", 479 | "\n", 480 | " # Autocorrelation analysis.\n", 481 | " auto_corr = sampler.get_autocorr_time()\n", 482 | " thin_rate = int(np.mean(np.array(auto_corr)) / 2)\n", 483 | "\n", 484 | " # Burn 1/4 of data, then make corner plots.\n", 485 | " flat_samples = sampler.get_chain(discard=int(steps / 4),\n", 486 | " thin=thin_rate,\n", 487 | " flat=True)\n", 488 | " amp_samples, power_samples = flat_samples[:, 0], flat_samples[:, 1]\n", 489 | "\n", 490 | " fig = corner.corner(flat_samples,\n", 491 | " labels=['amp', 'power'],\n", 492 | " truths=[self.amplitude, self.power],\n", 493 | " truth_color='r')\n", 494 | " fig.suptitle(\n", 495 | " f'Actual values: (amp,power)=({self.amplitude:.5f},{self.power:.5f})'\n", 496 | " )\n", 497 | " plt.show()" 498 | ] 499 | }, 500 | { 501 | "cell_type": "markdown", 502 | "metadata": {}, 503 | "source": [ 504 | "Run a realization and analyze. Choose the amplitude and power randomly.\n", 505 | "\n", 506 | "The momentum space correlator $P(k)=\\frac{\\texttt{amplitude}}{k^{\\texttt{power}}}$ only unambiguously defines a position-space correlator $G(x)$ for $2>{\\texttt{power}}>1/2$ (other values require regularization/analytic continuation; [relevant integral can be found here](https://dlmf.nist.gov/10.22#E43)). The precise form of is $G(x)=\\frac{{\\rm amplitude}\\,\\Gamma[1-{\\rm power}/2]}{2^{\\rm power}x^{\\rm 2-power}\\,\\Gamma[{\\rm power}/2]}$.\n", 507 | "\n", 508 | "Some parameter choices which run in reasonable time: `size_exponent`=8, `max_len`=15, `steps`=5000, `walkers`=64." 509 | ] 510 | }, 511 | { 512 | "cell_type": "code", 513 | "execution_count": null, 514 | "metadata": {}, 515 | "outputs": [ 516 | { 517 | "name": "stdout", 518 | "output_type": "stream", 519 | "text": [ 520 | "amplitude: 0.44866\n", 521 | "power: 1.74001\n", 522 | "size: 128\n", 523 | "min_len: 12\n", 524 | "max_len: 51\n" 525 | ] 526 | } 527 | ], 528 | "source": [ 529 | "rand_amp= np.random.uniform(0, 10,1)[0]\n", 530 | "rand_power = np.random.uniform(.75, 1.75, 1)[0]\n", 531 | "size_exponent = 7\n", 532 | "# Only using a subset of the data for speed\n", 533 | "min_len, max_len = int(.1 * 2 ** size_exponent), int(.4 * 2 ** size_exponent)\n", 534 | "print(f'amplitude: {rand_amp:.5f}',\n", 535 | " f'power: {rand_power:.5f}',\n", 536 | " f'size: {2 ** size_exponent}',\n", 537 | " f'min_len: {min_len}',\n", 538 | " f'max_len: {max_len}',\n", 539 | " sep='\\n')\n", 540 | "\n", 541 | "\n", 542 | "c = CmbPy(size_exponent=size_exponent, amplitude=rand_amp, power=rand_power)\n", 543 | "c.generate()\n", 544 | "c.spectrum_plot()\n", 545 | "c.hist()\n", 546 | "c.bin_pair_data(min_len=min_len, max_len=max_len)\n", 547 | "c.bayes(steps=10**4, walkers=64)" 548 | ] 549 | }, 550 | { 551 | "cell_type": "markdown", 552 | "metadata": {}, 553 | "source": [ 554 | "Power 1.5 did poorly\n", 555 | "Power " 556 | ] 557 | }, 558 | { 559 | "cell_type": "code", 560 | "execution_count": null, 561 | "metadata": {}, 562 | "outputs": [], 563 | "source": [] 564 | }, 565 | { 566 | "cell_type": "code", 567 | "execution_count": null, 568 | "metadata": {}, 569 | "outputs": [], 570 | "source": [] 571 | }, 572 | { 573 | "cell_type": "code", 574 | "execution_count": null, 575 | "metadata": {}, 576 | "outputs": [], 577 | "source": [] 578 | }, 579 | { 580 | "cell_type": "code", 581 | "execution_count": null, 582 | "metadata": {}, 583 | "outputs": [], 584 | "source": [] 585 | } 586 | ], 587 | "metadata": { 588 | "kernelspec": { 589 | "display_name": "Python 3.8.5 64-bit", 590 | "language": "python", 591 | "name": "python38564bit7f73d2527e45441faf021e5957178292" 592 | }, 593 | "language_info": { 594 | "codemirror_mode": { 595 | "name": "ipython", 596 | "version": 3 597 | }, 598 | "file_extension": ".py", 599 | "mimetype": "text/x-python", 600 | "name": "python", 601 | "nbconvert_exporter": "python", 602 | "pygments_lexer": "ipython3", 603 | "version": "3.8.10" 604 | } 605 | }, 606 | "nbformat": 4, 607 | "nbformat_minor": 2 608 | } 609 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cmbpy 2 | Simple 2d realizations of power spectra, essentially simplified Cosmic Microwave Background simulations. Includes simple fit, comparison to theory, and basic Bayesian analysis of model parameters in the Jupyter notebook. A more minimal _Mathematica_ notebook is also included. 3 | 4 | See https://garrettgoon.com/gaussian-fields/ for more background. 5 | -------------------------------------------------------------------------------- /SpectrumNormalization.nb: -------------------------------------------------------------------------------- 1 | (* Content-type: application/vnd.wolfram.mathematica *) 2 | 3 | (*** Wolfram Notebook File ***) 4 | (* http://www.wolfram.com/nb *) 5 | 6 | (* CreatedBy='Mathematica 12.1' *) 7 | 8 | (*CacheID: 234*) 9 | (* Internal cache information: 10 | NotebookFileLineBreakTest 11 | NotebookFileLineBreakTest 12 | NotebookDataPosition[ 158, 7] 13 | NotebookDataLength[ 18080, 457] 14 | NotebookOptionsPosition[ 15544, 408] 15 | NotebookOutlinePosition[ 16061, 427] 16 | CellTagsIndexPosition[ 16018, 424] 17 | WindowFrame->Normal*) 18 | 19 | (* Beginning of Notebook Content *) 20 | Notebook[{ 21 | Cell["\<\ 22 | Getting the normalization of the spectrum right in 2D, passing from momentum \ 23 | to position space.\ 24 | \>", "Text", 25 | CellChangeTimes->{{3.825512516506415*^9, 26 | 3.825512536015049*^9}},ExpressionUUID->"7f01145d-c140-4211-8860-\ 27 | 4ef5698740d9"], 28 | 29 | Cell["Angular integral:", "Text", 30 | CellChangeTimes->{{3.8255125387648563`*^9, 31 | 3.825512541588017*^9}},ExpressionUUID->"a9c1ab8a-41e6-4713-a0c9-\ 32 | 197c452ff03d"], 33 | 34 | Cell[BoxData[ 35 | RowBox[{ 36 | RowBox[{"P", "[", "k_", "]"}], ":=", 37 | FractionBox["amp", 38 | SuperscriptBox["k", "power"]]}]], "Input", 39 | CellChangeTimes->{{3.8257181128484297`*^9, 3.825718120406605*^9}}, 40 | CellLabel->"In[1]:=",ExpressionUUID->"b4c94ca0-165c-4337-aeac-c3d09521b40d"], 41 | 42 | Cell[CellGroupData[{ 43 | 44 | Cell[BoxData[ 45 | RowBox[{"angularInt", "=", 46 | RowBox[{"2", "*", 47 | RowBox[{"Integrate", "[", 48 | RowBox[{ 49 | RowBox[{"Exp", "[", 50 | RowBox[{"I", "*", "k", "*", "x", "*", 51 | RowBox[{"Cos", "[", "\[Theta]", "]"}]}], "]"}], ",", 52 | RowBox[{"{", 53 | RowBox[{"\[Theta]", ",", "0", ",", "\[Pi]"}], "}"}]}], "]"}], "*", "k", 54 | "*", 55 | RowBox[{"P", "[", "k", "]"}], "*", 56 | SuperscriptBox[ 57 | RowBox[{"(", 58 | FractionBox["1", 59 | RowBox[{"2", "\[Pi]"}]], ")"}], "2"]}]}]], "Input", 60 | CellChangeTimes->{{3.825511685200555*^9, 3.825511731316161*^9}, { 61 | 3.825511990665029*^9, 3.825511994969474*^9}, {3.8255131708316317`*^9, 62 | 3.825513175254395*^9}, {3.8257181232594643`*^9, 3.825718143913319*^9}, 63 | 3.825718197217881*^9, {3.8257183511178217`*^9, 3.825718355474298*^9}}, 64 | CellLabel->"In[2]:=",ExpressionUUID->"691f22a9-3d21-44f1-9be4-fd8c661c35b9"], 65 | 66 | Cell[BoxData[ 67 | TemplateBox[{ 68 | FractionBox[ 69 | RowBox[{"amp", " ", 70 | SuperscriptBox["k", 71 | RowBox[{"1", "-", "power"}]], " ", 72 | RowBox[{"BesselJ", "[", 73 | RowBox[{"0", ",", 74 | RowBox[{"k", " ", "x"}]}], "]"}]}], 75 | RowBox[{"2", " ", "\[Pi]"}]], 76 | RowBox[{ 77 | RowBox[{"k", " ", "x"}], "\[Element]", 78 | TemplateBox[{}, "Reals"]}]}, 79 | "ConditionalExpression"]], "Output", 80 | CellChangeTimes->{{3.8255117103107*^9, 3.825511732740407*^9}, 81 | 3.825511996394677*^9, 3.825513177898326*^9, 3.825518719868471*^9, 82 | 3.825676164918405*^9, 3.825718106034718*^9, {3.825718189588656*^9, 83 | 3.825718198790642*^9}, 3.825718357038307*^9, {3.825718413208715*^9, 84 | 3.825718427295665*^9}, 3.8257788867462807`*^9, 3.836400715776381*^9}, 85 | CellLabel->"Out[2]=",ExpressionUUID->"861eaa97-39cc-4f4c-a7d9-9368a33c9a72"] 86 | }, Open ]], 87 | 88 | Cell["Rescale:", "Text", 89 | CellChangeTimes->{{3.825512432839636*^9, 3.825512433868415*^9}, { 90 | 3.825512543299107*^9, 91 | 3.825512545772903*^9}},ExpressionUUID->"59114730-0e73-4c08-a900-\ 92 | 26791c4813fd"], 93 | 94 | Cell[CellGroupData[{ 95 | 96 | Cell[BoxData[ 97 | RowBox[{"angularIntRescaled", "=", 98 | RowBox[{"Assuming", "[", 99 | RowBox[{ 100 | RowBox[{"k", ">", "0"}], ",", 101 | RowBox[{ 102 | RowBox[{ 103 | RowBox[{ 104 | RowBox[{ 105 | RowBox[{"(", 106 | RowBox[{"angularInt", "/.", 107 | RowBox[{"k", "\[RuleDelayed]", 108 | RowBox[{"k", "/", "x"}]}]}], ")"}], "*", 109 | FractionBox["1", "x"]}], "//", "PowerExpand"}], "//", "Simplify"}], "//", 110 | "Expand"}]}], "]"}]}]], "Input", 111 | CellChangeTimes->{{3.825511958705675*^9, 3.825511963566634*^9}, 112 | 3.825512003668605*^9, {3.825512392089448*^9, 3.82551245309457*^9}, 113 | 3.8255131852030277`*^9, 3.825513273916944*^9, 3.8255133815583344`*^9, { 114 | 3.825718207143618*^9, 3.8257182140673647`*^9}, {3.82571836326752*^9, 115 | 3.8257183774008923`*^9}, {3.825778874934362*^9, 3.825778877134181*^9}}, 116 | CellLabel->"In[3]:=",ExpressionUUID->"dfdc3e9c-0591-4699-9745-b2bd825bb59a"], 117 | 118 | Cell[BoxData[ 119 | FractionBox[ 120 | RowBox[{"amp", " ", "k", " ", 121 | SuperscriptBox[ 122 | RowBox[{"(", 123 | FractionBox["x", "k"], ")"}], "power"], " ", 124 | RowBox[{"BesselJ", "[", 125 | RowBox[{"0", ",", "k"}], "]"}]}], 126 | RowBox[{"2", " ", "\[Pi]", " ", 127 | SuperscriptBox["x", "2"]}]]], "Output", 128 | CellChangeTimes->{{3.825512420985776*^9, 3.825512453310062*^9}, 129 | 3.825513189572835*^9, 3.825513381891367*^9, 3.825676165128318*^9, 130 | 3.825718106178887*^9, {3.825718191251666*^9, 3.825718214764594*^9}, { 131 | 3.8257183635567913`*^9, 3.8257183777096024`*^9}, {3.825718410823189*^9, 132 | 3.825718427328127*^9}, {3.825778877433337*^9, 3.825778886876252*^9}, 133 | 3.836400715877356*^9}, 134 | CellLabel->"Out[3]=",ExpressionUUID->"419ba5f1-218e-45a7-9a60-11228700522b"] 135 | }, Open ]], 136 | 137 | Cell["Integrate:", "Text", 138 | CellChangeTimes->{{3.825779003839164*^9, 139 | 3.825779010070204*^9}},ExpressionUUID->"6fb57fa8-f58a-4363-9bf2-\ 140 | 197ef6be605c"], 141 | 142 | Cell[CellGroupData[{ 143 | 144 | Cell[BoxData[ 145 | RowBox[{"Integrate", "[", 146 | RowBox[{"angularIntRescaled", ",", 147 | RowBox[{"{", 148 | RowBox[{"k", ",", "0", ",", "\[Infinity]"}], "}"}]}], "]"}]], "Input", 149 | CellChangeTimes->{{3.8257790110113*^9, 3.825779011013139*^9}}, 150 | CellLabel->"In[4]:=",ExpressionUUID->"ccee2977-6730-4b2a-aef5-94d0013b426a"], 151 | 152 | Cell[BoxData[ 153 | TemplateBox[{ 154 | RowBox[{"-", 155 | FractionBox[ 156 | RowBox[{ 157 | SuperscriptBox["2", 158 | RowBox[{ 159 | RowBox[{"-", "1"}], "-", "power"}]], " ", "amp", " ", "power", " ", 160 | SuperscriptBox["x", 161 | RowBox[{ 162 | RowBox[{"-", "2"}], "+", "power"}]], " ", 163 | RowBox[{"Gamma", "[", 164 | RowBox[{"-", 165 | FractionBox["power", "2"]}], "]"}]}], 166 | RowBox[{"\[Pi]", " ", 167 | RowBox[{"Gamma", "[", 168 | FractionBox["power", "2"], "]"}]}]]}], 169 | RowBox[{ 170 | FractionBox["1", "2"], "<", 171 | RowBox[{"Re", "[", "power", "]"}], "<", "2"}]}, 172 | "ConditionalExpression"]], "Output", 173 | CellChangeTimes->{3.82577902487379*^9, 3.836400730784237*^9}, 174 | CellLabel->"Out[4]=",ExpressionUUID->"17689209-09aa-4fe4-9435-1acf4173ac06"] 175 | }, Open ]], 176 | 177 | Cell["\<\ 178 | Agrees w/ result from the NIST site, https://dlmf.nist.gov/10.22#E43, after \ 179 | identities are used\ 180 | \>", "Text", 181 | CellChangeTimes->{{3.8255119762752*^9, 3.825511986769731*^9}, { 182 | 3.825676311212741*^9, 3.82567633123311*^9}, {3.825676361875558*^9, 183 | 3.825676367182002*^9}, {3.825676512538471*^9, 3.8256765136914997`*^9}, { 184 | 3.8257790147983932`*^9, 185 | 3.82577905931522*^9}},ExpressionUUID->"8ae66398-702c-413a-ae80-\ 186 | 880bb415f258"], 187 | 188 | Cell[CellGroupData[{ 189 | 190 | Cell[BoxData[{ 191 | RowBox[{"\[CapitalGamma]identity", "=", 192 | FractionBox[ 193 | RowBox[{"Gamma", "[", 194 | RowBox[{"1", "-", 195 | RowBox[{"power", "/", "2"}]}], "]"}], 196 | RowBox[{ 197 | RowBox[{"-", 198 | FractionBox["power", "2"]}], 199 | RowBox[{"Gamma", "[", 200 | RowBox[{ 201 | RowBox[{"-", "power"}], "/", "2"}], "]"}]}]]}], "\[IndentingNewLine]", 202 | RowBox[{"%", "//", "FullSimplify"}]}], "Input", 203 | CellChangeTimes->{{3.825779040057622*^9, 3.825779049951371*^9}, { 204 | 3.825779094722611*^9, 3.825779110907763*^9}, {3.8257791479629517`*^9, 205 | 3.825779151600512*^9}}, 206 | CellLabel->"In[5]:=",ExpressionUUID->"bf526400-8b4f-4e97-8119-afda63797e06"], 207 | 208 | Cell[BoxData[ 209 | RowBox[{"-", 210 | FractionBox[ 211 | RowBox[{"2", " ", 212 | RowBox[{"Gamma", "[", 213 | RowBox[{"1", "-", 214 | FractionBox["power", "2"]}], "]"}]}], 215 | RowBox[{"power", " ", 216 | RowBox[{"Gamma", "[", 217 | RowBox[{"-", 218 | FractionBox["power", "2"]}], "]"}]}]]}]], "Output", 219 | CellChangeTimes->{{3.825779099051723*^9, 3.825779111138852*^9}, { 220 | 3.8257791483732243`*^9, 3.825779151738903*^9}, 3.836400730836994*^9}, 221 | CellLabel->"Out[5]=",ExpressionUUID->"1986275c-f1e7-4f5d-9f4a-85eceda2f85c"], 222 | 223 | Cell[BoxData["1"], "Output", 224 | CellChangeTimes->{{3.825779099051723*^9, 3.825779111138852*^9}, { 225 | 3.8257791483732243`*^9, 3.825779151738903*^9}, 3.836400730848069*^9}, 226 | CellLabel->"Out[6]=",ExpressionUUID->"3d145bf6-2635-43b8-ba4a-c1ad9feff346"] 227 | }, Open ]], 228 | 229 | Cell["Clean up a little", "Text", 230 | CellChangeTimes->{{3.825779124459466*^9, 231 | 3.825779130679515*^9}},ExpressionUUID->"e16c85d9-b576-41ea-8433-\ 232 | e8721a945c4b"], 233 | 234 | Cell[CellGroupData[{ 235 | 236 | Cell[BoxData[ 237 | RowBox[{ 238 | RowBox[{"spectrum", "[", 239 | RowBox[{"amp_", ",", "power_"}], "]"}], "=", 240 | RowBox[{ 241 | RowBox[{"-", 242 | FractionBox[ 243 | RowBox[{ 244 | SuperscriptBox["2", 245 | RowBox[{ 246 | RowBox[{"-", "1"}], "-", "power"}]], " ", "amp", " ", "power", " ", 247 | SuperscriptBox["x", 248 | RowBox[{ 249 | RowBox[{"-", "2"}], "+", "power"}]], " ", 250 | RowBox[{"Gamma", "[", 251 | RowBox[{"-", 252 | FractionBox["power", "2"]}], "]"}]}], 253 | RowBox[{"\[Pi]", " ", 254 | RowBox[{"Gamma", "[", 255 | FractionBox["power", "2"], "]"}]}]]}], "*", 256 | "\[CapitalGamma]identity"}]}]], "Input", 257 | CellChangeTimes->{{3.825512008464058*^9, 3.8255120663985147`*^9}, { 258 | 3.8255122747087803`*^9, 3.825512275299057*^9}, {3.8255123084719563`*^9, 259 | 3.825512311855484*^9}, {3.8255124589883842`*^9, 3.82551245928977*^9}, { 260 | 3.82551301910949*^9, 3.825513025370142*^9}, {3.825513198659568*^9, 261 | 3.82551320542554*^9}, {3.8257182216340322`*^9, 3.825718229920189*^9}, { 262 | 3.825718393752838*^9, 3.82571841793817*^9}, {3.825779071255949*^9, 263 | 3.8257790818169518`*^9}, {3.825779131955352*^9, 3.825779139359202*^9}}, 264 | CellLabel->"In[7]:=",ExpressionUUID->"12d21341-9d85-4521-8efc-0d6f6795ae32"], 265 | 266 | Cell[BoxData[ 267 | FractionBox[ 268 | RowBox[{ 269 | SuperscriptBox["2", 270 | RowBox[{"-", "power"}]], " ", "amp", " ", 271 | SuperscriptBox["x", 272 | RowBox[{ 273 | RowBox[{"-", "2"}], "+", "power"}]], " ", 274 | RowBox[{"Gamma", "[", 275 | RowBox[{"1", "-", 276 | FractionBox["power", "2"]}], "]"}]}], 277 | RowBox[{"\[Pi]", " ", 278 | RowBox[{"Gamma", "[", 279 | FractionBox["power", "2"], "]"}]}]]], "Output", 280 | CellChangeTimes->{ 281 | 3.8257790825589123`*^9, {3.825779139600215*^9, 3.825779153921988*^9}, 282 | 3.836400730862441*^9}, 283 | CellLabel->"Out[7]=",ExpressionUUID->"cbfda516-c82f-4ba0-80ef-3c1a4d9deecc"] 284 | }, Open ]], 285 | 286 | Cell["\<\ 287 | Strictly only valid for 2>power>1/2; values outside this range defined by \ 288 | analytic continuation/ regularization.\ 289 | \>", "Text", 290 | CellChangeTimes->{{3.825676368963278*^9, 3.825676392852891*^9}, { 291 | 3.8256764373531733`*^9, 292 | 3.8256764731242247`*^9}},ExpressionUUID->"6ce1e5b1-3569-43c8-9fa2-\ 293 | d17693c13ef4"], 294 | 295 | Cell[CellGroupData[{ 296 | 297 | Cell[BoxData[ 298 | RowBox[{"Plot", "[", 299 | RowBox[{ 300 | RowBox[{ 301 | RowBox[{"spectrum", "[", 302 | RowBox[{"1", ",", "power"}], "]"}], "/.", 303 | RowBox[{"x", "\[RuleDelayed]", "1"}]}], ",", 304 | RowBox[{"{", 305 | RowBox[{"power", ",", 306 | RowBox[{"1", "/", "2"}], ",", "2"}], "}"}]}], "]"}]], "Input", 307 | CellChangeTimes->{{3.836401567968569*^9, 3.8364015929895973`*^9}, { 308 | 3.836402595427643*^9, 3.836402602106207*^9}, {3.836415034424532*^9, 309 | 3.8364150406967297`*^9}}, 310 | CellLabel->"In[19]:=",ExpressionUUID->"5a7c63ea-9d77-4a2c-8750-9d4a2f3d4cc1"], 311 | 312 | Cell[BoxData[ 313 | GraphicsBox[{{{}, {}, 314 | TagBox[ 315 | {RGBColor[0.368417, 0.506779, 0.709798], AbsoluteThickness[1.6], Opacity[ 316 | 1.], LineBox[CompressedData[" 317 | 1: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 362 | "]]}, 363 | Annotation[#, "Charting`Private`Tag$48043#1"]& ]}, {}}, 364 | AspectRatio->NCache[GoldenRatio^(-1), 0.6180339887498948], 365 | Axes->{True, True}, 366 | AxesLabel->{None, None}, 367 | AxesOrigin->{0.5, 0}, 368 | DisplayFunction->Identity, 369 | Frame->{{False, False}, {False, False}}, 370 | FrameLabel->{{None, None}, {None, None}}, 371 | FrameTicks->{{Automatic, Automatic}, {Automatic, Automatic}}, 372 | GridLines->{None, None}, 373 | GridLinesStyle->Directive[ 374 | GrayLevel[0.5, 0.4]], 375 | ImagePadding->All, 376 | Method->{ 377 | "DefaultBoundaryStyle" -> Automatic, 378 | "DefaultGraphicsInteraction" -> { 379 | "Version" -> 1.2, "TrackMousePosition" -> {True, False}, 380 | "Effects" -> { 381 | "Highlight" -> {"ratio" -> 2}, "HighlightPoint" -> {"ratio" -> 2}, 382 | "Droplines" -> { 383 | "freeformCursorMode" -> True, 384 | "placement" -> {"x" -> "All", "y" -> "None"}}}}, "DefaultMeshStyle" -> 385 | AbsolutePointSize[6], "ScalingFunctions" -> None, 386 | "CoordinatesToolOptions" -> {"DisplayFunction" -> ({ 387 | (Identity[#]& )[ 388 | Part[#, 1]], 389 | (Identity[#]& )[ 390 | Part[#, 2]]}& ), "CopiedValueFunction" -> ({ 391 | (Identity[#]& )[ 392 | Part[#, 1]], 393 | (Identity[#]& )[ 394 | Part[#, 2]]}& )}}, 395 | PlotRange->NCache[{{ 396 | Rational[1, 2], 2}, {0., 1.3521682546100284`}}, {{0.5, 2}, {0., 397 | 1.3521682546100284`}}], 398 | PlotRangeClipping->True, 399 | PlotRangePadding->{{ 400 | Scaled[0.02], 401 | Scaled[0.02]}, { 402 | Scaled[0.05], 403 | Scaled[0.05]}}, 404 | Ticks->{Automatic, Automatic}]], "Output", 405 | CellChangeTimes->{3.836415899764474*^9}, 406 | CellLabel->"Out[19]=",ExpressionUUID->"fac61c34-e273-4651-86c0-a613d5bacac1"] 407 | }, Open ]] 408 | }, 409 | WindowSize->{1440., 773.25}, 410 | WindowMargins->{{0, Automatic}, {0, Automatic}}, 411 | TaggingRules->{ 412 | "WelcomeScreenSettings" -> {"FEStarting" -> False}, "TryRealOnly" -> False}, 413 | Magnification:>1.7 Inherited, 414 | FrontEndVersion->"12.1 for Linux x86 (64-bit) (June 19, 2020)", 415 | StyleDefinitions->"Default.nb", 416 | ExpressionUUID->"5f27ea59-2188-4ccf-a4dd-f0e4f8b7b0eb" 417 | ] 418 | (* End of Notebook Content *) 419 | 420 | (* Internal cache information *) 421 | (*CellTagsOutline 422 | CellTagsIndex->{} 423 | *) 424 | (*CellTagsIndex 425 | CellTagsIndex->{} 426 | *) 427 | (*NotebookFileOutline 428 | Notebook[{ 429 | Cell[558, 20, 247, 6, 59, "Text",ExpressionUUID->"7f01145d-c140-4211-8860-4ef5698740d9"], 430 | Cell[808, 28, 161, 3, 59, "Text",ExpressionUUID->"a9c1ab8a-41e6-4713-a0c9-197c452ff03d"], 431 | Cell[972, 33, 277, 6, 70, "Input",ExpressionUUID->"b4c94ca0-165c-4337-aeac-c3d09521b40d"], 432 | Cell[CellGroupData[{ 433 | Cell[1274, 43, 870, 20, 74, "Input",ExpressionUUID->"691f22a9-3d21-44f1-9be4-fd8c661c35b9"], 434 | Cell[2147, 65, 837, 19, 96, "Output",ExpressionUUID->"861eaa97-39cc-4f4c-a7d9-9368a33c9a72"] 435 | }, Open ]], 436 | Cell[2999, 87, 199, 4, 59, "Text",ExpressionUUID->"59114730-0e73-4c08-a900-26791c4813fd"], 437 | Cell[CellGroupData[{ 438 | Cell[3223, 95, 897, 20, 73, "Input",ExpressionUUID->"dfdc3e9c-0591-4699-9745-b2bd825bb59a"], 439 | Cell[4123, 117, 755, 16, 87, "Output",ExpressionUUID->"419ba5f1-218e-45a7-9a60-11228700522b"] 440 | }, Open ]], 441 | Cell[4893, 136, 152, 3, 59, "Text",ExpressionUUID->"6fb57fa8-f58a-4363-9bf2-197ef6be605c"], 442 | Cell[CellGroupData[{ 443 | Cell[5070, 143, 313, 6, 49, "Input",ExpressionUUID->"ccee2977-6730-4b2a-aef5-94d0013b426a"], 444 | Cell[5386, 151, 801, 22, 115, "Output",ExpressionUUID->"17689209-09aa-4fe4-9435-1acf4173ac06"] 445 | }, Open ]], 446 | Cell[6202, 176, 440, 9, 59, "Text",ExpressionUUID->"8ae66398-702c-413a-ae80-880bb415f258"], 447 | Cell[CellGroupData[{ 448 | Cell[6667, 189, 641, 16, 128, "Input",ExpressionUUID->"bf526400-8b4f-4e97-8119-afda63797e06"], 449 | Cell[7311, 207, 509, 13, 99, "Output",ExpressionUUID->"1986275c-f1e7-4f5d-9f4a-85eceda2f85c"], 450 | Cell[7823, 222, 246, 3, 56, "Output",ExpressionUUID->"3d145bf6-2635-43b8-ba4a-c1ad9feff346"] 451 | }, Open ]], 452 | Cell[8084, 228, 159, 3, 59, "Text",ExpressionUUID->"e16c85d9-b576-41ea-8433-e8721a945c4b"], 453 | Cell[CellGroupData[{ 454 | Cell[8268, 235, 1207, 28, 99, "Input",ExpressionUUID->"12d21341-9d85-4521-8efc-0d6f6795ae32"], 455 | Cell[9478, 265, 587, 17, 99, "Output",ExpressionUUID->"cbfda516-c82f-4ba0-80ef-3c1a4d9deecc"] 456 | }, Open ]], 457 | Cell[10080, 285, 317, 7, 59, "Text",ExpressionUUID->"6ce1e5b1-3569-43c8-9fa2-d17693c13ef4"], 458 | Cell[CellGroupData[{ 459 | Cell[10422, 296, 546, 13, 49, "Input",ExpressionUUID->"5a7c63ea-9d77-4a2c-8750-9d4a2f3d4cc1"], 460 | Cell[10971, 311, 4557, 94, 408, "Output",ExpressionUUID->"fac61c34-e273-4651-86c0-a613d5bacac1"] 461 | }, Open ]] 462 | } 463 | ] 464 | *) 465 | 466 | --------------------------------------------------------------------------------