Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
d25b8f7
Implement initial Nautilus sampler
eshelDror Apr 6, 2026
eb63949
Add nautilus to requirements
eshelDror Apr 6, 2026
8512213
Add multithreading for nautilus
eshelDror Apr 9, 2026
66eca22
Add option to save nautilus progress
eshelDror Apr 10, 2026
34aa0af
This is a pytest implemented using test_nested_sampler but implemente…
Apr 13, 2026
91cd179
Vectorize nautilus likelihood and prior transform
eshelDror Apr 13, 2026
1e66635
Improve vectorization of ptform
eshelDror Apr 14, 2026
dd7824a
Merge remote-tracking branch 'eshel/nautilus_vectorized' into nautilu…
Apr 14, 2026
9e154b0
Allow weighted post in results
eshelDror Apr 22, 2026
4fbd6d7
Save Nautilus weighted post to results
eshelDror Apr 22, 2026
e9ef51f
Make corner plot with weighted post if available
eshelDror Apr 22, 2026
d5c2d57
Print results with weighted post
eshelDror Apr 23, 2026
250e544
Add saving and loading weighted results
eshelDror Apr 23, 2026
38da696
Add end-to-end test
eshelDror Apr 23, 2026
bb5d3c1
Make Lux compatible
eshelDror Apr 24, 2026
8ed95ff
Update GJ504 run parameters, fix Lux threads issue
eshelDror Apr 29, 2026
bc0cf83
Clean up Nautilus pool fix
eshelDror Apr 29, 2026
4c45a90
This is a pytest for Nautilus Sampler!
Apr 29, 2026
5a5e55e
Fix Nautilus threading for different python versions
eshelDror Apr 29, 2026
469610b
Add OFTI GJ504 fit
eshelDror May 1, 2026
5476754
Fix loading unweighted results
eshelDror May 2, 2026
ea2e847
Undo changes to print_results for python version compatability
eshelDror May 2, 2026
504be4d
Add downsampling for corner plotting
eshelDror May 5, 2026
2d4aacf
add a basic nautilus tutorial with orbit generation and plotting
AnCh2008 May 6, 2026
9218abe
Add hr8799e nautilus end-to-end test
eshelDror May 11, 2026
d960159
Fix datadir
eshelDror May 11, 2026
9e87bd4
add quick description and demonstration of weighted results in the tu…
AnCh2008 May 11, 2026
c76c982
Merge branch 'nautilus_initial' into nautilus
eshelDror May 27, 2026
86a2913
Merge branch 'nautilus' into nautilus_weighted_results
eshelDror May 27, 2026
66960e3
Remove visulizations.
May 27, 2026
376d058
Merge branch 'main' into nautilus
eshelDror Jun 2, 2026
79c9381
Merge branch 'nautilus' into nautilus_weighted_results
eshelDror Jun 2, 2026
29544de
Merge branch 'nautilus_weighted_results' into GJ504_naut
eshelDror Jun 2, 2026
8373c2c
Merge branch 'GJ504_naut' into GJ504_OFTI
eshelDror Jun 2, 2026
b2ad8db
Merge branch 'GJ504_OFTI' into naut_better_results
eshelDror Jun 2, 2026
7ce7a2b
Merge branch 'naut_better_results' into naut_hr8799e
eshelDror Jun 2, 2026
99a8e51
Add nautilus to pyproject.toml
eshelDror Jun 2, 2026
969a15b
Add BaseNestedSampler and document Nautilus
eshelDror Jun 2, 2026
0e6e5c8
Document changes to results.py
eshelDror Jun 2, 2026
9f611cc
Standardize load_results with array_not_none
eshelDror Jun 2, 2026
963f719
Fix misconfigured ptform and initialization of Nautilus
eshelDror Jun 2, 2026
f071dac
Fix mp.Pool import
eshelDror Jun 2, 2026
d33e018
Merge remote-tracking branch 'quin/pytest_clean' into naut_combined
eshelDror Jun 3, 2026
4698bc2
Merge remote-tracking branch 'upstream/main' into naut_combined
eshelDror Jun 3, 2026
7195311
added more on weights, faster plot params and fixed language
AnCh2008 Jun 3, 2026
cb889f8
Merge remote-tracking branch 'eshel/naut_better_results' into naut_tu…
AnCh2008 Jun 3, 2026
960ae55
add improvements to the tutorial and trimmed it down
AnCh2008 Jun 16, 2026
180517a
Clean and add tests for downsampled plot_corner
eshelDror Jun 16, 2026
ac3de45
Remove extra end-to-end tests
eshelDror Jun 16, 2026
50f6b88
Improve ptform and Nautilus sampler docstrings
eshelDror Jun 16, 2026
58b135c
Clean Nautilus end-to-end test
eshelDror Jun 16, 2026
7a96491
Fix downsampling for no lnlikes
eshelDror Jun 16, 2026
4c2a18c
Add tests for downsampling and weighted results
eshelDror Jun 16, 2026
6b7944f
Merge pull request #10 from AnCh2008/naut_tutorial
eshelDror Jun 16, 2026
4410b0d
Clean up tutorial
eshelDror Jun 16, 2026
2d7181d
Generalize nautilus tutorial thread count
eshelDror Jun 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
434 changes: 434 additions & 0 deletions docs/tutorials/Nautilus_tutorial.ipynb

