Skip to main content

scirs2/
special.rs

1//! Python bindings for scirs2-special
2//!
3//! Provides special mathematical functions similar to scipy.special
4
5use pyo3::prelude::*;
6use scirs2_core::python::numpy_compat::{scirs_to_numpy_array1, Array1};
7use scirs2_numpy::{PyArray1, PyReadonlyArray1};
8
9// Import special functions from scirs2-special
10use scirs2_special::{bessel, erf as erf_mod, gamma as gamma_fn};
11
12// =============================================================================
13// Gamma Functions
14// =============================================================================
15
16/// Gamma function
17#[pyfunction]
18fn gamma_py(x: f64) -> PyResult<f64> {
19    Ok(gamma_fn(x))
20}
21
22/// Log-gamma function
23#[pyfunction]
24fn lgamma_py(x: f64) -> PyResult<f64> {
25    Ok(scirs2_special::gamma::gammaln(x))
26}
27
28/// Digamma (psi) function
29#[pyfunction]
30fn digamma_py(x: f64) -> PyResult<f64> {
31    Ok(scirs2_special::gamma::digamma(x))
32}
33
34/// Beta function
35#[pyfunction]
36fn beta_py(a: f64, b: f64) -> PyResult<f64> {
37    Ok(scirs2_special::gamma::beta(a, b))
38}
39
40// =============================================================================
41// Bessel Functions
42// =============================================================================
43
44/// Bessel function of the first kind, J0
45#[pyfunction]
46fn j0_py(x: f64) -> PyResult<f64> {
47    Ok(bessel::j0(x))
48}
49
50/// Bessel function of the first kind, J1
51#[pyfunction]
52fn j1_py(x: f64) -> PyResult<f64> {
53    Ok(bessel::j1(x))
54}
55
56/// Bessel function of the first kind, Jn
57#[pyfunction]
58fn jn_py(n: i32, x: f64) -> PyResult<f64> {
59    Ok(bessel::jn(n, x))
60}
61
62/// Bessel function of the second kind, Y0
63#[pyfunction]
64fn y0_py(x: f64) -> PyResult<f64> {
65    Ok(bessel::y0(x))
66}
67
68/// Bessel function of the second kind, Y1
69#[pyfunction]
70fn y1_py(x: f64) -> PyResult<f64> {
71    Ok(bessel::y1(x))
72}
73
74/// Bessel function of the second kind, Yn
75#[pyfunction]
76fn yn_py(n: i32, x: f64) -> PyResult<f64> {
77    Ok(bessel::yn(n, x))
78}
79
80/// Modified Bessel function of the first kind, I0
81#[pyfunction]
82fn i0_py(x: f64) -> PyResult<f64> {
83    Ok(bessel::i0(x))
84}
85
86/// Modified Bessel function of the first kind, I1
87#[pyfunction]
88fn i1_py(x: f64) -> PyResult<f64> {
89    Ok(bessel::i1(x))
90}
91
92/// Modified Bessel function of the second kind, K0
93#[pyfunction]
94fn k0_py(x: f64) -> PyResult<f64> {
95    Ok(bessel::k0(x))
96}
97
98/// Modified Bessel function of the second kind, K1
99#[pyfunction]
100fn k1_py(x: f64) -> PyResult<f64> {
101    Ok(bessel::k1(x))
102}
103
104// =============================================================================
105// Error Functions
106// =============================================================================
107
108/// Error function
109#[pyfunction]
110fn erf_py(x: f64) -> PyResult<f64> {
111    Ok(erf_mod::erf(x))
112}
113
114/// Complementary error function
115#[pyfunction]
116fn erfc_py(x: f64) -> PyResult<f64> {
117    Ok(erf_mod::erfc(x))
118}
119
120/// Inverse error function
121#[pyfunction]
122fn erfinv_py(x: f64) -> PyResult<f64> {
123    Ok(erf_mod::erfinv(x))
124}
125
126/// Inverse complementary error function
127#[pyfunction]
128fn erfcinv_py(x: f64) -> PyResult<f64> {
129    Ok(erf_mod::erfcinv(x))
130}
131
132/// Scaled complementary error function
133#[pyfunction]
134fn erfcx_py(x: f64) -> PyResult<f64> {
135    Ok(erf_mod::erfcx(x))
136}
137
138/// Imaginary error function
139#[pyfunction]
140fn erfi_py(x: f64) -> PyResult<f64> {
141    Ok(erf_mod::erfi(x))
142}
143
144/// Dawson's integral
145#[pyfunction]
146fn dawsn_py(x: f64) -> PyResult<f64> {
147    Ok(erf_mod::dawsn(x))
148}
149
150// =============================================================================
151// Combinatorial Functions
152// =============================================================================
153
154/// Factorial function
155#[pyfunction]
156fn factorial_py(n: u32) -> PyResult<f64> {
157    scirs2_special::factorial(n)
158        .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))
159}
160
161/// Binomial coefficient (n choose k)
162#[pyfunction]
163fn comb_py(n: u32, k: u32) -> PyResult<f64> {
164    scirs2_special::comb(n, k)
165        .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))
166}
167
168/// Permutations (n permute k)
169#[pyfunction]
170fn perm_py(n: u32, k: u32) -> PyResult<f64> {
171    scirs2_special::perm(n, k)
172        .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))
173}
174
175// =============================================================================
176// Elliptic Integrals
177// =============================================================================
178
179/// Complete elliptic integral of the first kind
180#[pyfunction]
181fn ellipk_py(m: f64) -> PyResult<f64> {
182    Ok(scirs2_special::elliptic_k(m))
183}
184
185/// Complete elliptic integral of the second kind
186#[pyfunction]
187fn ellipe_py(m: f64) -> PyResult<f64> {
188    Ok(scirs2_special::elliptic_e(m))
189}
190
191/// Incomplete elliptic integral of the first kind
192#[pyfunction]
193fn ellipkinc_py(phi: f64, m: f64) -> PyResult<f64> {
194    Ok(scirs2_special::elliptic_f(phi, m))
195}
196
197/// Incomplete elliptic integral of the second kind
198#[pyfunction]
199fn ellipeinc_py(phi: f64, m: f64) -> PyResult<f64> {
200    Ok(scirs2_special::elliptic_e_inc(phi, m))
201}
202
203// =============================================================================
204// Vectorized versions (for arrays)
205// =============================================================================
206
207/// Vectorized gamma function
208#[pyfunction]
209fn gamma_array_py(py: Python, x: PyReadonlyArray1<f64>) -> PyResult<Py<PyArray1<f64>>> {
210    let input = x.as_array();
211    let output: Vec<f64> = input.iter().map(|&v| gamma_fn(v)).collect();
212    scirs_to_numpy_array1(Array1::from_vec(output), py)
213}
214
215/// Vectorized error function
216#[pyfunction]
217fn erf_array_py(py: Python, x: PyReadonlyArray1<f64>) -> PyResult<Py<PyArray1<f64>>> {
218    let input = x.as_array();
219    let output: Vec<f64> = input.iter().map(|&v| erf_mod::erf(v)).collect();
220    scirs_to_numpy_array1(Array1::from_vec(output), py)
221}
222
223/// Vectorized J0 Bessel function
224#[pyfunction]
225fn j0_array_py(py: Python, x: PyReadonlyArray1<f64>) -> PyResult<Py<PyArray1<f64>>> {
226    let input = x.as_array();
227    let output: Vec<f64> = input.iter().map(|&v| bessel::j0(v)).collect();
228    scirs_to_numpy_array1(Array1::from_vec(output), py)
229}
230
231/// Python module registration
232pub fn register_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
233    // Gamma functions
234    m.add_function(wrap_pyfunction!(gamma_py, m)?)?;
235    m.add_function(wrap_pyfunction!(lgamma_py, m)?)?;
236    m.add_function(wrap_pyfunction!(digamma_py, m)?)?;
237    m.add_function(wrap_pyfunction!(beta_py, m)?)?;
238
239    // Bessel functions
240    m.add_function(wrap_pyfunction!(j0_py, m)?)?;
241    m.add_function(wrap_pyfunction!(j1_py, m)?)?;
242    m.add_function(wrap_pyfunction!(jn_py, m)?)?;
243    m.add_function(wrap_pyfunction!(y0_py, m)?)?;
244    m.add_function(wrap_pyfunction!(y1_py, m)?)?;
245    m.add_function(wrap_pyfunction!(yn_py, m)?)?;
246    m.add_function(wrap_pyfunction!(i0_py, m)?)?;
247    m.add_function(wrap_pyfunction!(i1_py, m)?)?;
248    m.add_function(wrap_pyfunction!(k0_py, m)?)?;
249    m.add_function(wrap_pyfunction!(k1_py, m)?)?;
250
251    // Error functions
252    m.add_function(wrap_pyfunction!(erf_py, m)?)?;
253    m.add_function(wrap_pyfunction!(erfc_py, m)?)?;
254    m.add_function(wrap_pyfunction!(erfinv_py, m)?)?;
255    m.add_function(wrap_pyfunction!(erfcinv_py, m)?)?;
256    m.add_function(wrap_pyfunction!(erfcx_py, m)?)?;
257    m.add_function(wrap_pyfunction!(erfi_py, m)?)?;
258    m.add_function(wrap_pyfunction!(dawsn_py, m)?)?;
259
260    // Combinatorial functions
261    m.add_function(wrap_pyfunction!(factorial_py, m)?)?;
262    m.add_function(wrap_pyfunction!(comb_py, m)?)?;
263    m.add_function(wrap_pyfunction!(perm_py, m)?)?;
264
265    // Elliptic integrals
266    m.add_function(wrap_pyfunction!(ellipk_py, m)?)?;
267    m.add_function(wrap_pyfunction!(ellipe_py, m)?)?;
268    m.add_function(wrap_pyfunction!(ellipkinc_py, m)?)?;
269    m.add_function(wrap_pyfunction!(ellipeinc_py, m)?)?;
270
271    // Vectorized versions
272    m.add_function(wrap_pyfunction!(gamma_array_py, m)?)?;
273    m.add_function(wrap_pyfunction!(erf_array_py, m)?)?;
274    m.add_function(wrap_pyfunction!(j0_array_py, m)?)?;
275
276    Ok(())
277}