@@ -308,11 +308,21 @@ inline btas::Tensor<T, Range, Storage>&& shift_to(
308308 return std::move (arg);
309309}
310310
311+ // NOTE on empty-handling: these btas free functions mirror the empty-/null-
312+ // argument behavior of TA::Tensor's same-named member ops, so generic ToT
313+ // code (e.g. TA::Tensor<btas::Tensor<T>>'s lambdas in
314+ // TA::Tensor::{add,subt,mult,add_to,subt_to,mult_to,neg,scale}) can produce
315+ // or accumulate into default-constructed (empty) btas inner tiles without
316+ // hitting TA's congruent-range assertion in the underlying TensorInterface
317+ // machinery.
318+
311319// / result[i] = arg1[i] + arg2[i]
312320template <typename T, typename Range, typename Storage>
313321inline btas::Tensor<T, Range, Storage> add (
314322 const btas::Tensor<T, Range, Storage>& arg1,
315323 const btas::Tensor<T, Range, Storage>& arg2) {
324+ if (arg1.empty ()) return arg2;
325+ if (arg2.empty ()) return arg1;
316326 auto arg1_view = make_ti (arg1);
317327 auto arg2_view = make_ti (arg2);
318328 return arg1_view.add (arg2_view);
@@ -325,6 +335,9 @@ template <typename T, typename Range, typename Storage, typename Scalar,
325335inline btas::Tensor<T, Range, Storage> add (
326336 const btas::Tensor<T, Range, Storage>& arg1,
327337 const btas::Tensor<T, Range, Storage>& arg2, const Scalar factor) {
338+ if (arg1.empty () && arg2.empty ()) return {};
339+ if (arg1.empty ()) return scale (arg2, factor);
340+ if (arg2.empty ()) return scale (arg1, factor);
328341 auto arg1_view = make_ti (arg1);
329342 auto arg2_view = make_ti (arg2);
330343 return arg1_view.add (arg2_view, factor);
@@ -337,6 +350,9 @@ template <
337350inline btas::Tensor<T, Range, Storage> add (
338351 const btas::Tensor<T, Range, Storage>& arg1,
339352 const btas::Tensor<T, Range, Storage>& arg2, const Perm& perm) {
353+ if (arg1.empty () && arg2.empty ()) return {};
354+ if (arg1.empty ()) return permute (arg2, perm);
355+ if (arg2.empty ()) return permute (arg1, perm);
340356 auto arg1_view = make_ti (arg1);
341357 auto arg2_view = make_ti (arg2);
342358 return arg1_view.add (arg2_view, perm);
@@ -352,6 +368,9 @@ inline btas::Tensor<T, Range, Storage> add(
352368 const btas::Tensor<T, Range, Storage>& arg1,
353369 const btas::Tensor<T, Range, Storage>& arg2, const Scalar factor,
354370 const Perm& perm) {
371+ if (arg1.empty () && arg2.empty ()) return {};
372+ if (arg1.empty ()) return scale (permute (arg2, perm), factor);
373+ if (arg2.empty ()) return scale (permute (arg1, perm), factor);
355374 auto arg1_view = make_ti (arg1);
356375 auto arg2_view = make_ti (arg2);
357376 return arg1_view.add (arg2_view, factor, perm);
@@ -362,6 +381,15 @@ template <typename T, typename Range, typename Storage>
362381inline btas::Tensor<T, Range, Storage>& add_to (
363382 btas::Tensor<T, Range, Storage>& result,
364383 const btas::Tensor<T, Range, Storage>& arg) {
384+ // Mirror TA::Tensor::add_to's empty-handling so consumers (e.g. einsum's
385+ // inner-contraction loop, which default-constructs result inner tiles)
386+ // can accumulate into an empty result. Without these the make_ti's view
387+ // ranges differ and TA's congruent-range assertion fires.
388+ if (arg.empty ()) return result;
389+ if (result.empty ()) {
390+ result = arg;
391+ return result;
392+ }
365393 auto result_view = make_ti (result);
366394 auto arg_view = make_ti (arg);
367395 result_view.add_to (arg_view);
@@ -375,6 +403,15 @@ template <typename T, typename Range, typename Storage, typename Scalar,
375403inline btas::Tensor<T, Range, Storage>& add_to (
376404 btas::Tensor<T, Range, Storage>& result,
377405 const btas::Tensor<T, Range, Storage>& arg, const Scalar factor) {
406+ if (arg.empty ()) return result;
407+ if (result.empty ()) {
408+ result = arg;
409+ {
410+ auto result_view = make_ti (result);
411+ result_view.scale_to (factor);
412+ }
413+ return result;
414+ }
378415 auto result_view = make_ti (result);
379416 auto arg_view = make_ti (arg);
380417 result_view.add_to (arg_view, factor);
@@ -386,6 +423,8 @@ template <typename T, typename Range, typename Storage>
386423inline btas::Tensor<T, Range, Storage> subt (
387424 const btas::Tensor<T, Range, Storage>& arg1,
388425 const btas::Tensor<T, Range, Storage>& arg2) {
426+ if (arg2.empty ()) return arg1;
427+ if (arg1.empty ()) return neg (arg2);
389428 auto arg1_view = make_ti (arg1);
390429 auto arg2_view = make_ti (arg2);
391430 return arg1_view.subt (arg2_view);
@@ -398,6 +437,9 @@ template <typename T, typename Range, typename Storage, typename Scalar,
398437inline btas::Tensor<T, Range, Storage> subt (
399438 const btas::Tensor<T, Range, Storage>& arg1,
400439 const btas::Tensor<T, Range, Storage>& arg2, const Scalar factor) {
440+ if (arg1.empty () && arg2.empty ()) return {};
441+ if (arg2.empty ()) return scale (arg1, factor);
442+ if (arg1.empty ()) return scale (arg2, -factor);
401443 auto arg1_view = make_ti (arg1);
402444 auto arg2_view = make_ti (arg2);
403445 return arg1_view.subt (arg2_view, factor);
@@ -410,6 +452,9 @@ template <
410452inline btas::Tensor<T, Range, Storage> subt (
411453 const btas::Tensor<T, Range, Storage>& arg1,
412454 const btas::Tensor<T, Range, Storage>& arg2, const Perm& perm) {
455+ if (arg1.empty () && arg2.empty ()) return {};
456+ if (arg2.empty ()) return permute (arg1, perm);
457+ if (arg1.empty ()) return neg (permute (arg2, perm));
413458 auto arg1_view = make_ti (arg1);
414459 auto arg2_view = make_ti (arg2);
415460 return arg1_view.subt (arg2_view, perm);
@@ -425,6 +470,9 @@ inline btas::Tensor<T, Range, Storage> subt(
425470 const btas::Tensor<T, Range, Storage>& arg1,
426471 const btas::Tensor<T, Range, Storage>& arg2, const Scalar factor,
427472 const Perm& perm) {
473+ if (arg1.empty () && arg2.empty ()) return {};
474+ if (arg2.empty ()) return scale (permute (arg1, perm), factor);
475+ if (arg1.empty ()) return scale (permute (arg2, perm), -factor);
428476 auto arg1_view = make_ti (arg1);
429477 auto arg2_view = make_ti (arg2);
430478 return arg1_view.subt (arg2_view, factor, perm);
@@ -435,6 +483,11 @@ template <typename T, typename Range, typename Storage>
435483inline btas::Tensor<T, Range, Storage>& subt_to (
436484 btas::Tensor<T, Range, Storage>& result,
437485 const btas::Tensor<T, Range, Storage>& arg) {
486+ if (arg.empty ()) return result;
487+ if (result.empty ()) {
488+ result = neg (arg);
489+ return result;
490+ }
438491 auto result_view = make_ti (result);
439492 auto arg_view = make_ti (arg);
440493 result_view.subt_to (arg_view);
@@ -445,6 +498,11 @@ template <typename T, typename Range, typename Storage>
445498inline btas::Tensor<T, Range, Storage>&& subt_to(
446499 btas::Tensor<T, Range, Storage>&& result,
447500 const btas::Tensor<T, Range, Storage>& arg) {
501+ if (arg.empty ()) return std::move (result);
502+ if (result.empty ()) {
503+ result = neg (arg);
504+ return std::move (result);
505+ }
448506 auto result_view = make_ti (result);
449507 auto arg_view = make_ti (arg);
450508 result_view.subt_to (arg_view);
@@ -457,6 +515,15 @@ template <typename T, typename Range, typename Storage, typename Scalar,
457515inline btas::Tensor<T, Range, Storage>& subt_to (
458516 btas::Tensor<T, Range, Storage>& result,
459517 const btas::Tensor<T, Range, Storage>& arg, const Scalar factor) {
518+ // TA::Tensor::subt_to(right, factor): right empty -> scale_to(factor).
519+ if (arg.empty ()) {
520+ if (!result.empty ()) scale_to (result, factor);
521+ return result;
522+ }
523+ if (result.empty ()) {
524+ result = scale (arg, -factor);
525+ return result;
526+ }
460527 auto result_view = make_ti (result);
461528 auto arg_view = make_ti (arg);
462529 result_view.subt_to (arg_view, factor);
@@ -469,6 +536,14 @@ template <typename T, typename Range, typename Storage, typename Scalar,
469536inline btas::Tensor<T, Range, Storage>&& subt_to(
470537 btas::Tensor<T, Range, Storage>&& result,
471538 const btas::Tensor<T, Range, Storage>& arg, const Scalar factor) {
539+ if (arg.empty ()) {
540+ if (!result.empty ()) scale_to (result, factor);
541+ return std::move (result);
542+ }
543+ if (result.empty ()) {
544+ result = scale (arg, -factor);
545+ return std::move (result);
546+ }
472547 auto result_view = make_ti (result);
473548 auto arg_view = make_ti (arg);
474549 result_view.subt_to (arg_view, factor);
@@ -480,6 +555,7 @@ template <typename T, typename Range, typename Storage>
480555inline btas::Tensor<T, Range, Storage> mult (
481556 const btas::Tensor<T, Range, Storage>& arg1,
482557 const btas::Tensor<T, Range, Storage>& arg2) {
558+ if (arg1.empty () || arg2.empty ()) return {};
483559 auto arg1_view = make_ti (arg1);
484560 auto arg2_view = make_ti (arg2);
485561 return arg1_view.mult (arg2_view);
@@ -492,6 +568,7 @@ template <typename T, typename Range, typename Storage, typename Scalar,
492568inline btas::Tensor<T, Range, Storage> mult (
493569 const btas::Tensor<T, Range, Storage>& arg1,
494570 const btas::Tensor<T, Range, Storage>& arg2, const Scalar factor) {
571+ if (arg1.empty () || arg2.empty ()) return {};
495572 auto arg1_view = make_ti (arg1);
496573 auto arg2_view = make_ti (arg2);
497574 return arg1_view.mult (arg2_view, factor);
@@ -504,6 +581,7 @@ template <
504581inline btas::Tensor<T, Range, Storage> mult (
505582 const btas::Tensor<T, Range, Storage>& arg1,
506583 const btas::Tensor<T, Range, Storage>& arg2, const Perm& perm) {
584+ if (arg1.empty () || arg2.empty ()) return {};
507585 auto arg1_view = make_ti (arg1);
508586 auto arg2_view = make_ti (arg2);
509587 return arg1_view.mult (arg2_view, perm);
@@ -519,16 +597,23 @@ inline btas::Tensor<T, Range, Storage> mult(
519597 const btas::Tensor<T, Range, Storage>& arg1,
520598 const btas::Tensor<T, Range, Storage>& arg2, const Scalar factor,
521599 const Perm& perm) {
600+ if (arg1.empty () || arg2.empty ()) return {};
522601 auto arg1_view = make_ti (arg1);
523602 auto arg2_view = make_ti (arg2);
524603 return arg1_view.mult (arg2_view, factor, perm);
525604}
526605
527606// / result[i] *= arg[i]
607+ // / (TA::Tensor::mult_to: right empty -> result = {}; left empty -> result.)
528608template <typename T, typename Range, typename Storage>
529609inline btas::Tensor<T, Range, Storage>& mult_to (
530610 btas::Tensor<T, Range, Storage>& result,
531611 const btas::Tensor<T, Range, Storage>& arg) {
612+ if (arg.empty ()) {
613+ result = btas::Tensor<T, Range, Storage>{};
614+ return result;
615+ }
616+ if (result.empty ()) return result;
532617 auto result_view = make_ti (result);
533618 auto arg_view = make_ti (arg);
534619 result_view.mult_to (arg_view);
@@ -539,6 +624,11 @@ template <typename T, typename Range, typename Storage>
539624inline btas::Tensor<T, Range, Storage>&& mult_to(
540625 btas::Tensor<T, Range, Storage>&& result,
541626 const btas::Tensor<T, Range, Storage>& arg) {
627+ if (arg.empty ()) {
628+ result = btas::Tensor<T, Range, Storage>{};
629+ return std::move (result);
630+ }
631+ if (result.empty ()) return std::move (result);
542632 auto result_view = make_ti (result);
543633 auto arg_view = make_ti (arg);
544634 result_view.mult_to (arg_view);
@@ -552,6 +642,11 @@ template <typename T, typename Range, typename Storage, typename Scalar,
552642inline btas::Tensor<T, Range, Storage>& mult_to (
553643 btas::Tensor<T, Range, Storage>& result,
554644 const btas::Tensor<T, Range, Storage>& arg, const Scalar factor) {
645+ if (result.empty ()) return result;
646+ if (arg.empty ()) {
647+ result = btas::Tensor<T, Range, Storage>{};
648+ return result;
649+ }
555650 auto result_view = make_ti (result);
556651 auto arg_view = make_ti (arg);
557652 result_view.mult_to (arg_view, factor);
@@ -564,6 +659,11 @@ template <typename T, typename Range, typename Storage, typename Scalar,
564659inline btas::Tensor<T, Range, Storage>&& mult_to(
565660 btas::Tensor<T, Range, Storage>&& result,
566661 const btas::Tensor<T, Range, Storage>& arg, const Scalar factor) {
662+ if (result.empty ()) return std::move (result);
663+ if (arg.empty ()) {
664+ result = btas::Tensor<T, Range, Storage>{};
665+ return std::move (result);
666+ }
567667 auto result_view = make_ti (result);
568668 auto arg_view = make_ti (arg);
569669 result_view.mult_to (arg_view, factor);
@@ -605,6 +705,7 @@ template <typename T, typename Range, typename Storage, typename Scalar,
605705 std::enable_if_t <TiledArray::detail::is_numeric_v<Scalar>>* = nullptr >
606706inline btas::Tensor<T, Range, Storage>& scale_to (
607707 btas::Tensor<T, Range, Storage>& result, const Scalar factor) {
708+ if (result.empty ()) return result;
608709 auto result_view = make_ti (result);
609710 result_view.scale_to (factor);
610711 return result;
@@ -614,6 +715,7 @@ template <typename T, typename Range, typename Storage, typename Scalar,
614715 std::enable_if_t <TiledArray::detail::is_numeric_v<Scalar>>* = nullptr >
615716inline decltype (auto ) scale(const btas::Tensor<T, Range, Storage>& result,
616717 const Scalar factor) {
718+ if (result.empty ()) return btas::Tensor<T, Range, Storage>{};
617719 auto result_view = make_ti (result);
618720 return result_view.scale (factor);
619721}
@@ -625,13 +727,15 @@ template <
625727 TiledArray::detail::is_permutation_v<Perm>>* = nullptr >
626728inline decltype (auto ) scale(const btas::Tensor<T, Range, Storage>& result,
627729 const Scalar factor, const Perm& perm) {
730+ if (result.empty ()) return btas::Tensor<T, Range, Storage>{};
628731 auto result_view = make_ti (result);
629732 return result_view.scale (factor, perm);
630733}
631734
632735template <typename T, typename Range, typename Storage>
633736inline btas::Tensor<T, Range, Storage>& neg_to (
634737 btas::Tensor<T, Range, Storage>& result) {
738+ if (result.empty ()) return result;
635739 auto result_view = make_ti (result);
636740 result_view.neg_to ();
637741 return result;
@@ -640,6 +744,7 @@ inline btas::Tensor<T, Range, Storage>& neg_to(
640744template <typename T, typename Range, typename Storage>
641745inline btas::Tensor<T, Range, Storage>&& neg_to(
642746 btas::Tensor<T, Range, Storage>&& result) {
747+ if (result.empty ()) return std::move (result);
643748 auto result_view = make_ti (result);
644749 result_view.neg_to ();
645750 return std::move (result);
@@ -648,6 +753,7 @@ inline btas::Tensor<T, Range, Storage>&& neg_to(
648753template <typename T, typename Range, typename Storage>
649754inline btas::Tensor<T, Range, Storage> neg (
650755 const btas::Tensor<T, Range, Storage>& arg) {
756+ if (arg.empty ()) return {};
651757 auto arg_view = make_ti (arg);
652758 return arg_view.neg ();
653759}
@@ -657,6 +763,7 @@ template <
657763 typename = std::enable_if_t <TiledArray::detail::is_permutation_v<Perm>>>
658764inline btas::Tensor<T, Range, Storage> neg (
659765 const btas::Tensor<T, Range, Storage>& arg, const Perm& perm) {
766+ if (arg.empty ()) return {};
660767 auto arg_view = make_ti (arg);
661768 return arg_view.neg (perm);
662769}
0 commit comments