Large diffs are not rendered by default.

17 changes: 13 additions & 4 deletions orbitize/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@
cmap(np.linspace(0.0, 0.7, 1000)),
)


def plot_corner(results, param_list=None, **corner_kwargs):
def plot_corner(results, param_list=None, downsample=None, **corner_kwargs):
Comment thread
eshelDror marked this conversation as resolved.
"""
Make a corner plot of posterior on orbit fit from any sampler

Expand All @@ -55,6 +54,9 @@ def plot_corner(results, param_list=None, **corner_kwargs):
sigma: rv jitter
mi: mass of individual body i, for i = 0, 1, 2, ... (only if fit_secondary_mass == True)
mtot: total mass (only if fit_secondary_mass == False)

downsample (int):
amount of samples to randomly draw from the posterior using ``results.downsample``

**corner_kwargs: any remaining keyword args are sent to ``corner.corner``.
See `here <https://corner.readthedocs.io/>`_.
Expand Down Expand Up @@ -125,8 +127,15 @@ def plot_corner(results, param_list=None, **corner_kwargs):
else:
fixed_indices.append(i)

if downsample is not None:
post, _ = results.downsample(downsample)
weights = None
else:
post = results.weighted_post
weights = results.weights

samples = np.copy(
results.post[:, param_indices]
post[:, param_indices]
) # keep only chains for selected parameters
samples[:, angle_indices] = np.degrees(
samples[:, angle_indices]
Expand Down Expand Up @@ -161,7 +170,7 @@ def plot_corner(results, param_list=None, **corner_kwargs):

corner_kwargs["labels"] = reduced_labels_list

figure = corner.corner(samples, **corner_kwargs)
figure = corner.corner(samples, weights=weights, **corner_kwargs)
return figure


Expand Down
129 changes: 110 additions & 19 deletions orbitize/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import orbitize.plot
import orbitize.gaia, orbitize.hipparcos


class Results(object):
"""
A class to store accepted orbital configurations from the sampler
Expand All @@ -28,6 +27,15 @@ class Results(object):
data (astropy.table.Table): output from ``orbitize.read_input.read_file()``
curr_pos (np.array of float): for MCMC only. A multi-D array of the
current walker positions that is used for restarting a MCMC sampler.
weighted_post (np.array of float): RxN array of orbital parameters
Comment thread
eshelDror marked this conversation as resolved.
(posterior output from weighted orbit-fitting process), where R is the
number of orbits generated, and N is the number of varying orbital
parameters in the fit (default: None).
weighted_lnlike (np.array of float): R array of log-likelihoods corresponding to
the orbits described in ``weighted_post`` (default: None).
lnweights (np.array of float): R array of log-weights corresponding to the orbits
and log-likelihoods described in ``weighted_post`` and ``weighted_lnlike`` (default: None)


Written: Henry Ngo, Sarah Blunt, 2018

Expand All @@ -36,7 +44,7 @@ class Results(object):

def __init__(
self, system=None, sampler_name=None, post=None, lnlike=None,
version_number=None, curr_pos=None
version_number=None, curr_pos=None, weighted_post=None, weighted_lnlike=None, lnweight=None
):

self.system = system
Expand All @@ -45,6 +53,9 @@ def __init__(
self.lnlike = lnlike
self.curr_pos = curr_pos
self.version_number = version_number
self._weighted_post = weighted_post
self._weighted_lnlike = weighted_lnlike
self.lnweight = lnweight
self.ln_evidence = None
self.ln_evidence_err = None

Expand All @@ -58,7 +69,60 @@ def __init__(
self.param_idx = self.system.param_idx
self.standard_param_idx = self.system.basis.standard_basis_idx

def add_samples(self, orbital_params, lnlikes, curr_pos=None):
@property
def weighted_post(self):
"""
Returns the weighted posterior if it exists,
otherwise returns the unweighted posterior.
"""
if self._weighted_post is not None:
return self._weighted_post
return self.post

@property
def weighted_lnlike(self):
"""
Returns the weighted log-likelihoods if it exists,
otherwise returns the unweighted log-likelihoods.
"""
if self._weighted_lnlike is not None:
return self._weighted_lnlike
return self.lnlike

@property
def weights(self):
"""
Returns the weights of ``weighted_post`` and ``weighted_lnlike``
if it exists, otherwise returns None.
"""
if self.lnweight is None:
return None
return np.exp(self.lnweight)

def downsample(self, amount, duplicates=True):
"""
Samples from the posterior, or the weighted posterior if it exists.

Args:
amount (int): number of samples to draw from the posetrior
duplicates (bool): whether to replace sampled orbits from the posterior,
which may cause duplicate samples (default: True)

Returns:
tuple:

numpy.array of float: orbital parameters of the samples (``amount``xN, where N is number of varying orbital parameters)

numpy.array of float: log-likelihoods of the samples (length ``amount``)

"""

indexes = np.random.choice(len(self.weighted_post), amount, duplicates, self.weights)
if self.weighted_lnlike is None:
return self.weighted_post[indexes], None
return self.weighted_post[indexes], self.weighted_lnlike[indexes]

def add_samples(self, orbital_params, lnlikes, curr_pos=None, weighted_post=None, weighted_lnlike=None, lnweight=None):
"""
Add accepted orbits, their likelihoods, and the orbitize version number
to the results
Expand All @@ -69,6 +133,12 @@ def add_samples(self, orbital_params, lnlikes, curr_pos=None):
lnlike (np.array): add corresponding lnlike values to results
curr_pos (np.array of float): for MCMC only. A multi-D array of the
current walker positions
weighted_post (np.array): for Nautilus only. Sets of orbital params
associated with ``lnweight`` to add to results.
weighted_lnlike (np.array): for Nautilus only. Corresponding weighted lnlike
values for ``weighted_post`` to add to results.
weighted_post (np.array): for Nautilus only. Log of weights associated
with ``weighted_post`` and ``weighted_lnlike``.

Written: Henry Ngo, 2018

Expand All @@ -88,6 +158,16 @@ def add_samples(self, orbital_params, lnlikes, curr_pos=None):
else:
self.post = np.vstack((self.post, orbital_params))
self.lnlike = np.append(self.lnlike, lnlikes)

if weighted_post is not None:
if self._weighted_post is None:
self._weighted_post = weighted_post
self._weighted_lnlike = weighted_lnlike
self.lnweight = lnweight
else:
self._weighted_post = np.vstack((self._weighted_post, weighted_post))
self._weighted_lnlike = np.append(self._weighted_lnlike, weighted_lnlike)
self.lnweight = np.append(self.lnweight, lnweight)

if curr_pos is not None:
self.curr_pos = curr_pos
Expand Down Expand Up @@ -132,6 +212,12 @@ def save_results(self, filename):
if self.curr_pos is not None:
hf.create_dataset("curr_pos", data=self.curr_pos)

if self._weighted_post is not None and self._weighted_lnlike is not None and self.lnweight is not None:
hf.create_dataset("weighted_post", data=self._weighted_post)
hf.create_dataset("weighted_lnlike", data=self._weighted_lnlike)
hf.create_dataset("lnweight", data=self.lnweight)


self.system.save(hf)

hf.close() # Closes file object, which writes file to disk
Expand Down Expand Up @@ -160,12 +246,12 @@ def load_results(self, filename, append=False):
version_number = str(hf.attrs['version_number'])
else:
version_number = "<= 1.13"
post = hf.get('post')
if post is not None:
post = np.array(post)
lnlike = hf.get('lnlike')
if lnlike is not None:
lnlike = np.array(lnlike)

post = array_not_none(hf.get('post'))
lnlike = array_not_none(hf.get('lnlike'))
weighted_post = array_not_none(hf.get('weighted_post'))
weighted_lnlike = array_not_none(hf.get('weighted_lnlike'))
lnweight = array_not_none(hf.get("lnweight"))

if 'num_secondary_bodies' in hf.attrs:
num_secondary_bodies = int(hf.attrs['num_secondary_bodies'])
Expand Down Expand Up @@ -287,10 +373,7 @@ def load_results(self, filename, append=False):
if 'ln_evidence_err' in hf.attrs:
self.ln_evidence_err = hf.attrs['ln_evidence_err']

try:
curr_pos = np.array(hf.get('curr_pos'))
except KeyError:
curr_pos = None
curr_pos = array_not_none(hf.get('curr_pos'))

hf.close() # Closes file object

Expand All @@ -316,14 +399,14 @@ def load_results(self, filename, append=False):
'Unable to append file {} to Results object. version_number of object and file do not match'.format(filename))

# Now append post and lnlike
self.add_samples(post, lnlike)#, self.labels)
self.add_samples(post, lnlike, weighted_post=weighted_post, weighted_lnlike=weighted_lnlike, lnweight=lnweight)#, self.labels)
else:

# Only proceed if object is completely empty
if self.sampler_name is None and self.post is None and self.lnlike is None and self.version_number is None:# and self.tau_ref_epoch is None :
if self.sampler_name is None and self.post is None and self.lnlike is None and self.version_number is None and self._weighted_lnlike is None and self._weighted_post is None and self.lnweight is None:# and self.tau_ref_epoch is None :
self.sampler_name = sampler_name
self.version_number = version_number
self.add_samples(post, lnlike)#, self.labels)
self.add_samples(post, lnlike, weighted_post=weighted_post, weighted_lnlike=weighted_lnlike, lnweight=lnweight)#, self.labels)

else:
raise Exception(
Expand Down Expand Up @@ -390,11 +473,11 @@ def print_results(self):
self.results_str += '-------------------\n'
print(self.results_str)

def plot_corner(self, param_list=None, **corner_kwargs):
def plot_corner(self, param_list=None, downsample=None, **corner_kwargs):
"""
Wrapper for orbitize.plot.plot_corner
"""
return orbitize.plot.plot_corner(self, param_list, **corner_kwargs)
return orbitize.plot.plot_corner(self, param_list, downsample, **corner_kwargs)

def plot_orbits(self, object_to_plot=1, start_mjd=51544.,
num_orbits_to_plot=100, num_epochs_to_plot=100,
Expand Down Expand Up @@ -444,4 +527,12 @@ def plot_propermotion(self,
cmap=cmap,
cbar_param=cbar_param,
# fig=fig
)
)

def array_not_none(raw):
"""
Returns a numpy.array of the input if it is not None, else returns None
"""
if raw is not None:
return np.array(raw)
return raw
Loading
Loading