Bayesian Listener¶
The core class and auditory representation.
BayesianListener¶
The single entry point for all inference and simulation workflows.
- class bayesian_listener.BayesianListener(sofa, sigma_itd=0.569, sigma_ild=1.0, sigma_spectral=10.4, sigma_prior=69.0, kappa_motor=23.31)[source]¶
Bases:
objectBayesian model of human static sound localisation from HRTFs.
Implements the generative pipeline of Barumerli et al.[1], validated and extended in Barumerli et al.[2]: noisy spatial features (ITD, ILD, monaural spectra) are extracted from a binaural stimulus, compared against direction-labelled HRTF templates, combined with an elevation prior, and finally perturbed by motor noise to yield a directional response.
See Model and Statistical Framework for the full equations and parameter definitions.
References
Examples
Load an HRTF, prepare features, and infer a single direction:
>>> from bayesian_listener import BayesianListener >>> bl = BayesianListener('subject01.sofa') >>> bl.compute_template(interpolation='SHMAX') >>> posterior = bl.infer(repetitions=50, seed=0) >>> response = bl.estimate(posterior, seed=0)
- compute_target(convention='Barumerli2023', spectral_range=None, use_cache=True, force_recompute=False, cache_dir=None)[source]¶
Extract auditory features from
self.hrirand store them astarget.The target is the stimulus-side representation: ITD, ILD, and monaural spectral features computed at the measured HRTF directions. Each row of
target.coordscorresponds to one measured direction, preserving the original HRTF measurement grid. No interpolation is applied.Call this method before
compute_templatewhen you need control over feature extraction parameters such asspectral_rangeorconvention. Iftargetis already set whencompute_templateis called, it will not be recomputed. Caching is available only when the listener was initialised with a SOFA file path; it is automatically disabled otherwise.- Parameters:
convention (str, default='Barumerli2023') – Auditory representation to use. Must be a key in
bayesian_listener.auditory_representation.CONVENTIONS. Currently'Barumerli2023'(ITD + ILD + spectral amplitudes) is the only fully implemented convention.spectral_range (list of float or None, default=None) –
[low_Hz, high_Hz]frequency limits of the gammatone filterbank used for the monaural cues.Noneselects[700.0, 18000.0].use_cache (bool, default=True) – Load from cache if available and save after computing.
force_recompute (bool, default=False) – If
True, ignore any cached target and recompute from scratch.cache_dir (str or
pathlib.Pathor None, default=None) – Cache directory.NoneselectsPath.cwd() / 'data' / 'preprocessed'.
- Raises:
ValueError – If
conventionis not a registered key, or ifsofa_fileisNone(listener was initialised with asofar.Sofaobject rather than a file path).
Examples
>>> bl = BayesianListener('subject01.sofa') >>> bl.compute_target(spectral_range=[500, 16000]) >>> bl.target.features.shape (793, 58)
- compute_template(interpolation='SHMAX', interpolation_grid=None, use_cache=True, force_recompute=False, cache_dir=None)[source]¶
Build the template: the listener’s learned mapping from features to directions.
The template is constructed from the individual HRTF by extracting auditory features and interpolating them onto a quasi-uniform spherical grid (default: 2,112 directions at ~4° spacing). It represents the internal model the listener uses to infer source directions during
infer.If
targethas not been set,compute_targetis called automatically with default parameters. For finer control over feature extraction (e.g. spectral range, convention), callcompute_targetexplicitly before calling this method.This function does not modify
targetif already set. Caching is available only when the listener was initialised with a SOFA file path; it is automatically disabled otherwise.- Parameters:
interpolation ({'SH', 'SHMAX', 'barycentric', 'barumerli2023'} or None, default='SHMAX') – Interpolation method used to resample features onto the uniform grid. Pass
Noneto skip interpolation and settemplateequal totarget(useful when the HRTF is already on a uniform grid).interpolation_grid (
pyfar.Coordinatesor None, default=None) – Directions of the template grid.Noneselects a 64th-degree spherical t-design (2,112 directions, ~4° average spacing).use_cache (bool, default=True) – Load from cache if available and save after computing. Requires a SOFA file path; raises
ValueErrorifsofa_fileisNone.force_recompute (bool, default=False) – If
True, ignore any cached template and recompute from scratch.cache_dir (str or
pathlib.Pathor None, default=None) – Cache directory.NoneselectsPath.cwd() / 'data' / 'preprocessed'.
- Raises:
ValueError – If
sofa_fileisNone(i.e. listener was initialised with asofar.Sofaobject rather than a file path).
Examples
>>> bl = BayesianListener('subject01.sofa') >>> bl.compute_template(interpolation='SHMAX') >>> bl.template.features.shape[0] 2112
To control feature extraction before building the template:
>>> bl.compute_target(spectral_range=[500, 16000]) >>> bl.compute_template()
- estimate(posterior, kappa_motor=None, seed=None)[source]¶
Convert posterior(s) into pointing responses with motor noise applied.
Selects the MAP template direction for each (trial, repetition) pair and perturbs it with isotropic motor noise drawn from \(\mathrm{vMF}(\mathbf{0}, \kappa_m)\) (Eq. 7 of Barumerli et al.[1]).
- Parameters:
posterior (
numpy.ndarray) –Output of
infer. Either:log-posteriors of shape
(n_targets, repetitions, n_templates)(wheninferwas called withstore_posterior=True), orargmax indices of shape
(n_targets, repetitions).
kappa_motor (float,
False,0or None, default=None) –von Mises–Fisher concentration for the motor-noise step.
None— useself.parameters['kappa_motor'].Falseor0— disable motor noise (return raw MAP directions).any positive float — explicit concentration. Convert from a circular SD with
sigma_to_kappa.
seed (int or None, default=None) – Seed for the motor-noise RNG.
- Returns:
Pointing responses with cartesian array shape
(n_targets, repetitions, 3).- Return type:
- Raises:
ValueError – If
posteriorhas zero rows.
Examples
>>> idx = bl.infer(repetitions=50, seed=0) >>> responses = bl.estimate(idx, seed=0) >>> responses.cartesian.shape (793, 50, 3)
- infer(repetitions=50, prior='horizontal', store_posterior=False, seed=None)[source]¶
Run Monte Carlo Bayesian inference of source direction.
For each target direction, draw
repetitionsnoisy feature samples \(\mathbf{t}\) from \(\mathcal{N}(\mathbf{s}(\boldsymbol{\varphi}), \boldsymbol{\Sigma})\), then compute the log-posterior \(p(\boldsymbol{\varphi} \mid \mathbf{t}) \propto p(\mathbf{t} \mid \boldsymbol{\varphi})\, p(\boldsymbol{\varphi})\) (Eq. 3 of Barumerli et al.[1]) over all template directions.- Parameters:
repetitions (int, default=50) – Number of Monte Carlo trials per target direction.
prior ({‘uniform’, ‘horizontal’} or
numpy.ndarray, default=’horizontal’) –Direction prior \(p(\boldsymbol{\varphi})\):
'uniform'— flat prior over template directions.'horizontal'— Gaussian prior over elevation with widthsigma_prior(Eq. 5 of Barumerli et al.[1]).array of shape
(n_templates,)— custom unnormalised prior; normalised internally.
store_posterior (bool, default=False) – If
True, return the full normalised log-posterior over templates. IfFalse, return only argmax indices (memory-light).seed (int or None, default=None) – Seed for
numpy.random.default_rng.Noneyields a non-reproducible run.
- Returns:
If
store_posterior=True: log-posterior of shape(n_targets, repetitions, n_templates)(rows sum to 1 in linear domain). Otherwise: integer template indices of shape(n_targets, repetitions).- Return type:
- Raises:
ValueError – If
targetortemplateisNone, if their conventions differ, ifprioris an array of the wrong shape, or ifprioris an unknown string.TypeError – If
prioris neither a string nor anumpy.ndarray.
Examples
>>> bl.compute_target(); bl.compute_template() >>> idx = bl.infer(repetitions=200, seed=0) >>> idx.shape (793, 200)
- property kappa_motor¶
\(\kappa_m\), the von Mises–Fisher motor-noise concentration.
- localise(target=None, directions=None, repetitions=None, seed=None)[source]¶
Run the full localisation pipeline and return pointing responses.
Convenience wrapper around
inferandestimate. Ensures the template (the listener’s internal directional model) and the target (the stimulus features to be localised) are both available, then drawsrepetitionsnoisy percepts via Monte Carlo inference and converts each MAP estimate into a pointing response perturbed by motor noise.- Parameters:
target (
_AuditoryRepresentationor None, default=None) – Auditory representation of the source directions to localise. Must be the same subclass astemplate.Nonereusestarget, computing it viacompute_targetif not already set. When combined withdirections, acts as the search pool from which directions are selected.directions (
pyfar.Coordinatesor None, default=None) – Desired source directions. The nearest neighbours in the resolvedtargetare selected via great-circle distance. AUserWarningis raised for any match farther than 10°. RaisesValueErrorif the intersection is empty.repetitions (int or None, default=None) – Number of Monte Carlo trials passed to
infer.Noneuses the default ofinfer(50).seed (int or None, default=None) – Seed for
numpy.random.default_rng.Noneyields a non-reproducible run.
- Returns:
Pointing responses of shape
(n_targets, repetitions, 3).- Return type:
- Raises:
ValueError – If
targetanddirectionsare both provided.ValueError – If
targethas a different convention thantemplate.ValueError – If
directionsfinds no match within the provided or storedtarget(empty intersection).
Examples
>>> responses = bl.localise(repetitions=1, seed=0) >>> responses.cartesian.shape (793, 1, 3)
- property parameters¶
Noise and prior parameters as a dict.
Required keys:
sigma_itd,sigma_ild,sigma_spectral,sigma_prior,kappa_motor. The setter raisesValueErrorif any key is missing.
- property sigma_ild¶
\(\sigma_{\mathrm{ild}}\), the ILD noise in dB.
- property sigma_itd¶
\(\sigma_{\mathrm{itd}}\), the warped-ITD noise (dimensionless).
- property sigma_motor¶
Motor-noise circular standard deviation \(\sigma_m\) in degrees.
Computed from
kappa_motorvia the Bessel-ratio identity \(R = I_1(\kappa_m)/I_0(\kappa_m) = \exp(-\sigma_m^2/2)\). Seekappa_to_sigma.
- property sigma_prior¶
\(\sigma_{\mathrm{prior}}\), the elevation prior width in degrees.
- property sigma_spectral¶
\(\sigma_{\mathrm{mon}}\), the monaural spectral noise in dB.
- property target¶
Auditory representation of the stimulus, or
Noneif not computed.Set by
compute_target. Assigning a value validates that it is aBarumerli2023instance (or subclass, orNone); otherwise raisesTypeError.
- property template¶
Listener’s internal template, or
Noneif not computed.Set by
compute_template. Assigning a value validates that it is aBarumerli2023instance (or subclass, orNone); otherwise raisesTypeError.
Auditory Representation¶
The concrete auditory representation used by the default workflow: ITD + ILD + monaural spectral amplitude envelopes (Barumerli et al.[2]).
- class bayesian_listener.Barumerli2023(convention='Barumerli2023', coords=None, itd=None, ild=None, spectral_cues=None, freqs=None)[source]¶
Bases:
_AuditoryRepresentationITD + ILD + spectral-amplitude representation of Barumerli et al.[2].
Builds the feature vector \(\mathbf{t} = [x_{\mathrm{itd}}, x_{\mathrm{ild}}, \mathbf{x}_{L,\mathrm{mon}}, \mathbf{x}_{R,\mathrm{mon}}]\) (Eq. 1 of Barumerli et al.[1]) with monaural cues as gammatone-bank log-amplitudes.
n_features = 2 + 2 * n_freqswheren_freqsis typically 28 (ERB-spaced centre frequencies between 700 Hz and 18 kHz).- Parameters:
- coords¶
Source positions, one per row.
- Type:
- itd¶
Warped interaural time differences (signed log of ITD in seconds, as produced by
compute_features), shape(n_dirs, 1).- Type:
- ild¶
Interaural level differences in dB, shape
(n_dirs, 1).- Type:
- spectral_cues¶
Monaural log-amplitude spectra for the left and right ears in dB, shape
(n_dirs, n_freqs, 2).- Type:
- freqs¶
Filterbank centre frequencies in Hz, shape
(n_freqs,).- Type:
- features¶
Pre-computed concatenation
[itd, ild, spectral_L, spectral_R]of shape(n_dirs, 2 + 2*n_freqs). Built in__post_init__.- Type:
Examples
Construct from raw HRIRs via the helper in
bayesian_listener.utils:>>> from bayesian_listener.utils import compute_features >>> itd, ild, spec, freqs = compute_features(hrir, coords, fs) >>> repr_ = Barumerli2023(coords=coords, itd=itd, ild=ild, ... spectral_cues=spec, freqs=freqs) >>> repr_.features.shape[1] == 2 + 2 * freqs.size True
- sigma_matrix(parameters)[source]¶
Diagonal sensory covariance (Eq. 2 of Barumerli et al.[1]).
Builds \(\boldsymbol{\Sigma} = \mathrm{diag}( \sigma_{\mathrm{itd}}^2,\, \sigma_{\mathrm{ild}}^2,\, \sigma_{\mathrm{mon}}^2 \mathbf{I}_{2\,n_{\mathrm{freqs}}})\).
- Parameters:
parameters (dict) – Must contain keys
sigma_itd(dimensionless),sigma_ild(dB),sigma_spectral(dB).- Returns:
Diagonal covariance of shape
(n_features, n_features).- Return type:
Examples
>>> Sigma = repr_.sigma_matrix( ... {'sigma_itd': 0.569, 'sigma_ild': 1.0, 'sigma_spectral': 10.4} ... ) >>> Sigma.shape == (repr_.features.shape[1],) * 2 True