Skip to content

Port ComplexWatsonDistribution and BayesianComplexWatsonMixtureModel from MATLAB#1648

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/port-bayesian-complex-watson-mixture-model
Draft

Port ComplexWatsonDistribution and BayesianComplexWatsonMixtureModel from MATLAB#1648
Copilot wants to merge 2 commits intomainfrom
copilot/port-bayesian-complex-watson-mixture-model

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 6, 2026

Summary

Ports two MATLAB files from libDirectional to Python:

ComplexWatsonDistribution

Distribution on the complex unit sphere in C^D with PDF:

f(z; mu, kappa) = C(D, kappa)^{-1} * exp(kappa * |mu^H z|^2)

Key methods:

  • log_norm(D, kappa) — log normalisation constant using 3 numerical regimes (Taylor series / intermediate correction / asymptotic) for stability
  • pdf(Z) — evaluates PDF at columns of complex matrix Z
  • sample(n) — samples via the complex Bingham representation
  • fit(Z) / estimate_parameters(Z) — MLE via scatter matrix eigendecomposition + hypergeometric ratio inverse (with asymptotic fallback to avoid hyp1f1 overflow for large κ)

BayesianComplexWatsonMixtureModel

Bayesian mixture model with complex Bingham priors on mode vectors and a Dirichlet prior on mixture weights. Posterior fitting uses a variational EM algorithm.

Key methods:

  • fit(Z, parameters) / fit_default(Z, K) — run EM and return (model, posterior)
  • parameters_default(D, K) — default hyperparameter dict
  • estimate_posterior(Z, parameters) — full E/M-step loop
  • quadratic_expectation(dyadic_products, B) — E[z^H A z] under complex Bingham, via first-order moments computed by numerical differentiation of the simplex integral

Helper functions (module-private):

  • _simplex_integral — evaluates ∫_simplex exp(Λ·s) ds via the partial-fractions formula
  • _complex_bingham_first_order_moments — E[|z_i|^2] by numerical differentiation of the log-normalization

Changes

  • pyrecets/distributions/hypersphere_subset/complex_watson_distribution.py — new file
  • pyrecets/distributions/hypersphere_subset/bayesian_complex_watson_mixture_model.py — new file
  • pyrecets/distributions/__init__.py — exports ComplexWatsonDistribution and BayesianComplexWatsonMixtureModel
  • pyrecets/tests/distributions/test_complex_watson_distribution.py — 14 tests
  • pyrecets/tests/distributions/test_bayesian_complex_watson_mixture_model.py — 19 tests

All 33 tests pass with no warnings.

Copilot AI and others added 2 commits April 6, 2026 05:55
…from MATLAB

Agent-Logs-Url: https://github.com/FlorianPfaff/PyRecEst/sessions/3f0bd299-8203-4bff-91e8-b8e62be085a8

Co-authored-by: FlorianPfaff <6773539+FlorianPfaff@users.noreply.github.com>
…up mask evaluation

Agent-Logs-Url: https://github.com/FlorianPfaff/PyRecEst/sessions/3f0bd299-8203-4bff-91e8-b8e62be085a8

Co-authored-by: FlorianPfaff <6773539+FlorianPfaff@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

MegaLinter analysis: Error

Descriptor Linter Files Fixed Errors Warnings Elapsed time
✅ COPYPASTE jscpd yes no no 10.26s
✅ JSON prettier 2 0 0 0 0.47s
✅ JSON v8r 2 0 0 2.9s
✅ MARKDOWN markdownlint 1 0 0 0 0.66s
✅ MARKDOWN markdown-table-formatter 1 0 0 0 0.2s
✅ PYTHON bandit 290 0 0 3.7s
✅ PYTHON black 290 10 0 0 5.97s
❌ PYTHON flake8 290 3 0 2.13s
✅ PYTHON isort 290 14 0 0 0.56s
✅ PYTHON mypy 290 0 0 4.25s
❌ PYTHON pylint 290 15 0 88.02s
❌ PYTHON ruff 290 14 3 0 0.08s
✅ REPOSITORY checkov yes no no 23.44s
✅ REPOSITORY gitleaks yes no no 4.52s
✅ REPOSITORY git_diff yes no no 0.02s
✅ REPOSITORY secretlint yes no no 6.12s
✅ REPOSITORY syft yes no no 3.38s
✅ REPOSITORY trivy-sbom yes no no 1.93s
✅ REPOSITORY trufflehog yes no no 18.3s
✅ YAML prettier 4 0 0 0 0.53s
✅ YAML v8r 4 0 0 5.05s
✅ YAML yamllint 4 0 0 0.42s

Detailed Issues

