Skip to content

Port SE2BinghamDistribution from libDirectional#1603

Open
Copilot wants to merge 3 commits intomainfrom
copilot/port-se2bingham-distribution
Open

Port SE2BinghamDistribution from libDirectional#1603
Copilot wants to merge 3 commits intomainfrom
copilot/port-se2bingham-distribution

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 1, 2026

Ports SE2BinghamDistribution from the MATLAB libDirectional library. Models uncertainty on SE(2) = S¹ × ℝ² (rotation + 2D translation) via a Bingham–Gaussian density f(x) = (1/NC) · exp(xᵀCx) in dual-quaternion space.

New: SE2BinghamDistribution

  • Constructor — accepts a full 4×4 symmetric C or three 2×2 blocks (C1, C2, C3); validates symmetry and negative-definiteness of C3
  • pdf(xa) — evaluates density on N×3 angle-pos or N×4 dual-quaternion input; auto-converts between representations
  • mode() — closed-form via Schur complement eigenvector for rotation + conditional optimum for translation
  • sample(n) — two-step: Bingham marginal from Schur complement eigendecomp → Gaussian conditional with covariance −½ C3⁻¹
  • marginalize_linear() — exact Bingham marginal on S¹
  • marginalize_periodic()CustomLinearDistribution via numerical integration over S¹
  • fit(samples, weights=None) — moment-based MLE from DQ or angle-pos samples (Anderson 2003 regression for the Gaussian block)

Usage

import numpy as np
from pyrecest.backend import array
from pyrecest.distributions import SE2BinghamDistribution

C1 = array([[-3.0, 0.5], [0.5, -1.0]])   # Bingham (rotation) block
C2 = array([[0.1, 0.2], [-0.1, 0.3]])     # coupling
C3 = array([[-2.0, 0.1], [0.1, -1.5]])   # Gaussian (translation) block, must be neg-def

dist = SE2BinghamDistribution(C1, C2, C3)

samples = dist.sample(500)                 # (500, 4) dual-quaternion samples
fitted  = SE2BinghamDistribution.fit(samples)

Normalization constant uses the closed-form 2D Bingham formula 2π · exp((z₁+z₂)/2) · I₀((z₂−z₁)/2) on the Schur complement eigenvalues, avoiding costly numerical integration.

Agent-Logs-Url: https://github.com/FlorianPfaff/PyRecEst/sessions/ee18ade8-c36d-4ab1-bae9-704f18af7312

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

github-actions bot commented Apr 1, 2026

MegaLinter analysis: Success

Descriptor Linter Files Fixed Errors Warnings Elapsed time
✅ COPYPASTE jscpd yes no no 7.27s
✅ JSON prettier 2 0 0 0 0.47s
✅ JSON v8r 2 0 0 2.37s
✅ MARKDOWN markdownlint 1 0 0 0 0.68s
✅ MARKDOWN markdown-table-formatter 1 0 0 0 0.21s
✅ PYTHON bandit 252 0 0 3.18s
✅ PYTHON black 252 1 0 0 4.61s
✅ PYTHON flake8 252 0 0 1.76s
✅ PYTHON isort 252 1 0 0 0.49s
✅ PYTHON mypy 252 0 0 4.02s
✅ PYTHON pylint 252 0 0 71.88s
✅ PYTHON ruff 252 1 0 0 0.04s
✅ REPOSITORY checkov yes no no 21.41s
✅ REPOSITORY gitleaks yes no no 4.18s
✅ REPOSITORY git_diff yes no no 0.02s
✅ REPOSITORY secretlint yes no no 5.52s
✅ REPOSITORY syft yes no no 3.1s
✅ REPOSITORY trivy-sbom yes no no 1.69s
✅ REPOSITORY trufflehog yes no no 18.13s
✅ YAML prettier 4 0 0 0 0.48s
✅ YAML v8r 4 0 0 5.09s
✅ YAML yamllint 4 0 0 0.4s

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 1, 2026

Test Results

    4 files   -     8      4 suites   - 8   19m 51s ⏱️ - 2h 53m 4s
  596 tests +   25    596 ✅ +   25  0 💤 ±    0  0 ❌ ±0 
2 532 runs   - 4 532  2 532 ✅  - 3 348  0 💤  - 1 184  0 ❌ ±0 

Results for commit 5686351. ± Comparison against base commit 6c7ac6d.

♻️ This comment has been updated with latest results.

@FlorianPfaff
Copy link
Copy Markdown
Owner

@copilot Can you fix these linter problems?

❌ PYTHON / flake8 - 3 errors
pyrecest/distributions/cart_prod/se2_bingham_distribution.py:6:1: F401 'pyrecest.backend.random' imported but unused
pyrecest/distributions/cart_prod/se2_bingham_distribution.py:6:1: F401 'pyrecest.backend.zeros' imported but unused
pyrecest/distributions/cart_prod/se2_bingham_distribution.py:205:17: F821 undefined name 'argsort'
❌ PYTHON / pylint - 6 errors
************* Module pyrecest.distributions.cart_prod.se2_bingham_distribution
pyrecest/distributions/cart_prod/se2_bingham_distribution.py:131:4: W0237: Parameter 'xs' has been renamed to 'xa' in overriding 'SE2BinghamDistribution.pdf' method (arguments-renamed)
pyrecest/distributions/cart_prod/se2_bingham_distribution.py:176:4: R0914: Too many local variables (17/15) (too-many-locals)
pyrecest/distributions/cart_prod/se2_bingham_distribution.py:203:16: E0602: Undefined variable 'argsort' (undefined-variable)
pyrecest/distributions/cart_prod/se2_bingham_distribution.py:280:4: R0914: Too many local variables (20/15) (too-many-locals)
pyrecest/distributions/cart_prod/se2_bingham_distribution.py:6:0: W0611: Unused random imported from pyrecest.backend (unused-import)
pyrecest/distributions/cart_prod/se2_bingham_distribution.py:6:0: W0611: Unused zeros imported from pyrecest.backend (unused-import)


Your code has been rated at 9.99/10
❌ PYTHON / ruff - 3 errors
F821 Undefined name argsort
--> pyrecest/distributions/cart_prod/se2_bingham_distribution.py:202:17
|
200 | bingham_c = C1 - C2.T @ C3_inv @ C2
201 | eigenvalues, eigenvectors = linalg.eigh(bingham_c)
202 | order = argsort(eigenvalues) # ascending
| ^^^^^^^
203 | eigenvalues = eigenvalues[order]
204 | eigenvectors = eigenvectors[:, order]
|

Found 3 errors (2 fixed, 1 remaining).

Agent-Logs-Url: https://github.com/FlorianPfaff/PyRecEst/sessions/a660fc0d-1728-41d5-bac5-7eb01c48b623

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

Copilot AI commented Apr 2, 2026

@copilot Can you fix these linter problems?

❌ PYTHON / flake8 - 3 errors
...

Fixed in commit 5686351:

  • Removed unused random and zeros imports; added missing argsort
  • Renamed pdf(self, xa)pdf(self, xs) to match the parent method signature
  • Reduced local variable counts in sample() and fit() by extracting two private static helpers (_schur_from_scatter, _fit_gaussian_block)

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