Changelog#

Changelog#

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[1.2.0] - 2026-07-17#

Systematic audit of every module outside the BruteForce hot path (covered in 1.1.1): population fitting, LOS dust, offsets, stellar models, priors, dust maps, plotting, data loading, and utilities. 113 verified findings were fixed across pull requests #61-#69, each with a regression test (the suite grew from 725 to over 1000 tests). Highlights below; full details are in the PR descriptions. Items marked (output-changing) alter numerical results — in every case toward the statistically correct answer.

Fixed — statistical correctness (output-changing)#

  • Cluster fitting: binary modeling was silently non-functional (an smf= keyword typo swallowed by **kwargs); post-MS models were underweighted ~21x; the (mass, SMF) integration measure is now normalized; the uniform outlier model is a proper density (it previously favored flagging the best-measured stars as outliers).

  • LOS dust: monotonicity constraints now apply to the effective extinction profile, and kernels renormalize for truncation at the reddening bounds.

  • Photometric offsets: leave-one-out reweighting used incorrect importance weights, shrinking fitted-band offsets toward 1.

  • Priors: metallicity/age priors were silently dropped when passed as plain arrays; the disk/halo normalization was inconsistent near the Sun; age-prior weights now condition on metallicity; the dust-map A(V) prior renormalizes over avlim.

  • Dust maps: Bayestar queries honor the map’s per-pixel reliability metadata by default (apply_reliability_mask=False restores raw values).

  • Plotting / post-processing: distance posteriors regenerated from fit summaries were biased low (missing scale-to-distance Jacobian); bin_pdfs_distred(cdf=True) now cumulates along the reddening axis as documented; offset-diagnostic mask indexing fixed.

  • BruteForce: parallax-based model pre-selection now uses the marginal (not conditional) scale uncertainty, so heavily reddened stars with good parallaxes no longer lose valid models before the posterior stage.

  • Utilities: quantile uses one midpoint-CDF convention for weighted and unweighted input (unweighted values shift slightly); truncated-normal densities are tail-stable; a singular covariance in a batch no longer corrupts the parallel sampler.

Fixed — robustness and API contracts#

  • Silent failure modes now raise clear errors: omitting a multi-valued grid axis (was quietly pinned to the grid edge), zero surviving filters in load_models (which also now drops a constant afe column), a dust map without sky coordinates, invalid avlim, mis-shaped weights/coordinates in the plotting utilities, and half-specified offset priors.

  • NaN fluxes and zero errors are masked per band instead of invalidating the whole object; generated grids no longer contain invalid models; caller-supplied arrays (lnprior, hist2d_kwargs, labels) are no longer mutated in place.

  • Additions: dustfile accepts pathlib.Path; bin_pdfs_distred / dist_vs_red support importance weights and multi-object input; phot_loglike supports shared model grids and extra chi-square terms; draw_sar gained max_attempts (its RNG stream changed, so fixed-seed draws differ; the distribution is verified identical).

Performance (equivalence-tested)#

Grid generation 16.7x per model; photometric-offset calibration ~12x end-to-end; LOS dust likelihood up to 4.2x (prior transform 51x); draw_sar 2.6-4.5x; Bayestar() init 8x; galactic prior 1.75x (logp_extinction ~9x); Isochrone memory roughly halved; EEPTracks caches ~264 MB smaller (now versioned _cachev2.pkl).

Infrastructure#

  • The docs CI check no longer fails instantly on every pull request: the build was decoupled from the master-only GitHub Pages deployment environment, so PRs now actually build the documentation (deploys still run only from master).

[1.1.1] - 2026-06-27#

Performance pass on the individual-star BruteForce fitter (loglike_grid -> logpost_grid -> _fit). Benchmarked on the real MIST grid (grid_mist_v9.h5, 613,530 models) and the real Orion field (Gaia parallaxes), across a cohort spanning the full selected-model range (4 .. the max_models=50000 cap). End-to-end ~2.6x faster loglike_grid, ~2.3x faster fit() on a 4-core host (the parallel kernels scale further with physical cores). Every change is either verified bitwise-identical in the saved (float32) outputs or proven more accurate against a high-Nmc reference; see bench/RESULTS.md for the full methodology and numbers.

Changed#

  • Monte-Carlo prior integration in logpost_grid now uses antithetic sampling (affects output – more accurate): the per-model prior integral draws the proposal normals in antithetic pairs (z, -z). The estimator stays unbiased; because the integrand is dominated along ln(d) by the galactic-density falloff and the +log(d) volume Jacobian (both monotone in the sampling direction), this cuts the MC-integration variance ~20x and reduces the finite-Nmc (Jensen) bias of log(mean(w)). Versus an Nmc -> inf gold standard the per-model log-posterior RMSE drops to ~0.28x and the bias 3-65x, at the same Nmc and half the Gaussian draws. The resulting shift in posterior summaries is within the procedure’s intrinsic Monte-Carlo scatter. An adversarial review confirmed the one regime where antithetic could mildly increase variance (a very precise parallax centered on the photometric proposal mode with poorly-constraining photometry) is empirically negligible for real Gaia data (worst observed std ratio ~1.06) and never introduces bias.

  • mc_ess output is now a weight-concentration diagnostic, not a strict effective-sample count: under antithetic correlation 1 / sum(w_i^2) no longer counts independent samples (it can read up to ~2x optimistic for well-mixed models). It remains monotone and useful as a proposal/target mismatch indicator – low values still flag poor overlap.

  • sample_multivariate_normal gained an optional antithetic keyword (default False); logp_galactic_structure gained optional feh/loga array keywords (a fast path for the per-point metallicity/age prior). Existing calls are unaffected.

Performance#

All of the following are verified bitwise-identical to the prior results in the saved fit() outputs (any differences are float64 round-off below float32 storage precision that flip no discrete selection):

  • loglike_grid’s per-model numba kernels (_get_seds, _optimize_fit_mag, _optimize_fit_flux, _get_sed_mle) and the multivariate-normal sampler are parallelized with prange (rows are independent; reductions are exact). The two convergence max-reductions in _optimize_fit_mag are parallelized as well.

  • Removed dead computation in _get_sed_mle (models_int/reddening were computed – 4.9M pow calls per evaluation – but never read).

  • Fused kernels _chi2_from_resid and _init_mag_resid replace large NumPy temporaries; np.zeros -> np.empty for fully-overwritten arrays.

  • _get_seds flux conversion uses exp(fac*mag) instead of 10**(-0.4*mag) (~1.6x faster on that hot path; agreement ~1e-15).

  • The batched 3x3 covariance regularization uses an analytic symmetric-3x3 minimum-eigenvalue kernel instead of numpy.linalg.eigvalsh (no less accurate; the regularization decision is unchanged).

  • logpost_grid no longer tiles a structured label array across all MC samples for the default galactic prior (a numpy structured-dtype np.tile is ~100x slower than the float equivalent; it dominated logpost for large selected-model counts). It now hands the prior the per-point feh/loga as plain float arrays. Bitwise-identical; ~1.24x faster logpost_grid at Nsel=50000 (~165 ms/object), no change for small selections.

[1.1.0] - 2026-05-29#

Maintenance and polish release: verified bug fixes (several affecting numerical output on specific paths), documentation accuracy, test-suite hygiene, and release-process improvements. All fixes ship with regression tests.

Fixed#

  • FastNN.encode silent mis-scaling for 6-sample batches (affects output): a 2-D input of shape (6 params, 6 samples) broadcast against the parameter bounds without error and normalized along the wrong axis, producing silently corrupted SEDs/bolometric corrections only when exactly 6 valid samples were evaluated (reached via StellarPop synthesis). Now dispatches on input dimensionality explicitly.

  • los_dust kernels collapsing per-object cloud means (affects output): kernel_gauss/kernel_lorentz/kernel_tophat collapsed an array-valued mean to a scalar, so line-of-sight fits using a reddening template (template_reds, additive_foreground) evaluated every object against object 0’s cloud mean. Means are now kept broadcastable. The default uniform-cloud path is unchanged.

  • Binary companions no longer discard a valid primary SED (affects binary grid generation): StarEvolTrack.get_seds returned an all-NaN combined SED (dropping the valid primary) when the secondary’s age could not be matched to the unrealistically tight default tolerance. The default tol is relaxed 1e-6 -> 1e-2 dex and a primary-only fallback is used when the companion does not converge. Regenerate binary (smf>0) grids to benefit; single-star fitting and the shipped default grids are unaffected.

  • load_offsets no longer crashes on a single-filter offsets file.

  • logp_imf no longer raises ZeroDivisionError for a power-law slope of exactly 1.0 (flat-in-log); uses the logarithmic mass integral.

  • _fetch (data download) removes a stale/broken symlink before re-linking and falls back to a file copy on filesystems without symlink support.

  • BruteForce.logpost_grid floors the Monte-Carlo sample count at 1 (a very small mem_lim could drive it to 0 and crash).

  • BruteForce.loglike_grid guards the dimensional-prior log(Ndim) term and fails fast with a clear error on a fully-masked object (was an opaque ZeroDivisionError deep in the optimizer).

  • cornerplot no longer mutates a caller-supplied labels list in place.

  • dist_vs_red uses interpolation="none" so binned PDF images show raw bins; fixed a no-op smoothing guard in hist2d.

  • Removed a dead from scipy import polyfit import (scipy has no top-level polyfit; the code always used numpy.polyfit).

Changed#

  • Python support: dropped end-of-life Python 3.8 (requires-python>=3.9); added 3.13 to the classifiers and tooling targets.

  • Dependencies: raised the numba floor to >=0.59.0 (the old 0.53 floor predates numpy>=1.22/2.x support); numpy remains uncapped (the code is numpy-2.0 clean, verified on 2.2).

  • Documentation engine: standardized on a single NumPy-docstring processor (napoleon); removed the redundant numpydoc extension. The docs now build with zero warnings.

  • Tutorials: standardized on the committed Orion_l209.1_b-19.9 example field; replaced chi2/Nbands quality cuts with goodness-of-fit p-values.

Documentation#

  • Corrected numerous stale code examples across the docs and docstrings to match the real API (StarGrid(models, labels), get_seds 3-tuple in magnitudes, magnitude(flux, err), sample_multivariate_normal(size=), the prior call signatures, samps_dred, the data cache path/env var, real filter names, and the maggies flux convention).

  • Added summary_plot to the API reference; documented BruteForce.fit’s performance/accuracy parameters (max_models, precision_shrinkage, subsample_mode, R_solar, Z_solar); reframed fit-quality guidance around the p-value methodology.

Testing & packaging#

  • Added regression tests covering every fix above; reconciled the conftest coverage guidance with what CI actually runs (NUMBA_DISABLE_JIT=1 pytest --cov); the default pytest run no longer enables coverage (which crashed on some WSL/DrvFs paths).

  • Added a tag-triggered PyPI publish workflow (Trusted Publishing); fixed the stale MANIFEST.in data-file path.

[1.0.0] - 2025-12-08#

First stable release of brutus following code verification, testing, and documentation improvements.

Added#

  • Documentation: Scientific background pages, user guides, API documentation with examples, and ReadTheDocs hosting

  • Testing: 92% code coverage, 606 tests, GitHub Actions CI/CD with Codecov integration

  • Code verification: All functions verified for correctness; fixed IMF normalization bug, StarGrid distance reference, and other issues

Changed#

  • Development status updated to Production/Stable

  • Added tqdm as formal dependency

  • Enforced Black formatting across codebase

[0.9.0] - 2024-08-28#

Major refactoring to improve usability and maintainability while preserving scientific functionality.

Added#

  • Modern packaging: Migrated to pyproject.toml with black, isort, flake8, mypy

  • Testing: pytest framework with 100+ tests, coverage reporting, multi-platform CI

  • Modular architecture: Split into brutus.core, brutus.analysis, brutus.plotting, brutus.dust, brutus.utils, brutus.data, brutus.priors

  • Performance: Numba JIT compilation, vectorized operations, improved caching

Changed#

  • Minimum Python version: 3.8+ (dropped Python 2.7)

  • Split large modules (utils.py, plotting.py) into focused submodules

  • Updated all dependencies to modern versions

Fixed#

  • Windows/WSL compatibility documentation

  • Circular imports and module loading issues

  • Infinite loop bug in hist2d function

Migration#

Update imports:

# Old
from brutus.seds import Isochrone
from brutus.fitting import BruteForce

# New
from brutus import Isochrone, BruteForce
# or
from brutus.core import Isochrone
from brutus.analysis import BruteForce

All scientific algorithms, file formats, and core APIs remain unchanged.

[0.8.3] - Previous Release#

Final release using old project structure and Python 2 compatibility.

Features: individual star fitting, cluster modeling, 3D dust mapping, MIST support, neural network SED prediction.


For migration questions or bug reports, see the issue tracker.