NISMO: Nested Importance Sampling Morph Optimisation
NISMO is a Python sampler for Bayesian evidence estimation and weighted posterior inference. It combines nested importance sampling (NIS) with a normalized Morph approximation, concentrating the nested sampling calculation in the regions that matter most to the posterior.
NISMO provides:
- log-evidence estimates and weighted posterior samples;
- three replacement schemes:
fixed_morph,s-rwalk, anden-rwalk; - multiprocessing with FIFO replacement prefetching;
- configurable scientific stopping criteria and hard resource limits;
- reproducible run histories, diagnostics, and plotting helpers.
Nested importance sampling
For likelihood $L(\theta)$ and normalized prior $\pi(\theta)$, the marginal likelihood (Bayesian evidence) is
$$ Z = p(y) = \int_{\Theta} L(\theta),\pi(\theta),d\theta. $$
NIS introduces a normalized importance density $q_0(\theta)$ and the transformed integrand
$$ \Psi(\theta) = \frac{L(\theta),\pi(\theta)}{q_0(\theta)}. $$
The evidence can then be written as an expectation under $q_0$, or as the usual one-dimensional nested-sampling integral:
$$ Z = \int_{\Theta} \Psi(\theta),q_0(\theta),d\theta = \int_0^1 \Psi(X),dX, $$
where
$$ X(\lambda) = \int_{\Psi(\theta)>\lambda}q_0(\theta),d\theta $$
is the remaining probability mass under the importance density.
How NISMO implements NIS
- A normalized Morph density $q_0$ is fitted to representative posterior samples and then fixed for the evidence calculation.
- The initial $N_{\rm live}$ points are drawn independently from $q_0$.
- NISMO evaluates
log_psi0 = log_likelihood + log_prior - log_q0and removes the live point with the smallest transformed integrand. - Deterministic nested-volume shrinkage is used: $X_i=\exp(-i/N_{\rm live})$. Each dead point contributes $(X_{i-1}-X_i)\Psi_i$ to the evidence quadrature.
- A replacement is drawn from $q_0$, subject to the current
$\Psi$-constraint. The
fixed_morph,s-rwalk, anden-rwalkschemes are available. - At termination, the remaining live-point contribution is added and all contributions are normalized to produce posterior weights.
The fixed importance density must have support everywhere that $L(\theta)\pi(\theta)$ is nonzero. A missing mode cannot be recovered by the quadrature and can bias both the evidence and posterior. As with any evidence sampler, validate the complete configuration with repeated seeds and suitable benchmark problems.
Installation
Install NISMO 0.1.2 and all user-facing optional features from PyPI:
python -m pip install "nismo[all]==0.1.2"
The core package requires only NumPy and SciPy. Optional extras can be installed individually:
| Extra | Provides |
|---|---|
nismo[morph] |
MorphZ proposal fitting |
nismo[plot] |
Matplotlib diagnostics |
nismo[progress] |
tqdm terminal and notebook progress displays |
nismo[all] |
All user-facing optional features |
NISMO supports Python 3.10 or newer and is tested on Python 3.10–3.12.
API example
The example below defines a normalized one-dimensional model, fits the fixed Morph importance density, runs NISMO, and extracts posterior samples.
import numpy as np
from nismo import CallableModel, MorphProposal, NISMOSampler
LOG_2PI = np.log(2.0 * np.pi)
def log_likelihood(theta: np.ndarray) -> np.ndarray:
"""N(x=0 | theta, sigma=1), evaluated in batches."""
return -0.5 * (theta[:, 0] ** 2 + LOG_2PI)
def log_prior(theta: np.ndarray) -> np.ndarray:
"""Normalized N(theta | 0, 2) prior."""
return -0.5 * ((theta[:, 0] / 2.0) ** 2 + LOG_2PI) - np.log(2.0)
model = CallableModel(
ndim=1,
parameter_names=("x",),
log_likelihood_fn=log_likelihood,
log_prior_fn=log_prior,
)
# Representative posterior draws used only to fit q0.
training_rng = np.random.default_rng(7)
posterior_samples = training_rng.normal(
scale=np.sqrt(0.8),
size=(2_000, 1),
)
importance_morph = MorphProposal.fit(
posterior_samples,
param_names=model.parameter_names,
groups=[],
)
sampler = NISMOSampler(
model=model,
importance_morph=importance_morph,
proposal_scheme="fixed_morph",
n_live=200,
rng=42,
n_workers=1,
output_path="runs/example",
)
result = sampler.run(dlogz=1e-2, progress=True)
print(f"logZ = {result.logz:.6f} +/- {result.logzerr:.6f}")
print(result.success, result.termination_reason)
# The weighted representation is the primary posterior output.
points = result.all_points
weights = result.posterior_weights
posterior_mean = np.average(points, axis=0, weights=weights)
# Equal-weight samples are available when required by downstream software.
equal_weight_samples = result.resample_equal(rng=43, n_samples=10_000)
When output_path is set, every completed or valid partial run automatically
writes weighted_samples.npz, run_history.npz, diagnostics.json, and three
diagnostic figures. Install nismo[plot] to enable the PNG output; samples and
diagnostics are still saved when Matplotlib is unavailable. The same bundle can
be written later with result.save("runs/example").
Model functions receive batches with shape (n, ndim) and return one natural
logarithm per row. The prior must be normalized and include every normalization
constant. The samples used to fit the Morph are not reused as live points.
Replacement schemes
Select a scheme through NISMOSampler(..., proposal_scheme=...):
| Scheme | Replacement mechanism |
|---|---|
fixed_morph |
Independent constrained rejection draws from the fixed Morph |
s-rwalk |
Gaussian-covariance random walk targeting constrained $q_0$ |
en-rwalk |
Split-ensemble differential-evolution, stretch, and Gaussian move mixture |
Finite-length MCMC replacements must be calibrated for the dimension and geometry of the target. See the configuration guide for move settings, parallel workers, queue size, stopping policies, and resource limits.
Configure replacement parallelism directly in the sampler constructor, for
example with NISMOSampler(..., n_workers=8, queue_size=8).
Results and diagnostics
NISMOSampler.run(...) returns an immutable NISMOResult containing:
logz,logzerr,information,success, andtermination_reason;- weighted dead and final-live points;
- likelihood, prior, importance-density, transformed-integrand, and quadrature arrays;
- per-iteration stopping, acceptance, cost, MCMC, and queue histories;
- optional
s-rwalkcomponent timings throughSRWalkSettings(profile=True)andresult.srwalk_diagnostics; - reproducibility metadata and the initial and final random-number-generator states.
The theoretical logzerr = sqrt(H / n_live) estimate is not a complete error
budget: it does not include missing importance support, imperfect finite-length
MCMC mixing, or adaptive-proposal approximation error.
Documentation
- Quick-start guide
- Configuration reference
- Results and diagnostics
- Complete public API
- Runnable examples
Development
Clone the repository and create the development environment with uv:
git clone https://github.com/nz-gravity/nismo.git
cd nismo
uv sync --extra dev
Run the development checks and build the package:
uv run ruff format --check .
uv run ruff check .
uv run mypy
uv run pytest -m "not slow"
uv build
uvx twine check dist/*
See CONTRIBUTING.md for the contribution workflow and RELEASING.md for the release process.
Citation
If you use NISMO in research, cite the software using the repository's
CITATION.cff or
GitHub's Cite this repository menu. For version 0.1.2, a BibTeX entry is:
@software{nismo_2026,
author = {{NISMO contributors}},
title = {NISMO: Morphing Importance Nested Sampling},
year = {2026},
version = {0.1.2},
url = {https://github.com/nz-gravity/nismo}
}
Configured replacement methods may require additional citations. Inspect them
programmatically with sampler.citations.
License
NISMO is distributed under the BSD 3-Clause license.
Metadata
Release files for nismo 0.1.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nismo-0.1.4.tar.gz | 108.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nismo-0.1.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 174.7 kB
Release files / nismo-0.1.4.tar.gz
| Download URL | nismo-0.1.4.tar.gz |
|---|---|
| Size | 108.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0cdd465da55f41c64b917a4480e19d78e035d1d006451f45236e4bfaae96cfe3
|
|
BLAKE2b-256 checksum How to use checksums |
11f47f6464e56d697661e7ff737a4393b6aab259adb92ba92f11fd2b9f7ac6f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / nismo-0.1.4-py3-none-any.whl
| Download URL | nismo-0.1.4-py3-none-any.whl |
|---|---|
| Size | 66.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5500769bc2faa9841c30046cb22b1323a2d6a80c302312b32a74b0efec42bf36
|
|
BLAKE2b-256 checksum How to use checksums |
279856c5f9fec7b6bc7960603522dde1cd7706448f4ff27fdc8f9f3208146183
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency log