1use mdarray::Shape;
14use num_complex::Complex64;
15use std::panic::{AssertUnwindSafe, catch_unwind};
16use std::sync::Arc;
17
18use crate::gemm::{get_backend_handle, spir_gemm_backend};
19use crate::types::{BasisType, SamplingType, spir_basis, spir_sampling};
20use crate::utils::{
21 MemoryOrder, build_output_dims, convert_dims_for_row_major, create_dview_from_ptr,
22 create_dviewmut_from_ptr, read_tensor_nd,
23};
24use crate::{
25 SPIR_COMPUTATION_SUCCESS, SPIR_INVALID_ARGUMENT, SPIR_NOT_SUPPORTED, SPIR_STATISTICS_BOSONIC,
26 SPIR_STATISTICS_FERMIONIC, StatusCode,
27};
28use sparse_ir::fitters::InplaceFitter;
29use sparse_ir::{Bosonic, Fermionic};
30
31#[unsafe(no_mangle)]
33pub extern "C" fn spir_sampling_release(sampling: *mut spir_sampling) {
34 if !sampling.is_null() {
35 unsafe {
36 let _ = Box::from_raw(sampling);
37 }
38 }
39}
40
41#[unsafe(no_mangle)]
43pub extern "C" fn spir_sampling_clone(src: *const spir_sampling) -> *mut spir_sampling {
44 if src.is_null() {
45 return std::ptr::null_mut();
46 }
47
48 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
49 let src_ref = &*src;
50 let cloned = (*src_ref).clone();
51 Box::into_raw(Box::new(cloned))
52 }));
53
54 result.unwrap_or(std::ptr::null_mut())
55}
56
57#[unsafe(no_mangle)]
66pub extern "C" fn spir_sampling_is_assigned(obj: *const spir_sampling) -> i32 {
67 if obj.is_null() { 0 } else { 1 }
68}
69
70#[unsafe(no_mangle)]
88pub extern "C" fn spir_tau_sampling_new(
89 b: *const spir_basis,
90 num_points: libc::c_int,
91 points: *const f64,
92 status: *mut StatusCode,
93) -> *mut spir_sampling {
94 let result = catch_unwind(AssertUnwindSafe(|| {
95 if b.is_null() || points.is_null() {
97 return (std::ptr::null_mut(), SPIR_INVALID_ARGUMENT);
98 }
99 if num_points <= 0 {
100 return (std::ptr::null_mut(), SPIR_INVALID_ARGUMENT);
101 }
102
103 let basis_ref = unsafe { &*b };
104 let points_slice = unsafe { std::slice::from_raw_parts(points, num_points as usize) };
105
106 let tau_points: Vec<f64> = points_slice.to_vec();
108
109 let sampling_type = match basis_ref.inner() {
111 BasisType::LogisticFermionic(ir_basis) => {
112 let tau_sampling = sparse_ir::sampling::TauSampling::with_sampling_points(
113 ir_basis.as_ref(),
114 tau_points,
115 );
116 SamplingType::TauFermionic(Arc::new(tau_sampling))
117 }
118 BasisType::RegularizedBoseFermionic(ir_basis) => {
119 let tau_sampling = sparse_ir::sampling::TauSampling::with_sampling_points(
120 ir_basis.as_ref(),
121 tau_points,
122 );
123 SamplingType::TauFermionic(Arc::new(tau_sampling))
124 }
125 BasisType::LogisticBosonic(ir_basis) => {
126 let tau_sampling = sparse_ir::sampling::TauSampling::with_sampling_points(
127 ir_basis.as_ref(),
128 tau_points,
129 );
130 SamplingType::TauBosonic(Arc::new(tau_sampling))
131 }
132 BasisType::RegularizedBoseBosonic(ir_basis) => {
133 let tau_sampling = sparse_ir::sampling::TauSampling::with_sampling_points(
134 ir_basis.as_ref(),
135 tau_points,
136 );
137 SamplingType::TauBosonic(Arc::new(tau_sampling))
138 }
139 BasisType::DLRFermionic(dlr) => {
141 let tau_sampling = sparse_ir::sampling::TauSampling::with_sampling_points(
142 dlr.as_ref(),
143 tau_points,
144 );
145 SamplingType::TauFermionic(Arc::new(tau_sampling))
146 }
147 BasisType::DLRBosonic(dlr) => {
148 let tau_sampling = sparse_ir::sampling::TauSampling::with_sampling_points(
149 dlr.as_ref(),
150 tau_points,
151 );
152 SamplingType::TauBosonic(Arc::new(tau_sampling))
153 }
154 };
155
156 let inner = sampling_type;
157 let sampling = spir_sampling {
158 _private: Box::into_raw(Box::new(inner)) as *mut std::ffi::c_void,
159 };
160
161 (Box::into_raw(Box::new(sampling)), SPIR_COMPUTATION_SUCCESS)
162 }));
163
164 match result {
165 Ok((ptr, code)) => {
166 if !status.is_null() {
167 unsafe {
168 *status = code;
169 }
170 }
171 ptr
172 }
173 Err(_) => {
174 if !status.is_null() {
175 unsafe {
176 *status = crate::SPIR_INTERNAL_ERROR;
177 }
178 }
179 std::ptr::null_mut()
180 }
181 }
182}
183
184#[unsafe(no_mangle)]
196pub extern "C" fn spir_matsu_sampling_new(
197 b: *const spir_basis,
198 positive_only: bool,
199 num_points: libc::c_int,
200 points: *const i64,
201 status: *mut StatusCode,
202) -> *mut spir_sampling {
203 let result = catch_unwind(AssertUnwindSafe(|| {
204 if b.is_null() || points.is_null() {
206 return (std::ptr::null_mut(), SPIR_INVALID_ARGUMENT);
207 }
208 if num_points <= 0 {
209 return (std::ptr::null_mut(), SPIR_INVALID_ARGUMENT);
210 }
211
212 let basis_ref = unsafe { &*b };
213 let points_slice = unsafe { std::slice::from_raw_parts(points, num_points as usize) };
214
215 let matsu_points: Vec<i64> = points_slice.to_vec();
217
218 use sparse_ir::freq::MatsubaraFreq;
220
221 macro_rules! create_matsu_sampling {
223 ($basis:expr, Fermionic) => {
224 if positive_only {
225 let matsu_freqs: Vec<MatsubaraFreq<Fermionic>> = matsu_points
226 .iter()
227 .map(|&n| MatsubaraFreq::new(n).expect("Invalid Matsubara frequency"))
228 .collect();
229 let matsu_sampling = sparse_ir::matsubara_sampling::MatsubaraSamplingPositiveOnly::with_sampling_points(
230 $basis,
231 matsu_freqs,
232 );
233 SamplingType::MatsubaraPositiveOnlyFermionic(Arc::new(matsu_sampling))
234 } else {
235 let matsu_freqs: Vec<MatsubaraFreq<Fermionic>> = matsu_points
236 .iter()
237 .map(|&n| MatsubaraFreq::new(n).expect("Invalid Matsubara frequency"))
238 .collect();
239 let matsu_sampling = sparse_ir::matsubara_sampling::MatsubaraSampling::with_sampling_points(
240 $basis,
241 matsu_freqs,
242 );
243 SamplingType::MatsubaraFermionic(Arc::new(matsu_sampling))
244 }
245 };
246 ($basis:expr, Bosonic) => {
247 if positive_only {
248 let matsu_freqs: Vec<MatsubaraFreq<Bosonic>> = matsu_points
249 .iter()
250 .map(|&n| MatsubaraFreq::new(n).expect("Invalid Matsubara frequency"))
251 .collect();
252 let matsu_sampling = sparse_ir::matsubara_sampling::MatsubaraSamplingPositiveOnly::with_sampling_points(
253 $basis,
254 matsu_freqs,
255 );
256 SamplingType::MatsubaraPositiveOnlyBosonic(Arc::new(matsu_sampling))
257 } else {
258 let matsu_freqs: Vec<MatsubaraFreq<Bosonic>> = matsu_points
259 .iter()
260 .map(|&n| MatsubaraFreq::new(n).expect("Invalid Matsubara frequency"))
261 .collect();
262 let matsu_sampling = sparse_ir::matsubara_sampling::MatsubaraSampling::with_sampling_points(
263 $basis,
264 matsu_freqs,
265 );
266 SamplingType::MatsubaraBosonic(Arc::new(matsu_sampling))
267 }
268 };
269 }
270
271 let sampling_type = match basis_ref.inner() {
273 BasisType::LogisticFermionic(ir_basis) => {
274 create_matsu_sampling!(ir_basis.as_ref(), Fermionic)
275 }
276 BasisType::RegularizedBoseFermionic(ir_basis) => {
277 create_matsu_sampling!(ir_basis.as_ref(), Fermionic)
278 }
279 BasisType::LogisticBosonic(ir_basis) => {
280 create_matsu_sampling!(ir_basis.as_ref(), Bosonic)
281 }
282 BasisType::RegularizedBoseBosonic(ir_basis) => {
283 create_matsu_sampling!(ir_basis.as_ref(), Bosonic)
284 }
285 BasisType::DLRFermionic(dlr) => {
287 create_matsu_sampling!(dlr.as_ref(), Fermionic)
288 }
289 BasisType::DLRBosonic(dlr) => {
290 create_matsu_sampling!(dlr.as_ref(), Bosonic)
291 }
292 };
293
294 let inner = sampling_type;
295 let sampling = spir_sampling {
296 _private: Box::into_raw(Box::new(inner)) as *mut std::ffi::c_void,
297 };
298
299 (Box::into_raw(Box::new(sampling)), SPIR_COMPUTATION_SUCCESS)
300 }));
301
302 match result {
303 Ok((ptr, code)) => {
304 if !status.is_null() {
305 unsafe {
306 *status = code;
307 }
308 }
309 ptr
310 }
311 Err(_) => {
312 if !status.is_null() {
313 unsafe {
314 *status = crate::SPIR_INTERNAL_ERROR;
315 }
316 }
317 std::ptr::null_mut()
318 }
319 }
320}
321
322#[unsafe(no_mangle)]
339pub extern "C" fn spir_tau_sampling_new_with_matrix(
340 order: libc::c_int,
341 statistics: libc::c_int,
342 basis_size: libc::c_int,
343 num_points: libc::c_int,
344 points: *const f64,
345 matrix: *const f64,
346 status: *mut StatusCode,
347) -> *mut spir_sampling {
348 let result = catch_unwind(AssertUnwindSafe(|| {
349 if points.is_null() || matrix.is_null() {
351 return (std::ptr::null_mut(), SPIR_INVALID_ARGUMENT);
352 }
353 if num_points <= 0 || basis_size <= 0 {
354 return (std::ptr::null_mut(), SPIR_INVALID_ARGUMENT);
355 }
356
357 let mem_order = match MemoryOrder::from_c_int(order) {
359 Ok(o) => o,
360 Err(_) => return (std::ptr::null_mut(), SPIR_INVALID_ARGUMENT),
361 };
362
363 let points_slice = unsafe { std::slice::from_raw_parts(points, num_points as usize) };
365 let tau_points: Vec<f64> = points_slice.to_vec();
366
367 let orig_dims = [num_points as usize, basis_size as usize];
369 let dyn_tensor = unsafe { read_tensor_nd(matrix, &orig_dims, mem_order) };
370
371 let shape_dims = dyn_tensor.shape().with_dims(|dims| dims.to_vec());
373 assert_eq!(
374 shape_dims.len(),
375 2,
376 "Expected 2D tensor, got {}D",
377 shape_dims.len()
378 );
379 let num_points_actual = shape_dims[0];
380 let basis_size_actual = shape_dims[1];
381 let matrix_tensor =
382 sparse_ir::DTensor::<f64, 2>::from_fn([num_points_actual, basis_size_actual], |idx| {
383 dyn_tensor[&[idx[0], idx[1]][..]]
384 });
385 let sampling_type = match statistics {
387 SPIR_STATISTICS_FERMIONIC => {
388 let tau_sampling = sparse_ir::sampling::TauSampling::<Fermionic>::from_matrix(
390 tau_points,
391 matrix_tensor,
392 );
393 SamplingType::TauFermionic(Arc::new(tau_sampling))
394 }
395 SPIR_STATISTICS_BOSONIC => {
396 let tau_sampling = sparse_ir::sampling::TauSampling::<Bosonic>::from_matrix(
398 tau_points,
399 matrix_tensor,
400 );
401 SamplingType::TauBosonic(Arc::new(tau_sampling))
402 }
403 _ => return (std::ptr::null_mut(), SPIR_INVALID_ARGUMENT),
404 };
405
406 let inner = sampling_type;
407 let sampling = spir_sampling {
408 _private: Box::into_raw(Box::new(inner)) as *mut std::ffi::c_void,
409 };
410
411 (Box::into_raw(Box::new(sampling)), SPIR_COMPUTATION_SUCCESS)
412 }));
413
414 match result {
415 Ok((ptr, code)) => {
416 if !status.is_null() {
417 unsafe {
418 *status = code;
419 }
420 }
421 ptr
422 }
423 Err(_) => {
424 if !status.is_null() {
425 unsafe {
426 *status = crate::SPIR_INTERNAL_ERROR;
427 }
428 }
429 std::ptr::null_mut()
430 }
431 }
432}
433
434#[unsafe(no_mangle)]
452pub extern "C" fn spir_matsu_sampling_new_with_matrix(
453 order: libc::c_int,
454 statistics: libc::c_int,
455 basis_size: libc::c_int,
456 positive_only: bool,
457 num_points: libc::c_int,
458 points: *const i64,
459 matrix: *const Complex64,
460 status: *mut StatusCode,
461) -> *mut spir_sampling {
462 use std::io::Write;
463 debug_println!(
464 "spir_matsu_sampling_new_with_matrix: start, order={}, statistics={}, basis_size={}, positive_only={}, num_points={}",
465 order,
466 statistics,
467 basis_size,
468 positive_only,
469 num_points
470 );
471 std::io::stderr().flush().ok();
472 let result = catch_unwind(AssertUnwindSafe(|| {
473 use std::io::Write;
474 debug_println!("spir_matsu_sampling_new_with_matrix: inside catch_unwind");
475 std::io::stderr().flush().ok();
476 if points.is_null() || matrix.is_null() {
478 debug_eprintln!("spir_matsu_sampling_new_with_matrix: null pointer");
479 return (std::ptr::null_mut(), SPIR_INVALID_ARGUMENT);
480 }
481 if num_points <= 0 || basis_size <= 0 {
482 debug_eprintln!(
483 "spir_matsu_sampling_new_with_matrix: invalid size, num_points={}, basis_size={}",
484 num_points,
485 basis_size
486 );
487 return (std::ptr::null_mut(), SPIR_INVALID_ARGUMENT);
488 }
489 debug_println!("spir_matsu_sampling_new_with_matrix: input validation passed");
490 std::io::stderr().flush().ok();
491
492 let mem_order = match MemoryOrder::from_c_int(order) {
494 Ok(o) => o,
495 Err(_) => return (std::ptr::null_mut(), SPIR_INVALID_ARGUMENT),
496 };
497
498 debug_println!("spir_matsu_sampling_new_with_matrix: creating points slice...");
500 std::io::stderr().flush().ok();
501 let points_slice = unsafe { std::slice::from_raw_parts(points, num_points as usize) };
502 debug_println!(
503 "spir_matsu_sampling_new_with_matrix: points slice created, len = {}",
504 points_slice.len()
505 );
506 std::io::stderr().flush().ok();
507 let matsu_points: Vec<i64> = points_slice.to_vec();
508 debug_println!(
509 "spir_matsu_sampling_new_with_matrix: matsu_points created, len = {}",
510 matsu_points.len()
511 );
512 std::io::stderr().flush().ok();
513
514 use sparse_ir::freq::MatsubaraFreq;
515
516 let orig_dims = [num_points as usize, basis_size as usize];
518 debug_println!(
519 "spir_matsu_sampling_new_with_matrix: orig_dims = {:?}, mem_order = {:?}",
520 orig_dims,
521 mem_order
522 );
523 std::io::stderr().flush().ok();
524
525 debug_println!("spir_matsu_sampling_new_with_matrix: reading tensor from buffer...");
526 std::io::stderr().flush().ok();
527 let dyn_tensor = unsafe { read_tensor_nd(matrix, &orig_dims, mem_order) };
528 let shape_dims = dyn_tensor.shape().with_dims(|dims| dims.to_vec());
529 debug_println!(
530 "spir_matsu_sampling_new_with_matrix: dyn_tensor created, shape = {:?}",
531 shape_dims
532 );
533 std::io::stderr().flush().ok();
534
535 debug_println!("spir_matsu_sampling_new_with_matrix: converting to fixed 2D tensor...");
537 std::io::stderr().flush().ok();
538 assert_eq!(
539 shape_dims.len(),
540 2,
541 "Expected 2D tensor, got {}D",
542 shape_dims.len()
543 );
544 let num_points_actual = shape_dims[0];
545 let basis_size_actual = shape_dims[1];
546 debug_println!(
547 "spir_matsu_sampling_new_with_matrix: converting from shape {:?} to DTensor<Complex64, 2>",
548 shape_dims
549 );
550 std::io::stderr().flush().ok();
551 let matrix_tensor = sparse_ir::DTensor::<Complex64, 2>::from_fn(
552 [num_points_actual, basis_size_actual],
553 |idx| dyn_tensor[&[idx[0], idx[1]][..]],
554 );
555 debug_println!(
556 "spir_matsu_sampling_new_with_matrix: matrix_tensor created, shape = {:?}",
557 matrix_tensor.shape()
558 );
559 std::io::stderr().flush().ok();
560
561 debug_println!(
563 "spir_matsu_sampling_new_with_matrix: creating sampling, statistics={}, positive_only={}",
564 statistics,
565 positive_only
566 );
567 std::io::stderr().flush().ok();
568 let sampling_type = match (statistics, positive_only) {
569 (SPIR_STATISTICS_FERMIONIC, true) => {
570 debug_println!("spir_matsu_sampling_new_with_matrix: Fermionic, positive-only");
571 std::io::stderr().flush().ok();
572 let matsu_freqs: Vec<MatsubaraFreq<Fermionic>> = matsu_points
574 .iter()
575 .map(|&n| MatsubaraFreq::new(n).expect("Invalid Matsubara frequency"))
576 .collect();
577 debug_println!(
578 "spir_matsu_sampling_new_with_matrix: matsu_freqs created, len = {}",
579 matsu_freqs.len()
580 );
581 std::io::stderr().flush().ok();
582 debug_println!("spir_matsu_sampling_new_with_matrix: calling from_matrix...");
583 std::io::stderr().flush().ok();
584 let matsu_sampling =
585 sparse_ir::matsubara_sampling::MatsubaraSamplingPositiveOnly::from_matrix(
586 matsu_freqs,
587 matrix_tensor.clone(),
588 );
589 debug_println!("spir_matsu_sampling_new_with_matrix: from_matrix returned");
590 std::io::stderr().flush().ok();
591 SamplingType::MatsubaraPositiveOnlyFermionic(Arc::new(matsu_sampling))
592 }
593 (SPIR_STATISTICS_FERMIONIC, false) => {
594 debug_println!("spir_matsu_sampling_new_with_matrix: Fermionic, full range");
595 std::io::stderr().flush().ok();
596 let matsu_freqs: Vec<MatsubaraFreq<Fermionic>> = matsu_points
598 .iter()
599 .map(|&n| MatsubaraFreq::new(n).expect("Invalid Matsubara frequency"))
600 .collect();
601 debug_println!(
602 "spir_matsu_sampling_new_with_matrix: matsu_freqs created, len = {}",
603 matsu_freqs.len()
604 );
605 std::io::stderr().flush().ok();
606 debug_println!("spir_matsu_sampling_new_with_matrix: calling from_matrix...");
607 std::io::stderr().flush().ok();
608 let matsu_sampling = sparse_ir::matsubara_sampling::MatsubaraSampling::from_matrix(
609 matsu_freqs,
610 matrix_tensor.clone(),
611 );
612 debug_println!("spir_matsu_sampling_new_with_matrix: from_matrix returned");
613 std::io::stderr().flush().ok();
614 SamplingType::MatsubaraFermionic(Arc::new(matsu_sampling))
615 }
616 (SPIR_STATISTICS_BOSONIC, true) => {
617 let matsu_freqs: Vec<MatsubaraFreq<Bosonic>> = matsu_points
619 .iter()
620 .map(|&n| MatsubaraFreq::new(n).expect("Invalid Matsubara frequency"))
621 .collect();
622 let matsu_sampling =
623 sparse_ir::matsubara_sampling::MatsubaraSamplingPositiveOnly::from_matrix(
624 matsu_freqs,
625 matrix_tensor.clone(),
626 );
627 SamplingType::MatsubaraPositiveOnlyBosonic(Arc::new(matsu_sampling))
628 }
629 (SPIR_STATISTICS_BOSONIC, false) => {
630 let matsu_freqs: Vec<MatsubaraFreq<Bosonic>> = matsu_points
632 .iter()
633 .map(|&n| MatsubaraFreq::new(n).expect("Invalid Matsubara frequency"))
634 .collect();
635 let matsu_sampling = sparse_ir::matsubara_sampling::MatsubaraSampling::from_matrix(
636 matsu_freqs,
637 matrix_tensor.clone(),
638 );
639 SamplingType::MatsubaraBosonic(Arc::new(matsu_sampling))
640 }
641 _ => return (std::ptr::null_mut(), SPIR_INVALID_ARGUMENT),
642 };
643
644 let inner = sampling_type;
645 let sampling = spir_sampling {
646 _private: Box::into_raw(Box::new(inner)) as *mut std::ffi::c_void,
647 };
648
649 (Box::into_raw(Box::new(sampling)), SPIR_COMPUTATION_SUCCESS)
650 }));
651
652 match result {
653 Ok((ptr, code)) => {
654 if !status.is_null() {
655 unsafe {
656 *status = code;
657 }
658 }
659 ptr
660 }
661 Err(_) => {
662 if !status.is_null() {
663 unsafe {
664 *status = crate::SPIR_INTERNAL_ERROR;
665 }
666 }
667 std::ptr::null_mut()
668 }
669 }
670}
671
672#[unsafe(no_mangle)]
698pub extern "C" fn spir_sampling_get_npoints(
699 s: *const spir_sampling,
700 num_points: *mut libc::c_int,
701) -> StatusCode {
702 let result = catch_unwind(AssertUnwindSafe(|| {
703 if s.is_null() || num_points.is_null() {
704 return SPIR_INVALID_ARGUMENT;
705 }
706
707 let sampling_ref = unsafe { &*s };
708
709 let n_points = match sampling_ref.inner() {
710 SamplingType::TauFermionic(tau) => tau.n_sampling_points(),
711 SamplingType::TauBosonic(tau) => tau.n_sampling_points(),
712 SamplingType::MatsubaraFermionic(matsu) => matsu.n_sampling_points(),
713 SamplingType::MatsubaraBosonic(matsu) => matsu.n_sampling_points(),
714 SamplingType::MatsubaraPositiveOnlyFermionic(matsu) => matsu.n_sampling_points(),
715 SamplingType::MatsubaraPositiveOnlyBosonic(matsu) => matsu.n_sampling_points(),
716 };
717
718 unsafe {
719 *num_points = n_points as libc::c_int;
720 }
721 SPIR_COMPUTATION_SUCCESS
722 }));
723
724 result.unwrap_or(crate::SPIR_INTERNAL_ERROR)
725}
726
727#[unsafe(no_mangle)]
751pub extern "C" fn spir_sampling_get_taus(s: *const spir_sampling, points: *mut f64) -> StatusCode {
752 let result = catch_unwind(AssertUnwindSafe(|| {
753 if s.is_null() || points.is_null() {
754 return SPIR_INVALID_ARGUMENT;
755 }
756
757 let sampling_ref = unsafe { &*s };
758
759 match sampling_ref.inner() {
760 SamplingType::TauFermionic(tau) => {
761 let tau_points = tau.sampling_points();
762 let out_slice = unsafe { std::slice::from_raw_parts_mut(points, tau_points.len()) };
763 out_slice.copy_from_slice(tau_points);
764 SPIR_COMPUTATION_SUCCESS
765 }
766 SamplingType::TauBosonic(tau) => {
767 let tau_points = tau.sampling_points();
768 let out_slice = unsafe { std::slice::from_raw_parts_mut(points, tau_points.len()) };
769 out_slice.copy_from_slice(tau_points);
770 SPIR_COMPUTATION_SUCCESS
771 }
772 _ => SPIR_NOT_SUPPORTED,
773 }
774 }));
775
776 result.unwrap_or(crate::SPIR_INTERNAL_ERROR)
777}
778
779#[unsafe(no_mangle)]
781pub extern "C" fn spir_sampling_get_matsus(
782 s: *const spir_sampling,
783 points: *mut i64,
784) -> StatusCode {
785 let result = catch_unwind(AssertUnwindSafe(|| {
786 if s.is_null() || points.is_null() {
787 return SPIR_INVALID_ARGUMENT;
788 }
789
790 let sampling_ref = unsafe { &*s };
791
792 match sampling_ref.inner() {
793 SamplingType::MatsubaraFermionic(matsu) => {
794 let matsu_freqs = matsu.sampling_points();
795 let out_slice =
796 unsafe { std::slice::from_raw_parts_mut(points, matsu_freqs.len()) };
797 for (i, freq) in matsu_freqs.iter().enumerate() {
798 out_slice[i] = freq.n();
799 }
800 SPIR_COMPUTATION_SUCCESS
801 }
802 SamplingType::MatsubaraBosonic(matsu) => {
803 let matsu_freqs = matsu.sampling_points();
804 let out_slice =
805 unsafe { std::slice::from_raw_parts_mut(points, matsu_freqs.len()) };
806 for (i, freq) in matsu_freqs.iter().enumerate() {
807 out_slice[i] = freq.n();
808 }
809 SPIR_COMPUTATION_SUCCESS
810 }
811 SamplingType::MatsubaraPositiveOnlyFermionic(matsu) => {
812 let matsu_freqs = matsu.sampling_points();
813 let out_slice =
814 unsafe { std::slice::from_raw_parts_mut(points, matsu_freqs.len()) };
815 for (i, freq) in matsu_freqs.iter().enumerate() {
816 out_slice[i] = freq.n();
817 }
818 SPIR_COMPUTATION_SUCCESS
819 }
820 SamplingType::MatsubaraPositiveOnlyBosonic(matsu) => {
821 let matsu_freqs = matsu.sampling_points();
822 let out_slice =
823 unsafe { std::slice::from_raw_parts_mut(points, matsu_freqs.len()) };
824 for (i, freq) in matsu_freqs.iter().enumerate() {
825 out_slice[i] = freq.n();
826 }
827 SPIR_COMPUTATION_SUCCESS
828 }
829 _ => SPIR_NOT_SUPPORTED,
830 }
831 }));
832
833 result.unwrap_or(crate::SPIR_INTERNAL_ERROR)
834}
835
836#[unsafe(no_mangle)]
857pub extern "C" fn spir_sampling_get_cond_num(
858 s: *const spir_sampling,
859 cond_num: *mut f64,
860) -> StatusCode {
861 let result = catch_unwind(AssertUnwindSafe(|| {
862 if s.is_null() || cond_num.is_null() {
863 return SPIR_INVALID_ARGUMENT;
864 }
865
866 let sampling_ref = unsafe { &*s };
867
868 let condition_number = match sampling_ref.inner() {
870 SamplingType::TauFermionic(tau) => {
871 let matrix = tau.matrix();
873 compute_condition_number_real(matrix)
874 }
875 SamplingType::TauBosonic(tau) => {
876 let matrix = tau.matrix();
878 compute_condition_number_real(matrix)
879 }
880 SamplingType::MatsubaraFermionic(matsu) => {
881 let matrix = matsu.matrix();
883 compute_condition_number_complex(matrix)
884 }
885 SamplingType::MatsubaraBosonic(matsu) => {
886 let matrix = matsu.matrix();
887 compute_condition_number_complex(matrix)
888 }
889 SamplingType::MatsubaraPositiveOnlyFermionic(matsu) => {
890 let matrix = matsu.matrix();
893 compute_condition_number_complex(matrix)
894 }
895 SamplingType::MatsubaraPositiveOnlyBosonic(matsu) => {
896 let matrix = matsu.matrix();
897 compute_condition_number_complex(matrix)
898 }
899 };
900
901 unsafe {
902 *cond_num = condition_number;
903 }
904 SPIR_COMPUTATION_SUCCESS
905 }));
906
907 result.unwrap_or(crate::SPIR_INTERNAL_ERROR)
908}
909
910fn compute_condition_number_real(matrix: &mdarray::DTensor<f64, 2>) -> f64 {
912 use mdarray_linalg::prelude::SVD;
913 use mdarray_linalg::svd::SVDDecomp;
914 use mdarray_linalg_faer::Faer;
915
916 let mut matrix_copy = matrix.clone();
917 let SVDDecomp { s, .. } = Faer.svd(&mut *matrix_copy).expect("SVD computation failed");
918
919 let min_dim = s.shape().0.min(s.shape().1);
920 if min_dim == 0 {
921 return 1.0;
922 }
923
924 let max_sv = s[[0, 0]];
925 let min_sv = s[[0, min_dim - 1]];
926
927 if min_sv.abs() < 1e-15 {
928 return f64::INFINITY;
930 }
931
932 max_sv / min_sv
933}
934
935fn compute_condition_number_complex(matrix: &mdarray::DTensor<num_complex::Complex64, 2>) -> f64 {
937 use mdarray_linalg::prelude::SVD;
938 use mdarray_linalg::svd::SVDDecomp;
939 use mdarray_linalg_faer::Faer;
940
941 let mut matrix_copy = matrix.clone();
942 let SVDDecomp { s, .. } = Faer.svd(&mut *matrix_copy).expect("SVD computation failed");
943
944 let min_dim = s.shape().0.min(s.shape().1);
945 if min_dim == 0 {
946 return 1.0;
947 }
948
949 let max_sv = s[[0, 0]].re;
951 let min_sv = s[[0, min_dim - 1]].re;
952
953 if min_sv.abs() < 1e-15 {
954 return f64::INFINITY;
956 }
957
958 max_sv / min_sv
959}
960
961#[unsafe(no_mangle)]
1002pub extern "C" fn spir_sampling_eval_dd(
1003 s: *const spir_sampling,
1004 backend: *const spir_gemm_backend,
1005 order: libc::c_int,
1006 ndim: libc::c_int,
1007 input_dims: *const libc::c_int,
1008 target_dim: libc::c_int,
1009 input: *const f64,
1010 out: *mut f64,
1011) -> StatusCode {
1012 let result = catch_unwind(AssertUnwindSafe(|| {
1013 if s.is_null() || input_dims.is_null() || input.is_null() || out.is_null() {
1015 return SPIR_INVALID_ARGUMENT;
1016 }
1017 if ndim <= 0 || target_dim < 0 || target_dim >= ndim {
1018 return SPIR_INVALID_ARGUMENT;
1019 }
1020
1021 let mem_order = match MemoryOrder::from_c_int(order) {
1023 Ok(o) => o,
1024 Err(_) => return SPIR_INVALID_ARGUMENT,
1025 };
1026
1027 let sampling_ref = unsafe { &*s };
1028 let dims_slice = unsafe { std::slice::from_raw_parts(input_dims, ndim as usize) };
1029 let orig_dims: Vec<usize> = dims_slice.iter().map(|&d| d as usize).collect();
1030
1031 let (row_major_dims, row_major_target_dim) =
1034 convert_dims_for_row_major(&orig_dims, target_dim as usize, mem_order);
1035
1036 let input_view = unsafe { create_dview_from_ptr(input, &row_major_dims) };
1038
1039 let sampling_inner = sampling_ref.inner();
1041 let expected_basis_size = sampling_inner.basis_size();
1042 if row_major_dims[row_major_target_dim] != expected_basis_size {
1043 return crate::SPIR_INPUT_DIMENSION_MISMATCH;
1044 }
1045
1046 let n_points = sampling_inner.n_points();
1048 let out_dims = build_output_dims(&row_major_dims, row_major_target_dim, n_points);
1049
1050 let mut output_view = unsafe { create_dviewmut_from_ptr(out, &out_dims) };
1052
1053 let backend_handle = unsafe { get_backend_handle(backend) };
1055
1056 if !InplaceFitter::evaluate_nd_dd_to(
1058 sampling_inner,
1059 backend_handle,
1060 &input_view,
1061 row_major_target_dim,
1062 &mut output_view,
1063 ) {
1064 return SPIR_NOT_SUPPORTED;
1065 }
1066
1067 SPIR_COMPUTATION_SUCCESS
1068 }));
1069
1070 result.unwrap_or(crate::SPIR_INTERNAL_ERROR)
1071}
1072
1073#[unsafe(no_mangle)]
1078pub extern "C" fn spir_sampling_eval_dz(
1079 s: *const spir_sampling,
1080 backend: *const spir_gemm_backend,
1081 order: libc::c_int,
1082 ndim: libc::c_int,
1083 input_dims: *const libc::c_int,
1084 target_dim: libc::c_int,
1085 input: *const f64,
1086 out: *mut Complex64,
1087) -> StatusCode {
1088 let result = catch_unwind(AssertUnwindSafe(|| {
1089 if s.is_null() || input_dims.is_null() || input.is_null() || out.is_null() {
1091 return SPIR_INVALID_ARGUMENT;
1092 }
1093 if ndim <= 0 || target_dim < 0 || target_dim >= ndim {
1094 return SPIR_INVALID_ARGUMENT;
1095 }
1096
1097 let mem_order = match MemoryOrder::from_c_int(order) {
1099 Ok(o) => o,
1100 Err(_) => return SPIR_INVALID_ARGUMENT,
1101 };
1102
1103 let sampling_ref = unsafe { &*s };
1104 let dims_slice = unsafe { std::slice::from_raw_parts(input_dims, ndim as usize) };
1105 let orig_dims: Vec<usize> = dims_slice.iter().map(|&d| d as usize).collect();
1106
1107 let (row_major_dims, row_major_target_dim) =
1109 convert_dims_for_row_major(&orig_dims, target_dim as usize, mem_order);
1110
1111 let input_view = unsafe { create_dview_from_ptr(input, &row_major_dims) };
1113
1114 let sampling_inner = sampling_ref.inner();
1116 let expected_basis_size = sampling_inner.basis_size();
1117 if row_major_dims[row_major_target_dim] != expected_basis_size {
1118 return crate::SPIR_INPUT_DIMENSION_MISMATCH;
1119 }
1120
1121 let n_points = sampling_inner.n_points();
1123 let out_dims = build_output_dims(&row_major_dims, row_major_target_dim, n_points);
1124
1125 let mut output_view = unsafe { create_dviewmut_from_ptr(out, &out_dims) };
1127
1128 let backend_handle = unsafe { get_backend_handle(backend) };
1130
1131 if !InplaceFitter::evaluate_nd_dz_to(
1133 sampling_inner,
1134 backend_handle,
1135 &input_view,
1136 row_major_target_dim,
1137 &mut output_view,
1138 ) {
1139 return SPIR_NOT_SUPPORTED;
1140 }
1141
1142 SPIR_COMPUTATION_SUCCESS
1143 }));
1144
1145 result.unwrap_or(crate::SPIR_INTERNAL_ERROR)
1146}
1147
1148#[unsafe(no_mangle)]
1153pub extern "C" fn spir_sampling_eval_zz(
1154 s: *const spir_sampling,
1155 backend: *const spir_gemm_backend,
1156 order: libc::c_int,
1157 ndim: libc::c_int,
1158 input_dims: *const libc::c_int,
1159 target_dim: libc::c_int,
1160 input: *const Complex64,
1161 out: *mut Complex64,
1162) -> StatusCode {
1163 let result = catch_unwind(AssertUnwindSafe(|| {
1164 if s.is_null() || input_dims.is_null() || input.is_null() || out.is_null() {
1165 return SPIR_INVALID_ARGUMENT;
1166 }
1167 if ndim <= 0 || target_dim < 0 || target_dim >= ndim {
1168 return SPIR_INVALID_ARGUMENT;
1169 }
1170
1171 let mem_order = match MemoryOrder::from_c_int(order) {
1173 Ok(o) => o,
1174 Err(_) => return SPIR_INVALID_ARGUMENT,
1175 };
1176
1177 let sampling_ref = unsafe { &*s };
1178 let dims_slice = unsafe { std::slice::from_raw_parts(input_dims, ndim as usize) };
1179 let orig_dims: Vec<usize> = dims_slice.iter().map(|&d| d as usize).collect();
1180
1181 let (row_major_dims, row_major_target_dim) =
1183 convert_dims_for_row_major(&orig_dims, target_dim as usize, mem_order);
1184
1185 let input_view = unsafe { create_dview_from_ptr(input, &row_major_dims) };
1187
1188 let sampling_inner = sampling_ref.inner();
1190 let expected_basis_size = sampling_inner.basis_size();
1191 if row_major_dims[row_major_target_dim] != expected_basis_size {
1192 return crate::SPIR_INPUT_DIMENSION_MISMATCH;
1193 }
1194
1195 let n_points = sampling_inner.n_points();
1197 let out_dims = build_output_dims(&row_major_dims, row_major_target_dim, n_points);
1198
1199 let mut output_view = unsafe { create_dviewmut_from_ptr(out, &out_dims) };
1201
1202 let backend_handle = unsafe { get_backend_handle(backend) };
1204
1205 if !InplaceFitter::evaluate_nd_zz_to(
1207 sampling_inner,
1208 backend_handle,
1209 &input_view,
1210 row_major_target_dim,
1211 &mut output_view,
1212 ) {
1213 return SPIR_NOT_SUPPORTED;
1214 }
1215
1216 SPIR_COMPUTATION_SUCCESS
1217 }));
1218
1219 result.unwrap_or(crate::SPIR_INTERNAL_ERROR)
1220}
1221
1222#[unsafe(no_mangle)]
1262pub extern "C" fn spir_sampling_fit_dd(
1263 s: *const spir_sampling,
1264 backend: *const spir_gemm_backend,
1265 order: libc::c_int,
1266 ndim: libc::c_int,
1267 input_dims: *const libc::c_int,
1268 target_dim: libc::c_int,
1269 input: *const f64,
1270 out: *mut f64,
1271) -> StatusCode {
1272 let result = catch_unwind(AssertUnwindSafe(|| {
1273 if s.is_null() || input_dims.is_null() || input.is_null() || out.is_null() {
1274 return SPIR_INVALID_ARGUMENT;
1275 }
1276 if ndim <= 0 || target_dim < 0 || target_dim >= ndim {
1277 return SPIR_INVALID_ARGUMENT;
1278 }
1279
1280 let mem_order = match MemoryOrder::from_c_int(order) {
1282 Ok(o) => o,
1283 Err(_) => return SPIR_INVALID_ARGUMENT,
1284 };
1285
1286 let sampling_ref = unsafe { &*s };
1287 let dims_slice = unsafe { std::slice::from_raw_parts(input_dims, ndim as usize) };
1288 let orig_dims: Vec<usize> = dims_slice.iter().map(|&d| d as usize).collect();
1289
1290 let (row_major_dims, row_major_target_dim) =
1292 convert_dims_for_row_major(&orig_dims, target_dim as usize, mem_order);
1293
1294 let input_view = unsafe { create_dview_from_ptr(input, &row_major_dims) };
1296
1297 let sampling_inner = sampling_ref.inner();
1299 let expected_n_points = sampling_inner.n_points();
1300 if row_major_dims[row_major_target_dim] != expected_n_points {
1301 return crate::SPIR_INPUT_DIMENSION_MISMATCH;
1302 }
1303
1304 let basis_size = sampling_inner.basis_size();
1306 let out_dims = build_output_dims(&row_major_dims, row_major_target_dim, basis_size);
1307
1308 let mut output_view = unsafe { create_dviewmut_from_ptr(out, &out_dims) };
1310
1311 let backend_handle = unsafe { get_backend_handle(backend) };
1313
1314 if !InplaceFitter::fit_nd_dd_to(
1316 sampling_inner,
1317 backend_handle,
1318 &input_view,
1319 row_major_target_dim,
1320 &mut output_view,
1321 ) {
1322 return SPIR_NOT_SUPPORTED;
1323 }
1324
1325 SPIR_COMPUTATION_SUCCESS
1326 }));
1327
1328 result.unwrap_or(crate::SPIR_INTERNAL_ERROR)
1329}
1330
1331#[unsafe(no_mangle)]
1337pub extern "C" fn spir_sampling_fit_zz(
1338 s: *const spir_sampling,
1339 backend: *const spir_gemm_backend,
1340 order: libc::c_int,
1341 ndim: libc::c_int,
1342 input_dims: *const libc::c_int,
1343 target_dim: libc::c_int,
1344 input: *const Complex64,
1345 out: *mut Complex64,
1346) -> StatusCode {
1347 let result = catch_unwind(AssertUnwindSafe(|| {
1348 if s.is_null() || input_dims.is_null() || input.is_null() || out.is_null() {
1349 return SPIR_INVALID_ARGUMENT;
1350 }
1351 if ndim <= 0 || target_dim < 0 || target_dim >= ndim {
1352 return SPIR_INVALID_ARGUMENT;
1353 }
1354
1355 let mem_order = match MemoryOrder::from_c_int(order) {
1357 Ok(o) => o,
1358 Err(_) => return SPIR_INVALID_ARGUMENT,
1359 };
1360
1361 let sampling_ref = unsafe { &*s };
1362 let dims_slice = unsafe { std::slice::from_raw_parts(input_dims, ndim as usize) };
1363 let orig_dims: Vec<usize> = dims_slice.iter().map(|&d| d as usize).collect();
1364
1365 let (row_major_dims, row_major_target_dim) =
1367 convert_dims_for_row_major(&orig_dims, target_dim as usize, mem_order);
1368
1369 let input_view = unsafe { create_dview_from_ptr(input, &row_major_dims) };
1371
1372 let sampling_inner = sampling_ref.inner();
1374 let expected_n_points = sampling_inner.n_points();
1375 if row_major_dims[row_major_target_dim] != expected_n_points {
1376 return crate::SPIR_INPUT_DIMENSION_MISMATCH;
1377 }
1378
1379 let basis_size = sampling_inner.basis_size();
1381 let out_dims = build_output_dims(&row_major_dims, row_major_target_dim, basis_size);
1382
1383 let mut output_view = unsafe { create_dviewmut_from_ptr(out, &out_dims) };
1385
1386 let backend_handle = unsafe { get_backend_handle(backend) };
1388
1389 if !InplaceFitter::fit_nd_zz_to(
1391 sampling_inner,
1392 backend_handle,
1393 &input_view,
1394 row_major_target_dim,
1395 &mut output_view,
1396 ) {
1397 return SPIR_NOT_SUPPORTED;
1398 }
1399
1400 SPIR_COMPUTATION_SUCCESS
1401 }));
1402
1403 result.unwrap_or(crate::SPIR_INTERNAL_ERROR)
1404}
1405
1406#[unsafe(no_mangle)]
1447pub extern "C" fn spir_sampling_fit_zd(
1448 s: *const spir_sampling,
1449 backend: *const spir_gemm_backend,
1450 order: libc::c_int,
1451 ndim: libc::c_int,
1452 input_dims: *const libc::c_int,
1453 target_dim: libc::c_int,
1454 input: *const Complex64,
1455 out: *mut f64,
1456) -> StatusCode {
1457 let result = catch_unwind(AssertUnwindSafe(|| {
1458 if s.is_null() || input_dims.is_null() || input.is_null() || out.is_null() {
1459 return SPIR_INVALID_ARGUMENT;
1460 }
1461 if ndim <= 0 || target_dim < 0 || target_dim >= ndim {
1462 return SPIR_INVALID_ARGUMENT;
1463 }
1464
1465 let mem_order = match MemoryOrder::from_c_int(order) {
1467 Ok(o) => o,
1468 Err(_) => return SPIR_INVALID_ARGUMENT,
1469 };
1470
1471 let sampling_ref = unsafe { &*s };
1472 let dims_slice = unsafe { std::slice::from_raw_parts(input_dims, ndim as usize) };
1473 let orig_dims: Vec<usize> = dims_slice.iter().map(|&d| d as usize).collect();
1474
1475 let (row_major_dims, row_major_target_dim) =
1477 convert_dims_for_row_major(&orig_dims, target_dim as usize, mem_order);
1478
1479 let input_view = unsafe { create_dview_from_ptr(input, &row_major_dims) };
1481
1482 let sampling_inner = sampling_ref.inner();
1484 let expected_n_points = sampling_inner.n_points();
1485 if row_major_dims[row_major_target_dim] != expected_n_points {
1486 return crate::SPIR_INPUT_DIMENSION_MISMATCH;
1487 }
1488
1489 let basis_size = sampling_inner.basis_size();
1491 let out_dims = build_output_dims(&row_major_dims, row_major_target_dim, basis_size);
1492
1493 let mut output_view = unsafe { create_dviewmut_from_ptr(out, &out_dims) };
1495
1496 let backend_handle = unsafe { get_backend_handle(backend) };
1498
1499 if !InplaceFitter::fit_nd_zd_to(
1504 sampling_inner,
1505 backend_handle,
1506 &input_view,
1507 row_major_target_dim,
1508 &mut output_view,
1509 ) {
1510 return SPIR_NOT_SUPPORTED;
1511 }
1512
1513 SPIR_COMPUTATION_SUCCESS
1514 }));
1515
1516 result.unwrap_or(crate::SPIR_INTERNAL_ERROR)
1517}
1518
1519#[cfg(test)]
1520mod tests {
1521 use super::*;
1522
1523 #[test]
1524 fn test_tau_sampling_creation() {
1525 let mut status = 0;
1527 let kernel = crate::spir_logistic_kernel_new(10.0, &mut status);
1528 assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
1529
1530 let sve = crate::spir_sve_result_new(kernel, 1e-6, -1, -1, -1, &mut status);
1531 assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
1532
1533 let basis = crate::spir_basis_new(1, 10.0, 1.0, 1e-6, kernel, sve, 5, &mut status);
1535 assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
1536
1537 let mut actual_basis_size = 0;
1539 let ret = crate::spir_basis_get_size(basis, &mut actual_basis_size);
1540 assert_eq!(ret, SPIR_COMPUTATION_SUCCESS);
1541
1542 let tau_points: Vec<f64> = (0..actual_basis_size)
1544 .map(|i| (i as f64 + 1.0) * 10.0 / (actual_basis_size as f64 + 1.0))
1545 .collect();
1546
1547 let sampling = spir_tau_sampling_new(
1548 basis,
1549 tau_points.len() as i32,
1550 tau_points.as_ptr(),
1551 &mut status,
1552 );
1553 assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
1554 assert!(!sampling.is_null());
1555
1556 let mut n_points = 0;
1558 let ret = spir_sampling_get_npoints(sampling, &mut n_points);
1559 assert_eq!(ret, SPIR_COMPUTATION_SUCCESS);
1560 assert_eq!(n_points, actual_basis_size);
1561
1562 let mut retrieved_points = vec![0.0; actual_basis_size as usize];
1564 let ret = spir_sampling_get_taus(sampling, retrieved_points.as_mut_ptr());
1565 assert_eq!(ret, SPIR_COMPUTATION_SUCCESS);
1566
1567 for (i, (&retrieved, &original)) in
1569 retrieved_points.iter().zip(tau_points.iter()).enumerate()
1570 {
1571 assert!(
1572 (retrieved - original).abs() < 1e-10,
1573 "Point {} mismatch: {} vs {}",
1574 i,
1575 retrieved,
1576 original
1577 );
1578 }
1579
1580 let mut cond = 0.0;
1582 let ret = spir_sampling_get_cond_num(sampling, &mut cond);
1583 assert_eq!(ret, SPIR_COMPUTATION_SUCCESS);
1584 assert!(cond >= 1.0); crate::spir_sampling_release(sampling);
1588 crate::spir_basis_release(basis);
1589 crate::spir_sve_result_release(sve);
1590 crate::spir_kernel_release(kernel);
1591 }
1592}