plotastrodata 1.9.19.post1__tar.gz → 1.9.21__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {plotastrodata-1.9.19.post1/plotastrodata.egg-info → plotastrodata-1.9.21}/PKG-INFO +3 -3
  2. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/README.md +1 -1
  3. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/__init__.py +1 -1
  4. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/fitting_utils.py +60 -32
  5. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/other_utils.py +2 -2
  6. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/plot_utils.py +18 -19
  7. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21/plotastrodata.egg-info}/PKG-INFO +3 -3
  8. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata.egg-info/requires.txt +1 -1
  9. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/setup.cfg +1 -1
  10. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/LICENSE +0 -0
  11. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/MANIFEST.in +0 -0
  12. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/analysis_utils.py +0 -0
  13. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/const_utils.py +0 -0
  14. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/coord_utils.py +0 -0
  15. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/ext_utils.py +0 -0
  16. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/fft_utils.py +0 -0
  17. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/fits_utils.py +0 -0
  18. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/los_utils.py +0 -0
  19. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/matrix_utils.py +0 -0
  20. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata/noise_utils.py +0 -0
  21. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata.egg-info/SOURCES.txt +0 -0
  22. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata.egg-info/dependency_links.txt +0 -0
  23. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata.egg-info/not-zip-safe +0 -0
  24. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/plotastrodata.egg-info/top_level.txt +0 -0
  25. {plotastrodata-1.9.19.post1 → plotastrodata-1.9.21}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plotastrodata
3
- Version: 1.9.19.post1
3
+ Version: 1.9.21
4
4
  Summary: plotastrodata is a tool for astronomers to create figures from FITS files and perform fundamental data analyses with ease.
5
5
  Home-page: https://github.com/yusukeaso-astron/plotastrodata
6
6
  Download-URL: https://github.com/yusukeaso-astron/plotastrodata
@@ -14,7 +14,7 @@ License-File: LICENSE
14
14
  Requires-Dist: astropy>=7.2
15
15
  Requires-Dist: corner
16
16
  Requires-Dist: dynesty
17
- Requires-Dist: emcee
17
+ Requires-Dist: emcee>=3.0
18
18
  Requires-Dist: matplotlib
19
19
  Requires-Dist: numpy>=2.0
20
20
  Requires-Dist: pillow
@@ -84,7 +84,7 @@ plotastrodata can do the following things.
84
84
  * astropy >= 7.2
85
85
  * corner (only for fitting)
86
86
  * dynesty (only for fitting)
87
- * emcee (only for fitting)
87
+ * emcee >= 3.0 (only for fitting)
88
88
  * ffmpeg (only for movie)
89
89
  * matplotlib
90
90
  * numpy >= 2.0
@@ -56,7 +56,7 @@ plotastrodata can do the following things.
56
56
  * astropy >= 7.2
57
57
  * corner (only for fitting)
58
58
  * dynesty (only for fitting)
59
- * emcee (only for fitting)
59
+ * emcee >= 3.0 (only for fitting)
60
60
  * ffmpeg (only for movie)
61
61
  * matplotlib
62
62
  * numpy >= 2.0
@@ -1,4 +1,4 @@
1
1
  import warnings
2
2
 
3
3
  warnings.simplefilter('ignore', FutureWarning)
4
- __version__ = '1.9.19-1'
4
+ __version__ = '1.9.21'
@@ -2,9 +2,11 @@ import corner
2
2
  import emcee
3
3
  import matplotlib.pyplot as plt
4
4
  import numpy as np
5
+ import pickle
5
6
  import ptemcee
6
7
  import warnings
7
8
  from dynesty import DynamicNestedSampler as DNS
9
+ from functools import partial
8
10
  from multiprocessing import Pool
9
11
  from tqdm import tqdm
10
12
  from typing import Any, Callable
@@ -18,6 +20,25 @@ bar = None
18
20
  global_progressbar = True
19
21
 
20
22
 
23
+ def _gaussian_log_likelihood(x: np.ndarray, model: Callable,
24
+ xdata: np.ndarray, ydata: np.ndarray,
25
+ sigma: float | np.ndarray) -> float:
26
+ """Return a Gaussian log likelihood using pickleable arguments."""
27
+ chi2 = np.sum((ydata - model(xdata, *x))**2 / sigma**2)
28
+ return chi2 / (-2)
29
+
30
+
31
+ def _bounded_log_probability(x: np.ndarray, log_likelihood: Callable,
32
+ bounds: np.ndarray,
33
+ update_progress: bool = False) -> float:
34
+ """Combine a bounded uniform prior with a log likelihood."""
35
+ if update_progress:
36
+ bar.update(1)
37
+ if np.all((bounds[:, 0] < x) & (x < bounds[:, 1])):
38
+ return log_likelihood(x)
39
+ return -np.inf
40
+
41
+
21
42
  def logp(x: np.ndarray) -> float:
22
43
  """Log prior function made from the boundary (global_bounds) of fitting parameters.
23
44
 
@@ -38,9 +59,9 @@ def logp(x: np.ndarray) -> float:
38
59
  def _get_GR(samples: np.ndarray, nwalkers: int, ndata: int, dim: int
39
60
  ) -> np.ndarray:
40
61
  """Calculate the Gelman-Rubin statistics."""
41
- B = np.std(np.mean(samples, axis=1), axis=0)
42
- W = np.mean(np.std(samples, axis=1), axis=0)
43
- V = (len(samples[0]) - 1) / len(samples[0]) * W \
62
+ B = np.std(np.mean(samples, axis=0), axis=0)
63
+ W = np.mean(np.std(samples, axis=0), axis=0)
64
+ V = (len(samples) - 1) / len(samples) * W \
44
65
  + (nwalkers + 1) / (nwalkers - 1) * B
45
66
  d = ndata - dim - 1
46
67
  GR = np.sqrt((d + 3) / (d + 1) * V / W)
@@ -66,7 +87,7 @@ class EmceeCorner():
66
87
 
67
88
  This class wraps ``emcee`` and ``ptemcee`` for simple bounded-parameter fitting. The likelihood can be supplied directly through ``logl``, or it can be constructed from ``model``, ``xdata``, ``ydata``, and ``sigma``. Parameters are sampled with a uniform prior inside ``bounds`` and zero prior probability outside them.
68
89
 
69
- After calling :meth:`fit`, the main results are stored as attributes:``samples`` for the post-burn-in chain, ``popt`` for the maximum-likelihood parameter set, and ``plow``, ``pmid``, and ``phigh`` for posterior percentiles. The samples can be visualized with :meth:`plotcorner` and :meth:`plotchain`.
90
+ After calling :meth:`fit`, the main results are stored as attributes:``samples`` for the post-burn-in chain, ``popt`` for the maximum-likelihood parameter set, and ``plow``, ``pmid``, and ``phigh`` for posterior percentiles. ``samples`` has the shape ``(steps, walkers, dimensions)``. The samples can be visualized with :meth:`plotcorner` and :meth:`plotchain`.
70
91
 
71
92
  Args:
72
93
  bounds (np.ndarray): Parameter bounds with shape ``(dim, 2)``. logl (Callable, optional): Log-likelihood function. Defaults to None.
@@ -95,9 +116,8 @@ class EmceeCorner():
95
116
  if logl is None and (model is not None
96
117
  and xdata is not None
97
118
  and ydata is not None):
98
- def logl(x: np.ndarray) -> float:
99
- chi2 = np.sum((ydata - model(xdata, *x))**2 / sigma**2)
100
- return chi2 / (-2)
119
+ logl = partial(_gaussian_log_likelihood, model=model,
120
+ xdata=xdata, ydata=ydata, sigma=sigma)
101
121
  self.bounds = global_bounds
102
122
  self.dim = len(self.bounds)
103
123
  self.logl = logl
@@ -125,43 +145,50 @@ class EmceeCorner():
125
145
  'logl': self.logl, 'logp': self.logp}
126
146
  else:
127
147
  if ncores > 1:
128
- print('Use logl as log_prob_fn to avoid function-in-function.')
129
- log_prob_fn = self.logl
130
- else:
131
- def log_prob_fn(x: np.ndarray) -> float:
132
- return self.logp(x) + self.logl(x)
148
+ try:
149
+ pickle.dumps(self.logl)
150
+ except (pickle.PicklingError, AttributeError, TypeError) as exc:
151
+ raise TypeError(
152
+ 'logl and model must be pickleable when ncores > 1. '
153
+ 'Define them at module scope instead of inside another '
154
+ 'function.') from exc
133
155
 
134
156
  sampler_cls = emcee.EnsembleSampler
135
157
  sampler_kwargs = {'nwalkers': nwalkers, 'ndim': self.dim,
136
- 'log_prob_fn': log_prob_fn}
158
+ 'log_prob_fn': _bounded_log_probability,
159
+ 'args': (self.logl, self.bounds,
160
+ global_progressbar and ncores == 1)}
137
161
  if ncores > 1:
138
162
  with Pool(ncores) as pool:
139
163
  sampler = sampler_cls(**sampler_kwargs, pool=pool)
164
+ # This run_mcmc is duplicated so sampling finishes before the pool closes.
165
+ sampler.run_mcmc(pos0, nsteps)
140
166
  else:
141
167
  sampler = sampler_cls(**sampler_kwargs, pool=None)
142
- sampler.run_mcmc(pos0, nsteps)
168
+ sampler.run_mcmc(pos0, nsteps)
143
169
  return sampler
144
170
 
145
171
  def _get_samples(self, sampler: Any, nburnin: int,
146
172
  pt: bool) -> np.ndarray:
147
173
  """Extract post-burn-in samples from sampler chain."""
148
174
  if pt:
149
- return sampler.chain[0, :, nburnin:, :] # temperatures, walkers, steps, dim
175
+ chain = sampler.chain[0] # walkers, steps, dim
176
+ return np.swapaxes(chain, 0, 1)[nburnin:, :, :] # steps, walkers, dim
150
177
  else:
151
- return sampler.chain[:, nburnin:, :] # walkers, steps, dim
178
+ return sampler.get_chain(discard=nburnin) # steps, walkers, dim
152
179
 
153
180
  def _get_lnp_popt(self, sampler: Any, pt: bool, nburnin: int,
154
181
  ) -> tuple[np.ndarray, np.ndarray]:
155
182
  """Get log probabilities and best-fit parameters from sampler."""
156
183
  if pt:
157
- lnp = sampler.logprobability[0] # 0th temperature chain
158
- chain = sampler.chain[0]
184
+ lnp = np.swapaxes(sampler.logprobability[0], 0, 1)
185
+ chain = np.swapaxes(sampler.chain[0], 0, 1)
159
186
  else:
160
- lnp = sampler.lnprobability
161
- chain = sampler.chain
187
+ lnp = sampler.get_log_prob() # steps, walkers
188
+ chain = sampler.get_chain() # steps, walkers, dim
162
189
  idx_best = np.unravel_index(np.argmax(lnp), lnp.shape)
163
190
  popt = chain[idx_best]
164
- lnp = lnp[:, nburnin:]
191
+ lnp = lnp[nburnin:, :]
165
192
  return lnp, popt
166
193
 
167
194
  def _get_percentiles(self, samples: np.ndarray
@@ -189,7 +216,7 @@ class EmceeCorner():
189
216
  ntry (int, optional): Number of trials for the Gelman-Rubin check. Defaults to 1.
190
217
  pos0 (np.nparray, optional): Initial parameter set in the shape of (ntemps, nwalkers, dim). Defaults to None.
191
218
  savechain (str, optional): File name of the chain in format of .npy. Existing files with the same name are overwritten by ``numpy.save``. Defaults to None.
192
- ncores (int, optional): Number of cores for multiprocessing.Pool. ncores=1 does not use multiprocessing. Defaults to 1.
219
+ ncores (int, optional): Number of cores for multiprocessing.Pool. ncores=1 does not use multiprocessing. For ncores > 1, user-supplied logl and model functions must be pickleable, such as functions defined at module scope. Defaults to 1.
193
220
  grcheck (bool, optional): Whether to check Gelman-Rubin statistics. Defaults to False.
194
221
  pt (bool, optional): Whether to use ptemcee; otherwise, emcee is used. Defaults to False.
195
222
  """
