iminuit: Python frontend for MINUIT2

repository·develop·Indexed 43 days ago

https://github.com/scikit-hep/iminuit

A Jupyter-friendly Python interface for the Minuit2 C++ library, designed for optimizing statistical cost functions such as maximum-likelihood and least-squares fits. Version 2.33.0 offers robust optimization, reliable error estimates, and high-performance integration with NumPy, Numba, JAX, and TensorFlow. It includes built-in support for binned and unbinned NLL and provides access to Scipy optimizers via iminuit.Minuit.scipy.

Tokens
29.6K
Snippets
103
Records
121
Agent score
55%

What's inside iminuit

  1. Key features and compatibility of iminuit

    develop

    iminuit is a fast, interactive, and IPython-friendly minimizer based on the Minuit2 C++ library.

    Core Capabilities

    • Robust Optimization: Uses Minuit2 for minimization and provides reliable error estimates for parameters.
    • NumPy Interoperability: Supports functions that accept parameters as positional arguments or as a single NumPy array. Fit results are NumPy arrays or array-like types.
    • Interactive Convenience: Automatically extracts parameter names from your function signatures or docstrings, allowing you to access parameters by name in status printouts and Jupyter notebooks.
    • High Performance: Designed to work seamlessly with compiled functions from Cython, Numba, JAX, and TensorFlow.

    Migration from PyMinuit

    iminuit is mostly compatible with PyMinuit. To port existing PyMinuit code, you can typically just update the import statements.

  2. High performance with Numba acceleration

    develop
    When using iminuit with cost functions that are JIT-compiled with numba (for example, using JIT-compiled PDFs provided by numba_stats), performance is highly competitive with RooFit. Using numba with auto-parallelization can be significantly faster than the parallel computation used in RooFit.
  3. Run tests and coverage using nox

    develop

    The recommended way to build and test iminuit is using nox. nox automatically manages a dedicated virtual environment with all necessary dependencies.

    • Run tests:
      nox -s test
    • Generate coverage report: Run nox -s cov, then open the resulting report in your browser:
      nox -s cov
      <your-web-browser> build/htmlcov/index.htm

    If you prefer to manage your own environment, you can use the dependency groups test, doc, and dev (both) defined in pyproject.toml.

    nox -s test
    nox -s cov
  4. Set up a development environment for iminuit

    develop

    To develop on iminuit, clone the repository recursively to ensure all submodules are included. It is recommended to work in a dedicated feature branch branched off develop to keep your environment clean.

    1. Clone the repository:
      git clone --recursive https://github.com/scikit-hep/iminuit.git
      cd iminuit
    2. Create a feature branch:
      git checkout -b my_cool_feature develop
    git clone --recursive https://github.com/scikit-hep/iminuit.git
    cd iminuit
    git checkout -b my_cool_feature develop
  5. Install iminuit

    develop

    iminuit can be installed via PyPI. Pre-compiled binary wheels are available for maintained CPython versions on Windows, OSX, and Linux, as well as for the latest PyPy.

    If you are on a platform without binary wheels, you can install the source package provided you have a C++ compiler that supports C++14 or newer. The installation process will automatically compile iminuit for your machine and install all required dependencies from PyPI.

    pip install iminuit
  6. Install iminuit with optional features

    develop

    iminuit is a lean package that only depends on numpy. However, you can install additional features using pip extras:

    • iminuit[numba]: Enables JIT-compilation of cost functions.
    • iminuit[plot]: Enables visualization of fitted models (requires matplotlib).
    • iminuit[ipywidgets]: Enables interactive fitting in Jupyter notebooks (requires matplotlib and ipywidgets).
    • iminuit[qt]: Enables interactive fitting outside of Jupyter notebooks (requires matplotlib and PySide6).
    • iminuit[interactive]: Installs both ipywidgets and qt support.
    • iminuit[scipy]: Enables SciPy minimizers and Minos intervals.
    • iminuit[unicode]: Enables LaTeX-style parameter names via unicodeitplus.
    • iminuit[all]: Installs all optional features.
    pip install iminuit[all]
  7. Optimize unbinned fit performance with Numba

    develop

    To speed up unbinned fits, especially for large datasets or complex models, you can use numba to Just-In-Time (JIT) compile your negative log-likelihood (NLL) function.

    For maximum performance:

    1. Use numba_stats distributions instead of scipy equivalents.
    2. Use @nb.njit(inline="always") on inner model functions to allow the compiler to optimize across function boundaries.
    3. Use @nb.njit(parallel=True, fastmath=True) on the NLL function to enable auto-parallelization and out-of-order computation. This is most effective for large datasets.
    4. For the absolute minimum overhead, use the @nb.cfunc interface, which iminuit supports as a special case.
    import numba as nb
    import numpy as np
    from numba_stats import norm, truncexpon
    
    # 1. Use inline="always" for inner model functions
    @nb.njit(inline="always")
    def model(x, z, mu, sigma, slope):
        b = truncexpon.pdf(x, 0, 1, 0, slope)
        s = norm.pdf(x, mu, sigma)
        return (1 - z) * b + z * s
    
    # 2. Use parallel=True and fastmath=True for the NLL
    @nb.njit(parallel=True, fastmath=True)
    def nll(z, mu, sigma, slope):
        ll = np.log(model(x, z, mu, sigma, slope))
        return -np.sum(ll)
    
    # 3. Or use the cfunc interface for minimal overhead
    @nb.cfunc(nb.double(nb.uintc, nb.types.CPointer(nb.double)))
    def nll2(n, par):
        z, mu, sigma, slope = nb.carray(par, (n,))
        ll = np.log(model(x, z, mu, sigma, slope))
        return -np.sum(ll)
  8. Build iminuit with C++ changes

    develop

    If you are modifying the C++ source code, nox is not the ideal tool. Instead, ensure you have the build dependencies build, pybind11, and scikit-build-core installed, then use the build module:

    python -m build --no-isolation

    Note: This command builds the package but does not install it.

    Debugging C++ builds

    To increase build output verbosity, add the following to pyproject.toml:

    [tool.scikit-build]
    build.verbose = true

    To compile with debugging symbols for troubleshooting crashes in C++ code, add this to pyproject.toml:

    [tool.scikit-build]
    cmake.build-type = "DEBUG"

    On Linux and MacOS, you can then debug using gdb:

    gdb --args python -m pytest
  9. Speed up fits by switching to binned NLL

    develop
    If you are performing unbinned fits on large samples and performance is a bottleneck, switching to a binned fit can provide dramatic speedups (up to 100x). Use the iminuit.cost.BinnedNLL class instead of iminuit.cost.UnbinnedNLL.
  10. Optimize Numba JIT with fastmath

    develop

    When using Numba for numerical acceleration, you can pass a fastmath dictionary to the @njit decorator to enable aggressive optimizations. This is particularly useful for heavy numerical loops in cost functions.

    Common fastmath flags include:

    • reassoc: Allows reassociation of floating-point operations.
    • contract: Enables contraction (e.g., Fused Multiply-Add).
    • arcp: Allows approximate reciprocal calculations.
    kwd = {"parallel": True, "fastmath": {"reassoc", "contract", "arcp"}}
    
    @nb.njit(**kwd)
    def sum_log(fs, spdf, fb, bpdf):
        return np.sum(np.log(fs * spdf + fb * bpdf))