Skip to content

Commit f52bee6

Browse files
authored
Merge pull request #580 from ValeevGroup/evaleev/fix/arena-squared-norm-complex
ArenaTensor: squared_norm sums |z|^2, not z^2
2 parents 55795e1 + d05a94f commit f52bee6

2 files changed

Lines changed: 36 additions & 3 deletions

File tree

‎src/TiledArray/tensor/arena_tensor.h‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -539,13 +539,19 @@ void axpy_to(ArenaTensor<T, R>& dst, const ArenaTensor<T, R>& src,
539539
for (std::size_t i = 0; i < dst.size(); ++i) d[i] += alpha * s[i];
540540
}
541541

542-
/// Sum of squared elements; 0 for null views.
542+
/// Sum of squared magnitudes; 0 for null views. Returns the REAL scalar type
543+
/// underlying `T` (`scalar_t<complex<U>>` is `U`), matching
544+
/// `Tensor::squared_norm` and `TensorInterface::squared_norm`. Summing
545+
/// `s[i] * s[i]` instead would compute the complex `sum z^2` rather than
546+
/// `sum |z|^2`.
543547
template <typename T, typename R>
544548
auto squared_norm(const ArenaTensor<T, R>& src) noexcept {
545-
T acc{};
549+
using result_type = TiledArray::detail::scalar_t<T>;
550+
result_type acc{};
546551
if (!src) return acc;
547552
const auto* s = src.data();
548-
for (std::size_t i = 0; i < src.size(); ++i) acc += s[i] * s[i];
553+
for (std::size_t i = 0; i < src.size(); ++i)
554+
acc += TiledArray::detail::squared_norm(s[i]);
549555
return acc;
550556
}
551557

‎tests/arena_tensor.cpp‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <complex>
1616
#include <cstddef>
1717
#include <cstdint>
18+
#include <type_traits>
1819
#include <utility>
1920
#include <vector>
2021

@@ -218,6 +219,32 @@ BOOST_AUTO_TEST_CASE(squared_norm_sums_squares) {
218219
BOOST_CHECK_EQUAL(TA::squared_norm(x), 9.0);
219220
}
220221

222+
// A squared norm is sum |z|^2 -- real and non-negative -- not the complex
223+
// sum z^2. Summing z*z would give (3+4i)^2 + (1-2i)^2 = (-7+24i) + (-3-4i)
224+
// = -10+20i here; the magnitudes give 25 + 5 = 30. The return type is
225+
// likewise the real scalar type, not the element type.
226+
BOOST_AUTO_TEST_CASE(squared_norm_of_complex_sums_magnitudes) {
227+
using Z = std::complex<double>;
228+
using ZInner = TA::ArenaTensor<Z, TA::Range>;
229+
230+
// CellBuf above is specific to `Inner` (double cells); size and align a
231+
// buffer for a complex cell the same way.
232+
const std::size_t total = ZInner::cell_size(2);
233+
const std::size_t algn = ZInner::cell_alignment();
234+
std::vector<std::byte> bytes(total + algn, std::byte{0});
235+
auto base = reinterpret_cast<std::uintptr_t>(bytes.data());
236+
auto* aligned = reinterpret_cast<std::byte*>((base + algn - 1) & ~(algn - 1));
237+
238+
ZInner x = TA::detail::make_arena_tensor_in<Z>(aligned, TA::Range{2});
239+
x.data()[0] = Z(3.0, 4.0);
240+
x.data()[1] = Z(1.0, -2.0);
241+
242+
const auto sq = TA::squared_norm(x);
243+
static_assert(std::is_same_v<std::remove_const_t<decltype(sq)>, double>,
244+
"squared_norm must return the real scalar type");
245+
BOOST_CHECK_CLOSE(sq, 30.0, 1e-12);
246+
}
247+
221248
BOOST_AUTO_TEST_CASE(in_place_cpos_no_op_on_null) {
222249
Inner null;
223250
TA::zero(null);

0 commit comments

Comments
 (0)