❌ PYTHON / flake8 - 3 errors
pyrecest/distributions/hypersphere_subset/complex_watson_distribution.py:15:1: F401 'scipy.special.hyp1f1' imported but unused
pyrecest/tests/distributions/test_bayesian_complex_watson_mixture_model.py:61:9: F841 local variable 'rng' is assigned to but never used
pyrecest/tests/distributions/test_bayesian_complex_watson_mixture_model.py:180:9: F841 local variable 'rng' is assigned to but never used
❌ PYTHON / pylint - 15 errors
************* Module pyrecest.distributions.hypersphere_subset.bayesian_complex_watson_mixture_model
pyrecest/distributions/hypersphere_subset/bayesian_complex_watson_mixture_model.py:145:4: R0914: Too many local variables (27/15) (too-many-locals)
pyrecest/distributions/hypersphere_subset/bayesian_complex_watson_mixture_model.py:270:20: W0212: Access to a protected member _hypergeometric_ratio_inverse of a client class (protected-access)
pyrecest/distributions/hypersphere_subset/bayesian_complex_watson_mixture_model.py:145:4: R0915: Too many statements (56/50) (too-many-statements)
pyrecest/distributions/hypersphere_subset/bayesian_complex_watson_mixture_model.py:278:4: R0914: Too many local variables (18/15) (too-many-locals)
************* Module pyrecest.distributions.hypersphere_subset.complex_watson_distribution
pyrecest/distributions/hypersphere_subset/complex_watson_distribution.py:15:0: E0611: No name 'gammaln' in module 'scipy.special' (no-name-in-module)
pyrecest/distributions/hypersphere_subset/complex_watson_distribution.py:15:0: E0611: No name 'hyp1f1' in module 'scipy.special' (no-name-in-module)
pyrecest/distributions/hypersphere_subset/complex_watson_distribution.py:50:4: R0914: Too many local variables (16/15) (too-many-locals)
pyrecest/distributions/hypersphere_subset/complex_watson_distribution.py:15:0: W0611: Unused hyp1f1 imported from scipy.special (unused-import)
************* Module pyrecest.tests.distributions.test_bayesian_complex_watson_mixture_model
pyrecest/tests/distributions/test_bayesian_complex_watson_mixture_model.py:61:8: W0612: Unused variable 'rng' (unused-variable)
pyrecest/tests/distributions/test_bayesian_complex_watson_mixture_model.py:130:15: W0612: Unused variable 'posterior' (unused-variable)
pyrecest/tests/distributions/test_bayesian_complex_watson_mixture_model.py:180:8: W0612: Unused variable 'rng' (unused-variable)
************* Module pyrecest.tests.distributions.test_complex_watson_distribution
pyrecest/tests/distributions/test_complex_watson_distribution.py:105:13: W0212: Access to a protected member _hypergeometric_ratio of a client class (protected-access)
pyrecest/tests/distributions/test_complex_watson_distribution.py:106:18: W0212: Access to a protected member _hypergeometric_ratio of a client class (protected-access)
pyrecest/tests/distributions/test_complex_watson_distribution.py:114:16: W0212: Access to a protected member _hypergeometric_ratio of a client class (protected-access)
pyrecest/tests/distributions/test_complex_watson_distribution.py:115:24: W0212: Access to a protected member _hypergeometric_ratio_inverse of a client class (protected-access)

-----------------------------------
Your code has been rated at 9.98/10
❌ PYTHON / ruff - 3 errors
F841 Local variable `rng` is assigned to but never used
  --> pyrecest/tests/distributions/test_bayesian_complex_watson_mixture_model.py:60:9
   |
58 |         D = 3
59 |         K = 2
60 |         rng = np.random.default_rng(7)
   |         ^^^
61 |         B = np.zeros((D, D, K), dtype=complex)
62 |         I_3d = np.eye(D, dtype=complex)[:, :, np.newaxis]
   |
help: Remove assignment to unused variable `rng`

F841 Local variable `rng` is assigned to but never used
   --> pyrecest/tests/distributions/test_bayesian_complex_watson_mixture_model.py:179:9
    |
177 |     def test_fit_two_cluster_recovery(self):
178 |         """Fit on data from two distinct clusters should assign high weight to each."""
179 |         rng = np.random.default_rng(99)
    |         ^^^
180 |         D = 3
181 |         # Two orthogonal modes
    |
help: Remove assignment to unused variable `rng`

Found 3 errors (1 fixed, 2 remaining).
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).

See detailed reports in MegaLinter artifacts

Your project could benefit from a custom flavor, which would allow you to run only the linters you need, and thus improve runtime performances. (Skip this info by defining FLAVOR_SUGGESTIONS: false)

  • Documentation: Custom Flavors
  • Command: npx mega-linter-runner@9.4.0 --custom-flavor-setup --custom-flavor-linters PYTHON_PYLINT,PYTHON_BLACK,PYTHON_FLAKE8,PYTHON_ISORT,PYTHON_BANDIT,PYTHON_MYPY,PYTHON_RUFF,COPYPASTE_JSCPD,JSON_V8R,JSON_PRETTIER,MARKDOWN_MARKDOWNLINT,MARKDOWN_MARKDOWN_TABLE_FORMATTER,REPOSITORY_CHECKOV,REPOSITORY_GIT_DIFF,REPOSITORY_GITLEAKS,REPOSITORY_SECRETLINT,REPOSITORY_SYFT,REPOSITORY_TRIVY_SBOM,REPOSITORY_TRUFFLEHOG,YAML_PRETTIER,YAML_YAMLLINT,YAML_V8R

MegaLinter is graciously provided by OX Security
Show us your support by starring ⭐ the repository

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

Test Results

   12 files     12 suites   4h 43m 58s ⏱️
  768 tests   768 ✅     0 💤 0 ❌
9 500 runs  8 028 ✅ 1 472 💤 0 ❌

Results for commit 04f3df2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants