@@ -451,6 +451,76 @@ namespace cppdlr {
451
451
template <nda::MemoryArray T, nda::Scalar S = nda::get_value_t <T>>
452
452
nda::matrix<S> convmat (double beta, statistic_t statistic, T const &fc, bool time_order = false ) const {
453
453
454
+ int n, m;
455
+
456
+ if constexpr (T::rank == 1 ) { // Scalar-valued Green's function
457
+ n = r;
458
+ m = r;
459
+ } else if (T::rank == 3 ) { // Matrix-valued Green's function
460
+ n = r * fc.shape (1 );
461
+ m = r * fc.shape (2 );
462
+ } else {
463
+ throw std::runtime_error (" Input arrays must be rank 1 (scalar-valued Green's function) or 3 (matrix-valued Green's function)." );
464
+ }
465
+
466
+ auto fconv = nda::matrix<S, nda::C_layout>(n, m); // Matrix of convolution by f
467
+ convmat_inplace (nda::matrix_view<S, nda::C_layout>(fconv), beta, statistic, fc, time_order);
468
+
469
+ return fconv;
470
+ }
471
+
472
+ /* *
473
+ * @brief Compute matrix of convolution by an imaginary time Green's function
474
+ * in place
475
+ *
476
+ * The convolution of f and g is defined as h(t) = (f * g)(t) = int_0^beta
477
+ * f(t-t') g(t') dt', where fermionic/bosonic antiperiodicity/periodicity are
478
+ * used to define the Green's functions on (-beta, 0). This method takes the
479
+ * DLR coefficients of f as input and returns the matrix of convolution by f.
480
+ * This matrix can be applied to the values of g on the DLR imaginary time
481
+ * grid, to produce the values of h on the DLR imaginary time grid.
482
+ *
483
+ * By specifying the @p time_order flag, this method can be used to compute
484
+ * the time-ordered convolution of f and g, defined as h(t) = (f * g)(t) =
485
+ * int_0^tau f(t-t') g(t') dt'.
486
+ *
487
+ * The convolution matrix is constructed using the method described in
488
+ * Appendix A of
489
+ *
490
+ * J. Kaye, H. U. R. Strand, D. Golez, "Decomposing imaginary time Feynman
491
+ * diagrams using separable basis functions: Anderson impurity model strong
492
+ * coupling expansion," arXiv:2307.08566 (2023).
493
+ *
494
+ * @param[in/out] fconv Convolution matrix from DLR coefficients to DLR grid
495
+ * @param[in] beta Inverse temperature
496
+ * @param[in] statistic Fermionic ("Fermion" or 0) or bosonic ("Boson" or 1)
497
+ * @param[in] fc DLR coefficients of f
498
+ * @param[in] time_order Flag for ordinary (false or ORDINARY, default) or
499
+ * time-ordered (true or TIME_ORDERED) convolution
500
+ *
501
+ * \note This function builds the matrix of convolution, in place, in the
502
+ * provided matrix `fconv`. This makes it possible to control the memory
503
+ * allocation externally. If this is not a concern, we advise using the
504
+ * `convmat(...)` function instead of `convmat_inplace(...)`.
505
+ *
506
+ * \note Whereas the method imtime_ops::convolve takes the DLR coefficients
507
+ * of f and g as input and computes their convolution h directly, this method
508
+ * returns a matrix which should be applied to the DLR imaginary time grid values
509
+ * of g, rather than its DLR coefficients, in to order to obtain the
510
+ * convolution h. The purpose of this is to make the input and output
511
+ * representations of the convolution matrix equal, which is often convenient
512
+ * in practice.
513
+ *
514
+ * \note In the case of matrix-valued Green's functions, we think of the
515
+ * matrix of convolution by f as an r*norb x r*norb matrix, or a block r x r
516
+ * matrix of norb x norb blocks. Here r is the DLR rank and norb is the
517
+ * number of orbital indices. This matrix would then be applied to a Green's
518
+ * function g, represented as an r*norb x norb matrix, or a block r x 1
519
+ * matrix of norb x norb blocks.
520
+ * */
521
+ template <nda::MemoryArray T, nda::Scalar S = nda::get_value_t <T>>
522
+ void convmat_inplace (nda::matrix_view<S, nda::C_layout> fconv, double beta, statistic_t statistic, T const &fc, bool time_order = false ) const {
523
+
454
524
if (r != fc.shape (0 )) throw std::runtime_error (" First dim of input array must be equal to DLR rank r." );
455
525
456
526
// TODO: implement bosonic case and remove
@@ -466,10 +536,9 @@ namespace cppdlr {
466
536
467
537
if constexpr (T::rank == 1 ) { // Scalar-valued Green's function
468
538
469
- // First construct convolution matrix from DLR coefficients to DLR grid
470
- // values
471
- auto fconv = nda::matrix<S>(r, r); // Matrix of convolution by f
472
-
539
+ if (fconv.shape (0 ) != r || fconv.shape (1 ) != r)
540
+ throw std::runtime_error (" Matrix shape must be equal to DLR rank (r,r)." );
541
+
473
542
// Diagonal contribution (given by diag(tau_k) * K(tau_k, om_l) * diag(fc_l))
474
543
for (int k = 0 ; k < r; ++k) {
475
544
for (int l = 0 ; l < r; ++l) { fconv (k, l) = tcf2it_v (k, l) * fc (l); }
@@ -495,16 +564,16 @@ namespace cppdlr {
495
564
nda::lapack::getrs (transpose (it2cf.lu ), fconv, it2cf.piv );
496
565
}
497
566
498
- return beta * fconv ;
567
+ fconv *= beta ;
499
568
500
569
} else if (T::rank == 3 ) { // Matrix-valued Green's function
501
570
502
571
int norb1 = fc.shape (1 );
503
572
int norb2 = fc.shape (2 );
504
573
505
- // First construct convolution matrix from DLR coefficients to DLR grid
506
- // values
507
- auto fconv = nda::matrix<S>(r * norb1, r * norb2); // Matrix of convolution by f
574
+ if (fconv. shape ( 0 ) != r*norb1 || fconv. shape ( 1 ) != r*norb2)
575
+ throw std::runtime_error ( " Matrix shape must be equal to DLR rank times norbs (r*norb1,r*norb2). " );
576
+
508
577
auto fconv_rs = nda::reshape (fconv, r, norb1, r, norb2); // Array view to index into fconv for conevenience
509
578
510
579
// Diagonal contribution (given by diag(tau_k) * K(tau_k, om_l) * diag(fc_l))
@@ -547,7 +616,7 @@ namespace cppdlr {
547
616
for (int k = 0 ; k < r; ++k) { fconv_rs (_, _, k, i) = fconvtmp_rs (_, _, i, k); }
548
617
}
549
618
550
- return beta * fconv ;
619
+ fconv *= beta ;
551
620
552
621
} else {
553
622
throw std::runtime_error (" Input arrays must be rank 1 (scalar-valued Green's function) or 3 (matrix-valued Green's function)." );
0 commit comments