diff --git a/include/tensorwrapper/operations/operations.hpp b/include/tensorwrapper/operations/operations.hpp index b913bd97..0b9854a1 100644 --- a/include/tensorwrapper/operations/operations.hpp +++ b/include/tensorwrapper/operations/operations.hpp @@ -17,6 +17,7 @@ #pragma once #include #include +#include /// Namespace for free functions that act on tensors namespace tensorwrapper::operations {} diff --git a/include/tensorwrapper/operations/power.hpp b/include/tensorwrapper/operations/power.hpp new file mode 100644 index 00000000..f59a5721 --- /dev/null +++ b/include/tensorwrapper/operations/power.hpp @@ -0,0 +1,24 @@ +/* + * Copyright 2026 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include + +namespace tensorwrapper::operations { + +/** @brief Appplies a power element-wise to a tensor */ +Tensor power(Tensor A, double pow); +} // namespace tensorwrapper::operations diff --git a/include/tensorwrapper/types/floating_point.hpp b/include/tensorwrapper/types/floating_point.hpp index 72d75637..7e2de193 100644 --- a/include/tensorwrapper/types/floating_point.hpp +++ b/include/tensorwrapper/types/floating_point.hpp @@ -78,6 +78,17 @@ T exp(T value) { } } +template +T pow(T value, double pow) { + if constexpr(is_uncertain_v) { + return sigma::pow(value, pow); + } else if constexpr(is_interval_v) { + return T(sigma::pow(value, pow)); + } else { + return std::pow(value, pow); + } +} + #define TW_APPLY_FLOATING_POINT_TYPES(MACRO_IN) \ MACRO_IN(float); \ MACRO_IN(double); \ @@ -125,6 +136,11 @@ T exp(T value) { return std::exp(value); } +template +T pow(T value, double pow) { + return std::pow(value, pow); +} + #define TW_APPLY_FLOATING_POINT_TYPES(MACRO_IN) \ MACRO_IN(float); \ MACRO_IN(double) diff --git a/include/tensorwrapper/utilities/diagonal_matrix.hpp b/include/tensorwrapper/utilities/diagonal_matrix.hpp new file mode 100644 index 00000000..d205d190 --- /dev/null +++ b/include/tensorwrapper/utilities/diagonal_matrix.hpp @@ -0,0 +1,24 @@ +/* + * Copyright 2026 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include + +namespace tensorwrapper::utilities { + +Tensor diagonal_matrix(const Tensor& diagonal_elements); + +} // namespace tensorwrapper::utilities diff --git a/include/tensorwrapper/utilities/make_tensor.hpp b/include/tensorwrapper/utilities/make_tensor.hpp new file mode 100644 index 00000000..6bb84cbd --- /dev/null +++ b/include/tensorwrapper/utilities/make_tensor.hpp @@ -0,0 +1,37 @@ +/* + * Copyright 2026 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include +#include +namespace tensorwrapper::utilities { + +template +Tensor make_tensor(std::vector shape, BeginIterator begin, + EndIterator end) { + shape::Smooth smooth_shape(shape.begin(), shape.end()); + std::vector data(begin, end); + buffer::Contiguous buffer(data, smooth_shape); + return Tensor(std::move(smooth_shape), std::move(buffer)); +} + +template +Tensor make_tensor(std::initializer_list shape, + ContainerType&& value) { + return make_tensor(shape, value.begin(), value.end()); +} + +} // namespace tensorwrapper::utilities diff --git a/include/tensorwrapper/utilities/utilities.hpp b/include/tensorwrapper/utilities/utilities.hpp index 39632702..90143068 100644 --- a/include/tensorwrapper/utilities/utilities.hpp +++ b/include/tensorwrapper/utilities/utilities.hpp @@ -16,6 +16,8 @@ #pragma once #include +#include +#include #include /// Namespace for helper functions diff --git a/src/tensorwrapper/operations/power.cpp b/src/tensorwrapper/operations/power.cpp new file mode 100644 index 00000000..d5482026 --- /dev/null +++ b/src/tensorwrapper/operations/power.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2026 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +namespace tensorwrapper::operations { +namespace { + +class PowerKernel { +public: + PowerKernel(double pow) : m_pow_(pow) {} + + template + void operator()(std::span A) const { + if constexpr(std::is_const_v) { + // This path is only for the compiler, we won't actually get to + // it. + throw std::runtime_error("Can't modify const data"); + } else { + for(auto& a : A) { a = types::pow(a, m_pow_); } + } + } + +private: + double m_pow_; +}; +} // namespace + +Tensor power(Tensor A, double pow) { + PowerKernel kernel(pow); + auto& buffer = make_contiguous(A.buffer()); + buffer::visit_contiguous_buffer(kernel, buffer); + return A; +} +} // namespace tensorwrapper::operations diff --git a/src/tensorwrapper/utilities/diagonal_matrix.cpp b/src/tensorwrapper/utilities/diagonal_matrix.cpp new file mode 100644 index 00000000..775d00f5 --- /dev/null +++ b/src/tensorwrapper/utilities/diagonal_matrix.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 2026 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +namespace tensorwrapper::utilities { +namespace { +struct Kernel { + template + auto operator()(const std::span& diagonal_elements) { + using clean_type = std::decay_t; + const auto n = diagonal_elements.size(); + shape::Smooth new_shape{n, n}; + std::vector data(n * n, 0); + for(std::size_t i = 0; i < n; ++i) { + data[i * n + i] = diagonal_elements[i]; + } + buffer::Contiguous buffer(data, new_shape); + return Tensor(std::move(new_shape), std::move(buffer)); + } +}; +} // namespace + +Tensor diagonal_matrix(const Tensor& diagonal_elements) { + if(diagonal_elements.rank() != 1) { + throw std::runtime_error("Diagonal elements must be a vector"); + } + Kernel k; + auto& buffer = make_contiguous(diagonal_elements.buffer()); + return buffer::visit_contiguous_buffer(k, buffer); +} + +} // namespace tensorwrapper::utilities diff --git a/tests/cxx/unit_tests/tensorwrapper/operations/power.cpp b/tests/cxx/unit_tests/tensorwrapper/operations/power.cpp new file mode 100644 index 00000000..d89750c8 --- /dev/null +++ b/tests/cxx/unit_tests/tensorwrapper/operations/power.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 2026 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +using namespace tensorwrapper; +using namespace tensorwrapper::operations; +using namespace tensorwrapper::utilities; + +TEMPLATE_LIST_TEST_CASE("power", "", types::floating_point_types) { + SECTION("scalar") { + shape::Smooth s{}; + Tensor scalar(s, testing::eigen_scalar()); + auto rv = power(scalar, 2); + REQUIRE(approximately_equal( + rv, Tensor(s, testing::eigen_scalar(42 * 42)))); + } + + SECTION("vector") { + shape::Smooth s{5}; + Tensor vector(s, testing::eigen_vector()); + auto rv = power(vector, 0.5); + TestType sqrt2 = std::sqrt(2); + TestType sqrt3 = std::sqrt(3); + std::vector data{0, 1, sqrt2, sqrt3, 2}; + auto corr = make_tensor({5}, data.begin(), data.end()); + REQUIRE(approximately_equal(rv, corr)); + } +} diff --git a/tests/cxx/unit_tests/tensorwrapper/utilities/diagonal_matrix.cpp b/tests/cxx/unit_tests/tensorwrapper/utilities/diagonal_matrix.cpp new file mode 100644 index 00000000..69ece93f --- /dev/null +++ b/tests/cxx/unit_tests/tensorwrapper/utilities/diagonal_matrix.cpp @@ -0,0 +1,33 @@ +/* + * Copyright 2026 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +using namespace tensorwrapper; +using namespace tensorwrapper::operations; +using namespace tensorwrapper::utilities; +using namespace testing; + +TEMPLATE_LIST_TEST_CASE("diagonal_matrix", "", types::floating_point_types) { + auto diagonal_values = make_tensor({3}, std::vector{1, 2, 3}); + auto corr = + make_tensor({3, 3}, std::vector{1, 0, 0, 0, 2, 0, 0, 0, 3}); + auto result = diagonal_matrix(diagonal_values); + REQUIRE(approximately_equal(result, corr)); +} diff --git a/tests/cxx/unit_tests/tensorwrapper/utilities/make_tensor.cpp b/tests/cxx/unit_tests/tensorwrapper/utilities/make_tensor.cpp new file mode 100644 index 00000000..3d63f252 --- /dev/null +++ b/tests/cxx/unit_tests/tensorwrapper/utilities/make_tensor.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2026 NWChemEx-Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +using namespace tensorwrapper; +using namespace tensorwrapper::operations; +using namespace tensorwrapper::utilities; + +TEMPLATE_LIST_TEST_CASE("make_tensor", "", types::floating_point_types) { + SECTION("scalar") { + std::vector data{42}; + auto tensor = make_tensor({}, data.begin(), data.end()); + auto tensor2 = make_tensor({}, data); + Tensor corr(shape::Smooth{}, testing::eigen_scalar(42)); + REQUIRE(approximately_equal(tensor, corr)); + REQUIRE(approximately_equal(tensor2, corr)); + } + + SECTION("vector") { + std::vector data{0, 1, 2, 3, 4}; + auto tensor = make_tensor({5}, data.begin(), data.end()); + auto tensor2 = make_tensor({5}, data); + Tensor corr(shape::Smooth{5}, testing::eigen_vector()); + REQUIRE(approximately_equal(tensor, corr)); + REQUIRE(approximately_equal(tensor2, corr)); + } + + SECTION("matrix") { + std::vector data{1, 2, 3, 4}; + auto tensor = make_tensor({2, 2}, data.begin(), data.end()); + auto tensor2 = make_tensor({2, 2}, data); + Tensor corr(shape::Smooth{2, 2}, testing::eigen_matrix()); + REQUIRE(approximately_equal(tensor, corr)); + REQUIRE(approximately_equal(tensor2, corr)); + } + SECTION("tensor3") { + std::vector data{1, 2, 3, 4, 5, 6, 7, 8}; + auto tensor = make_tensor({2, 2, 2}, data.begin(), data.end()); + auto tensor2 = make_tensor({2, 2, 2}, data); + Tensor corr(shape::Smooth{2, 2, 2}, testing::eigen_tensor3()); + REQUIRE(approximately_equal(tensor, corr)); + REQUIRE(approximately_equal(tensor2, corr)); + } + SECTION("tensor4") { + std::vector data{1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16}; + auto tensor = make_tensor({2, 2, 2, 2}, data.begin(), data.end()); + auto tensor2 = make_tensor({2, 2, 2, 2}, data); + Tensor corr(shape::Smooth{2, 2, 2, 2}, + testing::eigen_tensor4()); + REQUIRE(approximately_equal(tensor, corr)); + REQUIRE(approximately_equal(tensor2, corr)); + } +}