Installation & Requirements
Requirements
PyFC requires Python 3.9+. Core dependencies include:
numpy >= 1.20scipynumbamatplotlib
Optional Dependencies:
ultranest(Required for utilizing the nested sampling optimizer)tqdm(Provides progress bars during execution)
Installation
Option 1: Direct PyPI Installation (Recommended)
To install the latest stable release directly from the Python Package Index (PyPI), run:
pip install PyFeldmanCousins
pip is not case-sensitive, so using pip install pyfeldmancousins will have the same result.
Option 2: Local Installation from Source
If you want to run the latest development version or modify the codebase, clone the repository and install via pip to automatically resolve and install all dependencies:
git clone https://github.com/mbustama/FeldmanCousins.git
cd FeldmanCousins
pip install -e .
Verifying the Installation
After installing the package, you can run the local test suite to ensure everything is configured correctly for your system architecture. We utilize pytest to validate statistical correctness, optimizer behavior, and parallelization scaling.
# Install the testing framework
pip install pytest
# Run the test suite from the repository root
pytest tests/ -v
Measuring test coverage
The test extra also installs pytest-cov, so the same suite can report
which lines and branches of pyfc/ it exercises:
bash scripts/run_coverage.sh
Add --html for a browsable htmlcov/ tree, which is the more useful form
when the question is which branch of a particular function is cold, or
--xml for the Cobertura coverage.xml that CI uploads. What to
measure – the source tree, branch coverage, and what is excluded – is
configured once in [tool.coverage.run] in pyproject.toml, so a local run
measures exactly what CI measures.
That wrapper exists because of Numba. PyFC’s mathematical core
(pyfc/binned.py) is six @njit-compiled functions, calc_nll among
them, and coverage.py traces Python bytecode – which Numba never executes,
since it compiles those functions to machine code on first call. Every line
inside them is therefore reported as missed no matter how hard the suite hits
them: measured over the three test files that target calc_nll directly,
binned.py reports 10% with the JIT on and 90% under
NUMBA_DISABLE_JIT=1. The first number is not real, and publishing it would
be actively misleading about the part of PyFC that most needs to be
trustworthy.
Disabling the JIT is not uniformly free, though. Most files are faster
without it, since they stop paying compilation overhead, but
strategy="grid"’s @njit(parallel=True) toy generators degrade into
plain Python loops, and a whole-suite run with the JIT disabled does not finish
in any reasonable time. So run_coverage.sh splits the suite in two – the
pathological files run with the JIT on, everything else with it off – and lets
coverage combine the halves. A bare pytest tests/ --cov still works and
is measured identically; it just reports the misleading ~10% for binned.py.
Branch coverage is on deliberately. A plain line-coverage figure flatters this
codebase: the orchestrator is a dispatch machine, and most of what can go wrong
in it is a branch only ever taken one way – strategy="grid" vs
"adaptive", a resumed checkpoint vs a fresh run, the disconnected-interval
reporting path, the if ultranest is None guards. Line coverage counts those
as covered the moment the function runs at all.
Developer Installation
If you plan to modify the codebase or contribute to the project, you should install the package with its optional testing and development dependencies included:
pip install -e ".[test]"
# To reproduce the CI coverage job exactly, add the optimizers extra so the
# UltraNest code paths are measured rather than counted as missed
pip install -e ".[test,optimizers]"
File Tree
The project structure is organized modularly to separate analytical likelihood math from plotting, execution loops, and documentation (file tree generated by running git ls-files):
FeldmanCousins/
├── .github/
│ └── workflows/
│ ├── lint.yml # CI linting and formatting pipeline
│ ├── pages.yml # GitHub Pages deployment for documentation
│ ├── publish.yml # PyPI (OIDC) automated publishing workflow
│ └── pytest.yml # GitHub Actions CI testing and coverage pipeline
├── config/
│ └── example_fc_config.json # Template configuration file for CLI usage
├── docs/ # Sphinx documentation configuration and source
│ ├── dev/ # Handoff/planning notes from past refactors (not part of the built docs)
│ │ ├── AUDIT_BRIEF_dev-no-templates.md
│ │ ├── FIXES_BRIEF_dev-no-templates.md
│ │ ├── FOLLOWUP_BRIEF_dev-no-templates.md
│ │ └── REFACTOR_BRIEF_dev-no-templates.md
│ ├── source/
│ │ ├── _static/
│ │ │ └── pyfc_logo.png # Project branding asset
│ │ ├── changelog.rst # Renders the root CHANGELOG.md via myst-parser
│ │ ├── conf.py # Sphinx build configuration
│ │ ├── configuration.rst # Interactive CLI tool and parameter definitions
│ │ ├── index.rst # Master documentation page with high-level overview and table of contents
│ │ ├── installation.rst # System requirements, dependencies, and setup instructions
│ │ ├── methodology.rst # Statistical math formulations and optimizer execution strategies
│ │ ├── modules.rst # Autodoc stubs for the PyFC submodules
│ │ ├── outputs.rst # Explanation of checkpointing files and returned array structures
│ │ ├── pyfc.rst # Package-level API structure
│ │ ├── quickstart.rst # Code tutorials for setting up binned and unbinned models
│ │ ├── references.rst # Bibliography page rendering
│ │ ├── refs.bib # BibTeX citations for physics and statistics literature
│ │ └── tutorials.rst # Guide to the numbered example notebooks in examples/
│ ├── Makefile # Build commands for Unix
│ └── make.bat # Build commands for Windows
├── examples/ # Numbered in suggested reading order -- see the Tutorials page
│ ├── 01_pyfc_quickstart_tutorial.ipynb # Runnable counterpart to the Quick Start Guide (binned + unbinned)
│ ├── 02_pyfc_high_dimensional_tutorial.ipynb # Scaling to many parameters (5- and 10-parameter models)
│ ├── 03_pyfc_non_contiguous_data_tutorial.ipynb # Passing non-contiguous (transposed) N-D data arrays directly
│ ├── 04_pyfc_algorithmic_features_tutorial.ipynb # sparsify_grid / smooth_1d,2d / finite-MC correction, on vs. off
│ ├── 05_pyfc_strategy_comparison_tutorial.ipynb # grid/scipy/hybrid strategy comparison + custom plotting from disk
│ ├── 06_pyfc_joint_constraints_tutorial.ipynb # Worked example: bounds_func/constraints for joint & simplex-constrained parameters
│ └── 07_pyfc_checkpointing_tutorial.ipynb # Interrupting (SIGTERM) and resuming a real run via warm_start
├── pyfc/ # Main Python package
│ ├── __init__.py # Package initialization and metadata
│ ├── binned.py # Binned NLL math and Numba-accelerated optimizers
│ ├── config.py # CLI argument definitions and JSON parsing
│ ├── generate_config.py # Interactive CLI wizard for creating configuration files
│ ├── optimizers.py # Wrapper functions mapping objective functions to SciPy/UltraNest
│ ├── orchestrator.py # The main pipeline executing the Feldman-Cousins algorithm
│ ├── plotting.py # Visualization suite for 1D profiles and 2D contours
│ ├── toys.py # Multiprocessing engines for MC pseudo-experiment generation
│ └── unbinned.py # Extended Unbinned Maximum Likelihood (EUML) formulations
├── scripts/
│ └── run_coverage.sh # Coverage measurement, splitting the suite around Numba's JIT
├── tests/ # Unit and integration test suite
│ ├── test_2d_resume_and_sparsify.py # Tests for 2D checkpoint resume and sparsify_grid's no-SciPy fallback
│ ├── test_adaptive_toys.py # Tests for the validated adaptive_toys/toy_batch_size early-stopping behavior
│ ├── test_bounds_func.py # Tests for the bounds_func parameter-dependent bounds mechanism
│ ├── test_checkpoint_resume.py # Tests for the real warm_start/checkpoint-resume machinery, simulating a genuine mid-run interruption
│ ├── test_config_layer.py # Structural + behavioural tests for the CLI/JSON config layer and the interactive wizard
│ ├── test_constraints.py # Tests for scipy/UltraNest LinearConstraint/NonlinearConstraint support
│ ├── test_core.py # Core installation and import tests, including checks for optional dependencies
│ ├── test_disconnected_intervals.py # Tests for the contiguous-run helper and disconnected 1D interval reporting
│ ├── test_finite_mc_likelihood.py # Hand-computed-reference tests for the finite-MC likelihood formula
│ ├── test_json_export.py # Tests for the JSON results export: NumpyEncoder conversions and the 2D payload
│ ├── test_io.py # Tests for File I/O and intermediate .npz checkpoint recovery mechanisms
│ ├── test_multiprocessing.py # Tests for unbinned execution and multi-processing concurrency using ProcessPoolExecutor
│ ├── test_nd_binned.py # Tests for N-dimensional binned data (flatten-invariance, 2D histogram smoke test)
│ ├── test_numba_compilation.py # Tests to ensure Numba JIT compilation executes correctly on the host architecture
│ ├── test_optimizers.py # Tests for continuous optimization routines, including SciPy boundary clamping
│ ├── test_pdf_components.py # Tests for the pdf_components mechanism (2- and 3+-component correctness)
│ ├── test_plotting.py # Tests for generate_corner_plot's figure/axes shape, smoothing toggles, and output file
│ ├── test_restarts.py # Tests for optimizer restarts, neighbor warm-starting, and res.success handling
│ ├── test_smoothing.py # Tests for the smoothed unphysical-rate NLL penalty, including NaN handling
│ ├── test_sparsify_grid.py # Regression tests for the sparsify_grid boundary-refinement guard fix
│ ├── test_statistics.py # Tests for statistical correctness, likelihood behavior, and Asimov treatment convergence
│ ├── test_toy_pool_fallback.py # Tests for the toy pool's ProcessPoolExecutor -> ThreadPoolExecutor recovery on unpicklable callables
│ ├── test_ultranest_fits.py # Tests that actually run the three UltraNest fits against an Asimov dataset
│ ├── test_ultranest_retry.py # Tests for the UltraNest internal-bug retry wrapper
│ ├── test_unbinned_grid.py # Tests for the unbinned grid-search path, against brute-force oracles and the PLR invariants
│ ├── test_unbinned_toys.py # Tests for the unbinned toy-generation/fitting pipeline via a real ProcessPoolExecutor
│ └── test_version.py # Tests that the version is written down once and reaches package and docs from there
├── xbranch_compare/ # Cross-branch (dev vs dev-no-templates) regression harness
│ ├── comparator.py # Recursive .npz diff (shape + NaN-mask + tolerant allclose)
│ ├── compare_results.py # Diffs the cross-branch scenario .npz outputs
│ ├── run_comparison.sh # Driver: git worktree add/remove + both scenario scripts + diff
│ ├── shared_constants.py # Physical-model constants shared by both scenario scripts (no pyfc import)
│ ├── xbranch_new_api.py # Runs all scenarios against the new (pdf_components) API
│ └── xbranch_old_api.py # Runs the old-API-compatible scenarios against a `dev` worktree
├── .gitignore # Git untracked files exclusions
├── CHANGELOG.md # Version history and notable changes
├── LICENSE # Open-source license terms
├── pyproject.toml # Build system and dependency specifications
└── README.md # Project documentation