Quick Start Guide
=================
This guide will walk you through the basic usage of ``discoverysamplers`` with a simple example.
Basic Concepts
--------------
All sampler interfaces in ``discoverysamplers`` follow a common pattern:
1. **Define a model**: A callable that accepts a parameter dictionary and returns a log-likelihood
2. **Specify priors**: Define prior distributions for your parameters
3. **Create a bridge**: Instantiate the appropriate bridge class for your chosen sampler
4. **Run the sampler**: Execute the sampling algorithm
5. **Analyze results**: Process and visualize the output
Example: Discovery Pulsar Likelihood
-------------------------------------
Let's start with a Discovery likelihood for a single pulsar. Discovery likelihoods are the recommended way to use this package:
.. code-block:: python
import numpy as np
import jax
jax.config.update('jax_enable_x64', True)
import discovery as ds
# Load pulsar data
psr = ds.Pulsar.read_feather('path/to/pulsar.feather')
# Create a PulsarLikelihood with measurement noise
likelihood = ds.PulsarLikelihood([
psr.residuals,
ds.makenoise_measurement(psr, psr.noisedict)
])
# The likelihood object has a logL attribute that's callable
# and a params attribute containing parameter information
print(f"Parameters: {likelihood.logL.params}")
**Note**: Discovery likelihood objects contain:
- ``logL``: The callable log-likelihood function
- ``logL.params``: Parameter specifications with bounds and priors
**Tip**: When creating a bridge, you can pass either ``likelihood`` or ``likelihood.logL`` — both work. The bridge automatically detects whether you passed the object or its ``logL`` callable.
Defining Priors
^^^^^^^^^^^^^^^
``discoverysamplers`` supports multiple prior specification formats. Here are three equivalent ways to specify uniform priors:
**Dictionary Format**:
.. code-block:: python
priors = {
'x': {'dist': 'uniform', 'min': -5.0, 'max': 5.0},
'y': {'dist': 'uniform', 'min': -5.0, 'max': 5.0},
}
**Tuple Format** (shorthand):
.. code-block:: python
priors = {
'x': ('uniform', -5.0, 5.0),
'y': ('uniform', -5.0, 5.0),
}
**Mixed Format**:
.. code-block:: python
priors = {
'x': ('uniform', -5.0, 5.0),
'y': {'dist': 'loguniform', 'min': 0.1, 'max': 10.0},
'fixed_param': ('fixed', 1.0), # Fixed parameters
}
See :doc:`prior_specification` for all supported prior types and formats.
Using Different Samplers
-------------------------
Nested Sampling with Nessai
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Nessai is a flow-based nested sampling algorithm, ideal for multi-modal posteriors:
.. code-block:: python
from discoverysamplers.nessai_interface import DiscoveryNessaiBridge
import discovery as ds
# Load pulsar and create likelihood
psr = ds.Pulsar.read_feather('path/to/pulsar.feather')
likelihood = ds.PulsarLikelihood([
psr.residuals,
ds.makenoise_measurement(psr, psr.noisedict),
ds.makegp_ecorr(psr, psr.noisedict),
ds.makegp_timing(psr, svd=True)
])
# Define priors (or use default from likelihood.logL.params)
priors = {
'param1': ('uniform', -5.0, 5.0),
'param2': ('loguniform', 1e-10, 1e-5),
# ... etc for your model parameters
}
# Create the bridge - you can pass likelihood or likelihood.logL
bridge = DiscoveryNessaiBridge(
discovery_model=likelihood, # Pass the likelihood object (or likelihood.logL)
priors=priors,
jit=True # Enable JAX JIT compilation
)
# Run the sampler
results = bridge.run_sampler(
nlive=1000, # Number of live points
output='output/nessai/', # Output directory
resume=False, # Start fresh
max_iteration=10000 # Maximum iterations
)
# Access results
print(f"Log evidence: {results['logZ']} +/- {results['logZ_err']}")
samples = results['posterior_samples'] # Posterior samples
Nested Sampling with JAX-NS
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
JAX-NS is a pure JAX implementation with efficient vectorization:
.. code-block:: python
from discoverysamplers.jaxns_interface import DiscoveryJAXNSBridge
import jax
jax.config.update('jax_enable_x64', True)
# Create the bridge with Discovery likelihood
bridge = DiscoveryJAXNSBridge(
discovery_model=likelihood, # Pass the likelihood object (or likelihood.logL)
priors=priors,
jit=True
)
# Configure vectorized likelihood evaluation
bridge.configure_array_api(order=['param1', 'param2'])
# Run the sampler
results = bridge.run_sampler(
nlive=1000,
max_samples=10000,
termination_frac=0.01,
rng_seed=42
)
# Results structure similar to Nessai
print(f"Log evidence: {results['logZ']}")
MCMC with Eryn
^^^^^^^^^^^^^^
Eryn is an ensemble MCMC sampler (similar to emcee) with optional parallel tempering:
.. code-block:: python
from discoverysamplers.eryn_interface import DiscoveryErynBridge
# Create the bridge with Discovery likelihood
bridge = DiscoveryErynBridge(
model=likelihood, # Pass the likelihood object (or likelihood.logL)
priors=priors
)
# Create the sampler (basic MCMC)
sampler = bridge.create_sampler(
nwalkers=32, # Number of walkers
)
# Generate initial positions from priors
initial_state = bridge.sample_priors(nwalkers=32)
# Run MCMC
sampler.run_mcmc(
initial_state,
nsteps=10000,
progress=True
)
# Get samples (discard burn-in)
samples = sampler.get_chain(discard=1000, flat=True)
log_probs = sampler.get_log_prob(discard=1000, flat=True)
**Parallel Tempering** (recommended for multi-modal posteriors):
.. code-block:: python
# Create sampler with parallel tempering
sampler = bridge.create_sampler(
nwalkers=32,
tempering_kwargs=dict(ntemps=8) # 8 temperature chains
)
# Initial state must match shape (ntemps, nwalkers, ndim)
initial_state = bridge.sample_priors(nwalkers=32, ntemps=8)
# Run and extract cold chain (temp=0) samples
sampler.run_mcmc(initial_state, nsteps=10000, progress=True)
samples = sampler.get_chain(discard=1000, flat=True) # Cold chain only
GPry via Cobaya
^^^^^^^^^^^^^^^
`GPry `_ uses Gaussian Process emulation to accelerate inference for expensive likelihoods. It can be accessed through `Cobaya `_:
.. code-block:: python
from discoverysamplers.gpry_interface import DiscoveryGPryCobayaBridge
# Create the bridge with Discovery likelihood
bridge = DiscoveryGPryCobayaBridge(
discovery_model=likelihood, # Pass the likelihood object (or likelihood.logL)
priors=priors,
like_name='pulsar_likelihood'
)
# Run sampler
updated_info, sampler = bridge.run_sampler(
max_samples=10000,
# Additional GPry/Cobaya options
)
# Access results through Cobaya
products = sampler.products()
samples = products["sample"]
Working with Fixed Parameters
------------------------------
Often you'll want to fix certain parameters while sampling others. With Discovery likelihoods, you can either:
1. **Use noise dictionary values**: Parameters specified in ``psr.noisedict`` are automatically fixed
2. **Override with prior specifications**: Define which parameters to sample vs. fix
.. code-block:: python
import discovery as ds
# Create likelihood with some parameters from noisedict (fixed)
likelihood = ds.PulsarLikelihood([
psr.residuals,
ds.makenoise_measurement(psr, psr.noisedict), # Uses fixed values from noisedict
ds.makegp_timing(psr, svd=True) # Timing parameters will be sampled
])
# Define priors - only for parameters you want to sample
# Any parameters not in priors will use default bounds from likelihood.logL.params
priors = {
'timing_param1': ('uniform', -1e-7, 1e-7),
'timing_param2': ('uniform', -1e-7, 1e-7),
# Other parameters will use defaults or be fixed
}
# The bridge automatically handles parameter management
bridge = DiscoveryNessaiBridge(likelihood.logL, priors)
results = bridge.run_sampler(nlive=500, output='output/')
Fixed parameters are automatically separated and injected into the model during sampling.
Model Requirements
------------------
When using Discovery likelihoods:
1. **Use the likelihood object**: Pass ``likelihood`` (or ``likelihood.logL``) to the bridge
2. **Parameter dictionary**: The logL callable accepts a dict mapping parameter names to values
3. **Returns log-likelihood**: Returns a scalar log-likelihood value
4. **JAX compatible**: Discovery likelihoods are JAX-compatible and can be JIT-compiled
.. code-block:: python
import discovery as ds
# Create Discovery likelihood
likelihood = ds.PulsarLikelihood([...])
# The logL attribute is the callable likelihood function
print(type(likelihood.logL)) #
# It accepts parameter dictionaries
params = ds.sample_uniform(likelihood.logL.params)
log_like = likelihood.logL(params)
print(log_like) # Returns a scalar
# Pass likelihood object to the bridge (or likelihood.logL — both work)
bridge = DiscoveryNessaiBridge(
discovery_model=likelihood, # The bridge extracts logL automatically
priors=priors
)
**Alternative**: You can also use plain Python functions (see :doc:`model_requirements` for details), but Discovery likelihoods are recommended for PTA analysis.
Next Steps
----------
Now that you understand the basics, explore:
- :doc:`prior_specification` - Complete guide to prior specifications
- :doc:`eryn_usage` - Advanced MCMC features (parallel tempering, RJMCMC)
- :doc:`nessai_usage` - Detailed Nessai configuration
- :doc:`jaxns_usage` - JAX-NS performance tuning
- :doc:`gpry_usage` - GPry and Cobaya options
- :doc:`../advanced/performance` - Performance optimization tips
Or check out the :doc:`../examples/notebooks` for real-world examples.