@@ -242,13 +269,14 @@ class EmceeCorner():
242
269
  labels = [f'Par {i:d}' for i in range(self.dim)]
243
270
  if cornerrange is None:
244
271
  cornerrange = self.bounds
245
- corner.corner(np.reshape(self.samples, (-1, self.dim)),
246
- truths=self.popt,
247
- quantiles=[self.percent[0] / 100,
248
- 0.5,
249
- self.percent[1] / 100],
250
- show_titles=True, labels=labels, range=cornerrange)
251
- close_figure(plt, savefig, show, tight=False)
272
+ fig = corner.corner(np.reshape(self.samples, (-1, self.dim)),
273
+ truths=self.popt,
274
+ quantiles=[self.percent[0] / 100,
275
+ 0.5,
276
+ self.percent[1] / 100],
277
+ show_titles=True, labels=labels,
278
+ range=cornerrange)
279
+ close_figure(fig, savefig, show, tight=False)
252
280
 
253
281
  def plotchain(self, labels: list | None = None, ylim: list | None = None,
254
282
  savefig: dict | str | None = None,
@@ -266,14 +294,14 @@ class EmceeCorner():
266
294
  if ylim is None:
267
295
  ylim = self.bounds
268
296
  fig = plt.figure(figsize=(4, 2 * self.dim))
269
- x = np.arange(np.shape(self.samples)[1])
297
+ x = np.arange(np.shape(self.samples)[0])
270
298
  naverage = max(1, len(x) // 100)
271
299
  nend = len(x) - len(x) % 100 if naverage > 1 else len(x)
272
300
  x = x[:nend:naverage]
273
301
  for i in range(self.dim):
274
- y = self.samples[:, :, i] # walkers, steps, dim
302
+ y = self.samples[:, :, i] # steps, walkers, dim
275
303
  plist = [self.percent[0], 50, self.percent[1]]
276
- y = [np.percentile(y, p, axis=0) for p in plist] # percent over the walkers, steps
304
+ y = [np.percentile(y, p, axis=1) for p in plist] # percent over the walkers, steps
277
305
  y = [[np.percentile(np.reshape(yy[:nend], (naverage, -1)), p, axis=0)
278
306
  for p in plist] for yy in y] # percent over the walkers, percent over the steps
279
307
  ax = fig.add_subplot(self.dim, 1, i + 1)
@@ -296,7 +296,7 @@ def close_figure(fig: object, savefig: dict | str | None = None,
296
296
  show: bool = False, tight: bool = True) -> None:
297
297
  """Save, show, and close the figure.
298
298
 
299
- If ``savefig`` is provided, the figure is saved with Matplotlib ``Figure.savefig``. Existing files with the same name are overwritten by Matplotlib. After optional saving/showing, the figure is closed with ``plt.close()``.
299
+ If ``savefig`` is provided, the figure is saved with Matplotlib ``Figure.savefig``. Existing files with the same name are overwritten by Matplotlib. After optional saving/showing, the figure is closed with ``plt.close(fig)``.
300
300
 
301
301
  Default keyword values:
302
302
  Figure.savefig: ``bbox_inches='tight'`` and ``transparent=True``. Values in ``savefig`` override these defaults.
@@ -316,4 +316,4 @@ def close_figure(fig: object, savefig: dict | str | None = None,
316
316
  fig.savefig(**savefig0)
317
317
  if show:
318
318
  plt.show()
319
- plt.close()
319
+ plt.close(fig)
@@ -564,13 +564,18 @@ class PlotAstroData(AstroFrame):
564
564
  figsize=figsize,
565
565
  ncols=ncols, nrows=nrows, nchan=nchan)
566
566
  need_vlabel = nchan > 1 or animation
567
+ figs = []
567
568
  for ch in range(nchan):
568
569
  n, i, j = ch2nij(ch)
569
- if internalfig and n not in plt.get_fignums():
570
- fig = plt.figure(n, figsize=figsize)
570
+ if n == len(figs):
571
+ if internalfig:
572
+ fig = plt.figure(figsize=figsize)
573
+ figs.append(fig)
571
574
  if need_vlabel:
572
575
  fig.subplots_adjust(hspace=0, wspace=0,
573
576
  right=0.87, top=0.87)
577
+ else:
578
+ fig = figs[n]
574
579
  if internalax:
575
580
  sharex = ax[nij2ch(n, i - 1, j)] if i > 0 else None
576
581
  sharey = ax[nij2ch(n, i, j - 1)] if j > 0 else None
@@ -582,6 +587,7 @@ class PlotAstroData(AstroFrame):
582
587
  rf'${vlabel:.{veldigit}f}$', color='black',
583
588
  backgroundcolor='white', zorder=20)
584
589
  self.fig = None if internalfig else fig
590
+ self.figs = figs
585
591
  self.ax = ax
586
592
  self.rowcol = nrows * ncols
587
593
  self.npages = npages
@@ -657,8 +663,6 @@ class PlotAstroData(AstroFrame):
657
663
  for ch, axnow in enumerate(self.ax):
658
664
  if ch not in self._validchan(include_chan):
659
665
  continue
660
- if self.fig is None:
661
- plt.figure(ch // self.rowcol)
662
666
  if patch == 'rectangle':
663
667
  a = np.radians(angle)
664
668
  xp = x - (width*np.cos(a) + height*np.sin(a)) / 2.
@@ -857,16 +861,13 @@ class PlotAstroData(AstroFrame):
857
861
  if not show_cbar:
858
862
  return
859
863
 
860
- if self.fig is None:
861
- fig = plt.figure(ch // self.rowcol)
862
- else:
863
- fig = self.fig
864
+ fig = self.figs[ch // self.rowcol]
864
865
  if len(self.ax) == 1:
865
866
  ax = self.ax[ch]
866
867
  cb = fig.colorbar(mappable[ch], ax=ax, label=cblabel,
867
868
  format=cbformat, location=cblocation)
868
869
  else:
869
- cax = plt.axes([0.88, 0.105, 0.015, 0.77])
870
+ cax = fig.add_axes([0.88, 0.105, 0.015, 0.77])
870
871
  cb = fig.colorbar(mappable[ch], cax=cax, label=cblabel,
871
872
  format=cbformat)
872
873
  cb.ax.tick_params(labelsize=cbtickfontsize)
@@ -1104,14 +1105,13 @@ class PlotAstroData(AstroFrame):
1104
1105
  axnow.set_ylabel('')
1105
1106
  if len(self.ax) == 1:
1106
1107
  if self.fig is None:
1107
- plt.figure(0).tight_layout()
1108
+ self.figs[0].tight_layout()
1108
1109
  if title is not None:
1109
1110
  if len(self.ax) > 1:
1110
1111
  t = {'y': 0.9}
1111
1112
  t_in = {'t': title} if isinstance(title, str) else title
1112
1113
  t.update(t_in)
1113
- for i in range(self.npages):
1114
- fig = plt.figure(i)
1114
+ for fig in self.figs:
1115
1115
  fig.suptitle(**t)
1116
1116
  else:
1117
1117
  t = {'label': title} if isinstance(title, str) else title
@@ -1249,13 +1249,13 @@ class PlotAstroData(AstroFrame):
1249
1249
  show: bool = False, **kwargs: Any) -> None:
1250
1250
  """Use savefig of matplotlib.
1251
1251
 
1252
- If ``filename`` is provided, existing files with the same name are overwritten by Matplotlib. This method closes all Matplotlib figures with ``plt.close('all')`` after optional saving/showing.
1252
+ If ``filename`` is provided, existing files with the same name are overwritten by Matplotlib. After optional saving/showing, figures managed by this instance are closed.
1253
1253
 
1254
1254
  Default keyword values:
1255
1255
  Figure.savefig: ``transparent=True`` and ``bbox_inches='tight'``. User-supplied keyword arguments override these values.
1256
1256
 
1257
1257
  Args:
1258
- filename (str, optional): Output image file name. Existing files may be overwritten, and all Matplotlib figures are closed after saving/showing. Defaults to None.
1258
+ filename (str, optional): Output image file name. Existing files may be overwritten, and figures managed by this instance are closed after saving/showing. Defaults to None.
1259
1259
  show (bool, optional): True means doing plt.show(). Defaults to False.
1260
1260
  """
1261
1261
  _kw = {'transparent': True, 'bbox_inches': 'tight'}
@@ -1265,15 +1265,15 @@ class PlotAstroData(AstroFrame):
1265
1265
  axnow.set_ylim(*self.Ylim)
1266
1266
  if isinstance(filename, str):
1267
1267
  ext = filename.split('.')[-1]
1268
- for i in range(self.npages):
1268
+ for i, fig in enumerate(self.figs):
1269
1269
  ver = '' if self.npages == 1 else f'_{i:d}'
1270
- fig = plt.figure(i)
1271
1270
  fig.patch.set_alpha(0)
1272
1271
  fname = filename.replace(f'.{ext}', f'{ver}.{ext}')
1273
1272
  fig.savefig(fname, **_kw)
1274
1273
  if show:
1275
1274
  plt.show()
1276
- plt.close('all')
1275
+ for fig in self.figs:
1276
+ plt.close(fig)
1277
1277
 
1278
1278
  def get_figax(self) -> tuple[object, object] | None:
1279
1279
  """Output the external fig and ax after plotting.
@@ -1286,8 +1286,7 @@ class PlotAstroData(AstroFrame):
1286
1286
  + ' with channel maps')
1287
1287
  return
1288
1288
 
1289
- fig = plt.figure(0) if self.fig is None else self.fig
1290
- return fig, self.ax[0]
1289
+ return self.figs[0], self.ax[0]
1291
1290
 
1292
1291
 
1293
1292
  def _get_ylabel_profile(_kw: dict, Tb: bool, flux: bool, bunit: str
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plotastrodata
3
- Version: 1.9.19.post1
3
+ Version: 1.9.21
4
4
  Summary: plotastrodata is a tool for astronomers to create figures from FITS files and perform fundamental data analyses with ease.
5
5
  Home-page: https://github.com/yusukeaso-astron/plotastrodata
6
6
  Download-URL: https://github.com/yusukeaso-astron/plotastrodata
@@ -14,7 +14,7 @@ License-File: LICENSE
14
14
  Requires-Dist: astropy>=7.2
15
15
  Requires-Dist: corner
16
16
  Requires-Dist: dynesty
17
- Requires-Dist: emcee
17
+ Requires-Dist: emcee>=3.0
18
18
  Requires-Dist: matplotlib
19
19
  Requires-Dist: numpy>=2.0
20
20
  Requires-Dist: pillow
@@ -84,7 +84,7 @@ plotastrodata can do the following things.
84
84
  * astropy >= 7.2
85
85
  * corner (only for fitting)
86
86
  * dynesty (only for fitting)
87
- * emcee (only for fitting)
87
+ * emcee >= 3.0 (only for fitting)
88
88
  * ffmpeg (only for movie)
89
89
  * matplotlib
90
90
  * numpy >= 2.0
@@ -1,7 +1,7 @@
1
1
  astropy>=7.2
2
2
  corner
3
3
  dynesty
4
- emcee
4
+ emcee>=3.0
5
5
  matplotlib
6
6
  numpy>=2.0
7
7
  pillow
@@ -19,7 +19,7 @@ install_requires =
19
19
  astropy >= 7.2
20
20
  corner
21
21
  dynesty
22
- emcee
22
+ emcee >= 3.0
23
23
  matplotlib
24
24
  numpy >= 2.0
25
25
  pillow