├── .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 |
--------------------------------------------------------------------------------